summaryrefslogtreecommitdiff
path: root/cpp/src
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src')
-rw-r--r--cpp/src/CMakeLists.txt200
-rw-r--r--cpp/src/Makefile.am158
-rw-r--r--cpp/src/acl.mk4
-rw-r--r--cpp/src/amqp.cmake127
-rw-r--r--cpp/src/asyncstore.cmake1
-rw-r--r--cpp/src/config.h.cmake2
-rw-r--r--cpp/src/finddb.cmake74
-rwxr-xr-xcpp/src/generate.sh67
-rw-r--r--cpp/src/ha.mk5
-rw-r--r--cpp/src/legacystore.cmake157
-rw-r--r--cpp/src/posix/QpiddBroker.cpp20
-rw-r--r--cpp/src/qpid/Modules.cpp49
-rw-r--r--cpp/src/qpid/Modules.h2
-rw-r--r--cpp/src/qpid/NullSaslServer.cpp86
-rw-r--r--cpp/src/qpid/NullSaslServer.h49
-rw-r--r--cpp/src/qpid/Options.cpp96
-rw-r--r--cpp/src/qpid/Sasl.h2
-rw-r--r--cpp/src/qpid/SaslFactory.cpp213
-rw-r--r--cpp/src/qpid/SaslFactory.h3
-rw-r--r--cpp/src/qpid/SaslServer.h48
-rw-r--r--cpp/src/qpid/Url.cpp13
-rw-r--r--cpp/src/qpid/UrlArray.h6
-rw-r--r--cpp/src/qpid/acl/Acl.cpp62
-rw-r--r--cpp/src/qpid/acl/Acl.h17
-rw-r--r--cpp/src/qpid/acl/AclConnectionCounter.cpp212
-rw-r--r--cpp/src/qpid/acl/AclConnectionCounter.h11
-rw-r--r--cpp/src/qpid/acl/AclData.cpp146
-rw-r--r--cpp/src/qpid/acl/AclData.h46
-rw-r--r--cpp/src/qpid/acl/AclPlugin.cpp5
-rw-r--r--cpp/src/qpid/acl/AclReader.cpp168
-rw-r--r--cpp/src/qpid/acl/AclReader.h12
-rw-r--r--cpp/src/qpid/acl/AclResourceCounter.cpp165
-rw-r--r--cpp/src/qpid/acl/AclResourceCounter.h78
-rw-r--r--cpp/src/qpid/acl/AclTopicMatch.h2
-rw-r--r--cpp/src/qpid/acl/AclValidator.cpp16
-rw-r--r--cpp/src/qpid/acl/management-schema.xml4
-rw-r--r--cpp/src/qpid/agent/ManagementAgentImpl.cpp42
-rw-r--r--cpp/src/qpid/agent/ManagementAgentImpl.h6
-rw-r--r--cpp/src/qpid/amqp/CharSequence.cpp47
-rw-r--r--cpp/src/qpid/amqp/CharSequence.h49
-rw-r--r--cpp/src/qpid/amqp/Codec.h83
-rw-r--r--cpp/src/qpid/amqp/Constructor.h42
-rw-r--r--cpp/src/qpid/amqp/Decoder.cpp545
-rw-r--r--cpp/src/qpid/amqp/Decoder.h99
-rw-r--r--cpp/src/qpid/amqp/Descriptor.cpp52
-rw-r--r--cpp/src/qpid/amqp/Descriptor.h54
-rw-r--r--cpp/src/qpid/amqp/Encoder.cpp402
-rw-r--r--cpp/src/qpid/amqp/Encoder.h149
-rw-r--r--cpp/src/qpid/amqp/ListReader.h103
-rw-r--r--cpp/src/qpid/amqp/LoggingReader.h64
-rw-r--r--cpp/src/qpid/amqp/MapReader.cpp289
-rw-r--r--cpp/src/qpid/amqp/MapReader.h104
-rw-r--r--cpp/src/qpid/amqp/MessageEncoder.cpp313
-rw-r--r--cpp/src/qpid/amqp/MessageEncoder.h100
-rw-r--r--cpp/src/qpid/amqp/MessageId.cpp69
-rw-r--r--cpp/src/qpid/amqp/MessageId.h54
-rw-r--r--cpp/src/qpid/amqp/MessageReader.cpp759
-rw-r--r--cpp/src/qpid/amqp/MessageReader.h301
-rw-r--r--cpp/src/qpid/amqp/Reader.h80
-rw-r--r--cpp/src/qpid/amqp/Sasl.cpp136
-rw-r--r--cpp/src/qpid/amqp/Sasl.h54
-rw-r--r--cpp/src/qpid/amqp/SaslClient.cpp154
-rw-r--r--cpp/src/qpid/amqp/SaslClient.h54
-rw-r--r--cpp/src/qpid/amqp/SaslServer.cpp183
-rw-r--r--cpp/src/qpid/amqp/SaslServer.h50
-rw-r--r--cpp/src/qpid/amqp/descriptors.h88
-rw-r--r--cpp/src/qpid/amqp/typecodes.h115
-rw-r--r--cpp/src/qpid/amqp_0_10/Connection.cpp9
-rw-r--r--cpp/src/qpid/amqp_0_10/Connection.h7
-rw-r--r--cpp/src/qpid/asyncStore/OperationQueue.cpp2
-rw-r--r--cpp/src/qpid/asyncStore/Plugin.cpp9
-rw-r--r--cpp/src/qpid/broker/AclModule.h27
-rw-r--r--cpp/src/qpid/broker/Bridge.cpp65
-rw-r--r--cpp/src/qpid/broker/Bridge.h5
-rw-r--r--cpp/src/qpid/broker/Broker.cpp286
-rw-r--r--cpp/src/qpid/broker/Broker.h99
-rw-r--r--cpp/src/qpid/broker/ConfigurationObserver.h4
-rw-r--r--cpp/src/qpid/broker/Connection.cpp78
-rw-r--r--cpp/src/qpid/broker/Connection.h59
-rw-r--r--cpp/src/qpid/broker/ConnectionFactory.cpp61
-rw-r--r--cpp/src/qpid/broker/ConnectionHandler.cpp50
-rw-r--r--cpp/src/qpid/broker/ConnectionState.cpp (renamed from cpp/src/qpid/sys/alloca.h)36
-rw-r--r--cpp/src/qpid/broker/ConnectionState.h32
-rw-r--r--cpp/src/qpid/broker/Consumer.h9
-rw-r--r--cpp/src/qpid/broker/ConsumerFactory.h7
-rw-r--r--cpp/src/qpid/broker/DeliverableMessage.cpp5
-rw-r--r--cpp/src/qpid/broker/DeliverableMessage.h1
-rw-r--r--cpp/src/qpid/broker/DeliveryAdapter.h0
-rw-r--r--cpp/src/qpid/broker/DirectExchange.cpp2
-rw-r--r--cpp/src/qpid/broker/DtxManager.cpp41
-rw-r--r--cpp/src/qpid/broker/DtxManager.h18
-rw-r--r--cpp/src/qpid/broker/DtxWorkRecord.cpp23
-rw-r--r--cpp/src/qpid/broker/DtxWorkRecord.h13
-rw-r--r--cpp/src/qpid/broker/Exchange.cpp42
-rw-r--r--cpp/src/qpid/broker/Exchange.h13
-rw-r--r--cpp/src/qpid/broker/ExchangeRegistry.cpp44
-rw-r--r--cpp/src/qpid/broker/ExchangeRegistry.h25
-rw-r--r--cpp/src/qpid/broker/FanOutExchange.cpp2
-rw-r--r--cpp/src/qpid/broker/HeadersExchange.cpp14
-rw-r--r--cpp/src/qpid/broker/HeadersExchange.h3
-rw-r--r--cpp/src/qpid/broker/LegacyLVQ.cpp0
-rw-r--r--cpp/src/qpid/broker/LegacyLVQ.h0
-rw-r--r--cpp/src/qpid/broker/Link.cpp259
-rw-r--r--cpp/src/qpid/broker/Link.h20
-rw-r--r--cpp/src/qpid/broker/LinkRegistry.cpp94
-rw-r--r--cpp/src/qpid/broker/LinkRegistry.h15
-rw-r--r--cpp/src/qpid/broker/Lvq.cpp2
-rw-r--r--cpp/src/qpid/broker/Message.cpp12
-rw-r--r--cpp/src/qpid/broker/Message.h4
-rw-r--r--cpp/src/qpid/broker/MessageGroupManager.cpp110
-rw-r--r--cpp/src/qpid/broker/MessageGroupManager.h16
-rw-r--r--cpp/src/qpid/broker/MessageStore.h14
-rw-r--r--cpp/src/qpid/broker/MessageStoreModule.cpp5
-rw-r--r--cpp/src/qpid/broker/MessageStoreModule.h1
-rw-r--r--cpp/src/qpid/broker/Messages.h7
-rw-r--r--cpp/src/qpid/broker/NullMessageStore.cpp2
-rw-r--r--cpp/src/qpid/broker/NullMessageStore.h1
-rw-r--r--cpp/src/qpid/broker/Protocol.cpp70
-rw-r--r--cpp/src/qpid/broker/Protocol.h82
-rw-r--r--cpp/src/qpid/broker/Queue.cpp145
-rw-r--r--cpp/src/qpid/broker/Queue.h47
-rw-r--r--cpp/src/qpid/broker/QueueCleaner.cpp33
-rw-r--r--cpp/src/qpid/broker/QueueCleaner.h20
-rw-r--r--cpp/src/qpid/broker/QueueFlowLimit.cpp87
-rw-r--r--cpp/src/qpid/broker/QueueFlowLimit.h19
-rw-r--r--cpp/src/qpid/broker/QueueObserver.h1
-rw-r--r--cpp/src/qpid/broker/QueueRegistry.cpp42
-rw-r--r--cpp/src/qpid/broker/QueueRegistry.h10
-rw-r--r--cpp/src/qpid/broker/QueueSettings.cpp14
-rw-r--r--cpp/src/qpid/broker/QueueSettings.h6
-rw-r--r--cpp/src/qpid/broker/RecoverableExchange.h3
-rw-r--r--cpp/src/qpid/broker/RecoverableMessage.h3
-rw-r--r--cpp/src/qpid/broker/RecoverableMessageImpl.h49
-rw-r--r--cpp/src/qpid/broker/RecoveryManagerImpl.cpp40
-rw-r--r--cpp/src/qpid/broker/RecoveryManagerImpl.h4
-rw-r--r--cpp/src/qpid/broker/SaslAuthenticator.cpp17
-rw-r--r--cpp/src/qpid/broker/SecureConnection.cpp2
-rw-r--r--cpp/src/qpid/broker/SecureConnection.h2
-rw-r--r--cpp/src/qpid/broker/SecureConnectionFactory.cpp14
-rw-r--r--cpp/src/qpid/broker/SemanticState.cpp167
-rw-r--r--cpp/src/qpid/broker/SemanticState.h217
-rw-r--r--cpp/src/qpid/broker/SessionAdapter.cpp28
-rw-r--r--cpp/src/qpid/broker/SessionHandler.cpp12
-rw-r--r--cpp/src/qpid/broker/SessionHandler.h12
-rw-r--r--cpp/src/qpid/broker/SessionState.cpp32
-rw-r--r--cpp/src/qpid/broker/SessionState.h22
-rw-r--r--cpp/src/qpid/broker/StatefulQueueObserver.h63
-rw-r--r--cpp/src/qpid/broker/System.cpp4
-rw-r--r--cpp/src/qpid/broker/System.h4
-rw-r--r--cpp/src/qpid/broker/TopicExchange.cpp2
-rw-r--r--cpp/src/qpid/broker/TxAccept.h3
-rw-r--r--cpp/src/qpid/broker/TxOpVisitor.h0
-rw-r--r--cpp/src/qpid/broker/TxPublish.cpp0
-rw-r--r--cpp/src/qpid/broker/TxPublish.h0
-rw-r--r--cpp/src/qpid/broker/Vhost.cpp4
-rw-r--r--cpp/src/qpid/broker/Vhost.h4
-rw-r--r--cpp/src/qpid/broker/amqp/Connection.cpp247
-rw-r--r--cpp/src/qpid/broker/amqp/Connection.h73
-rw-r--r--cpp/src/qpid/broker/amqp/DataReader.cpp187
-rw-r--r--cpp/src/qpid/broker/amqp/DataReader.h53
-rw-r--r--cpp/src/qpid/broker/amqp/Filter.cpp150
-rw-r--r--cpp/src/qpid/broker/amqp/Filter.h63
-rw-r--r--cpp/src/qpid/broker/amqp/Header.cpp65
-rw-r--r--cpp/src/qpid/broker/amqp/Header.h50
-rw-r--r--cpp/src/qpid/broker/amqp/ManagedConnection.cpp98
-rw-r--r--cpp/src/qpid/broker/amqp/ManagedConnection.h59
-rw-r--r--cpp/src/qpid/broker/amqp/ManagedOutgoingLink.cpp70
-rw-r--r--cpp/src/qpid/broker/amqp/ManagedOutgoingLink.h53
-rw-r--r--cpp/src/qpid/broker/amqp/ManagedSession.cpp88
-rw-r--r--cpp/src/qpid/broker/amqp/ManagedSession.h59
-rw-r--r--cpp/src/qpid/broker/amqp/Message.cpp264
-rw-r--r--cpp/src/qpid/broker/amqp/Message.h148
-rw-r--r--cpp/src/qpid/broker/amqp/NodeProperties.cpp179
-rw-r--r--cpp/src/qpid/broker/amqp/NodeProperties.h71
-rw-r--r--cpp/src/qpid/broker/amqp/Outgoing.cpp244
-rw-r--r--cpp/src/qpid/broker/amqp/Outgoing.h108
-rw-r--r--cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp117
-rw-r--r--cpp/src/qpid/broker/amqp/Sasl.cpp167
-rw-r--r--cpp/src/qpid/broker/amqp/Sasl.h72
-rw-r--r--cpp/src/qpid/broker/amqp/Session.cpp332
-rw-r--r--cpp/src/qpid/broker/amqp/Session.h87
-rw-r--r--cpp/src/qpid/broker/amqp/Translation.cpp241
-rw-r--r--cpp/src/qpid/broker/amqp/Translation.h58
-rw-r--r--cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp13
-rw-r--r--cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h1
-rw-r--r--cpp/src/qpid/broker/windows/SaslAuthenticator.cpp1
-rw-r--r--cpp/src/qpid/broker/windows/SslProtocolFactory.cpp729
-rw-r--r--cpp/src/qpid/client/ConnectionHandler.cpp8
-rw-r--r--cpp/src/qpid/client/ConnectionImpl.cpp20
-rw-r--r--cpp/src/qpid/client/FailoverManager.cpp3
-rw-r--r--cpp/src/qpid/client/LoadPlugins.cpp2
-rw-r--r--cpp/src/qpid/client/LoadPlugins.h4
-rw-r--r--cpp/src/qpid/client/QueueOptions.cpp4
-rw-r--r--cpp/src/qpid/client/RdmaConnector.cpp6
-rw-r--r--cpp/src/qpid/client/SessionImpl.cpp108
-rw-r--r--cpp/src/qpid/client/SessionImpl.h19
-rw-r--r--cpp/src/qpid/client/SslConnector.cpp106
-rw-r--r--cpp/src/qpid/client/TCPConnector.cpp25
-rw-r--r--cpp/src/qpid/client/TCPConnector.h7
-rw-r--r--cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp3
-rw-r--r--cpp/src/qpid/client/windows/ClientDllMain.cpp22
-rw-r--r--cpp/src/qpid/client/windows/SslConnector.cpp20
-rw-r--r--cpp/src/qpid/framing/FieldValue.cpp14
-rw-r--r--cpp/src/qpid/framing/FrameSet.h8
-rw-r--r--cpp/src/qpid/framing/Handler.h43
-rw-r--r--cpp/src/qpid/framing/ProtocolInitiation.cpp31
-rw-r--r--cpp/src/qpid/framing/ProtocolVersion.cpp8
-rw-r--r--cpp/src/qpid/ha/AlternateExchangeSetter.h4
-rw-r--r--cpp/src/qpid/ha/Backup.cpp106
-rw-r--r--cpp/src/qpid/ha/Backup.h20
-rw-r--r--cpp/src/qpid/ha/BrokerInfo.cpp24
-rw-r--r--cpp/src/qpid/ha/BrokerInfo.h8
-rw-r--r--cpp/src/qpid/ha/BrokerReplicator.cpp606
-rw-r--r--cpp/src/qpid/ha/BrokerReplicator.h52
-rw-r--r--cpp/src/qpid/ha/ConnectionObserver.cpp6
-rw-r--r--cpp/src/qpid/ha/ConnectionObserver.h2
-rw-r--r--cpp/src/qpid/ha/HaBroker.cpp252
-rw-r--r--cpp/src/qpid/ha/HaBroker.h62
-rw-r--r--cpp/src/qpid/ha/HaPlugin.cpp26
-rw-r--r--cpp/src/qpid/ha/Membership.cpp113
-rw-r--r--cpp/src/qpid/ha/Membership.h45
-rw-r--r--cpp/src/qpid/ha/Primary.cpp122
-rw-r--r--cpp/src/qpid/ha/Primary.h23
-rw-r--r--cpp/src/qpid/ha/QueueGuard.cpp94
-rw-r--r--cpp/src/qpid/ha/QueueGuard.h12
-rw-r--r--cpp/src/qpid/ha/QueueRange.h11
-rw-r--r--cpp/src/qpid/ha/QueueReplicator.cpp161
-rw-r--r--cpp/src/qpid/ha/QueueReplicator.h18
-rw-r--r--cpp/src/qpid/ha/RemoteBackup.cpp53
-rw-r--r--cpp/src/qpid/ha/RemoteBackup.h22
-rw-r--r--cpp/src/qpid/ha/ReplicatingSubscription.cpp57
-rw-r--r--cpp/src/qpid/ha/ReplicatingSubscription.h17
-rw-r--r--cpp/src/qpid/ha/ReplicationTest.cpp39
-rw-r--r--cpp/src/qpid/ha/ReplicationTest.h26
-rw-r--r--cpp/src/qpid/ha/Role.h55
-rw-r--r--cpp/src/qpid/ha/Settings.h16
-rw-r--r--cpp/src/qpid/ha/StandAlone.h (renamed from cpp/src/qpid/broker/ConnectionFactory.h)44
-rw-r--r--cpp/src/qpid/ha/StatusCheck.cpp132
-rw-r--r--cpp/src/qpid/ha/StatusCheck.h71
-rw-r--r--cpp/src/qpid/ha/types.cpp6
-rw-r--r--cpp/src/qpid/ha/types.h1
-rw-r--r--cpp/src/qpid/legacystore/BindingDbt.cpp50
-rw-r--r--cpp/src/qpid/legacystore/BindingDbt.h56
-rw-r--r--cpp/src/qpid/legacystore/BufferValue.cpp56
-rw-r--r--cpp/src/qpid/legacystore/BufferValue.h46
-rw-r--r--cpp/src/qpid/legacystore/Cursor.h50
-rw-r--r--cpp/src/qpid/legacystore/DataTokenImpl.cpp28
-rw-r--r--cpp/src/qpid/legacystore/DataTokenImpl.h47
-rw-r--r--cpp/src/qpid/legacystore/IdDbt.cpp42
-rw-r--r--cpp/src/qpid/legacystore/IdDbt.h42
-rw-r--r--cpp/src/qpid/legacystore/IdSequence.cpp40
-rw-r--r--cpp/src/qpid/legacystore/IdSequence.h44
-rw-r--r--cpp/src/qpid/legacystore/JournalImpl.cpp633
-rw-r--r--cpp/src/qpid/legacystore/JournalImpl.h265
-rw-r--r--cpp/src/qpid/legacystore/MessageStoreImpl.cpp1732
-rw-r--r--cpp/src/qpid/legacystore/MessageStoreImpl.h380
-rw-r--r--cpp/src/qpid/legacystore/PreparedTransaction.cpp81
-rw-r--r--cpp/src/qpid/legacystore/PreparedTransaction.h74
-rw-r--r--cpp/src/qpid/legacystore/StoreException.h56
-rw-r--r--cpp/src/qpid/legacystore/StorePlugin.cpp81
-rw-r--r--cpp/src/qpid/legacystore/TxnCtxt.cpp184
-rw-r--r--cpp/src/qpid/legacystore/TxnCtxt.h117
-rw-r--r--cpp/src/qpid/legacystore/jrnl/aio.cpp41
-rw-r--r--cpp/src/qpid/legacystore/jrnl/aio.h153
-rw-r--r--cpp/src/qpid/legacystore/jrnl/aio_callback.h57
-rw-r--r--cpp/src/qpid/legacystore/jrnl/cvar.cpp33
-rw-r--r--cpp/src/qpid/legacystore/jrnl/cvar.h87
-rw-r--r--cpp/src/qpid/legacystore/jrnl/data_tok.cpp194
-rw-r--r--cpp/src/qpid/legacystore/jrnl/data_tok.h172
-rw-r--r--cpp/src/qpid/legacystore/jrnl/deq_hdr.h141
-rw-r--r--cpp/src/qpid/legacystore/jrnl/deq_rec.cpp459
-rw-r--r--cpp/src/qpid/legacystore/jrnl/deq_rec.h103
-rw-r--r--cpp/src/qpid/legacystore/jrnl/enq_hdr.h165
-rw-r--r--cpp/src/qpid/legacystore/jrnl/enq_map.cpp183
-rw-r--r--cpp/src/qpid/legacystore/jrnl/enq_map.h127
-rw-r--r--cpp/src/qpid/legacystore/jrnl/enq_rec.cpp638
-rw-r--r--cpp/src/qpid/legacystore/jrnl/enq_rec.h116
-rw-r--r--cpp/src/qpid/legacystore/jrnl/enums.h108
-rw-r--r--cpp/src/qpid/legacystore/jrnl/fcntl.cpp375
-rw-r--r--cpp/src/qpid/legacystore/jrnl/fcntl.h156
-rw-r--r--cpp/src/qpid/legacystore/jrnl/file_hdr.h211
-rw-r--r--cpp/src/qpid/legacystore/jrnl/jcfg.h91
-rw-r--r--cpp/src/qpid/legacystore/jrnl/jcntl.cpp984
-rw-r--r--cpp/src/qpid/legacystore/jrnl/jcntl.h722
-rw-r--r--cpp/src/qpid/legacystore/jrnl/jdir.cpp463
-rw-r--r--cpp/src/qpid/legacystore/jrnl/jdir.h379
-rw-r--r--cpp/src/qpid/legacystore/jrnl/jerrno.cpp253
-rw-r--r--cpp/src/qpid/legacystore/jrnl/jerrno.h173
-rw-r--r--cpp/src/qpid/legacystore/jrnl/jexception.cpp183
-rw-r--r--cpp/src/qpid/legacystore/jrnl/jexception.h142
-rw-r--r--cpp/src/qpid/legacystore/jrnl/jinf.cpp540
-rw-r--r--cpp/src/qpid/legacystore/jrnl/jinf.h133
-rw-r--r--cpp/src/qpid/legacystore/jrnl/jrec.cpp119
-rw-r--r--cpp/src/qpid/legacystore/jrnl/jrec.h183
-rw-r--r--cpp/src/qpid/legacystore/jrnl/lp_map.cpp82
-rw-r--r--cpp/src/qpid/legacystore/jrnl/lp_map.h83
-rw-r--r--cpp/src/qpid/legacystore/jrnl/lpmgr.cpp226
-rw-r--r--cpp/src/qpid/legacystore/jrnl/lpmgr.h303
-rw-r--r--cpp/src/qpid/legacystore/jrnl/pmgr.cpp215
-rw-r--r--cpp/src/qpid/legacystore/jrnl/pmgr.h142
-rw-r--r--cpp/src/qpid/legacystore/jrnl/rcvdat.h181
-rw-r--r--cpp/src/qpid/legacystore/jrnl/rec_hdr.h143
-rw-r--r--cpp/src/qpid/legacystore/jrnl/rec_tail.h98
-rw-r--r--cpp/src/qpid/legacystore/jrnl/rfc.cpp82
-rw-r--r--cpp/src/qpid/legacystore/jrnl/rfc.h193
-rw-r--r--cpp/src/qpid/legacystore/jrnl/rmgr.cpp698
-rw-r--r--cpp/src/qpid/legacystore/jrnl/rmgr.h114
-rw-r--r--cpp/src/qpid/legacystore/jrnl/rrfc.cpp125
-rw-r--r--cpp/src/qpid/legacystore/jrnl/rrfc.h179
-rw-r--r--cpp/src/qpid/legacystore/jrnl/slock.cpp33
-rw-r--r--cpp/src/qpid/legacystore/jrnl/slock.h85
-rw-r--r--cpp/src/qpid/legacystore/jrnl/smutex.cpp33
-rw-r--r--cpp/src/qpid/legacystore/jrnl/smutex.h64
-rw-r--r--cpp/src/qpid/legacystore/jrnl/time_ns.cpp55
-rw-r--r--cpp/src/qpid/legacystore/jrnl/time_ns.h105
-rw-r--r--cpp/src/qpid/legacystore/jrnl/txn_hdr.h125
-rw-r--r--cpp/src/qpid/legacystore/jrnl/txn_map.cpp256
-rw-r--r--cpp/src/qpid/legacystore/jrnl/txn_map.h159
-rw-r--r--cpp/src/qpid/legacystore/jrnl/txn_rec.cpp447
-rw-r--r--cpp/src/qpid/legacystore/jrnl/txn_rec.h101
-rw-r--r--cpp/src/qpid/legacystore/jrnl/wmgr.cpp1051
-rw-r--r--cpp/src/qpid/legacystore/jrnl/wmgr.h147
-rw-r--r--cpp/src/qpid/legacystore/jrnl/wrfc.cpp162
-rw-r--r--cpp/src/qpid/legacystore/jrnl/wrfc.h154
-rw-r--r--cpp/src/qpid/legacystore/management-schema.xml99
-rw-r--r--cpp/src/qpid/log/Logger.cpp16
-rw-r--r--cpp/src/qpid/management/ManagementAgent.cpp1110
-rw-r--r--cpp/src/qpid/management/ManagementAgent.h183
-rw-r--r--cpp/src/qpid/messaging/Connection.cpp27
-rw-r--r--cpp/src/qpid/messaging/ConnectionOptions.cpp121
-rw-r--r--cpp/src/qpid/messaging/ConnectionOptions.h51
-rw-r--r--cpp/src/qpid/messaging/Message.cpp28
-rw-r--r--cpp/src/qpid/messaging/MessageImpl.cpp161
-rw-r--r--cpp/src/qpid/messaging/MessageImpl.h53
-rw-r--r--cpp/src/qpid/messaging/ProtocolRegistry.cpp73
-rw-r--r--cpp/src/qpid/messaging/ProtocolRegistry.h42
-rw-r--r--cpp/src/qpid/messaging/ReceiverImpl.h2
-rw-r--r--cpp/src/qpid/messaging/SenderImpl.h1
-rw-r--r--cpp/src/qpid/messaging/amqp/AddressHelper.cpp182
-rw-r--r--cpp/src/qpid/messaging/amqp/AddressHelper.h57
-rw-r--r--cpp/src/qpid/messaging/amqp/ConnectionContext.cpp612
-rw-r--r--cpp/src/qpid/messaging/amqp/ConnectionContext.h150
-rw-r--r--cpp/src/qpid/messaging/amqp/ConnectionHandle.cpp84
-rw-r--r--cpp/src/qpid/messaging/amqp/ConnectionHandle.h58
-rw-r--r--cpp/src/qpid/messaging/amqp/DriverImpl.cpp74
-rw-r--r--cpp/src/qpid/messaging/amqp/DriverImpl.h (renamed from cpp/src/qpid/sys/ClusterSafe.cpp)76
-rw-r--r--cpp/src/qpid/messaging/amqp/EncodedMessage.cpp263
-rw-r--r--cpp/src/qpid/messaging/amqp/EncodedMessage.h177
-rw-r--r--cpp/src/qpid/messaging/amqp/ReceiverContext.cpp146
-rw-r--r--cpp/src/qpid/messaging/amqp/ReceiverContext.h68
-rw-r--r--cpp/src/qpid/messaging/amqp/ReceiverHandle.cpp106
-rw-r--r--cpp/src/qpid/messaging/amqp/ReceiverHandle.h63
-rw-r--r--cpp/src/qpid/messaging/amqp/Sasl.cpp157
-rw-r--r--cpp/src/qpid/messaging/amqp/Sasl.h72
-rw-r--r--cpp/src/qpid/messaging/amqp/SenderContext.cpp363
-rw-r--r--cpp/src/qpid/messaging/amqp/SenderContext.h90
-rw-r--r--cpp/src/qpid/messaging/amqp/SenderHandle.cpp75
-rw-r--r--cpp/src/qpid/messaging/amqp/SenderHandle.h58
-rw-r--r--cpp/src/qpid/messaging/amqp/SessionContext.cpp156
-rw-r--r--cpp/src/qpid/messaging/amqp/SessionContext.h81
-rw-r--r--cpp/src/qpid/messaging/amqp/SessionHandle.cpp148
-rw-r--r--cpp/src/qpid/messaging/amqp/SessionHandle.h64
-rw-r--r--cpp/src/qpid/messaging/amqp/SslTransport.cpp160
-rw-r--r--cpp/src/qpid/messaging/amqp/SslTransport.h74
-rw-r--r--cpp/src/qpid/messaging/amqp/TcpTransport.cpp162
-rw-r--r--cpp/src/qpid/messaging/amqp/TcpTransport.h71
-rw-r--r--cpp/src/qpid/messaging/amqp/Transport.cpp50
-rw-r--r--cpp/src/qpid/messaging/amqp/Transport.h48
-rw-r--r--cpp/src/qpid/messaging/amqp/TransportContext.h47
-rw-r--r--cpp/src/qpid/store/CMakeLists.txt7
-rw-r--r--cpp/src/qpid/store/MessageStorePlugin.cpp6
-rw-r--r--cpp/src/qpid/store/MessageStorePlugin.h26
-rw-r--r--cpp/src/qpid/store/StorageProvider.h14
-rw-r--r--cpp/src/qpid/store/ms-clfs/MSSqlClfsProvider.cpp20
-rw-r--r--cpp/src/qpid/store/ms-sql/MSSqlProvider.cpp19
-rw-r--r--cpp/src/qpid/store/ms-sql/MessageRecordset.cpp2
-rw-r--r--cpp/src/qpid/sys/AggregateOutput.cpp13
-rw-r--r--cpp/src/qpid/sys/AggregateOutput.h4
-rw-r--r--cpp/src/qpid/sys/AsynchIO.h10
-rw-r--r--cpp/src/qpid/sys/AsynchIOHandler.cpp54
-rw-r--r--cpp/src/qpid/sys/AsynchIOHandler.h9
-rw-r--r--cpp/src/qpid/sys/ClusterSafe.h87
-rw-r--r--cpp/src/qpid/sys/Codec.h2
-rw-r--r--cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h1
-rwxr-xr-xcpp/src/qpid/sys/FileSysDir.h9
-rw-r--r--cpp/src/qpid/sys/OutputControl.h9
-rw-r--r--cpp/src/qpid/sys/ProtocolFactory.h2
-rw-r--r--cpp/src/qpid/sys/RdmaIOPlugin.cpp11
-rw-r--r--cpp/src/qpid/sys/SecurityLayer.h4
-rw-r--r--cpp/src/qpid/sys/SecuritySettings.h2
-rw-r--r--cpp/src/qpid/sys/Socket.h56
-rw-r--r--cpp/src/qpid/sys/SocketAddress.h3
-rw-r--r--cpp/src/qpid/sys/SslPlugin.cpp289
-rw-r--r--cpp/src/qpid/sys/TCPIOPlugin.cpp136
-rw-r--r--cpp/src/qpid/sys/Timer.cpp21
-rw-r--r--cpp/src/qpid/sys/Timer.h6
-rw-r--r--cpp/src/qpid/sys/TimerWarnings.cpp6
-rw-r--r--cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp10
-rw-r--r--cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.h4
-rw-r--r--cpp/src/qpid/sys/epoll/EpollPoller.cpp10
-rw-r--r--cpp/src/qpid/sys/posix/AsynchIO.cpp67
-rw-r--r--cpp/src/qpid/sys/posix/BSDSocket.cpp (renamed from cpp/src/qpid/sys/posix/Socket.cpp)127
-rw-r--r--cpp/src/qpid/sys/posix/BSDSocket.h113
-rwxr-xr-xcpp/src/qpid/sys/posix/FileSysDir.cpp26
-rw-r--r--cpp/src/qpid/sys/posix/IOHandle.cpp15
-rw-r--r--cpp/src/qpid/sys/posix/PollableCondition.cpp7
-rw-r--r--cpp/src/qpid/sys/posix/PosixPoller.cpp8
-rw-r--r--cpp/src/qpid/sys/posix/SocketAddress.cpp5
-rwxr-xr-xcpp/src/qpid/sys/posix/SystemInfo.cpp133
-rw-r--r--cpp/src/qpid/sys/rdma/rdma_wrap.cpp22
-rw-r--r--cpp/src/qpid/sys/rdma/rdma_wrap.h11
-rwxr-xr-xcpp/src/qpid/sys/solaris/SystemInfo.cpp31
-rw-r--r--cpp/src/qpid/sys/ssl/SslHandler.cpp217
-rw-r--r--cpp/src/qpid/sys/ssl/SslHandler.h85
-rw-r--r--cpp/src/qpid/sys/ssl/SslIo.cpp470
-rw-r--r--cpp/src/qpid/sys/ssl/SslIo.h193
-rw-r--r--cpp/src/qpid/sys/ssl/SslSocket.cpp182
-rw-r--r--cpp/src/qpid/sys/ssl/SslSocket.h32
-rw-r--r--cpp/src/qpid/sys/ssl/util.cpp19
-rw-r--r--cpp/src/qpid/sys/windows/AsynchIO.cpp79
-rw-r--r--cpp/src/qpid/sys/windows/FileSysDir.cpp35
-rwxr-xr-xcpp/src/qpid/sys/windows/IOHandle.cpp13
-rwxr-xr-xcpp/src/qpid/sys/windows/IoHandlePrivate.h17
-rwxr-xr-xcpp/src/qpid/sys/windows/IocpPoller.cpp4
-rw-r--r--cpp/src/qpid/sys/windows/PollableCondition.cpp11
-rw-r--r--cpp/src/qpid/sys/windows/QpidDllMain.h72
-rw-r--r--cpp/src/qpid/sys/windows/SslAsynchIO.cpp20
-rw-r--r--cpp/src/qpid/sys/windows/SslAsynchIO.h5
-rwxr-xr-xcpp/src/qpid/sys/windows/SystemInfo.cpp65
-rwxr-xr-xcpp/src/qpid/sys/windows/Thread.cpp23
-rw-r--r--cpp/src/qpid/sys/windows/WinSocket.cpp (renamed from cpp/src/qpid/sys/windows/Socket.cpp)124
-rw-r--r--cpp/src/qpid/sys/windows/WinSocket.h118
-rw-r--r--cpp/src/qpid/types/Variant.cpp25
-rw-r--r--cpp/src/qpidd.cpp44
-rw-r--r--cpp/src/qpidd.h3
-rw-r--r--cpp/src/rdma.cmake16
-rw-r--r--cpp/src/ssl.cmake9
-rw-r--r--cpp/src/ssl.mk17
-rw-r--r--cpp/src/tests/.valgrind.supp23
-rw-r--r--cpp/src/tests/BrokerMgmtAgent.cpp979
-rw-r--r--cpp/src/tests/CMakeLists.txt108
-rw-r--r--cpp/src/tests/ClusterFailover.cpp115
-rw-r--r--cpp/src/tests/ClusterFixture.cpp160
-rw-r--r--cpp/src/tests/ClusterFixture.h115
-rw-r--r--cpp/src/tests/DispatcherTest.cpp5
-rw-r--r--cpp/src/tests/FieldTable.cpp1
-rw-r--r--cpp/src/tests/ForkedBroker.cpp157
-rw-r--r--cpp/src/tests/ForkedBroker.h82
-rw-r--r--cpp/src/tests/InitialStatusMap.cpp239
-rw-r--r--cpp/src/tests/Makefile.am46
-rw-r--r--cpp/src/tests/MessageUtils.h5
-rw-r--r--cpp/src/tests/MessagingSessionTests.cpp53
-rw-r--r--cpp/src/tests/PartialFailure.cpp291
-rw-r--r--cpp/src/tests/PollerTest.cpp9
-rw-r--r--cpp/src/tests/QueueTest.cpp1
-rw-r--r--cpp/src/tests/StoreStatus.cpp117
-rw-r--r--cpp/src/tests/SystemInfo.cpp16
-rw-r--r--cpp/src/tests/Variant.cpp10
-rwxr-xr-xcpp/src/tests/acl.py688
-rwxr-xr-xcpp/src/tests/benchmark95
-rw-r--r--cpp/src/tests/brokertest.py102
-rw-r--r--cpp/src/tests/cluster_authentication_soak.cpp310
-rwxr-xr-xcpp/src/tests/cluster_failover19
-rwxr-xr-xcpp/src/tests/cluster_python_tests28
-rw-r--r--cpp/src/tests/cluster_python_tests_failing.txt4
-rwxr-xr-xcpp/src/tests/cluster_read_credit29
-rw-r--r--cpp/src/tests/cluster_test.cpp1231
-rwxr-xr-xcpp/src/tests/cluster_test_logs.py123
-rw-r--r--cpp/src/tests/cluster_test_scripts/README.txt20
-rwxr-xr-xcpp/src/tests/cluster_test_scripts/cluster_check37
-rwxr-xr-xcpp/src/tests/cluster_test_scripts/cluster_start56
-rwxr-xr-xcpp/src/tests/cluster_test_scripts/cluster_stop38
-rwxr-xr-xcpp/src/tests/cluster_test_scripts/config_example.sh44
-rwxr-xr-xcpp/src/tests/cluster_test_scripts/perftest54
-rw-r--r--cpp/src/tests/cluster_tests.fail3
-rwxr-xr-xcpp/src/tests/cluster_tests.py1834
-rwxr-xr-xcpp/src/tests/clustered_replication_test111
-rwxr-xr-xcpp/src/tests/dynamic_log_hires_timestamp75
-rw-r--r--cpp/src/tests/failover_soak.cpp827
-rwxr-xr-xcpp/src/tests/federated_cluster_test153
-rwxr-xr-xcpp/src/tests/federation.py106
-rwxr-xr-xcpp/src/tests/federation_sys.py1251
-rwxr-xr-xcpp/src/tests/ha_store_tests.py130
-rwxr-xr-xcpp/src/tests/ha_test.py304
-rwxr-xr-xcpp/src/tests/ha_tests.py1097
-rwxr-xr-xcpp/src/tests/ipv6_test40
-rw-r--r--cpp/src/tests/legacystore/.valgrind.supp35
-rw-r--r--cpp/src/tests/legacystore/.valgrindrc7
-rw-r--r--cpp/src/tests/legacystore/CMakeLists.txt117
-rw-r--r--cpp/src/tests/legacystore/MessageUtils.h105
-rw-r--r--cpp/src/tests/legacystore/OrderingTest.cpp168
-rw-r--r--cpp/src/tests/legacystore/SimpleTest.cpp497
-rw-r--r--cpp/src/tests/legacystore/TestFramework.cpp30
-rw-r--r--cpp/src/tests/legacystore/TestFramework.h37
-rw-r--r--cpp/src/tests/legacystore/TransactionalTest.cpp351
-rw-r--r--cpp/src/tests/legacystore/TwoPhaseCommitTest.cpp675
-rw-r--r--[-rwxr-xr-x]cpp/src/tests/legacystore/clean.sh (renamed from cpp/src/tests/stop_cluster)23
-rw-r--r--cpp/src/tests/legacystore/persistence.py574
-rw-r--r--[-rwxr-xr-x]cpp/src/tests/legacystore/run_long_python_tests (renamed from cpp/src/tests/federated_cluster_test_with_node_failure)8
-rw-r--r--cpp/src/tests/legacystore/run_python_tests64
-rw-r--r--[-rwxr-xr-x]cpp/src/tests/legacystore/run_short_python_tests (renamed from cpp/src/tests/run_long_cluster_tests)9
-rw-r--r--cpp/src/tests/legacystore/run_test69
-rw-r--r--[-rwxr-xr-x]cpp/src/tests/legacystore/start_broker (renamed from cpp/src/tests/run_cluster_test)14
-rw-r--r--[-rwxr-xr-x]cpp/src/tests/legacystore/stop_broker (renamed from cpp/src/tests/cpg_check.sh.in)36
-rw-r--r--cpp/src/tests/legacystore/system_test.sh51
-rw-r--r--cpp/src/tests/legacystore/tests_env.sh260
-rw-r--r--cpp/src/tests/legacystore/unit_test.cpp28
-rw-r--r--cpp/src/tests/legacystore/unit_test.h69
-rwxr-xr-xcpp/src/tests/long_cluster_tests.py38
-rwxr-xr-xcpp/src/tests/qpid-cluster-benchmark2
-rwxr-xr-xcpp/src/tests/qpid-cpp-benchmark10
-rw-r--r--cpp/src/tests/qpid-receive.cpp2
-rw-r--r--cpp/src/tests/qpid-send.cpp2
-rwxr-xr-xcpp/src/tests/qpid-test-cluster109
-rwxr-xr-xcpp/src/tests/reliable_replication_test84
-rwxr-xr-xcpp/src/tests/replication_test182
-rwxr-xr-xcpp/src/tests/restart_cluster38
-rwxr-xr-xcpp/src/tests/run_acl_tests18
-rwxr-xr-xcpp/src/tests/run_cluster_authentication_soak27
-rwxr-xr-xcpp/src/tests/run_cluster_authentication_test27
-rwxr-xr-xcpp/src/tests/run_cluster_tests39
-rwxr-xr-xcpp/src/tests/run_failover_soak39
-rwxr-xr-xcpp/src/tests/run_federation_sys_tests28
-rwxr-xr-xcpp/src/tests/sasl_fed_ex92
-rwxr-xr-xcpp/src/tests/sasl_fed_ex_dynamic_cluster30
-rwxr-xr-xcpp/src/tests/sasl_fed_ex_link_cluster29
-rwxr-xr-xcpp/src/tests/sasl_fed_ex_queue_cluster29
-rwxr-xr-xcpp/src/tests/sasl_fed_ex_route_cluster29
-rwxr-xr-xcpp/src/tests/ssl_test26
-rwxr-xr-xcpp/src/tests/start_cluster43
-rwxr-xr-xcpp/src/tests/start_cluster_hosts70
-rw-r--r--cpp/src/tests/storePerftools/asyncPerf/PerfTest.cpp2
-rw-r--r--cpp/src/tests/test_env.ps1.in2
-rw-r--r--cpp/src/tests/test_env.sh.in4
-rw-r--r--cpp/src/tests/test_store.cpp6
-rw-r--r--cpp/src/tests/testagent.mk2
-rw-r--r--cpp/src/tests/testlib.py766
-rwxr-xr-xcpp/src/tests/verify_cluster_objects107
-rw-r--r--cpp/src/versions.cmake1
-rw-r--r--cpp/src/windows/QpiddBroker.cpp48
-rw-r--r--cpp/src/xml.mk2
541 files changed, 43533 insertions, 16888 deletions
diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt
index 9b641558f8..6fa4b23eea 100644
--- a/cpp/src/CMakeLists.txt
+++ b/cpp/src/CMakeLists.txt
@@ -208,6 +208,7 @@ execute_process(COMMAND ${RUBY_EXECUTABLE} -I ${rgen_dir} ${rgen_dir}/generate $
set(mgmt_specs ${AMQP_SPEC_DIR}/management-schema.xml
${CMAKE_CURRENT_SOURCE_DIR}/qpid/acl/management-schema.xml
${CMAKE_CURRENT_SOURCE_DIR}/qpid/ha/management-schema.xml
+# ${CMAKE_CURRENT_SOURCE_DIR}/qpid/legacystore/management-schema.xml
)
set(mgen_dir ${qpid-cpp_SOURCE_DIR}/managementgen)
set(regen_mgmt OFF)
@@ -271,16 +272,28 @@ if (CMAKE_COMPILER_IS_GNUCXX)
set (WARNING_FLAGS
"-Werror -pedantic -Wall -Wextra -Wno-shadow -Wpointer-arith -Wcast-qual -Wcast-align -Wno-long-long -Wvolatile-register-var -Winvalid-pch -Wno-system-headers -Woverloaded-virtual")
- set (GCC_CATCH_UNDEFINED "-Wl,--no-undefined")
+ set (CATCH_UNDEFINED "-Wl,--no-undefined")
# gcc on SunOS uses native linker whose "-z defs" is too fussy
if (CMAKE_SYSTEM_NAME STREQUAL SunOS)
- set (GCC_CATCH_UNDEFINED "")
+ set (CATCH_UNDEFINED "")
endif (CMAKE_SYSTEM_NAME STREQUAL SunOS)
+ set (COMPILER_FLAGS "-fvisibility-inlines-hidden")
+ # gcc 4.1.2 on RHEL 5 needs -Wno-attributes to avoid an error that's fixed
+ # in later gcc versions.
+ execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion
+ OUTPUT_VARIABLE GCC_VERSION)
+ if (GCC_VERSION VERSION_EQUAL 4.1.2)
+ set (HIDE_SYMBOL_FLAGS "-fvisibility=hidden -Wno-attributes")
+ else (GCC_VERSION VERSION_EQUAL 4.1.2)
+ set (HIDE_SYMBOL_FLAGS "-fvisibility=hidden")
+ endif (GCC_VERSION VERSION_EQUAL 4.1.2)
endif (CMAKE_COMPILER_IS_GNUCXX)
if (CMAKE_CXX_COMPILER_ID STREQUAL SunPro)
set (COMPILER_FLAGS "-library=stlport4 -mt")
set (WARNING_FLAGS "+w")
+ set (CATCH_UNDEFINED "")
+ set (HIDE_SYMBOL_FLAGS "")
endif (CMAKE_CXX_COMPILER_ID STREQUAL SunPro)
option(ENABLE_WARNINGS "Enable lots of compiler warnings (recommended)" ON)
@@ -293,9 +306,9 @@ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILER_FLAGS} ${WARNING_FLAGS}")
# Expand a bit from the basic Find_Boost; be specific about what's needed.
# Boost.system is sometimes needed; it's handled separately, below.
if (CMAKE_SYSTEM_NAME STREQUAL Windows)
- set (Boost_components filesystem program_options date_time thread unit_test_framework regex)
+ set (Boost_components program_options date_time thread unit_test_framework regex)
else (CMAKE_SYSTEM_NAME STREQUAL Windows)
- set (Boost_components filesystem program_options unit_test_framework)
+ set (Boost_components program_options unit_test_framework)
endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
# Visual Studio 2010 requires boost 1.45 or better.
@@ -303,7 +316,10 @@ endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
# where Boost 1.45 is supported, or we can just accept some versions using
# the Additional_versions variable.
if (NOT DEFINED Boost_ADDITIONAL_VERSIONS)
- set (Boost_ADDITIONAL_VERSIONS "1.45" "1.45.0" "1.46" "1.46.0" "1.47" "1.47.0")
+ set (Boost_ADDITIONAL_VERSIONS
+ "1.45" "1.45.0" "1.46" "1.46.0" "1.47" "1.47.0"
+ "1.48" "1.48.0" "1.49" "1.49.0" "1.50" "1.50.0"
+ "1.51" "1.51.0" "1.52" "1.52.0" "1.53" "1.53.0")
endif (NOT DEFINED Boost_ADDITIONAL_VERSIONS)
find_package(Boost 1.33 REQUIRED COMPONENTS ${Boost_components})
@@ -328,10 +344,6 @@ if (NOT Boost_PROGRAM_OPTIONS_LIBRARY)
set(Boost_PROGRAM_OPTIONS_LIBRARY boost_program_options)
endif (NOT Boost_PROGRAM_OPTIONS_LIBRARY)
-if (NOT Boost_FILESYSTEM_LIBRARY)
- set(Boost_FILESYSTEM_LIBRARY boost_filesystem)
-endif (NOT Boost_FILESYSTEM_LIBRARY)
-
if (NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
set(Boost_UNIT_TEST_FRAMEWORK_LIBRARY boost_unit_test_framework)
endif (NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
@@ -357,7 +369,6 @@ option(QPID_LINK_BOOST_DYNAMIC "Link with dynamic Boost libs (OFF to link static
if (MSVC)
install (PROGRAMS
${Boost_DATE_TIME_LIBRARY_DEBUG} ${Boost_DATE_TIME_LIBRARY_RELEASE}
- ${Boost_FILESYSTEM_LIBRARY_DEBUG} ${Boost_FILESYSTEM_LIBRARY_RELEASE}
${Boost_PROGRAM_OPTIONS_LIBRARY_DEBUG} ${Boost_PROGRAM_OPTIONS_LIBRARY_RELEASE}
${Boost_REGEX_LIBRARY_DEBUG} ${Boost_REGEX_LIBRARY_RELEASE}
${Boost_THREAD_LIBRARY_DEBUG} ${Boost_THREAD_LIBRARY_RELEASE}
@@ -378,10 +389,6 @@ if (MSVC)
string (REPLACE .lib .dll
_boost_date_time_release ${Boost_DATE_TIME_LIBRARY_RELEASE})
string (REPLACE .lib .dll
- _boost_filesystem_debug ${Boost_FILESYSTEM_LIBRARY_DEBUG})
- string (REPLACE .lib .dll
- _boost_filesystem_release ${Boost_FILESYSTEM_LIBRARY_RELEASE})
- string (REPLACE .lib .dll
_boost_program_options_debug ${Boost_PROGRAM_OPTIONS_LIBRARY_DEBUG})
string (REPLACE .lib .dll
_boost_program_options_release ${Boost_PROGRAM_OPTIONS_LIBRARY_RELEASE})
@@ -404,7 +411,6 @@ if (MSVC)
endif (NOT Boost_VERSION LESS 103500)
install (PROGRAMS
${_boost_date_time_debug} ${_boost_date_time_release}
- ${_boost_filesystem_debug} ${_boost_filesystem_release}
${_boost_program_options_debug} ${_boost_program_options_release}
${_boost_regex_debug} ${_boost_regex_release}
${_boost_system_debug} ${_boost_system_release}
@@ -453,7 +459,6 @@ if (MSVC)
set(Boost_DATE_TIME_LIBRARY "")
set(Boost_THREAD_LIBRARY "")
set(Boost_PROGRAM_OPTIONS_LIBRARY "")
- set(Boost_FILESYSTEM_LIBRARY "")
set(Boost_UNIT_TEST_FRAMEWORK_LIBRARY "")
set(Boost_REGEX_LIBRARY "")
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/windows/resources )
@@ -571,13 +576,11 @@ if (BUILD_XML)
qpid/xml/XmlExchange.cpp
qpid/xml/XmlExchange.h
qpid/xml/XmlExchangePlugin.cpp)
- set_target_properties (xml PROPERTIES PREFIX "")
target_link_libraries (xml xerces-c xqilla qpidbroker pthread)
- if (CMAKE_COMPILER_IS_GNUCXX)
- set_target_properties (xml PROPERTIES
- PREFIX ""
- LINK_FLAGS ${GCC_CATCH_UNDEFINED})
- endif (CMAKE_COMPILER_IS_GNUCXX)
+ set_target_properties (xml PROPERTIES
+ PREFIX ""
+ COMPILE_DEFINITIONS _IN_QPID_BROKER
+ LINK_FLAGS "${CATCH_UNDEFINED}")
install (TARGETS xml
DESTINATION ${QPIDD_MODULE_DIR}
COMPONENT ${QPID_COMPONENT_BROKER})
@@ -600,6 +603,8 @@ if (BUILD_ACL)
qpid/acl/AclPlugin.cpp
qpid/acl/AclReader.cpp
qpid/acl/AclReader.h
+ qpid/acl/AclResourceCounter.cpp
+ qpid/acl/AclResourceCounter.h
qpid/acl/AclValidator.cpp
qpid/acl/AclValidator.h
)
@@ -608,13 +613,11 @@ if (BUILD_ACL)
# the qpidbroker platform-specific source list.
if (NOT CMAKE_SYSTEM_NAME STREQUAL Windows)
add_library (acl MODULE ${acl_SOURCES})
- set_target_properties (acl PROPERTIES PREFIX "")
target_link_libraries (acl qpidbroker ${Boost_PROGRAM_OPTIONS_LIBRARY})
- if (CMAKE_COMPILER_IS_GNUCXX)
- set_target_properties (acl PROPERTIES
- PREFIX ""
- LINK_FLAGS "${GCC_CATCH_UNDEFINED}")
- endif (CMAKE_COMPILER_IS_GNUCXX)
+ set_target_properties (acl PROPERTIES
+ PREFIX ""
+ COMPILE_DEFINITIONS _IN_QPID_BROKER
+ LINK_FLAGS "${CATCH_UNDEFINED}")
install (TARGETS acl
DESTINATION ${QPIDD_MODULE_DIR}
COMPONENT ${QPID_COMPONENT_BROKER})
@@ -652,6 +655,8 @@ if (BUILD_HA)
qpid/ha/ReplicatingSubscription.cpp
qpid/ha/ReplicatingSubscription.h
qpid/ha/Settings.h
+ qpid/ha/StatusCheck.cpp
+ qpid/ha/StatusCheck.h
qpid/ha/types.cpp
qpid/ha/types.h
qpid/ha/RemoteBackup.cpp
@@ -659,8 +664,8 @@ if (BUILD_HA)
)
add_library (ha MODULE ${ha_SOURCES})
- set_target_properties (ha PROPERTIES PREFIX "")
- target_link_libraries (ha qpidtypes qpidcommon qpidbroker)
+ set_target_properties (ha PROPERTIES PREFIX "" COMPILE_DEFINITIONS _IN_QPID_BROKER)
+ target_link_libraries (ha qpidtypes qpidcommon qpidbroker qpidmessaging)
if (CMAKE_COMPILER_IS_GNUCXX)
set_target_properties (ha PROPERTIES
PREFIX ""
@@ -680,6 +685,9 @@ include (ssl.cmake)
# Check for optional async store build requirements
#include (asyncstore.cmake)
+# Check for optional AMQP 1.0 support requirements
+include (amqp.cmake)
+
# Check for syslog capabilities not present on all systems
check_symbol_exists (LOG_AUTHPRIV "sys/syslog.h" HAVE_LOG_AUTHPRIV)
check_symbol_exists (LOG_FTP "sys/syslog.h" HAVE_LOG_FTP)
@@ -744,7 +752,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL Windows)
qpid/sys/windows/PipeHandle.cpp
qpid/sys/windows/PollableCondition.cpp
qpid/sys/windows/Shlib.cpp
- qpid/sys/windows/Socket.cpp
+ qpid/sys/windows/WinSocket.cpp
qpid/sys/windows/SocketAddress.cpp
qpid/sys/windows/StrError.cpp
qpid/sys/windows/SystemInfo.cpp
@@ -755,7 +763,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL Windows)
)
set (qpidcommon_platform_LIBS
- ${Boost_THREAD_LIBRARY} ${windows_ssl_libs} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} ws2_32 )
+ ${Boost_THREAD_LIBRARY} ${windows_ssl_libs} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${Boost_SYSTEM_LIBRARY} ws2_32 )
set (qpidbroker_platform_SOURCES
qpid/broker/windows/BrokerDefaults.cpp
qpid/broker/windows/SaslAuthenticator.cpp
@@ -767,6 +775,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL Windows)
)
set (qpidclient_platform_SOURCES
${sslclient_windows_SOURCES}
+ qpid/client/windows/ClientDllMain.cpp
)
set (qpidclient_platform_LIBS
${windows_ssl_libs}
@@ -803,10 +812,8 @@ else (CMAKE_SYSTEM_NAME STREQUAL Windows)
if (CMAKE_SYSTEM_NAME STREQUAL Linux)
add_definitions(-pthread)
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread")
- if (CMAKE_COMPILER_IS_GNUCXX)
- set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GCC_CATCH_UNDEFINED} -pthread")
- set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -pthread")
- endif (CMAKE_COMPILER_IS_GNUCXX)
+ set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${CATCH_UNDEFINED} -pthread")
+ set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -pthread")
# On Linux override memory status module
set (qpid_memstat_module
qpid/sys/posix/MemStat.cpp
@@ -848,7 +855,7 @@ else (CMAKE_SYSTEM_NAME STREQUAL Windows)
qpid/sys/posix/PollableCondition.cpp
qpid/sys/posix/Shlib.cpp
qpid/log/posix/SinkOptions.cpp
- qpid/sys/posix/Socket.cpp
+ qpid/sys/posix/BSDSocket.cpp
qpid/sys/posix/SocketAddress.cpp
qpid/sys/posix/StrError.cpp
qpid/sys/posix/Thread.cpp
@@ -860,7 +867,6 @@ else (CMAKE_SYSTEM_NAME STREQUAL Windows)
)
set (qpidcommon_platform_LIBS
${Boost_PROGRAM_OPTIONS_LIBRARY}
- ${Boost_FILESYSTEM_LIBRARY}
${CMAKE_DL_LIBS}
)
@@ -900,6 +906,7 @@ set (qpidcommon_SOURCES
qpid/StringUtils.cpp
qpid/Url.cpp
qpid/UrlArray.cpp
+ qpid/NullSaslServer.cpp
qpid/amqp_0_10/SessionHandler.cpp
qpid/framing/AccumulatedAck.cpp
qpid/framing/AMQBody.cpp
@@ -937,7 +944,6 @@ set (qpidcommon_SOURCES
qpid/management/ManagementObject.cpp
qpid/sys/AggregateOutput.cpp
qpid/sys/AsynchIOHandler.cpp
- qpid/sys/ClusterSafe.cpp
qpid/sys/Dispatcher.cpp
qpid/sys/DispatchHandle.cpp
qpid/sys/Runnable.cpp
@@ -945,6 +951,32 @@ set (qpidcommon_SOURCES
qpid/sys/Timer.cpp
qpid/sys/TimerWarnings.cpp
qpid/amqp_0_10/Codecs.cpp
+ qpid/amqp/CharSequence.h
+ qpid/amqp/CharSequence.cpp
+ qpid/amqp/Decoder.h
+ qpid/amqp/Decoder.cpp
+ qpid/amqp/Descriptor.h
+ qpid/amqp/Descriptor.cpp
+ qpid/amqp/Encoder.h
+ qpid/amqp/Encoder.cpp
+ qpid/amqp/MapReader.h
+ qpid/amqp/MapReader.cpp
+ qpid/amqp/MessageEncoder.h
+ qpid/amqp/MessageEncoder.cpp
+ qpid/amqp/MessageId.h
+ qpid/amqp/MessageId.cpp
+ qpid/amqp/MessageReader.h
+ qpid/amqp/MessageReader.cpp
+ qpid/amqp/Reader.h
+ qpid/amqp/Sasl.h
+ qpid/amqp/Sasl.cpp
+ qpid/amqp/SaslClient.h
+ qpid/amqp/SaslClient.cpp
+ qpid/amqp/SaslServer.h
+ qpid/amqp/SaslServer.cpp
+ qpid/messaging/amqp/Transport.h
+ qpid/messaging/amqp/Transport.cpp
+ qpid/messaging/amqp/TransportContext.h
${qpid_memstat_module}
)
add_msvc_version (qpidcommon library dll)
@@ -1029,26 +1061,12 @@ install (DIRECTORY ../include/qpid
PATTERN ".svn" EXCLUDE)
install_pdb (qpidclient ${QPID_COMPONENT_CLIENT})
-
-set (qpidmessaging_SOURCES
- ${qpidmessaging_platform_SOURCES}
- qpid/messaging/Address.cpp
+set (qpidmessaging_SOURCES_hidden
qpid/messaging/AddressParser.h
- qpid/messaging/AddressParser.cpp
- qpid/messaging/Connection.cpp
qpid/messaging/ConnectionImpl.h
- qpid/messaging/Duration.cpp
- qpid/messaging/exceptions.cpp
- qpid/messaging/Message.cpp
- qpid/messaging/MessageImpl.h
- qpid/messaging/MessageImpl.cpp
- qpid/messaging/Receiver.cpp
qpid/messaging/ReceiverImpl.h
- qpid/messaging/Session.cpp
qpid/messaging/SessionImpl.h
- qpid/messaging/Sender.cpp
qpid/messaging/SenderImpl.h
- qpid/messaging/FailoverUpdates.cpp
qpid/client/amqp0_10/AcceptTracker.h
qpid/client/amqp0_10/AcceptTracker.cpp
qpid/client/amqp0_10/AddressResolution.h
@@ -1068,6 +1086,33 @@ set (qpidmessaging_SOURCES
qpid/client/amqp0_10/SenderImpl.h
qpid/client/amqp0_10/SenderImpl.cpp
)
+set_source_files_properties(
+ ${qpidmessaging_SOURCES_hidden}
+ PROPERTIES
+ COMPILE_FLAGS "${HIDE_SYMBOL_FLAGS}")
+
+set (qpidmessaging_SOURCES
+ ${qpidmessaging_platform_SOURCES}
+ ${qpidmessaging_SOURCES_hidden}
+ qpid/messaging/Address.cpp
+ qpid/messaging/AddressParser.cpp # The functions in here are not in the public interface, but qmf uses them
+ qpid/messaging/Connection.cpp
+ qpid/messaging/Duration.cpp
+ qpid/messaging/exceptions.cpp
+ qpid/messaging/FailoverUpdates.cpp
+ qpid/messaging/Message.cpp
+ qpid/messaging/Receiver.cpp
+ qpid/messaging/Session.cpp
+ qpid/messaging/Sender.cpp
+ #functions from the following are not in the public interface but are used by the AMQP 1.0 client module
+ qpid/messaging/ConnectionOptions.h
+ qpid/messaging/ConnectionOptions.cpp
+ qpid/messaging/MessageImpl.h
+ qpid/messaging/MessageImpl.cpp
+ qpid/messaging/ProtocolRegistry.cpp
+ qpid/messaging/amqp/EncodedMessage.h
+ qpid/messaging/amqp/EncodedMessage.cpp
+)
add_msvc_version (qpidmessaging library dll)
add_library (qpidmessaging SHARED ${qpidmessaging_SOURCES})
@@ -1116,6 +1161,7 @@ set (qpidbroker_SOURCES
qpid/broker/MessageDeque.cpp
qpid/broker/MessageMap.cpp
qpid/broker/PriorityQueue.cpp
+ qpid/broker/Protocol.cpp
qpid/broker/Queue.cpp
qpid/broker/QueueCleaner.cpp
qpid/broker/QueueListeners.cpp
@@ -1130,7 +1176,7 @@ set (qpidbroker_SOURCES
qpid/broker/ConfigHandle.cpp
qpid/broker/Connection.cpp
qpid/broker/ConnectionHandler.cpp
- qpid/broker/ConnectionFactory.cpp
+ qpid/broker/ConnectionState.cpp
qpid/broker/DeliverableMessage.cpp
qpid/broker/DeliveryRecord.cpp
qpid/broker/DirectExchange.cpp
@@ -1203,7 +1249,7 @@ set (qpidbroker_SOURCES
add_msvc_version (qpidbroker library dll)
add_library (qpidbroker SHARED ${qpidbroker_SOURCES})
target_link_libraries (qpidbroker qpidcommon ${qpidbroker_platform_LIBS})
-set_target_properties (qpidbroker PROPERTIES VERSION ${qpidbroker_version})
+set_target_properties (qpidbroker PROPERTIES VERSION ${qpidbroker_version} COMPILE_DEFINITIONS _IN_QPID_BROKER)
if (MSVC)
set_target_properties (qpidbroker PROPERTIES COMPILE_FLAGS /wd4290)
endif (MSVC)
@@ -1220,8 +1266,8 @@ set (qpidd_SOURCES
)
add_msvc_version (qpidd application exe)
add_executable (qpidd ${qpidd_SOURCES})
-target_link_libraries (qpidd qpidbroker qpidcommon ${Boost_PROGRAM_OPTIONS_LIBRARY}
- ${Boost_FILESYSTEM_LIBRARY})
+target_link_libraries (qpidd qpidbroker qpidcommon ${Boost_PROGRAM_OPTIONS_LIBRARY})
+set_target_properties (qpidd PROPERTIES COMPILE_DEFINITIONS _IN_QPID_BROKER)
install (TARGETS qpidd RUNTIME
DESTINATION ${QPID_INSTALL_SBINDIR}
COMPONENT ${QPID_COMPONENT_BROKER})
@@ -1443,13 +1489,16 @@ install (FILES ${qmfconsole_HEADERS}
install_pdb (qmfconsole ${QPID_COMPONENT_QMF})
+#
+# Legacy store
+#
+#include (legacystore.cmake)
+
# This is only really needed until all the trunk builds (Linux, UNIX, Windows)
# are all on cmake only. This is because cmake builds always have a config.h
# file whereas older builds only have config.h on autoconf-generated builds.
add_definitions(-DHAVE_CONFIG_H)
-add_definitions(-DBOOST_FILESYSTEM_VERSION=2)
-
# Now create the config file from all the info learned above.
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/config.h)
@@ -1457,21 +1506,21 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake
add_subdirectory(tests)
# Support for pkg-config
-if (UNIX)
- add_custom_target(pkgconfig ALL echo DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/qpid.pc)
- configure_file(${CMAKE_CURRENT_SOURCE_DIR}/qpid.pc.in
- ${CMAKE_CURRENT_BINARY_DIR}/qpid.pc)
- install (FILES ${CMAKE_CURRENT_BINARY_DIR}/qpid.pc
- DESTINATION ${QPID_INSTALL_LIBDIR}/pkgconfig
- COMPONENT ${QPID_COMPONENT_COMMON})
-
- add_dependencies(pkgconfig ${CMAKE_CURRENT_BINARY_DIR}/qmf2.pc)
- configure_file(${CMAKE_CURRENT_SOURCE_DIR}/qmf2.pc.in
- ${CMAKE_CURRENT_BINARY_DIR}/qmf2.pc)
- install (FILES ${CMAKE_CURRENT_BINARY_DIR}/qmf2.pc
- DESTINATION ${QPID_INSTALL_LIBDIR}/pkgconfig
- COMPONENT ${QPID_COMPONENT_COMMON})
-endif (UNIX)
+
+# Compatible variable names used in the pkg config files also for autoconf
+set (prefix ${CMAKE_INSTALL_PREFIX})
+set (exec_prefix ${CMAKE_INSTALL_PREFIX})
+set_absolute_install_path (libdir ${LIB_INSTALL_DIR})
+set_absolute_install_path (includedir ${INCLUDE_INSTALL_DIR})
+set (VERSION ${QPID_VERSION})
+
+#add_custom_target(pkgconfig ALL echo DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/qpid.pc)
+#add_dependencies(pkgconfig ${CMAKE_CURRENT_BINARY_DIR}/qmf2.pc)
+configure_file(qpid.pc.in ${CMAKE_CURRENT_BINARY_DIR}/qpid.pc @ONLY)
+configure_file(qmf2.pc.in ${CMAKE_CURRENT_BINARY_DIR}/qmf2.pc @ONLY)
+install (FILES ${CMAKE_CURRENT_BINARY_DIR}/qpid.pc ${CMAKE_CURRENT_BINARY_DIR}/qmf2.pc
+ DESTINATION ${LIB_INSTALL_DIR}/pkgconfig
+ COMPONENT ${QPID_COMPONENT_COMMON})
# Async Store support
# -------------------
@@ -1482,3 +1531,4 @@ endif (UNIX)
# corrected separately, and when it is, this can be moved up to the location
# of the other module includes.
include (asyncstore.cmake)
+
diff --git a/cpp/src/Makefile.am b/cpp/src/Makefile.am
index a65230b091..82ad151d2f 100644
--- a/cpp/src/Makefile.am
+++ b/cpp/src/Makefile.am
@@ -25,6 +25,7 @@ SUBDIRS = . tests
windows_dist = \
qpid/client/windows/SaslFactory.cpp \
qpid/client/windows/SslConnector.cpp \
+ qpid/client/windows/ClientDllMain.cpp \
qpid/log/windows/SinkOptions.cpp \
qpid/log/windows/SinkOptions.h \
../include/qpid/sys/windows/check.h \
@@ -42,9 +43,9 @@ windows_dist = \
qpid/sys/windows/PollableCondition.cpp \
qpid/sys/windows/PipeHandle.cpp \
../include/qpid/sys/windows/Mutex.h \
+ qpid/sys/windows/QpidDllMain.h \
qpid/sys/windows/Shlib.cpp \
qpid/sys/windows/SocketAddress.cpp \
- qpid/sys/windows/Socket.cpp \
qpid/sys/windows/SslAsynchIO.cpp \
qpid/sys/windows/SslAsynchIO.h \
qpid/sys/windows/StrError.cpp \
@@ -54,6 +55,8 @@ windows_dist = \
../include/qpid/sys/windows/Time.h \
qpid/sys/windows/uuid.cpp \
qpid/sys/windows/uuid.h \
+ qpid/sys/windows/WinSocket.cpp \
+ qpid/sys/windows/WinSocket.h \
windows/QpiddBroker.cpp \
windows/SCM.h \
windows/SCM.cpp \
@@ -127,7 +130,6 @@ INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(srcdir) -I=$(bu
# Destination for intalled programs and tests defined here
#
qpidexecdir = $(libexecdir)/qpid
-AM_CXXFLAGS += -DQPID_LIBEXEC_DIR=\"$(qpidexecdir)\"
qpidexec_PROGRAMS =
qpidexec_SCRIPTS =
qpidtestdir = $(qpidexecdir)/tests
@@ -136,15 +138,16 @@ qpidtest_SCRIPTS =
tmoduleexecdir = $(libdir)/qpid/tests
tmoduleexec_LTLIBRARIES=
-AM_CXXFLAGS += -DBOOST_FILESYSTEM_VERSION=2
+BROKER_CXXFLAGS = -D_IN_QPID_BROKER
## Automake macros to build libraries and executables.
-qpidd_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDD_MODULE_DIR=\"$(dmoduleexecdir)\" -DQPIDD_CONF_FILE=\"$(sysconfdir)/qpidd.conf\" -DQPIDC_CONF_FILE=\"$(confdir)/qpidc.conf\"
+qpidd_CXXFLAGS = $(AM_CXXFLAGS) $(BROKER_CXXFLAGS) -DQPIDD_MODULE_DIR=\"$(dmoduleexecdir)\" -DQPIDD_CONF_FILE=\"$(sysconfdir)/qpidd.conf\" -DQPIDC_CONF_FILE=\"$(confdir)/qpidc.conf\"
libqpidclient_la_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDC_MODULE_DIR=\"$(cmoduleexecdir)\" -DQPIDC_CONF_FILE=\"$(confdir)/qpidc.conf\"
qpidd_LDADD = \
libqpidbroker.la \
- libqpidcommon.la
+ libqpidcommon.la \
+ -lboost_program_options
posix_qpidd_src = posix/QpiddBroker.cpp
@@ -157,7 +160,8 @@ qpidd_SOURCES = qpidd.cpp qpidd.h $(posix_qpidd_src)
libqpidcommon_la_SOURCES += \
qpid/log/posix/SinkOptions.cpp \
qpid/sys/posix/IOHandle.cpp \
- qpid/sys/posix/Socket.cpp \
+ qpid/sys/posix/BSDSocket.cpp \
+ qpid/sys/posix/BSDSocket.h \
qpid/sys/posix/SocketAddress.cpp \
qpid/sys/posix/AsynchIO.cpp \
qpid/sys/posix/FileSysDir.cpp \
@@ -208,6 +212,7 @@ posix_broker_src = \
lib_LTLIBRARIES = libqpidtypes.la libqpidcommon.la libqpidbroker.la libqpidclient.la libqpidmessaging.la
+
# Definitions for client and daemon plugins
PLUGINLDFLAGS=-no-undefined -module -avoid-version
confdir=$(sysconfdir)/qpid
@@ -254,7 +259,7 @@ rdma_la_LIBADD = \
-libverbs
rdma_la_LDFLAGS = $(PLUGINLDFLAGS)
rdma_la_CXXFLAGS = \
- $(AM_CXXFLAGS) -Wno-missing-field-initializers
+ $(AM_CXXFLAGS) -Wno-missing-field-initializers -D_IN_QPID_BROKER
dmoduleexec_LTLIBRARIES += \
rdma.la
@@ -290,8 +295,10 @@ endif
EXTRA_DIST +=\
CMakeLists.txt \
config.h.cmake \
+ amqp.cmake \
rdma.cmake \
ssl.cmake \
+ legacystore.cmake \
managementgen.cmake \
rubygen.cmake \
versions.cmake \
@@ -331,7 +338,6 @@ EXTRA_DIST +=\
libqpidcommon_la_LIBADD = \
libqpidtypes.la \
-lboost_program_options \
- -lboost_filesystem \
-luuid \
-lpthread \
$(LIB_DLOPEN) \
@@ -357,6 +363,9 @@ libqpidcommon_la_SOURCES += \
qpid/Sasl.h \
qpid/SaslFactory.cpp \
qpid/SaslFactory.h \
+ qpid/SaslServer.h \
+ qpid/NullSaslServer.h \
+ qpid/NullSaslServer.cpp \
qpid/Serializer.h \
qpid/SessionId.cpp \
qpid/SessionState.cpp \
@@ -462,8 +471,6 @@ libqpidcommon_la_SOURCES += \
qpid/sys/AtomicValue_gcc.h \
qpid/sys/AtomicValue_mutex.h \
qpid/sys/BlockingQueue.h \
- qpid/sys/ClusterSafe.h \
- qpid/sys/ClusterSafe.cpp \
qpid/sys/Codec.h \
qpid/sys/ConnectionCodec.h \
qpid/sys/ConnectionInputHandler.h \
@@ -506,10 +513,48 @@ libqpidcommon_la_SOURCES += \
qpid/sys/TimerWarnings.cpp \
qpid/sys/TimerWarnings.h \
qpid/sys/Waitable.h \
- qpid/sys/alloca.h \
qpid/sys/uuid.h \
qpid/sys/unordered_map.h \
- qpid/amqp_0_10/Codecs.cpp
+ qpid/amqp_0_10/Codecs.cpp \
+ qpid/amqp/CharSequence.h \
+ qpid/amqp/CharSequence.cpp \
+ qpid/amqp/Codec.h \
+ qpid/amqp/Constructor.h \
+ qpid/amqp/Decoder.h \
+ qpid/amqp/Decoder.cpp \
+ qpid/amqp/Descriptor.h \
+ qpid/amqp/Descriptor.cpp \
+ qpid/amqp/descriptors.h \
+ qpid/amqp/Encoder.h \
+ qpid/amqp/Encoder.cpp \
+ qpid/amqp/ListReader.h \
+ qpid/amqp/LoggingReader.h \
+ qpid/amqp/MapReader.h \
+ qpid/amqp/MapReader.cpp \
+ qpid/amqp/MessageEncoder.h \
+ qpid/amqp/MessageEncoder.cpp \
+ qpid/amqp/MessageId.h \
+ qpid/amqp/MessageId.cpp \
+ qpid/amqp/MessageReader.h \
+ qpid/amqp/MessageReader.cpp \
+ qpid/amqp/Reader.h \
+ qpid/amqp/Sasl.h \
+ qpid/amqp/Sasl.cpp \
+ qpid/amqp/SaslClient.h \
+ qpid/amqp/SaslClient.cpp \
+ qpid/amqp/SaslServer.h \
+ qpid/amqp/SaslServer.cpp \
+ qpid/amqp/typecodes.h
+
+#libqpidcommon is not really the 'right' place for the Transport
+#interface, which is only used in 1.0 impl of messaging API, but this
+#lets the 1.0 SSL support be included in the existing sslconnector lib
+#which in turn addresses common ssl needs in qpidclient and
+#qpidmessaging:
+libqpidcommon_la_SOURCES += \
+ qpid/messaging/amqp/Transport.h \
+ qpid/messaging/amqp/Transport.cpp \
+ qpid/messaging/amqp/TransportContext.h
if HAVE_SASL
libqpidcommon_la_SOURCES += qpid/sys/cyrus/CyrusSecurityLayer.h
@@ -537,10 +582,9 @@ libqpidbroker_la_SOURCES = \
qpid/broker/BrokerImportExport.h \
qpid/broker/Connection.cpp \
qpid/broker/Connection.h \
- qpid/broker/ConnectionFactory.cpp \
- qpid/broker/ConnectionFactory.h \
qpid/broker/ConnectionHandler.cpp \
qpid/broker/ConnectionHandler.h \
+ qpid/broker/ConnectionState.cpp \
qpid/broker/ConnectionState.h \
qpid/broker/ConnectionToken.h \
qpid/broker/Consumer.h \
@@ -610,6 +654,8 @@ libqpidbroker_la_SOURCES = \
qpid/broker/MessageStoreModule.h \
qpid/broker/PriorityQueue.h \
qpid/broker/PriorityQueue.cpp \
+ qpid/broker/Protocol.h \
+ qpid/broker/Protocol.cpp \
qpid/broker/NameGenerator.cpp \
qpid/broker/NameGenerator.h \
qpid/broker/NullMessageStore.cpp \
@@ -659,6 +705,7 @@ libqpidbroker_la_SOURCES = \
qpid/broker/RecoveryManager.h \
qpid/broker/RecoveryManagerImpl.cpp \
qpid/broker/RecoveryManagerImpl.h \
+ qpid/broker/RecoverableMessageImpl.h \
qpid/broker/RetryList.cpp \
qpid/broker/RetryList.h \
qpid/broker/SaslAuthenticator.cpp \
@@ -683,7 +730,6 @@ libqpidbroker_la_SOURCES = \
qpid/broker/SessionState.h \
qpid/broker/SignalHandler.cpp \
qpid/broker/SignalHandler.h \
- qpid/broker/StatefulQueueObserver.h \
qpid/broker/System.cpp \
qpid/broker/System.h \
qpid/broker/ThresholdAlerts.cpp \
@@ -716,6 +762,76 @@ libqpidbroker_la_SOURCES = \
QPIDBROKER_VERSION_INFO = 2:0:0
libqpidbroker_la_LDFLAGS = -version-info $(QPIDBROKER_VERSION_INFO)
+libqpidbroker_la_CXXFLAGS=$(AM_CXXFLAGS) $(BROKER_CXXFLAGS)
+
+if HAVE_PROTON
+
+dmoduleexec_LTLIBRARIES += amqp.la
+amqp_la_LIBADD = libqpidcommon.la
+amqp_la_SOURCES = \
+ qpid/broker/amqp/Connection.h \
+ qpid/broker/amqp/Connection.cpp \
+ qpid/broker/amqp/DataReader.h \
+ qpid/broker/amqp/DataReader.cpp \
+ qpid/broker/amqp/Filter.h \
+ qpid/broker/amqp/Filter.cpp \
+ qpid/broker/amqp/Header.h \
+ qpid/broker/amqp/Header.cpp \
+ qpid/broker/amqp/ManagedConnection.h \
+ qpid/broker/amqp/ManagedConnection.cpp \
+ qpid/broker/amqp/ManagedSession.h \
+ qpid/broker/amqp/ManagedSession.cpp \
+ qpid/broker/amqp/ManagedOutgoingLink.h \
+ qpid/broker/amqp/ManagedOutgoingLink.cpp \
+ qpid/broker/amqp/Message.h \
+ qpid/broker/amqp/Message.cpp \
+ qpid/broker/amqp/NodeProperties.h \
+ qpid/broker/amqp/NodeProperties.cpp \
+ qpid/broker/amqp/Outgoing.h \
+ qpid/broker/amqp/Outgoing.cpp \
+ qpid/broker/amqp/ProtocolPlugin.cpp \
+ qpid/broker/amqp/Sasl.h \
+ qpid/broker/amqp/Sasl.cpp \
+ qpid/broker/amqp/Session.h \
+ qpid/broker/amqp/Session.cpp \
+ qpid/broker/amqp/Translation.h \
+ qpid/broker/amqp/Translation.cpp
+
+amqp_la_CXXFLAGS=$(AM_CXXFLAGS) $(BROKER_CXXFLAGS) $(PROTON_CFLAGS)
+amqp_la_LDFLAGS = $(PLUGINLDFLAGS) $(PROTON_LIBS)
+
+cmoduleexec_LTLIBRARIES += amqpc.la
+amqpc_la_LIBADD = libqpidcommon.la
+amqpc_la_SOURCES = \
+ qpid/messaging/amqp/AddressHelper.h \
+ qpid/messaging/amqp/AddressHelper.cpp \
+ qpid/messaging/amqp/ConnectionContext.h \
+ qpid/messaging/amqp/ConnectionContext.cpp \
+ qpid/messaging/amqp/ConnectionHandle.h \
+ qpid/messaging/amqp/ConnectionHandle.cpp \
+ qpid/messaging/amqp/DriverImpl.h \
+ qpid/messaging/amqp/DriverImpl.cpp \
+ qpid/messaging/amqp/ReceiverContext.h \
+ qpid/messaging/amqp/ReceiverContext.cpp \
+ qpid/messaging/amqp/ReceiverHandle.h \
+ qpid/messaging/amqp/ReceiverHandle.cpp \
+ qpid/messaging/amqp/Sasl.h \
+ qpid/messaging/amqp/Sasl.cpp \
+ qpid/messaging/amqp/SenderContext.h \
+ qpid/messaging/amqp/SenderContext.cpp \
+ qpid/messaging/amqp/SenderHandle.h \
+ qpid/messaging/amqp/SenderHandle.cpp \
+ qpid/messaging/amqp/SessionContext.h \
+ qpid/messaging/amqp/SessionContext.cpp \
+ qpid/messaging/amqp/SessionHandle.h \
+ qpid/messaging/amqp/SessionHandle.cpp \
+ qpid/messaging/amqp/TcpTransport.h \
+ qpid/messaging/amqp/TcpTransport.cpp
+
+amqpc_la_CXXFLAGS=$(AM_CXXFLAGS) $(PROTON_CFLAGS)
+amqpc_la_LDFLAGS = $(PLUGINLDFLAGS) $(PROTON_LIBS)
+
+endif #HAVE_PROTON
libqpidclient_la_LIBADD = libqpidcommon.la -luuid
@@ -793,12 +909,16 @@ libqpidmessaging_la_SOURCES = \
qpid/messaging/AddressParser.h \
qpid/messaging/AddressParser.cpp \
qpid/messaging/Connection.cpp \
+ qpid/messaging/ConnectionOptions.h \
+ qpid/messaging/ConnectionOptions.cpp \
qpid/messaging/Duration.cpp \
qpid/messaging/exceptions.cpp \
qpid/messaging/Message.cpp \
qpid/messaging/MessageImpl.h \
qpid/messaging/MessageImpl.cpp \
qpid/messaging/PrivateImplRef.h \
+ qpid/messaging/ProtocolRegistry.h \
+ qpid/messaging/ProtocolRegistry.cpp \
qpid/messaging/Sender.cpp \
qpid/messaging/Receiver.cpp \
qpid/messaging/Session.cpp \
@@ -807,6 +927,8 @@ libqpidmessaging_la_SOURCES = \
qpid/messaging/ReceiverImpl.h \
qpid/messaging/SessionImpl.h \
qpid/messaging/FailoverUpdates.cpp \
+ qpid/messaging/amqp/EncodedMessage.h \
+ qpid/messaging/amqp/EncodedMessage.cpp \
qpid/client/amqp0_10/AcceptTracker.h \
qpid/client/amqp0_10/AcceptTracker.cpp \
qpid/client/amqp0_10/AddressResolution.h \
@@ -832,6 +954,7 @@ libqpidmessaging_la_LDFLAGS = -version-info $(QPIDMESSAGING_VERSION_INFO)
# NOTE: only public header files (which should be in ../include)
# should go in this list. Private headers should go in the SOURCES
# list for one of the libraries or executables that includes it.
+# Also included are the swig descriptor files.
nobase_include_HEADERS += \
../include/qpid/Address.h \
@@ -918,7 +1041,10 @@ nobase_include_HEADERS += \
../include/qpid/types/Exception.h \
../include/qpid/types/Uuid.h \
../include/qpid/types/Variant.h \
- ../include/qpid/types/ImportExport.h
+ ../include/qpid/types/ImportExport.h \
+ ../include/qpid/qpid.i \
+ ../include/qmf/qmfengine.i \
+ ../include/qmf/qmf2.i
# Create the default data directory
install-data-local:
diff --git a/cpp/src/acl.mk b/cpp/src/acl.mk
index 0301f8c754..87821a3741 100644
--- a/cpp/src/acl.mk
+++ b/cpp/src/acl.mk
@@ -31,6 +31,9 @@ acl_la_SOURCES = \
qpid/acl/AclPlugin.cpp \
qpid/acl/AclReader.cpp \
qpid/acl/AclReader.h \
+ qpid/acl/AclResourceCounter.cpp \
+ qpid/acl/AclResourceCounter.h \
+ qpid/acl/AclTopicMatch.h \
qpid/acl/AclValidator.cpp \
qpid/acl/AclValidator.h
@@ -40,4 +43,5 @@ if SUNOS
endif
acl_la_LDFLAGS = $(PLUGINLDFLAGS)
+acl_la_CXXFLAGS = $(AM_CXXFLAGS) -D_IN_QPID_BROKER
diff --git a/cpp/src/amqp.cmake b/cpp/src/amqp.cmake
new file mode 100644
index 0000000000..718e6fe342
--- /dev/null
+++ b/cpp/src/amqp.cmake
@@ -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.
+#
+
+# Optional AMQP1.0 support. Requires proton toolkit.
+
+include(FindPkgConfig)
+
+pkg_check_modules(PROTON libqpid-proton)
+
+set (amqp_default ${amqp_force})
+if (PROTON_FOUND)
+ message(STATUS "Qpid proton found, amqp 1.0 support enabled")
+ set (amqp_default ON)
+else (PROTON_FOUND)
+ message(STATUS "Qpid proton not found, amqp 1.0 support not enabled")
+endif (PROTON_FOUND)
+
+option(BUILD_AMQP "Build with support for AMQP 1.0" ${amqp_default})
+if (BUILD_AMQP)
+
+ if (NOT PROTON_FOUND)
+ message(FATAL_ERROR "Qpid proton not found, required for amqp 1.0 support")
+ endif (NOT PROTON_FOUND)
+
+ foreach(f ${PROTON_CFLAGS})
+ set (PROTON_COMPILE_FLAGS "${PROTON_COMPILE_FLAGS} ${f}")
+ endforeach(f)
+
+ foreach(f ${PROTON_LDFLAGS})
+ set (PROTON_LINK_FLAGS "${PROTON_LINK_FLAGS} ${f}")
+ endforeach(f)
+
+
+ set (amqp_SOURCES
+ qpid/broker/amqp/Connection.h
+ qpid/broker/amqp/Connection.cpp
+ qpid/broker/amqp/DataReader.h
+ qpid/broker/amqp/DataReader.cpp
+ qpid/broker/amqp/Filter.h
+ qpid/broker/amqp/Filter.cpp
+ qpid/broker/amqp/Header.h
+ qpid/broker/amqp/Header.cpp
+ qpid/broker/amqp/ManagedConnection.h
+ qpid/broker/amqp/ManagedConnection.cpp
+ qpid/broker/amqp/ManagedSession.h
+ qpid/broker/amqp/ManagedSession.cpp
+ qpid/broker/amqp/ManagedOutgoingLink.h
+ qpid/broker/amqp/ManagedOutgoingLink.cpp
+ qpid/broker/amqp/Message.h
+ qpid/broker/amqp/Message.cpp
+ qpid/broker/amqp/NodeProperties.h
+ qpid/broker/amqp/NodeProperties.cpp
+ qpid/broker/amqp/Outgoing.h
+ qpid/broker/amqp/Outgoing.cpp
+ qpid/broker/amqp/ProtocolPlugin.cpp
+ qpid/broker/amqp/Sasl.h
+ qpid/broker/amqp/Sasl.cpp
+ qpid/broker/amqp/Session.h
+ qpid/broker/amqp/Session.cpp
+ qpid/broker/amqp/Translation.h
+ qpid/broker/amqp/Translation.cpp
+ )
+ add_library (amqp MODULE ${amqp_SOURCES})
+ target_link_libraries (amqp qpidbroker qpidcommon)
+ set_target_properties (amqp PROPERTIES
+ PREFIX ""
+ COMPILE_FLAGS "${PROTON_COMPILE_FLAGS}"
+ LINK_FLAGS "${PROTON_LINK_FLAGS}")
+ set_target_properties (amqp PROPERTIES COMPILE_DEFINITIONS _IN_QPID_BROKER)
+ install (TARGETS amqp
+ DESTINATION ${QPIDD_MODULE_DIR}
+ COMPONENT ${QPID_COMPONENT_BROKER})
+
+
+ set (amqpc_SOURCES
+ qpid/messaging/amqp/AddressHelper.h
+ qpid/messaging/amqp/AddressHelper.cpp
+ qpid/messaging/amqp/ConnectionContext.h
+ qpid/messaging/amqp/ConnectionContext.cpp
+ qpid/messaging/amqp/ConnectionHandle.h
+ qpid/messaging/amqp/ConnectionHandle.cpp
+ qpid/messaging/amqp/DriverImpl.h
+ qpid/messaging/amqp/DriverImpl.cpp
+ qpid/messaging/amqp/ReceiverContext.h
+ qpid/messaging/amqp/ReceiverContext.cpp
+ qpid/messaging/amqp/ReceiverHandle.h
+ qpid/messaging/amqp/ReceiverHandle.cpp
+ qpid/messaging/amqp/Sasl.h
+ qpid/messaging/amqp/Sasl.cpp
+ qpid/messaging/amqp/SenderContext.h
+ qpid/messaging/amqp/SenderContext.cpp
+ qpid/messaging/amqp/SenderHandle.h
+ qpid/messaging/amqp/SenderHandle.cpp
+ qpid/messaging/amqp/SessionContext.h
+ qpid/messaging/amqp/SessionContext.cpp
+ qpid/messaging/amqp/SessionHandle.h
+ qpid/messaging/amqp/SessionHandle.cpp
+ qpid/messaging/amqp/TcpTransport.h
+ qpid/messaging/amqp/TcpTransport.cpp
+ )
+ add_library (amqpc MODULE ${amqpc_SOURCES})
+ target_link_libraries (amqpc qpidclient qpidcommon)
+ set_target_properties (amqpc PROPERTIES
+ PREFIX ""
+ COMPILE_FLAGS "${PROTON_COMPILE_FLAGS}"
+ LINK_FLAGS "${PROTON_LINK_FLAGS}")
+ install (TARGETS amqpc
+ DESTINATION ${QPIDC_MODULE_DIR}
+ COMPONENT ${QPID_COMPONENT_CLIENT})
+
+endif (BUILD_AMQP)
diff --git a/cpp/src/asyncstore.cmake b/cpp/src/asyncstore.cmake
index 477091a927..017b8fb880 100644
--- a/cpp/src/asyncstore.cmake
+++ b/cpp/src/asyncstore.cmake
@@ -91,6 +91,7 @@ if (UNIX)
PREFIX ""
OUTPUT_NAME asyncStore
SOVERSION ${asyncStore_version}
+ COMPILE_DEFINITIONS _IN_QPID_BROKER
)
target_link_libraries (asyncStore
aio
diff --git a/cpp/src/config.h.cmake b/cpp/src/config.h.cmake
index f55f68afde..5c8c56c749 100644
--- a/cpp/src/config.h.cmake
+++ b/cpp/src/config.h.cmake
@@ -37,8 +37,6 @@
#cmakedefine QPIDC_MODULE_DIR "${QPIDC_MODULE_DIR}"
#cmakedefine QPIDD_MODULE_DIR "${QPIDD_MODULE_DIR}"
-#cmakedefine QPID_LIBEXEC_DIR "${QPID_LIBEXEC_DIR}"
-
#define QPID_SHLIB_PREFIX "${CMAKE_SHARED_LIBRARY_PREFIX}"
#define QPID_MODULE_PREFIX
#cmakedefine QPID_DEBUG_POSTFIX "${QPID_DEBUG_POSTFIX}"
diff --git a/cpp/src/finddb.cmake b/cpp/src/finddb.cmake
new file mode 100644
index 0000000000..fad827cffe
--- /dev/null
+++ b/cpp/src/finddb.cmake
@@ -0,0 +1,74 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+
+if(UNIX)
+# - Find BerkeleyDB
+# Find the BerkeleyDB includes and library
+# This module defines
+# DB_INCLUDE_DIR, where to find db.h, etc.
+# DB_LIBRARIES, the libraries needed to use BerkeleyDB.
+# DB_FOUND, If false, do not try to use BerkeleyDB.
+# also defined, but not for general use are
+# DB_LIBRARY, where to find the BerkeleyDB library.
+
+FIND_PATH(DB_INCLUDE_DIR db.h
+ /usr/local/include/db4
+ /usr/local/include
+ /usr/include/db4
+ /usr/include
+)
+
+SET(DB_NAMES ${DB_NAMES} db_cxx)
+FIND_LIBRARY(DB_LIBRARY
+ NAMES ${DB_NAMES}
+ PATHS /usr/lib /usr/local/lib
+)
+
+IF (DB_LIBRARY AND DB_INCLUDE_DIR)
+ SET(DB_LIBRARIES ${DB_LIBRARY})
+ SET(DB_FOUND "YES")
+ELSE (DB_LIBRARY AND DB_INCLUDE_DIR)
+ UNSET( DB_FOUND )
+ENDIF (DB_LIBRARY AND DB_INCLUDE_DIR)
+
+
+IF (DB_FOUND)
+ IF (NOT DB_FIND_QUIETLY)
+ MESSAGE(STATUS "Found BerkeleyDB: ${DB_LIBRARIES}")
+ ENDIF (NOT DB_FIND_QUIETLY)
+ELSE (DB_FOUND)
+ IF (DB_FIND_REQUIRED)
+ MESSAGE(FATAL_ERROR "Could not find BerkeleyDB library")
+ ENDIF (DB_FIND_REQUIRED)
+ENDIF (DB_FOUND)
+
+# Deprecated declarations.
+SET (NATIVE_DB_INCLUDE_PATH ${DB_INCLUDE_DIR} )
+GET_FILENAME_COMPONENT (NATIVE_DB_LIB_PATH ${DB_LIBRARY} PATH)
+
+MARK_AS_ADVANCED(
+ DB_LIBRARY
+ DB_INCLUDE_DIR
+)
+
+else(UNIX)
+ MESSAGE(STATUS "BerkeleyDB is ignored on non-Unix platforms")
+ UNSET( DB_FOUND )
+endif(UNIX) \ No newline at end of file
diff --git a/cpp/src/generate.sh b/cpp/src/generate.sh
deleted file mode 100755
index 581a45ff7f..0000000000
--- a/cpp/src/generate.sh
+++ /dev/null
@@ -1,67 +0,0 @@
-# !/bin/sh
-
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Generate code from AMQP specification.
-# specs and gentools_dir are set by Makefile
-#
-set -e
-
-test -z "$JAVA" && JAVA=java ;
-test -z "$JAVAC" && JAVAC=javac ;
-
-srcdir=`dirname $0`
-checkspecs() {
- for s in $specs; do test -f $s || return 1; done
- return 0
-}
-
-# Can we generate code?
-if { test -d $gentools_dir && checkspecs &&
- which $JAVA && which $JAVAC; } > /dev/null;
-then
- echo "Generating code."
- mkdir -p gen/qpid/framing
- ( cd $gentools_dir/src && $JAVAC `find -name '*.java' -print` ; )
- $JAVA -cp $gentools_dir/src org.apache.qpid.gentools.Main \
- -c -o gen/qpid/framing -t $gentools_dir/templ.cpp $specs
- GENERATED=yes
-fi
-
-# Print a Makefile variable assignment.
-make_assign() {
- echo -n "$1 = "; shift
- prefix=$1; shift
- for f in $*; do echo "\\" ; echo -n " $prefix$f "; done
- echo
-}
-
-# Generate a Makefile fragment
-(
- make_assign "generated_cpp" "" `find gen -name '*.cpp' -print`
- make_assign "generated_h" "" `find gen -name '*.h' -print`
- if test x$GENERATED = xyes; then
- make_assign "generator" "" $specs \
- `find ../gentools \( -name '*.java' -o -name '*.tmpl' \) -print`
- fi
-) > generate.mk-t
-mv generate.mk-t $srcdir/generate.mk
-
-
-
diff --git a/cpp/src/ha.mk b/cpp/src/ha.mk
index 96a3d872e4..31b3bc243d 100644
--- a/cpp/src/ha.mk
+++ b/cpp/src/ha.mk
@@ -50,10 +50,13 @@ ha_la_SOURCES = \
qpid/ha/ReplicationTest.cpp \
qpid/ha/ReplicationTest.h \
qpid/ha/Settings.h \
+ qpid/ha/StatusCheck.cpp \
+ qpid/ha/StatusCheck.h \
qpid/ha/RemoteBackup.cpp \
qpid/ha/RemoteBackup.h \
qpid/ha/types.cpp \
qpid/ha/types.h
-ha_la_LIBADD = libqpidbroker.la
+ha_la_LIBADD = libqpidbroker.la libqpidmessaging.la
ha_la_LDFLAGS = $(PLUGINLDFLAGS)
+ha_la_CXXFLAGS = $(AM_CXXFLAGS) -D_IN_QPID_BROKER
diff --git a/cpp/src/legacystore.cmake b/cpp/src/legacystore.cmake
new file mode 100644
index 0000000000..10a166f50c
--- /dev/null
+++ b/cpp/src/legacystore.cmake
@@ -0,0 +1,157 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+# Legacy store library CMake fragment, to be included in CMakeLists.txt
+#
+
+if (DEFINED legacystore_force)
+ set (legacystore_default ${legacystore_force})
+else (DEFINED legacystore_force)
+ set (legacystore_default OFF)
+ if (UNIX)
+ #
+ # Find required BerkelyDB
+ #
+ include (finddb.cmake)
+ if (DB_FOUND)
+ #
+ # find libaio
+ #
+ CHECK_LIBRARY_EXISTS (aio io_queue_init "" HAVE_AIO)
+ CHECK_INCLUDE_FILES (libaio.h HAVE_AIO_H)
+ if (HAVE_AIO AND HAVE_AIO_H)
+ #
+ # find libuuid
+ #
+ CHECK_LIBRARY_EXISTS (uuid uuid_compare "" HAVE_UUID)
+ CHECK_INCLUDE_FILES(uuid/uuid.h HAVE_UUID_H)
+ IF (HAVE_UUID AND HAVE_UUID_H)
+ #
+ # allow legacystore to be built
+ #
+ set (legacystore_default ON)
+ ENDIF (HAVE_UUID AND HAVE_UUID_H)
+ endif (HAVE_AIO AND HAVE_AIO_H)
+ endif (DB_FOUND)
+ endif (UNIX)
+endif (DEFINED legacystore_force)
+
+option(BUILD_LEGACYSTORE "Build legacystore persistent store" ${legacystore_default})
+
+if (BUILD_LEGACYSTORE)
+ if (NOT UNIX)
+ message(FATAL_ERROR "Legacystore produced only on Unix platforms")
+ endif (NOT UNIX)
+ if (NOT DB_FOUND)
+ message(FATAL_ERROR "Legacystore requires BerkeleyDB which is absent.")
+ endif (NOT DB_FOUND)
+ if (NOT HAVE_AIO)
+ message(FATAL_ERROR "Legacystore requires libaio which is absent.")
+ endif (NOT HAVE_AIO)
+ if (NOT HAVE_AIO_H)
+ message(FATAL_ERROR "Legacystore requires libaio.h which is absent.")
+ endif (NOT HAVE_AIO_H)
+ if (NOT HAVE_UUID)
+ message(FATAL_ERROR "Legacystore requires uuid which is absent.")
+ endif (NOT HAVE_UUID)
+ if (NOT HAVE_UUID_H)
+ message(FATAL_ERROR "Legacystore requires uuid.h which is absent.")
+ endif (NOT HAVE_UUID_H)
+
+ # Journal source files
+ set (legacy_jrnl_SOURCES
+ qpid/legacystore/jrnl/aio.cpp
+ qpid/legacystore/jrnl/cvar.cpp
+ qpid/legacystore/jrnl/data_tok.cpp
+ qpid/legacystore/jrnl/deq_rec.cpp
+ qpid/legacystore/jrnl/enq_map.cpp
+ qpid/legacystore/jrnl/enq_rec.cpp
+ qpid/legacystore/jrnl/fcntl.cpp
+ qpid/legacystore/jrnl/jcntl.cpp
+ qpid/legacystore/jrnl/jdir.cpp
+ qpid/legacystore/jrnl/jerrno.cpp
+ qpid/legacystore/jrnl/jexception.cpp
+ qpid/legacystore/jrnl/jinf.cpp
+ qpid/legacystore/jrnl/jrec.cpp
+ qpid/legacystore/jrnl/lp_map.cpp
+ qpid/legacystore/jrnl/lpmgr.cpp
+ qpid/legacystore/jrnl/pmgr.cpp
+ qpid/legacystore/jrnl/rmgr.cpp
+ qpid/legacystore/jrnl/rfc.cpp
+ qpid/legacystore/jrnl/rrfc.cpp
+ qpid/legacystore/jrnl/slock.cpp
+ qpid/legacystore/jrnl/smutex.cpp
+ qpid/legacystore/jrnl/time_ns.cpp
+ qpid/legacystore/jrnl/txn_map.cpp
+ qpid/legacystore/jrnl/txn_rec.cpp
+ qpid/legacystore/jrnl/wmgr.cpp
+ qpid/legacystore/jrnl/wrfc.cpp
+ )
+
+ # legacyStore source files
+ set (legacy_store_SOURCES
+ qpid/legacystore/StorePlugin.cpp
+ qpid/legacystore/BindingDbt.cpp
+ qpid/legacystore/BufferValue.cpp
+ qpid/legacystore/DataTokenImpl.cpp
+ qpid/legacystore/IdDbt.cpp
+ qpid/legacystore/IdSequence.cpp
+ qpid/legacystore/JournalImpl.cpp
+ qpid/legacystore/MessageStoreImpl.cpp
+ qpid/legacystore/PreparedTransaction.cpp
+ qpid/legacystore/TxnCtxt.cpp
+ )
+
+ # legacyStore include directories
+ get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
+ set (legacy_include_DIRECTORIES
+ ${dirs}
+ ${CMAKE_CURRENT_SOURCE_DIR}/qpid/legacystore
+ )
+
+ if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/db-inc.h)
+ message(STATUS "Including BDB from ${DB_INCLUDE_DIR}/db_cxx.h")
+ file(WRITE
+ ${CMAKE_CURRENT_BINARY_DIR}/db-inc.h
+ "#include <${DB_INCLUDE_DIR}/db_cxx.h>")
+ endif()
+
+ add_library (legacystore SHARED
+ ${legacy_jrnl_SOURCES}
+ ${legacy_store_SOURCES}
+ ${legacy_qmf_SOURCES}
+ )
+
+ set_target_properties (legacystore PROPERTIES
+ PREFIX ""
+ COMPILE_DEFINITIONS _IN_QPID_BROKER
+ OUTPUT_NAME legacystore
+ SOVERSION ${legacystore_version}
+ INCLUDE_DIRECTORIES "${legacy_include_DIRECTORIES}"
+ )
+
+ target_link_libraries (legacystore
+ aio
+ uuid
+ qpidcommon qpidtypes qpidbroker
+ ${DB_LIBRARY}
+ )
+else (BUILD_LEGACYSTORE)
+ message(STATUS "Legacystore is excluded from build.")
+endif (BUILD_LEGACYSTORE)
diff --git a/cpp/src/posix/QpiddBroker.cpp b/cpp/src/posix/QpiddBroker.cpp
index 76e3bb6674..a681a6d18d 100644
--- a/cpp/src/posix/QpiddBroker.cpp
+++ b/cpp/src/posix/QpiddBroker.cpp
@@ -48,6 +48,10 @@ BootstrapOptions::BootstrapOptions(const char* argv0)
add(log);
}
+void BootstrapOptions::usage() const {
+ cout << "Usage: qpidd [OPTIONS]" << endl << endl << *this << endl;
+}
+
namespace {
const std::string TCP = "tcp";
}
@@ -111,17 +115,17 @@ void QpiddOptions::usage() const {
// Set the broker pointer on the signal handler, then reset at end of scope.
// This is to ensure that the signal handler doesn't keep a broker
// reference after main() has returned.
-//
+//
struct ScopedSetBroker {
ScopedSetBroker(const boost::intrusive_ptr<Broker>& broker) {
qpid::broker::SignalHandler::setBroker(broker.get());
}
~ScopedSetBroker() { qpid::broker::SignalHandler::setBroker(0); }
};
-
+
struct QpiddDaemon : public Daemon {
QpiddPosixOptions *options;
-
+
QpiddDaemon(std::string pidDir, QpiddPosixOptions *opts)
: Daemon(pidDir), options(opts) {}
@@ -129,7 +133,7 @@ struct QpiddDaemon : public Daemon {
void parent() {
uint16_t port = wait(options->daemon.wait);
if (options->parent->broker.port == 0 || options->daemon.transport != TCP)
- cout << port << endl;
+ cout << port << endl;
}
/** Code for forked child process */
@@ -140,7 +144,7 @@ struct QpiddDaemon : public Daemon {
uint16_t port=brokerPtr->getPort(options->daemon.transport);
ready(port); // Notify parent.
if (options->parent->broker.enableMgmt && (options->parent->broker.port == 0 || options->daemon.transport != TCP)) {
- dynamic_cast<qmf::org::apache::qpid::broker::Broker*>(brokerPtr->GetManagementObject())->set_port(port);
+ boost::dynamic_pointer_cast<qmf::org::apache::qpid::broker::Broker>(brokerPtr->GetManagementObject())->set_port(port);
}
brokerPtr->run();
}
@@ -162,12 +166,12 @@ int QpiddBroker::execute (QpiddOptions *options) {
QPID_LOG(notice, "Cannot stop broker: " << e.what());
return 1;
}
- if (pid < 0)
+ if (pid < 0)
return 1;
if (myOptions->daemon.check)
cout << pid << endl;
if (myOptions->daemon.quit) {
- if (kill(pid, SIGINT) < 0)
+ if (kill(pid, SIGINT) < 0)
throw Exception("Failed to stop daemon: " + qpid::sys::strError(errno));
// Wait for the process to die before returning
int retry=10000; // Try up to 10 seconds
@@ -196,7 +200,7 @@ int QpiddBroker::execute (QpiddOptions *options) {
uint16_t port = brokerPtr->getPort(myOptions->daemon.transport);
cout << port << endl;
if (options->broker.enableMgmt) {
- dynamic_cast<qmf::org::apache::qpid::broker::Broker*>(brokerPtr->GetManagementObject())->set_port(port);
+ boost::dynamic_pointer_cast<qmf::org::apache::qpid::broker::Broker>(brokerPtr->GetManagementObject())->set_port(port);
}
}
brokerPtr->run();
diff --git a/cpp/src/qpid/Modules.cpp b/cpp/src/qpid/Modules.cpp
index 727e05d212..049ededaa7 100644
--- a/cpp/src/qpid/Modules.cpp
+++ b/cpp/src/qpid/Modules.cpp
@@ -24,11 +24,7 @@
#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;
+#include "qpid/sys/FileSysDir.h"
namespace {
@@ -43,7 +39,7 @@ inline std::string& suffix() {
}
bool isShlibName(const std::string& name) {
- return name.find (suffix()) == name.length() - suffix().length();
+ return name.substr(name.size()-suffix().size()) == suffix();
}
}
@@ -59,39 +55,40 @@ ModuleOptions::ModuleOptions(const std::string& defaultModuleDir)
("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();
+void tryShlib(const std::string& libname) {
+ sys::Shlib shlib( isShlibName(libname) ? libname : (libname + suffix()));
+}
+
+namespace {
+
+void tryOnlyShlib(const std::string& libname) throw() {
try {
- sys::Shlib shlib(libname);
+ if (isShlibName(libname)) sys::Shlib shlib( libname );
}
catch (const std::exception& /*e*/) {
- if (!noThrow)
- throw;
}
}
+}
+
void loadModuleDir (std::string dirname, bool isDefault)
{
- fs::path dirPath (dirname, fs::native);
- if (!fs::exists (dirPath))
+ sys::FileSysDir dirPath (dirname);
+
+ bool exists;
+ try
{
- if (isDefault)
- return;
- throw Exception ("Directory not found: " + dirname);
+ exists = dirPath.exists();
+ } catch (Exception& e) {
+ throw Exception ("Invalid value for module-dir: " + e.getMessage());
}
- if (!fs::is_directory(dirPath))
- {
- throw Exception ("Invalid value for module-dir: " + dirname + " is not a directory");
+ if (!exists) {
+ if (isDefault) return;
+ throw Exception ("Directory not found: " + dirname);
}
- 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);
- }
+ dirPath.forEachFile(&tryOnlyShlib);
}
} // namespace qpid
diff --git a/cpp/src/qpid/Modules.h b/cpp/src/qpid/Modules.h
index 159dd156c1..9fb91d60eb 100644
--- a/cpp/src/qpid/Modules.h
+++ b/cpp/src/qpid/Modules.h
@@ -36,7 +36,7 @@ struct ModuleOptions : public qpid::Options {
QPID_COMMON_EXTERN ModuleOptions(const std::string& defaultModuleDir);
};
-QPID_COMMON_EXTERN void tryShlib(const char* libname, bool noThrow);
+QPID_COMMON_EXTERN void tryShlib(const std::string& libname);
QPID_COMMON_EXTERN void loadModuleDir (std::string dirname, bool isDefault);
} // namespace qpid
diff --git a/cpp/src/qpid/NullSaslServer.cpp b/cpp/src/qpid/NullSaslServer.cpp
new file mode 100644
index 0000000000..40bd9ebbc6
--- /dev/null
+++ b/cpp/src/qpid/NullSaslServer.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 "NullSaslServer.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/SecurityLayer.h"
+#include <assert.h>
+#include <boost/format.hpp>
+
+namespace qpid {
+NullSaslServer::NullSaslServer(const std::string& r) : realm(r) {}
+NullSaslServer::Status NullSaslServer::start(const std::string& mechanism, const std::string* response, std::string& /*challenge*/)
+{
+ if (mechanism == "PLAIN") {
+ if (response) {
+ std::string uid;
+ std::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 != std::string::npos) uid = response->substr(1, i-1);
+ } else if (i != std::string::npos) {
+ //authorization id is first null delimited field
+ uid = response->substr(0, i);
+ } else {
+ QPID_LOG(error, "Invalid PLAIN request, null delimiter not found in response data");
+ return FAIL;
+ }
+ if (!uid.empty()) {
+ //append realm if it has not already been added
+ i = uid.find(realm);
+ if (i == std::string::npos || realm.size() + i < uid.size()) {
+ uid = boost::str(boost::format("%1%@%2%") % uid % realm);
+ }
+ userid = uid;
+ }
+ return OK;
+ } else {
+ QPID_LOG(error, "Invalid PLAIN request, expected response containing user credentials");
+ return FAIL;
+ }
+ } else if (mechanism == "ANONYMOUS") {
+ userid = "anonymous";
+ return OK;
+ } else {
+ return FAIL;
+ }
+}
+
+NullSaslServer::Status NullSaslServer::step(const std::string* /*response*/, std::string& /*challenge*/)
+{
+ assert(false);
+ return FAIL;
+}
+std::string NullSaslServer::getMechanisms()
+{
+ return std::string("ANONYMOUS PLAIN");
+}
+std::string NullSaslServer::getUserid()
+{
+ return userid;
+}
+
+std::auto_ptr<qpid::sys::SecurityLayer> NullSaslServer::getSecurityLayer(size_t)
+{
+ return std::auto_ptr<qpid::sys::SecurityLayer>();
+}
+
+} // namespace qpid
diff --git a/cpp/src/qpid/NullSaslServer.h b/cpp/src/qpid/NullSaslServer.h
new file mode 100644
index 0000000000..810defe574
--- /dev/null
+++ b/cpp/src/qpid/NullSaslServer.h
@@ -0,0 +1,49 @@
+#ifndef QPID_NULLSASLSERVER_H
+#define QPID_NULLSASLSERVER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/SaslServer.h"
+
+namespace qpid {
+
+/**
+ * Dummy implementation of the SASL server role. This will advertise
+ * ANONYMOUS and PLAIN, and parse the reponse data for those
+ * accordingly, but will make no attempt to actually authenticate
+ * users.
+ */
+class NullSaslServer : public SaslServer
+{
+ public:
+ NullSaslServer(const std::string& realm);
+ Status start(const std::string& mechanism, const std::string* response, std::string& challenge);
+ Status step(const std::string* response, std::string& challenge);
+ std::string getMechanisms();
+ std::string getUserid();
+ std::auto_ptr<qpid::sys::SecurityLayer> getSecurityLayer(size_t);
+ private:
+ std::string realm;
+ std::string userid;
+};
+} // namespace qpid
+
+#endif /*!QPID_NULLSASLSERVER_H*/
diff --git a/cpp/src/qpid/Options.cpp b/cpp/src/qpid/Options.cpp
index 35787aa8f3..c0e955e2b3 100644
--- a/cpp/src/qpid/Options.cpp
+++ b/cpp/src/qpid/Options.cpp
@@ -41,13 +41,13 @@ struct EnvOptMapper {
return desc->long_name().size() == env.size() &&
std::equal(env.begin(), env.end(), desc->long_name().begin(), &matchChar);
}
-
+
static bool matchCase(const string& env, boost::shared_ptr<po::option_description> desc) {
return env == desc->long_name();
}
-
+
EnvOptMapper(const Options& o) : opts(o) {}
-
+
string operator()(const string& envVar) {
static const std::string prefix("QPID_");
if (envVar.substr(0, prefix.size()) == prefix) {
@@ -74,31 +74,60 @@ struct EnvOptMapper {
}
- string configFileLine (string& line) {
-
- if ( isComment ( line ) )
- return string();
+ void badArg ( string& line ) {
+ ostringstream msg;
+ msg << "Bad argument: |" << line << "|\n";
+ throw Exception(msg.str());
+ }
+
+
+ string configFileLine (string& line, bool allowUnknowns=true) {
- size_t pos = line.find ('=');
- if (pos == string::npos)
+ if ( isComment ( line ) ) {
return string();
+ }
+
+ size_t pos = line.find ('=');
+ if (pos == string::npos) {
+ if ( allowUnknowns ) {
+ return string();
+ }
+ else {
+ badArg ( line );
+ }
+ }
string key = line.substr (0, pos);
#if (BOOST_VERSION >= 103300)
typedef const std::vector< boost::shared_ptr<po::option_description> > OptDescs;
- OptDescs::const_iterator i =
+ OptDescs::const_iterator i =
find_if(opts.options().begin(), opts.options().end(), boost::bind(matchCase, key, _1));
if (i != opts.options().end())
return string (line) + "\n";
- else
- return string();
+ else {
+ if ( allowUnknowns ) {
+ return string();
+ }
+ else {
+ badArg ( line );
+ }
+ }
#else
- // Use 'count' to see if this option exists. Using 'find' will SEGV or hang
- // if the option has not been defined yet.
+ // Use 'count' to see if this option exists. Using 'find' will
+ // SEGV or hang if the option has not been defined yet.
if ( opts.count(key.c_str()) > 0 )
return string ( line ) + "\n";
- else
- return string ( );
+ else {
+ if ( allowUnknowns ) {
+ return string ( );
+ }
+ else {
+ badArg ( line );
+ }
+ }
#endif
+ // Control will not arrive here, but the compiler things it could.
+ // Calls to badArg(), that I used above, throw.
+ return string();
}
const Options& opts;
@@ -109,8 +138,8 @@ std::string prettyArg(const std::string& name, const std::string& value) {
return value.empty() ? name+" " : name+" ("+value+") ";
}
-Options::Options(const string& name) :
- po::options_description(name)
+Options::Options(const string& name) :
+ po::options_description(name)
{
}
@@ -150,6 +179,7 @@ void Options::parse(int argc, char const* const* argv, const std::string& config
if (!configFile.empty()) {
parsing="configuration file "+configFile;
ifstream conf(configFile.c_str());
+ conf.peek();
if (conf.good()) {
// Remove this hack when we get a stable version of boost that
// can allow unregistered options in config files.
@@ -159,7 +189,7 @@ void Options::parse(int argc, char const* const* argv, const std::string& config
while (!conf.eof()) {
string line;
getline (conf, line);
- filtered << mapper.configFileLine (line);
+ filtered << mapper.configFileLine (line, allowUnknown);
}
po::store(po::parse_config_file(filtered, *this), vm);
@@ -197,5 +227,33 @@ CommonOptions::CommonOptions(const string& name, const string& configfile, const
}
+
+bool Options::findArg(int argc, char const* const* argv, const std::string& theArg)
+{
+ const string parsing("command line options");
+ bool result(false);
+ try {
+ if (argc > 0 && argv != 0) {
+ po::command_line_parser clp = po::command_line_parser(argc, const_cast<char**>(argv)).
+ options(*this).allow_unregistered();
+ po::parsed_options opts = clp.run();
+
+ for (std::vector< po::basic_option<char> >::iterator
+ i = opts.options.begin(); i != opts.options.end(); i++) {
+ if (theArg.compare(i->string_key) == 0) {
+ result = true;
+ break;
+ }
+ }
+ }
+ return result;
+ }
+ catch (const std::exception& e) {
+ ostringstream msg;
+ msg << "Error in " << parsing << ": " << e.what() << endl;
+ throw Exception(msg.str());
+ }
+}
+
} // namespace qpid
diff --git a/cpp/src/qpid/Sasl.h b/cpp/src/qpid/Sasl.h
index 4d579fa051..efda92d718 100644
--- a/cpp/src/qpid/Sasl.h
+++ b/cpp/src/qpid/Sasl.h
@@ -34,7 +34,7 @@ struct SecuritySettings;
}
/**
- * Interface to SASL support. This class is implemented by platform-specific
+ * Interface to support for the SASL client role. This class is implemented by platform-specific
* SASL providers.
*/
class Sasl
diff --git a/cpp/src/qpid/SaslFactory.cpp b/cpp/src/qpid/SaslFactory.cpp
index a8d1f94c1e..4d81a793ee 100644
--- a/cpp/src/qpid/SaslFactory.cpp
+++ b/cpp/src/qpid/SaslFactory.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,7 +18,9 @@
* under the License.
*
*/
-#include "qpid//SaslFactory.h"
+#include "qpid/SaslFactory.h"
+#include "qpid/SaslServer.h"
+#include "qpid/NullSaslServer.h"
#include <map>
#include <string.h>
@@ -32,6 +34,7 @@ namespace qpid {
//Null implementation
+
SaslFactory::SaslFactory() {}
SaslFactory::~SaslFactory() {}
@@ -50,6 +53,12 @@ std::auto_ptr<Sasl> SaslFactory::create( const std::string &, const std::string
return std::auto_ptr<Sasl>();
}
+std::auto_ptr<SaslServer> SaslFactory::createServer(const std::string& realm, bool /*encryptionRequired*/, const qpid::sys::SecuritySettings&)
+{
+ std::auto_ptr<SaslServer> server(new NullSaslServer(realm));
+ return server;
+}
+
qpid::sys::Mutex SaslFactory::lock;
std::auto_ptr<SaslFactory> SaslFactory::instance;
@@ -140,6 +149,22 @@ typedef int CallbackProc();
qpid::sys::Mutex SaslFactory::lock;
std::auto_ptr<SaslFactory> SaslFactory::instance;
+class CyrusSaslServer : public SaslServer
+{
+ public:
+ CyrusSaslServer(const std::string& realm, bool encryptionRequired, const qpid::sys::SecuritySettings& external);
+ ~CyrusSaslServer();
+ Status start(const std::string& mechanism, const std::string* response, std::string& challenge);
+ Status step(const std::string* response, std::string& challenge);
+ std::string getMechanisms();
+ std::string getUserid();
+ std::auto_ptr<qpid::sys::SecurityLayer> getSecurityLayer(size_t);
+ private:
+ std::string realm;
+ std::string userid;
+ sasl_conn_t *sasl_conn;
+};
+
SaslFactory::SaslFactory()
{
sasl_callback_t* callbacks = 0;
@@ -169,6 +194,12 @@ std::auto_ptr<Sasl> SaslFactory::create(const std::string & username, const std:
return sasl;
}
+std::auto_ptr<SaslServer> SaslFactory::createServer(const std::string& realm, bool encryptionRequired, const qpid::sys::SecuritySettings& external)
+{
+ std::auto_ptr<SaslServer> server(new CyrusSaslServer(realm, encryptionRequired, external));
+ return server;
+}
+
CyrusSasl::CyrusSasl(const std::string & username, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf, bool allowInteraction)
: conn(0), settings(username, password, serviceName, hostName, minSsf, maxSsf), allowInteraction(allowInteraction)
{
@@ -377,7 +408,180 @@ std::auto_ptr<SecurityLayer> CyrusSasl::getSecurityLayer(uint16_t maxFrameSize)
std::auto_ptr<SecurityLayer> securityLayer;
if (ssf) {
QPID_LOG(info, "Installing security layer, SSF: "<< ssf);
- securityLayer = std::auto_ptr<SecurityLayer>(new CyrusSecurityLayer(conn, maxFrameSize));
+ securityLayer = std::auto_ptr<SecurityLayer>(new CyrusSecurityLayer(conn, maxFrameSize, ssf));
+ }
+ return securityLayer;
+}
+
+CyrusSaslServer::CyrusSaslServer(const std::string& r, bool encryptionRequired, const qpid::sys::SecuritySettings& external) : realm(r), sasl_conn(0)
+{
+ int code = sasl_server_new(BROKER_SASL_NAME, /* Service name */
+ NULL, /* Server FQDN, gethostname() */
+ realm.c_str(), /* Authentication realm */
+ NULL, /* Local IP, needed for some mechanism */
+ NULL, /* Remote IP, needed for some mechanism */
+ NULL, /* Callbacks */
+ 0, /* Connection flags */
+ &sasl_conn);
+
+ if (SASL_OK != code) {
+ QPID_LOG(error, "SASL: Connection creation failed: [" << code << "] " << sasl_errdetail(sasl_conn));
+
+ // TODO: Change this to an exception signaling
+ // server error, when one is available
+ throw qpid::framing::ConnectionForcedException("Unable to perform authentication");
+ }
+
+ sasl_security_properties_t secprops;
+
+ //TODO: should the actual SSF values be configurable here?
+ secprops.min_ssf = encryptionRequired ? 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.
+ QPID_LOG(debug, "External ssf=" << external.ssf << " and auth=" << external.authid);
+ sasl_ssf_t external_ssf = (sasl_ssf_t) external.ssf;
+ if (external_ssf) {
+ int result = sasl_setprop(sasl_conn, SASL_SSF_EXTERNAL, &external_ssf);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external SSF: " << result));
+ }
+
+ secprops.max_ssf = secprops.min_ssf = 0;
+ }
+
+ QPID_LOG(debug, "min_ssf: " << secprops.min_ssf <<
+ ", max_ssf: " << secprops.max_ssf <<
+ ", external_ssf: " << external_ssf );
+
+ if (!external.authid.empty()) {
+ const char* external_authid = external.authid.c_str();
+ int result = sasl_setprop(sasl_conn, SASL_AUTH_EXTERNAL, external_authid);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external auth: " << result));
+ }
+
+ QPID_LOG(debug, "external auth detected and set to " << external_authid);
+ }
+ secprops.maxbufsize = 65535;
+ secprops.property_names = 0;
+ secprops.property_values = 0;
+ secprops.security_flags = 0; /* or SASL_SEC_NOANONYMOUS etc as appropriate */
+ /*
+ * The nodict flag restricts SASL authentication mechanisms
+ * to those that are not susceptible to dictionary attacks.
+ * They are:
+ * SRP
+ * PASSDSS-3DES-1
+ * EXTERNAL
+ */
+ if (external.nodict) secprops.security_flags |= SASL_SEC_NODICTIONARY;
+ int result = sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL error: " << result));
+ }
+}
+
+CyrusSaslServer::~CyrusSaslServer()
+{
+ if (sasl_conn) {
+ sasl_dispose(&sasl_conn);
+ sasl_conn = 0;
+ }
+}
+
+CyrusSaslServer::Status CyrusSaslServer::start(const std::string& mechanism, const std::string* response, std::string& chllng)
+{
+ const char *challenge;
+ unsigned int challenge_len;
+
+ // This should be at same debug level as mech list in getMechanisms().
+ QPID_LOG(info, "SASL: Starting authentication with mechanism: " << mechanism);
+ int code = sasl_server_start(sasl_conn,
+ mechanism.c_str(),
+ (response ? response->c_str() : 0), (response ? response->size() : 0),
+ &challenge, &challenge_len);
+ switch (code) {
+ case SASL_OK:
+ return SaslServer::OK;
+ case SASL_CONTINUE:
+ chllng = std::string(challenge, challenge_len);
+ return SaslServer::CHALLENGE;
+ case SASL_NOMECH:
+ QPID_LOG(info, "Unsupported mechanism: " << mechanism);
+ default:
+ return SaslServer::FAIL;
+ }
+}
+
+CyrusSaslServer::Status CyrusSaslServer::step(const std::string* response, std::string& chllng)
+{
+ const char *challenge;
+ unsigned int challenge_len;
+
+ int code = sasl_server_step(sasl_conn,
+ (response ? response->c_str() : 0), (response ? response->size() : 0),
+ &challenge, &challenge_len);
+
+ switch (code) {
+ case SASL_OK:
+ return SaslServer::OK;
+ case SASL_CONTINUE:
+ chllng = std::string(challenge, challenge_len);
+ return SaslServer::CHALLENGE;
+ default:
+ return SaslServer::FAIL;
+ }
+
+}
+std::string CyrusSaslServer::getMechanisms()
+{
+ const char *separator = " ";
+ const char *list;
+ unsigned int list_len;
+ int count;
+
+ int code = sasl_listmech(sasl_conn, NULL,
+ "", separator, "",
+ &list, &list_len,
+ &count);
+
+ if (SASL_OK != code) {
+ QPID_LOG(info, "SASL: Mechanism listing failed: " << sasl_errdetail(sasl_conn));
+
+ // TODO: Change this to an exception signaling
+ // server error, when one is available
+ throw qpid::framing::ConnectionForcedException("Mechanism listing failed");
+ } else {
+ std::string mechanisms(list, list_len);
+ QPID_LOG(info, "SASL: Mechanism list: " << mechanisms);
+ return mechanisms;
+ }
+}
+std::string CyrusSaslServer::getUserid()
+{
+ const void* ptr;
+ int code = sasl_getprop(sasl_conn, SASL_USERNAME, &ptr);
+ if (SASL_OK == code) {
+ userid = static_cast<const char*>(ptr);
+ } else {
+ QPID_LOG(warning, "Failed to retrieve sasl username");
+ }
+ return userid;
+}
+
+std::auto_ptr<SecurityLayer> CyrusSaslServer::getSecurityLayer(size_t maxFrameSize)
+{
+ const void* value(0);
+ int result = sasl_getprop(sasl_conn, SASL_SSF, &value);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL error: " << sasl_errdetail(sasl_conn)));
+ }
+ uint ssf = *(reinterpret_cast<const unsigned*>(value));
+ std::auto_ptr<SecurityLayer> securityLayer;
+ if (ssf) {
+ securityLayer = std::auto_ptr<SecurityLayer>(new CyrusSecurityLayer(sasl_conn, maxFrameSize, ssf));
}
return securityLayer;
}
@@ -428,7 +632,6 @@ int getPasswordFromSettings(sasl_conn_t* conn, void* context, int /*id*/, sasl_s
return SASL_FAIL;
}
}
-
} // namespace qpid
#endif
diff --git a/cpp/src/qpid/SaslFactory.h b/cpp/src/qpid/SaslFactory.h
index 8554597147..da7e892f1d 100644
--- a/cpp/src/qpid/SaslFactory.h
+++ b/cpp/src/qpid/SaslFactory.h
@@ -26,7 +26,7 @@
#include <memory>
namespace qpid {
-
+class SaslServer;
/**
* Factory for instances of the Sasl interface through which Sasl
* support is provided to a ConnectionHandler.
@@ -35,6 +35,7 @@ class SaslFactory
{
public:
QPID_COMMON_EXTERN std::auto_ptr<Sasl> create(const std::string & userName, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf, bool allowInteraction=true );
+ QPID_COMMON_EXTERN std::auto_ptr<SaslServer> createServer(const std::string& realm, bool encryptionRequired, const qpid::sys::SecuritySettings&);
QPID_COMMON_EXTERN static SaslFactory& getInstance();
QPID_COMMON_EXTERN ~SaslFactory();
private:
diff --git a/cpp/src/qpid/SaslServer.h b/cpp/src/qpid/SaslServer.h
new file mode 100644
index 0000000000..a707a468eb
--- /dev/null
+++ b/cpp/src/qpid/SaslServer.h
@@ -0,0 +1,48 @@
+#ifndef QPID_SASLSERVER_H
+#define QPID_SASLSERVER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <string>
+#include <memory>
+
+namespace qpid {
+namespace sys {
+class SecurityLayer;
+}
+/**
+ *
+ */
+class SaslServer
+{
+ public:
+ typedef enum {OK, FAIL, CHALLENGE} Status;
+ virtual ~SaslServer() {}
+ virtual Status start(const std::string& mechanism, const std::string* response, std::string& challenge) = 0;
+ virtual Status step(const std::string* response, std::string& challenge) = 0;
+ virtual std::string getMechanisms() = 0;
+ virtual std::string getUserid() = 0;
+ virtual std::auto_ptr<qpid::sys::SecurityLayer> getSecurityLayer(size_t) = 0;
+ private:
+};
+} // namespace qpid
+
+#endif /*!QPID_SASLSERVER_H*/
diff --git a/cpp/src/qpid/Url.cpp b/cpp/src/qpid/Url.cpp
index 840f46e928..21de32aaa3 100644
--- a/cpp/src/qpid/Url.cpp
+++ b/cpp/src/qpid/Url.cpp
@@ -64,19 +64,6 @@ class ProtocolTags {
Url::Invalid::Invalid(const string& s) : Exception(s) {}
-Url Url::getHostNameUrl(uint16_t port) {
- Address address("tcp", std::string(), port);
- if (!sys::SystemInfo::getLocalHostname(address))
- throw Url::Invalid(QPID_MSG("Cannot get host name: " << qpid::sys::strError(errno)));
- return Url(address);
-}
-
-Url Url::getIpAddressesUrl(uint16_t port) {
- Url url;
- sys::SystemInfo::getLocalIpAddresses(port, url);
- return url;
-}
-
string Url::str() const {
if (cache.empty() && !this->empty()) {
ostringstream os;
diff --git a/cpp/src/qpid/UrlArray.h b/cpp/src/qpid/UrlArray.h
index ce9e42f248..f0065f0f0c 100644
--- a/cpp/src/qpid/UrlArray.h
+++ b/cpp/src/qpid/UrlArray.h
@@ -1,5 +1,5 @@
-#ifndef QPID_CLUSTER_URLARRAY_H
-#define QPID_CLUSTER_URLARRAY_H
+#ifndef QPID_URLARRAY_H
+#define QPID_URLARRAY_H
/*
*
@@ -33,4 +33,4 @@ QPID_COMMON_EXTERN std::vector<Url> urlArrayToVector(const framing::Array& array
QPID_COMMON_EXTERN framing::Array vectorToUrlArray(const std::vector<Url>& urls);
} // namespace qpid
-#endif /* !QPID_CLUSTER_URLARRAY_H */
+#endif /* !QPID_URLARRAY_H */
diff --git a/cpp/src/qpid/acl/Acl.cpp b/cpp/src/qpid/acl/Acl.cpp
index 89c4b3402a..31ad9a38ac 100644
--- a/cpp/src/qpid/acl/Acl.cpp
+++ b/cpp/src/qpid/acl/Acl.cpp
@@ -18,11 +18,13 @@
#include "qpid/acl/Acl.h"
#include "qpid/acl/AclConnectionCounter.h"
+#include "qpid/acl/AclResourceCounter.h"
#include "qpid/acl/AclData.h"
#include "qpid/acl/AclValidator.h"
#include "qpid/sys/Mutex.h"
#include "qpid/broker/Broker.h"
+#include "qpid/broker/Connection.h"
#include "qpid/Plugin.h"
#include "qpid/Options.h"
#include "qpid/log/Logger.h"
@@ -32,6 +34,7 @@
#include "qmf/org/apache/qpid/acl/Package.h"
#include "qmf/org/apache/qpid/acl/EventAllow.h"
#include "qmf/org/apache/qpid/acl/EventConnectionDeny.h"
+#include "qmf/org/apache/qpid/acl/EventQueueQuotaDeny.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"
@@ -50,19 +53,29 @@ using qpid::management::Manageable;
using qpid::management::Args;
namespace _qmf = qmf::org::apache::qpid::acl;
-Acl::Acl (AclValues& av, Broker& b): aclValues(av), broker(&b), transferAcl(false), mgmtObject(0),
- connectionCounter(new ConnectionCounter(*this, aclValues.aclMaxConnectPerUser, aclValues.aclMaxConnectPerIp, aclValues.aclMaxConnectTotal))
-{
+Acl::Acl (AclValues& av, Broker& b): aclValues(av), broker(&b), transferAcl(false),
+ connectionCounter(new ConnectionCounter(*this, aclValues.aclMaxConnectPerUser, aclValues.aclMaxConnectPerIp, aclValues.aclMaxConnectTotal)),
+ resourceCounter(new ResourceCounter(*this, aclValues.aclMaxQueuesPerUser)){
+
+ if (aclValues.aclMaxConnectPerUser > AclData::getConnectMaxSpec())
+ throw Exception("--connection-limit-per-user switch cannot be larger than " + AclData::getMaxConnectSpecStr());
+ if (aclValues.aclMaxConnectPerIp > AclData::getConnectMaxSpec())
+ throw Exception("--connection-limit-per-ip switch cannot be larger than " + AclData::getMaxConnectSpecStr());
+ if (aclValues.aclMaxConnectTotal > AclData::getConnectMaxSpec())
+ throw Exception("--max-connections switch cannot be larger than " + AclData::getMaxConnectSpecStr());
+ if (aclValues.aclMaxQueuesPerUser > AclData::getConnectMaxSpec())
+ throw Exception("--max-queues-per-user switch cannot be larger than " + AclData::getMaxConnectSpecStr());
agent = broker->getManagementAgent();
if (agent != 0){
_qmf::Package packageInit(agent);
- mgmtObject = new _qmf::Acl (agent, this, broker);
+ mgmtObject = _qmf::Acl::shared_ptr(new _qmf::Acl (agent, this, broker));
agent->addObject (mgmtObject);
mgmtObject->set_maxConnections(aclValues.aclMaxConnectTotal);
mgmtObject->set_maxConnectionsPerIp(aclValues.aclMaxConnectPerIp);
mgmtObject->set_maxConnectionsPerUser(aclValues.aclMaxConnectPerUser);
+ mgmtObject->set_maxQueuesPerUser(aclValues.aclMaxQueuesPerUser);
}
std::string errorString;
if (!readAclFile(errorString)){
@@ -84,6 +97,15 @@ void Acl::reportConnectLimit(const std::string user, const std::string addr)
}
+void Acl::reportQueueLimit(const std::string user, const std::string queueName)
+{
+ if (mgmtObject!=0)
+ mgmtObject->inc_queueQuotaDenyCount();
+
+ agent->raiseEvent(_qmf::EventQueueQuotaDeny(user, queueName));
+}
+
+
bool Acl::authorise(
const std::string& id,
const Action& action,
@@ -126,13 +148,29 @@ bool Acl::authorise(
bool Acl::approveConnection(const qpid::broker::Connection& conn)
{
- return connectionCounter->approveConnection(conn);
+ const std::string& userName(conn.getUserId());
+ uint16_t connectionLimit(0);
+
+ boost::shared_ptr<AclData> dataLocal;
+ {
+ Mutex::ScopedLock locker(dataLock);
+ dataLocal = data; //rcu copy
+ }
+
+ bool enforcingConnQuotas = dataLocal->getConnQuotaForUser(userName, &connectionLimit);
+
+ return connectionCounter->approveConnection(conn, enforcingConnQuotas, connectionLimit);
+}
+
+bool Acl::approveCreateQueue(const std::string& userId, const std::string& queueName)
+{
+ return resourceCounter->approveCreateQueue(userId, queueName);
}
-void Acl::setUserId(const qpid::broker::Connection& connection, const std::string& username)
+void Acl::recordDestroyQueue(const std::string& queueName)
{
- connectionCounter->setUserId(connection, username);
+ resourceCounter->recordDestroyQueue(queueName);
}
@@ -190,7 +228,7 @@ bool Acl::readAclFile(std::string& errorText)
bool Acl::readAclFile(std::string& aclFile, std::string& errorText) {
boost::shared_ptr<AclData> d(new AclData);
- AclReader ar;
+ AclReader ar(aclValues.aclMaxConnectPerUser);
if (ar.read(aclFile, d)){
agent->raiseEvent(_qmf::EventFileLoadFailed("", ar.getError()));
errorText = ar.getError();
@@ -211,6 +249,10 @@ bool Acl::readAclFile(std::string& aclFile, std::string& errorText) {
QPID_LOG(debug,"ACL: Transfer ACL is Enabled!");
}
+ if (data->enforcingConnectionQuotas()){
+ QPID_LOG(debug, "ACL: Connection quotas are Enabled.");
+ }
+
data->aclSource = aclFile;
if (mgmtObject!=0){
mgmtObject->set_transferAcl(transferAcl?1:0);
@@ -300,9 +342,9 @@ Acl::~Acl(){
broker->getConnectionObservers().remove(connectionCounter);
}
-ManagementObject* Acl::GetManagementObject(void) const
+ManagementObject::shared_ptr Acl::GetManagementObject(void) const
{
- return (ManagementObject*) mgmtObject;
+ return mgmtObject;
}
Manageable::status_t Acl::ManagementMethod (uint32_t methodId, Args& args, string& text)
diff --git a/cpp/src/qpid/acl/Acl.h b/cpp/src/qpid/acl/Acl.h
index 4787934275..ea3c6586a3 100644
--- a/cpp/src/qpid/acl/Acl.h
+++ b/cpp/src/qpid/acl/Acl.h
@@ -43,12 +43,14 @@ class Connection;
namespace acl {
class ConnectionCounter;
+class ResourceCounter;
struct AclValues {
std::string aclFile;
uint16_t aclMaxConnectPerUser;
uint16_t aclMaxConnectPerIp;
uint16_t aclMaxConnectTotal;
+ uint16_t aclMaxQueuesPerUser;
};
@@ -60,10 +62,11 @@ private:
broker::Broker* broker;
bool transferAcl;
boost::shared_ptr<AclData> data;
- qmf::org::apache::qpid::acl::Acl* mgmtObject; // mgnt owns lifecycle
+ qmf::org::apache::qpid::acl::Acl::shared_ptr mgmtObject;
qpid::management::ManagementAgent* agent;
mutable qpid::sys::Mutex dataLock;
boost::shared_ptr<ConnectionCounter> connectionCounter;
+ boost::shared_ptr<ResourceCounter> resourceCounter;
public:
Acl (AclValues& av, broker::Broker& b);
@@ -72,11 +75,16 @@ public:
* issue management counts and alerts for denied connections
*/
void reportConnectLimit(const std::string user, const std::string addr);
+ void reportQueueLimit(const std::string user, const std::string queueName);
inline virtual bool doTransferAcl() {
return transferAcl;
};
+ inline virtual uint16_t getMaxConnectTotal() {
+ return aclValues.aclMaxConnectTotal;
+ };
+
// create specilied authorise methods for cases that need faster matching as needed.
virtual bool authorise(
const std::string& id,
@@ -92,9 +100,10 @@ public:
const std::string& ExchangeName,
const std::string& RoutingKey);
+ // Resource quota tracking
virtual bool approveConnection(const broker::Connection& connection);
-
- virtual void setUserId(const broker::Connection& connection, const std::string& username);
+ virtual bool approveCreateQueue(const std::string& userId, const std::string& queueName);
+ virtual void recordDestroyQueue(const std::string& queueName);
virtual ~Acl();
private:
@@ -108,7 +117,7 @@ private:
bool readAclFile(std::string& aclFile, std::string& errorText);
Manageable::status_t lookup (management::Args& args, std::string& text);
Manageable::status_t lookupPublish(management::Args& args, std::string& text);
- virtual qpid::management::ManagementObject* GetManagementObject(void) const;
+ virtual qpid::management::ManagementObject::shared_ptr GetManagementObject(void) const;
virtual management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text);
};
diff --git a/cpp/src/qpid/acl/AclConnectionCounter.cpp b/cpp/src/qpid/acl/AclConnectionCounter.cpp
index 8c6e3eef6e..875137bf55 100644
--- a/cpp/src/qpid/acl/AclConnectionCounter.cpp
+++ b/cpp/src/qpid/acl/AclConnectionCounter.cpp
@@ -85,32 +85,32 @@ bool ConnectionCounter::limitApproveLH(
//
// countConnectionLH
//
-// Increment the name's count in map and return a comparison against the limit.
-// called with dataLock already taken
+// Increment the name's count in map and return an optional comparison
+// against a connection limit.
+// Called with dataLock already taken.
//
bool ConnectionCounter::countConnectionLH(
connectCountsMap_t& theMap,
const std::string& theName,
uint16_t theLimit,
- bool emitLog) {
+ bool emitLog,
+ bool enforceLimit) {
bool result(true);
uint16_t count(0);
- if (theLimit > 0) {
- connectCountsMap_t::iterator eRef = theMap.find(theName);
- if (eRef != theMap.end()) {
- count = (uint16_t)(*eRef).second + 1;
- (*eRef).second = count;
- result = count <= theLimit;
- } else {
- theMap[theName] = count = 1;
- }
- if (emitLog) {
- QPID_LOG(trace, "ACL ConnectionApprover user=" << theName
- << " limit=" << theLimit
- << " curValue=" << count
- << " result=" << (result ? "allow" : "deny"));
- }
+ connectCountsMap_t::iterator eRef = theMap.find(theName);
+ if (eRef != theMap.end()) {
+ count = (uint16_t)(*eRef).second + 1;
+ (*eRef).second = count;
+ result = (enforceLimit ? count <= theLimit : true);
+ } else {
+ theMap[theName] = count = 1;
+ }
+ if (emitLog) {
+ QPID_LOG(trace, "ACL ConnectionApprover user=" << theName
+ << " limit=" << theLimit
+ << " curValue=" << count
+ << " result=" << (result ? "allow" : "deny"));
}
return result;
}
@@ -123,23 +123,21 @@ bool ConnectionCounter::countConnectionLH(
// called with dataLock already taken
//
void ConnectionCounter::releaseLH(
- connectCountsMap_t& theMap, const std::string& theName, uint16_t theLimit) {
-
- if (theLimit > 0) {
- connectCountsMap_t::iterator eRef = theMap.find(theName);
- if (eRef != theMap.end()) {
- uint16_t count = (uint16_t) (*eRef).second;
- assert (count > 0);
- if (1 == count) {
- theMap.erase (eRef);
- } else {
- (*eRef).second = count - 1;
- }
+ connectCountsMap_t& theMap, const std::string& theName) {
+
+ connectCountsMap_t::iterator eRef = theMap.find(theName);
+ if (eRef != theMap.end()) {
+ uint16_t count = (uint16_t) (*eRef).second;
+ assert (count > 0);
+ if (1 == count) {
+ theMap.erase (eRef);
} else {
- // User had no connections.
- QPID_LOG(notice, "ACL ConnectionCounter Connection for '" << theName
- << "' not found in connection count pool");
+ (*eRef).second = count - 1;
}
+ } else {
+ // User had no connections.
+ QPID_LOG(notice, "ACL ConnectionCounter Connection for '" << theName
+ << "' not found in connection count pool");
}
}
@@ -161,7 +159,7 @@ void ConnectionCounter::connection(broker::Connection& connection) {
connectProgressMap[connection.getMgmtId()] = C_CREATED;
// Count the connection from this host.
- (void) countConnectionLH(connectByHostMap, hostName, hostLimit, false);
+ (void) countConnectionLH(connectByHostMap, hostName, hostLimit, false, false);
}
@@ -180,8 +178,7 @@ void ConnectionCounter::closed(broker::Connection& connection) {
// Normal case: connection was created and opened.
// Decrement user in-use counts
releaseLH(connectByNameMap,
- connection.getUserId(),
- nameLimit);
+ connection.getUserId());
} else {
// Connection was created but not opened.
// Don't decrement user count.
@@ -189,8 +186,7 @@ void ConnectionCounter::closed(broker::Connection& connection) {
// Decrement host in-use count.
releaseLH(connectByHostMap,
- getClientHost(connection.getMgmtId()),
- hostLimit);
+ getClientHost(connection.getMgmtId()));
// destroy connection progress indicator
connectProgressMap.erase(eRef);
@@ -211,7 +207,10 @@ void ConnectionCounter::closed(broker::Connection& connection) {
// check total connections, connections from IP, connections by user and
// disallow if over any limit
//
-bool ConnectionCounter::approveConnection(const broker::Connection& connection)
+bool ConnectionCounter::approveConnection(
+ const broker::Connection& connection,
+ bool enforcingConnectionQuotas,
+ uint16_t connectionUserQuota )
{
const std::string& hostName(getClientHost(connection.getMgmtId()));
const std::string& userName( connection.getUserId());
@@ -220,122 +219,53 @@ bool ConnectionCounter::approveConnection(const broker::Connection& connection)
// Bump state from CREATED to OPENED
(void) countConnectionLH(connectProgressMap, connection.getMgmtId(),
- C_OPENED, false);
+ C_OPENED, false, false);
// Approve total connections
bool okTotal = true;
if (totalLimit > 0) {
okTotal = totalCurrentConnections <= totalLimit;
- if (!connection.isShadow()) {
- QPID_LOG(trace, "ACL ConnectionApprover totalLimit=" << totalLimit
- << " curValue=" << totalCurrentConnections
- << " result=" << (okTotal ? "allow" : "deny"));
- }
+ QPID_LOG(trace, "ACL ConnectionApprover totalLimit=" << totalLimit
+ << " curValue=" << totalCurrentConnections
+ << " result=" << (okTotal ? "allow" : "deny"));
}
// Approve by IP host connections
- bool okByIP = limitApproveLH(connectByHostMap, hostName, hostLimit, !connection.isShadow());
+ bool okByIP = limitApproveLH(connectByHostMap, hostName, hostLimit, true);
// Count and Approve the connection by the user
- bool okByUser = countConnectionLH(connectByNameMap, userName, nameLimit, !connection.isShadow());
-
- if (!connection.isShadow()) {
- // Emit separate log for each disapproval
- if (!okTotal) {
- QPID_LOG(error, "Client max total connection count limit of " << totalLimit
- << " exceeded by '"
- << connection.getMgmtId() << "', user: '"
- << userName << "'. Connection refused");
- }
- if (!okByIP) {
- QPID_LOG(error, "Client max per-host connection count limit of "
- << hostLimit << " exceeded by '"
- << connection.getMgmtId() << "', user: '"
- << userName << "'. Connection refused.");
- }
- if (!okByUser) {
- QPID_LOG(error, "Client max per-user connection count limit of "
- << nameLimit << " exceeded by '"
- << connection.getMgmtId() << "', user: '"
- << userName << "'. Connection refused.");
- }
-
- // Count/Event once for each disapproval
- bool result = okTotal && okByIP && okByUser;
- if (!result) {
- acl.reportConnectLimit(userName, hostName);
- }
-
- return result;
- } else {
- // Always allow shadow connections
- if (!okTotal) {
- QPID_LOG(warning, "Client max total connection count limit of " << totalLimit
- << " exceeded by '"
- << connection.getMgmtId() << "', user: '"
- << userName << "' but still within tolerance. Cluster connection allowed");
- }
- if (!okByIP) {
- QPID_LOG(warning, "Client max per-host connection count limit of "
- << hostLimit << " exceeded by '"
- << connection.getMgmtId() << "', user: '"
- << userName << "' but still within tolerance. Cluster connection allowed");
- }
- if (!okByUser) {
- QPID_LOG(warning, "Client max per-user connection count limit of "
- << nameLimit << " exceeded by '"
- << connection.getMgmtId() << "', user: '"
- << userName << "' but still within tolerance. Cluster connection allowed");
- }
- if (okTotal && okByIP && okByUser) {
- QPID_LOG(debug, "Cluster client connection: '"
- << connection.getMgmtId() << "', user '"
- << userName << "' allowed");
- }
- return true;
+ bool okByUser = countConnectionLH(connectByNameMap, userName,
+ connectionUserQuota, true,
+ enforcingConnectionQuotas);
+
+ // Emit separate log for each disapproval
+ if (!okTotal) {
+ QPID_LOG(error, "Client max total connection count limit of " << totalLimit
+ << " exceeded by '"
+ << connection.getMgmtId() << "', user: '"
+ << userName << "'. Connection refused");
+ }
+ if (!okByIP) {
+ QPID_LOG(error, "Client max per-host connection count limit of "
+ << hostLimit << " exceeded by '"
+ << connection.getMgmtId() << "', user: '"
+ << userName << "'. Connection refused.");
+ }
+ if (!okByUser) {
+ QPID_LOG(error, "Client max per-user connection count limit of "
+ << connectionUserQuota << " exceeded by '"
+ << connection.getMgmtId() << "', user: '"
+ << userName << "'. Connection refused.");
}
-}
-
-
-//
-// setUserId
-// On cluster shadow connections, track a new user id for this connection.
-//
-void ConnectionCounter::setUserId(const broker::Connection& connection,
- const std::string& username)
-{
- Mutex::ScopedLock locker(dataLock);
- connectCountsMap_t::iterator eRef = connectProgressMap.find(connection.getMgmtId());
- if (eRef != connectProgressMap.end()) {
- if ((*eRef).second == C_OPENED){
- // Connection has been opened so that current user has been counted
- if (connection.isShadow()) {
- // This is a shadow connection and therefore receives userId changes
- QPID_LOG(debug, "Changing User ID for cluster connection: "
- << connection.getMgmtId() << ", old user:'" << connection.getUserId()
- << "', new user:'" << username << "'");
-
- // Decrement user in-use count for old userId
- releaseLH(connectByNameMap,
- connection.getUserId(),
- nameLimit);
- // Increment user in-use count for new userId
- (void) countConnectionLH(connectByNameMap, username, nameLimit, false);
- } else {
- QPID_LOG(warning, "Changing User ID for non-cluster connections is not supported: "
- << connection.getMgmtId() << ", old user " << connection.getUserId()
- << ", new user " << username);
- }
- } else {
- // connection exists but has not been opened.
- // setUserId is called in normal course. The user gets counted when connection is opened.
- }
- } else {
- // Connection does not exist.
+ // Count/Event once for each disapproval
+ bool result = okTotal && okByIP && okByUser;
+ if (!result) {
+ acl.reportConnectLimit(userName, hostName);
}
-}
+ return result;
+}
//
// getClientIp - given a connection's mgmtId return the client host part.
diff --git a/cpp/src/qpid/acl/AclConnectionCounter.h b/cpp/src/qpid/acl/AclConnectionCounter.h
index 54fa6933ff..e8ef35c1ba 100644
--- a/cpp/src/qpid/acl/AclConnectionCounter.h
+++ b/cpp/src/qpid/acl/AclConnectionCounter.h
@@ -77,12 +77,12 @@ private:
bool countConnectionLH(connectCountsMap_t& theMap,
const std::string& theName,
uint16_t theLimit,
- bool emitLog);
+ bool emitLog,
+ bool enforceLimit);
/** Release a connection */
void releaseLH(connectCountsMap_t& theMap,
- const std::string& theName,
- uint16_t theLimit);
+ const std::string& theName);
public:
ConnectionCounter(Acl& acl, uint16_t nl, uint16_t hl, uint16_t tl);
@@ -93,8 +93,9 @@ public:
void closed(broker::Connection& connection);
// Connection counting
- bool approveConnection(const broker::Connection& conn);
- void setUserId(const broker::Connection& connection, const std::string& username);
+ bool approveConnection(const broker::Connection& conn,
+ bool enforcingConnectionQuotas,
+ uint16_t connectionLimit );
};
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/acl/AclData.cpp b/cpp/src/qpid/acl/AclData.cpp
index 7c14d0985d..ca866ab7d3 100644
--- a/cpp/src/qpid/acl/AclData.cpp
+++ b/cpp/src/qpid/acl/AclData.cpp
@@ -25,11 +25,19 @@ namespace qpid {
namespace acl {
//
- // Instantiate the substitution keyword string
+ // Instantiate the keyword strings
//
- const std::string AclData::USER_SUBSTITUTION_KEYWORD = "${user}";
- const std::string AclData::DOMAIN_SUBSTITUTION_KEYWORD = "${domain}";
- const std::string AclData::USERDOMAIN_SUBSTITUTION_KEYWORD = "${userdomain}";
+ const std::string AclData::ACL_KEYWORD_USER_SUBST = "${user}";
+ const std::string AclData::ACL_KEYWORD_DOMAIN_SUBST = "${domain}";
+ const std::string AclData::ACL_KEYWORD_USERDOMAIN_SUBST = "${userdomain}";
+ const std::string AclData::ACL_KEYWORD_ALL = "all";
+ const std::string AclData::ACL_KEYWORD_ACL = "acl";
+ const std::string AclData::ACL_KEYWORD_GROUP = "group";
+ const std::string AclData::ACL_KEYWORD_QUOTA = "quota";
+ const std::string AclData::ACL_KEYWORD_QUOTA_CONNECTIONS = "connections";
+ const char AclData::ACL_SYMBOL_WILDCARD = '*';
+ const std::string AclData::ACL_KEYWORD_WILDCARD = "*";
+ const char AclData::ACL_SYMBOL_LINE_CONTINUATION = '\\';
//
// constructor
@@ -37,7 +45,9 @@ namespace acl {
AclData::AclData():
decisionMode(qpid::acl::DENY),
transferAcl(false),
- aclSource("UNKNOWN")
+ aclSource("UNKNOWN"),
+ connQuotaRulesExist(false),
+ connQuotaRuleSettings(new quotaRuleSet)
{
for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++)
{
@@ -60,6 +70,9 @@ namespace acl {
}
delete[] actionList[cnt];
}
+ transferAcl = false;
+ connQuotaRulesExist = false;
+ connQuotaRuleSettings->clear();
}
@@ -73,7 +86,7 @@ namespace acl {
const std::string& lookupStr)
{
// allow wildcard on the end of rule strings...
- if (ruleStr.data()[ruleStr.size()-1]=='*')
+ if (ruleStr.data()[ruleStr.size()-1]==ACL_SYMBOL_WILDCARD)
{
return ruleStr.compare(0,
ruleStr.size()-1,
@@ -124,7 +137,7 @@ namespace acl {
// If individual actorId not found then find a rule set for '*'.
if (itrRule == actionList[action][objType]->end())
- itrRule = actionList[action][objType]->find("*");
+ itrRule = actionList[action][objType]->find(ACL_KEYWORD_WILDCARD);
if (itrRule != actionList[action][objType]->end())
{
@@ -199,6 +212,16 @@ namespace acl {
lookupParamItr = params->find(PROP_MAXQUEUESIZE);
break;
+ case acl::SPECPROP_MAXFILECOUNTUPPERLIMIT:
+ case acl::SPECPROP_MAXFILECOUNTLOWERLIMIT:
+ lookupParamItr = params->find(PROP_MAXFILECOUNT);
+ break;
+
+ case acl::SPECPROP_MAXFILESIZEUPPERLIMIT:
+ case acl::SPECPROP_MAXFILESIZELOWERLIMIT:
+ lookupParamItr = params->find(PROP_MAXFILESIZE);
+ break;
+
default:
lookupParamItr = params->find((Property)rulePropMapItr->first);
break;
@@ -222,6 +245,8 @@ namespace acl {
{
case acl::SPECPROP_MAXQUEUECOUNTUPPERLIMIT:
case acl::SPECPROP_MAXQUEUESIZEUPPERLIMIT:
+ case acl::SPECPROP_MAXFILECOUNTUPPERLIMIT:
+ case acl::SPECPROP_MAXFILESIZEUPPERLIMIT:
limitChecked &=
compareIntMax(
rulePropMapItr->first,
@@ -231,6 +256,8 @@ namespace acl {
case acl::SPECPROP_MAXQUEUECOUNTLOWERLIMIT:
case acl::SPECPROP_MAXQUEUESIZELOWERLIMIT:
+ case acl::SPECPROP_MAXFILECOUNTLOWERLIMIT:
+ case acl::SPECPROP_MAXFILESIZELOWERLIMIT:
limitChecked &=
compareIntMin(
rulePropMapItr->first,
@@ -241,14 +268,31 @@ namespace acl {
default:
bool result;
if ((SPECPROP_ALTERNATE == rulePropMapItr->first && rsItr->ruleHasUserSub[PROP_ALTERNATE]) ||
- (SPECPROP_ROUTINGKEY == rulePropMapItr->first && rsItr->ruleHasUserSub[PROP_ROUTINGKEY]) ||
(SPECPROP_QUEUENAME == rulePropMapItr->first && rsItr->ruleHasUserSub[PROP_QUEUENAME]))
{
// These properties are allowed to have username substitution
std::string sName(rulePropMapItr->second);
substituteUserId(sName, id);
result = matchProp(sName, lookupParamItr->second);
- } else {
+ }
+ else if (SPECPROP_ROUTINGKEY == rulePropMapItr->first)
+ {
+ // Routing key is allowed to have username substitution
+ // and it gets topic exchange matching
+ if (rsItr->ruleHasUserSub[PROP_ROUTINGKEY])
+ {
+ std::string sKey(lookupParamItr->second);
+ substituteKeywords(sKey, id);
+ result = rsItr->matchRoutingKey(sKey);
+ }
+ else
+ {
+ result = rsItr->matchRoutingKey(lookupParamItr->second);
+ }
+ }
+ else
+ {
+ // Rules without substitution
result = matchProp(rulePropMapItr->second, lookupParamItr->second);
}
@@ -359,7 +403,7 @@ namespace acl {
AclData::actObjItr itrRule = actionList[action][objType]->find(id);
if (itrRule == actionList[action][objType]->end())
- itrRule = actionList[action][objType]->find("*");
+ itrRule = actionList[action][objType]->find(ACL_KEYWORD_WILDCARD);
if (itrRule != actionList[action][objType]->end() )
{
@@ -405,9 +449,9 @@ namespace acl {
if (match && rsItr->pubRoutingKeyInRule)
{
- if ((routingKey.find(USER_SUBSTITUTION_KEYWORD, 0) != std::string::npos) ||
- (routingKey.find(DOMAIN_SUBSTITUTION_KEYWORD, 0) != std::string::npos) ||
- (routingKey.find(USERDOMAIN_SUBSTITUTION_KEYWORD, 0) != std::string::npos))
+ if ((routingKey.find(ACL_KEYWORD_USER_SUBST, 0) != std::string::npos) ||
+ (routingKey.find(ACL_KEYWORD_DOMAIN_SUBST, 0) != std::string::npos) ||
+ (routingKey.find(ACL_KEYWORD_USERDOMAIN_SUBST, 0) != std::string::npos))
{
// The user is not allowed to present a routing key with the substitution key in it
QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum <<
@@ -458,6 +502,62 @@ namespace acl {
}
+
+ //
+ //
+ //
+ void AclData::setConnQuotaRuleSettings (
+ bool rulesExist, boost::shared_ptr<quotaRuleSet> quotaPtr)
+ {
+ connQuotaRulesExist = rulesExist;
+ connQuotaRuleSettings = quotaPtr;
+ }
+
+
+ //
+ // getConnQuotaForUser
+ //
+ // Return the true or false value of connQuotaRulesExist,
+ // indicating whether any kind of lookup was done or not.
+ //
+ // When lookups are performed return the result value of
+ // 1. The user's setting else
+ // 2. The 'all' user setting else
+ // 3. Zero
+ // When lookups are not performed then return a result value of Zero.
+ //
+ bool AclData::getConnQuotaForUser(const std::string& theUserName,
+ uint16_t* theResult) const {
+ if (connQuotaRulesExist) {
+ // look for this user explicitly
+ quotaRuleSetItr nameItr = (*connQuotaRuleSettings).find(theUserName);
+ if (nameItr != (*connQuotaRuleSettings).end()) {
+ QPID_LOG(trace, "ACL: Connection quota for user " << theUserName
+ << " explicitly set to : " << (*nameItr).second);
+ *theResult = (*nameItr).second;
+ } else {
+ // Look for the 'all' user
+ nameItr = (*connQuotaRuleSettings).find(ACL_KEYWORD_ALL);
+ if (nameItr != (*connQuotaRuleSettings).end()) {
+ QPID_LOG(trace, "ACL: Connection quota for user " << theUserName
+ << " chosen through value for 'all' : " << (*nameItr).second);
+ *theResult = (*nameItr).second;
+ } else {
+ // Neither userName nor "all" found.
+ QPID_LOG(trace, "ACL: Connection quota for user " << theUserName
+ << " absent in quota settings. Return value : 0");
+ *theResult = 0;
+ }
+ }
+ } else {
+ // Rules do not exist
+ QPID_LOG(trace, "ACL: Connection quota for user " << theUserName
+ << " unavailable; quota settings are not specified. Return value : 0");
+ *theResult = 0;
+ }
+ return connQuotaRulesExist;
+ }
+
//
//
//
@@ -607,8 +707,8 @@ namespace acl {
// Given an Acl rule and an authenticated userId
// do the keyword substitutions on the rule.
//
- void AclData::AclData::substituteUserId(std::string& ruleString,
- const std::string& userId)
+ void AclData::substituteUserId(std::string& ruleString,
+ const std::string& userId)
{
size_t locDomSeparator(0);
std::string user("");
@@ -625,9 +725,9 @@ namespace acl {
domain = normalizeUserId(userId.substr(locDomSeparator+1));
}
- substituteString(ruleString, USER_SUBSTITUTION_KEYWORD, user);
- substituteString(ruleString, DOMAIN_SUBSTITUTION_KEYWORD, domain);
- substituteString(ruleString, USERDOMAIN_SUBSTITUTION_KEYWORD, userdomain);
+ substituteString(ruleString, ACL_KEYWORD_USER_SUBST, user);
+ substituteString(ruleString, ACL_KEYWORD_DOMAIN_SUBST, domain);
+ substituteString(ruleString, ACL_KEYWORD_USERDOMAIN_SUBST, userdomain);
}
@@ -640,8 +740,8 @@ namespace acl {
// topic key lookups where the keyword string proper is in the
// topic key search tree.
//
- void AclData::AclData::substituteKeywords(std::string& ruleString,
- const std::string& userId)
+ void AclData::substituteKeywords(std::string& ruleString,
+ const std::string& userId)
{
size_t locDomSeparator(0);
std::string user("");
@@ -658,8 +758,8 @@ namespace acl {
domain = normalizeUserId(userId.substr(locDomSeparator+1));
}
std::string oRule(ruleString);
- substituteString(ruleString, userdomain, USERDOMAIN_SUBSTITUTION_KEYWORD);
- substituteString(ruleString, user, USER_SUBSTITUTION_KEYWORD);
- substituteString(ruleString, domain, DOMAIN_SUBSTITUTION_KEYWORD);
+ substituteString(ruleString, userdomain, ACL_KEYWORD_USERDOMAIN_SUBST);
+ substituteString(ruleString, user, ACL_KEYWORD_USER_SUBST);
+ substituteString(ruleString, domain, ACL_KEYWORD_DOMAIN_SUBST);
}
}}
diff --git a/cpp/src/qpid/acl/AclData.h b/cpp/src/qpid/acl/AclData.h
index b4b13c44b6..43cb5193f5 100644
--- a/cpp/src/qpid/acl/AclData.h
+++ b/cpp/src/qpid/acl/AclData.h
@@ -111,6 +111,8 @@ public:
typedef std::map<std::string, ruleSet > actionObject; // user
typedef actionObject::iterator actObjItr;
typedef actionObject* aclAction;
+ typedef std::map<std::string, uint16_t> quotaRuleSet; // <username, N>
+ typedef quotaRuleSet::const_iterator quotaRuleSetItr;
// Action*[] -> Object*[] -> map<user -> set<Rule> >
aclAction* actionList[qpid::acl::ACTIONSIZE];
@@ -134,9 +136,18 @@ public:
bool matchProp(const std::string & src, const std::string& src1);
void clear ();
- static const std::string USER_SUBSTITUTION_KEYWORD;
- static const std::string DOMAIN_SUBSTITUTION_KEYWORD;
- static const std::string USERDOMAIN_SUBSTITUTION_KEYWORD;
+ static const std::string ACL_KEYWORD_USER_SUBST;
+ static const std::string ACL_KEYWORD_DOMAIN_SUBST;
+ static const std::string ACL_KEYWORD_USERDOMAIN_SUBST;
+ static const std::string ACL_KEYWORD_ALL;
+ static const std::string ACL_KEYWORD_ACL;
+ static const std::string ACL_KEYWORD_GROUP;
+ static const std::string ACL_KEYWORD_QUOTA;
+ static const std::string ACL_KEYWORD_QUOTA_CONNECTIONS;
+ static const char ACL_SYMBOL_WILDCARD;
+ static const std::string ACL_KEYWORD_WILDCARD;
+ static const char ACL_SYMBOL_LINE_CONTINUATION;
+
void substituteString(std::string& targetString,
const std::string& placeholder,
const std::string& replacement);
@@ -146,6 +157,31 @@ public:
void substituteKeywords(std::string& ruleString,
const std::string& userId);
+ // Per user connection quotas extracted from acl rule file
+ // Set by reader
+ void setConnQuotaRuleSettings (bool, boost::shared_ptr<quotaRuleSet>);
+ // Get by connection approvers
+ bool enforcingConnectionQuotas() { return connQuotaRulesExist; }
+ bool getConnQuotaForUser(const std::string&, uint16_t*) const;
+
+ /** getConnectMaxSpec
+ * Connection quotas are held in uint16_t variables.
+ * This function specifies the largest value that a user is allowed
+ * to declare for a connection quota. The upper limit serves two
+ * purposes: 1. It leaves room for magic numbers that may be declared
+ * by keyword names in Acl files and not have those numbers conflict
+ * with innocent user declared values, and 2. It makes the unsigned
+ * math very close to _MAX work reliably with no risk of accidental
+ * wrapping back to zero.
+ */
+ static uint16_t getConnectMaxSpec() {
+ return 65530;
+ }
+ static std::string getMaxConnectSpecStr() {
+ return "65530";
+ }
+
+
AclData();
virtual ~AclData();
@@ -157,6 +193,10 @@ private:
bool compareIntMin(const qpid::acl::SpecProperty theProperty,
const std::string theAclValue,
const std::string theLookupValue);
+
+ // Per-user connection quota
+ bool connQuotaRulesExist;
+ boost::shared_ptr<quotaRuleSet> connQuotaRuleSettings; // Map of user-to-N values from rule file
};
}} // namespace qpid::acl
diff --git a/cpp/src/qpid/acl/AclPlugin.cpp b/cpp/src/qpid/acl/AclPlugin.cpp
index ebf5e90afe..c666eb5420 100644
--- a/cpp/src/qpid/acl/AclPlugin.cpp
+++ b/cpp/src/qpid/acl/AclPlugin.cpp
@@ -42,9 +42,10 @@ struct AclOptions : public Options {
values.aclMaxConnectTotal = 500;
addOptions()
("acl-file", optValue(values.aclFile, "FILE"), "The policy file to load from, loaded from data dir")
+ ("connection-limit-per-user", optValue(values.aclMaxConnectPerUser, "N"), "The maximum number of connections allowed per user. 0 implies no limit.")
("max-connections" , optValue(values.aclMaxConnectTotal, "N"), "The maximum combined number of connections allowed. 0 implies no limit.")
- ("max-connections-per-user", optValue(values.aclMaxConnectPerUser, "N"), "The maximum number of connections allowed per user. 0 implies no limit.")
- ("max-connections-per-ip" , optValue(values.aclMaxConnectPerIp, "N"), "The maximum number of connections allowed per host IP address. 0 implies no limit.")
+ ("connection-limit-per-ip" , optValue(values.aclMaxConnectPerIp, "N"), "The maximum number of connections allowed per host IP address. 0 implies no limit.")
+ ("max-queues-per-user", optValue(values.aclMaxQueuesPerUser, "N"), "The maximum number of queues allowed per user. 0 implies no limit.")
;
}
};
diff --git a/cpp/src/qpid/acl/AclReader.cpp b/cpp/src/qpid/acl/AclReader.cpp
index fae67d0325..7eb9b82c64 100644
--- a/cpp/src/qpid/acl/AclReader.cpp
+++ b/cpp/src/qpid/acl/AclReader.cpp
@@ -24,6 +24,7 @@
#include <sstream>
#include "qpid/log/Statement.h"
#include "qpid/Exception.h"
+#include <boost/lexical_cast.hpp>
#include <iomanip> // degug
#include <iostream> // debug
@@ -95,7 +96,7 @@ namespace acl {
<< cnt << " " << (*i)->toString());
if (!foundmode && (*i)->actionAll && (*i)->names.size() == 1
- && (*((*i)->names.begin())).compare("*") == 0) {
+ && (*((*i)->names.begin())).compare(AclData::ACL_KEYWORD_WILDCARD) == 0) {
d->decisionMode = (*i)->res;
QPID_LOG(debug, "ACL: FoundMode "
<< AclHelper::getAclResultStr(d->decisionMode));
@@ -105,13 +106,23 @@ namespace acl {
// Record which properties have the user substitution string
for (pmCitr pItr=rule.props.begin(); pItr!=rule.props.end(); pItr++) {
- if ((pItr->second.find(AclData::USER_SUBSTITUTION_KEYWORD, 0) != std::string::npos) ||
- (pItr->second.find(AclData::DOMAIN_SUBSTITUTION_KEYWORD, 0) != std::string::npos) ||
- (pItr->second.find(AclData::USERDOMAIN_SUBSTITUTION_KEYWORD, 0) != std::string::npos)) {
+ if ((pItr->second.find(AclData::ACL_KEYWORD_USER_SUBST, 0) != std::string::npos) ||
+ (pItr->second.find(AclData::ACL_KEYWORD_DOMAIN_SUBST, 0) != std::string::npos) ||
+ (pItr->second.find(AclData::ACL_KEYWORD_USERDOMAIN_SUBST, 0) != std::string::npos)) {
rule.ruleHasUserSub[pItr->first] = true;
}
}
+ // Find possible routingkey property and cache its pattern
+ for (pmCitr pItr=rule.props.begin(); pItr!=rule.props.end(); pItr++) {
+ if (acl::SPECPROP_ROUTINGKEY == pItr->first)
+ {
+ rule.pubRoutingKeyInRule = true;
+ rule.pubRoutingKey = (std::string)pItr->second;
+ rule.addTopicTest(rule.pubRoutingKey);
+ }
+ }
+
// Action -> Object -> map<user -> set<Rule> >
std::ostringstream actionstr;
for (int acnt = ((*i)->actionAll ? 0 : (*i)->action);
@@ -126,13 +137,6 @@ namespace acl {
// Go through the rule properties and find the name and the key.
// If found then place them specially for the lookup engine.
for (pmCitr pItr=(*i)->props.begin(); pItr!=(*i)->props.end(); pItr++) {
- if (acl::SPECPROP_ROUTINGKEY == pItr->first)
- {
- rule.pubRoutingKeyInRule = true;
- rule.pubRoutingKey = (std::string)pItr->second;
- rule.addTopicTest(rule.pubRoutingKey);
- break;
- }
if (acl::SPECPROP_NAME == pItr->first)
{
rule.pubExchNameInRule = true;
@@ -164,7 +168,7 @@ namespace acl {
// add users and Rule to object set
bool allNames = false;
// check to see if names.begin is '*'
- if ((*(*i)->names.begin()).compare("*") == 0)
+ if ((*(*i)->names.begin()).compare(AclData::ACL_KEYWORD_WILDCARD) == 0)
allNames = true;
for (nsCitr itr = (allNames ? names.begin() : (*i)->names.begin());
@@ -196,7 +200,7 @@ namespace acl {
objstr << AclHelper::getObjectTypeStr((ObjectType) ocnt) << ",";
}
- bool allNames = ((*(*i)->names.begin()).compare("*") == 0);
+ bool allNames = ((*(*i)->names.begin()).compare(AclData::ACL_KEYWORD_WILDCARD) == 0);
std::ostringstream userstr;
for (nsCitr itr = (allNames ? names.begin() : (*i)->names.begin());
itr != (allNames ? names.end() : (*i)->names.end());
@@ -215,12 +219,15 @@ namespace acl {
<< "}" );
}
}
+
+ // connection quota
+ d->setConnQuotaRuleSettings(connQuotaRulesExist, connQuota);
}
void AclReader::aclRule::processName(const std::string& name, const groupMap& groups) {
- if (name.compare("all") == 0) {
- names.insert("*");
+ if (name.compare(AclData::ACL_KEYWORD_ALL) == 0) {
+ names.insert(AclData::ACL_KEYWORD_WILDCARD);
} else {
gmCitr itr = groups.find(name);
if (itr == groups.end()) {
@@ -231,9 +238,13 @@ namespace acl {
}
}
- AclReader::AclReader() : lineNumber(0), contFlag(false), validationMap(new AclHelper::objectMap) {
+ AclReader::AclReader(uint16_t theCliMaxConnPerUser) : lineNumber(0), contFlag(false),
+ validationMap(new AclHelper::objectMap),
+ cliMaxConnPerUser (theCliMaxConnPerUser),
+ connQuotaRulesExist(false),
+ connQuota(new AclData::quotaRuleSet) {
AclHelper::loadValidationMap(validationMap);
- names.insert("*");
+ names.insert(AclData::ACL_KEYWORD_WILDCARD);
}
AclReader::~AclReader() {}
@@ -251,6 +262,11 @@ namespace acl {
errorStream << "Unable to open ACL file \"" << fn << "\": eof=" << (ifs.eof()?"T":"F") << "; fail=" << (ifs.fail()?"T":"F") << "; bad=" << (ifs.bad()?"T":"F");
return -1;
}
+ // Propagate nonzero per-user max connection setting from CLI
+ if (cliMaxConnPerUser > 0) {
+ (*connQuota)[AclData::ACL_KEYWORD_ACL] = cliMaxConnPerUser;
+ }
+ // Loop to process the Acl file
try {
bool err = false;
while (ifs.good()) {
@@ -279,6 +295,7 @@ namespace acl {
}
printNames();
printRules();
+ printConnectionQuotas();
loadDecisionData(d);
return 0;
@@ -289,7 +306,7 @@ namespace acl {
std::vector<std::string> toks;
// Check for continuation
- char* contCharPtr = std::strrchr(line, '\\');
+ char* contCharPtr = std::strrchr(line, AclData::ACL_SYMBOL_LINE_CONTINUATION);
bool cont = contCharPtr != 0;
if (cont) *contCharPtr = 0;
@@ -300,10 +317,12 @@ namespace acl {
return false;
}
- if (numToks && (toks[0].compare("group") == 0 || contFlag)) {
+ if (numToks && (toks[0].compare(AclData::ACL_KEYWORD_GROUP) == 0 || contFlag)) {
ret = processGroupLine(toks, cont);
- } else if (numToks && toks[0].compare("acl") == 0) {
+ } else if (numToks && toks[0].compare(AclData::ACL_KEYWORD_ACL) == 0) {
ret = processAclLine(toks);
+ } else if (numToks && toks[0].compare(AclData::ACL_KEYWORD_QUOTA) == 0) {
+ ret = processQuotaLine(toks);
} else {
// Check for whitespace only line, ignore these
bool ws = true;
@@ -314,7 +333,10 @@ namespace acl {
ret = true;
} else {
errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
- << ", Non-continuation line must start with \"group\" or \"acl\".";
+ << ", Non-continuation line must start with \""
+ << AclData::ACL_KEYWORD_GROUP << "\", \""
+ << AclData::ACL_KEYWORD_ACL << "\". or \""
+ << AclData::ACL_KEYWORD_QUOTA << "\".";
ret = false;
}
}
@@ -334,6 +356,102 @@ namespace acl {
return cnt;
}
+
+ // Process 'quota' rule lines
+ // Return true if the line is successfully processed without errors
+ bool AclReader::processQuotaLine(tokList& toks) {
+ const unsigned toksSize = toks.size();
+ const unsigned minimumSize = 3;
+ if (toksSize < minimumSize) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Insufficient tokens for quota definition.";
+ return false;
+ }
+
+ if (toks[1].compare(AclData::ACL_KEYWORD_QUOTA_CONNECTIONS) == 0) {
+ return processQuotaConnLine(toks);
+ } else {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Quota type \"" << toks[1] << "\" unrecognized.";
+ return false;
+ }
+ }
+
+
+ // Process 'quota connections' rule lines
+ // Return true if the line is successfully processed without errors
+ bool AclReader::processQuotaConnLine(tokList& toks) {
+ const unsigned toksSize = toks.size();
+
+ uint16_t nConns(0);
+ try {
+ nConns = boost::lexical_cast<uint16_t>(toks[2]);
+ } catch(const boost::bad_lexical_cast&) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Connection quota value \"" << toks[2]
+ << "\" cannot be converted to a 16-bit unsigned integer.";
+ return false;
+ }
+
+ // limit check the connection setting
+ if (nConns > AclData::getConnectMaxSpec())
+ {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Connection quota value \"" << toks[2]
+ << "\" exceeds maximum configuration setting of "
+ << AclData::getConnectMaxSpec();
+ return false;
+ }
+
+ // Apply the connection count to all names in rule
+ for (unsigned idx = 3; idx < toksSize; idx++) {
+ if (groups.find(toks[idx]) == groups.end()) {
+ // This is the name of an individual, not a group
+ (*connQuota)[toks[idx]] = nConns;
+ } else {
+ if (!processQuotaConnGroup(toks[idx], nConns))
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ // Process 'quota connections' group expansion
+ // Return true if the quota is applied to all members of the group
+ bool AclReader::processQuotaConnGroup(const std::string& theGroup, uint16_t theQuota) {
+ gmCitr citr = groups.find(theGroup);
+
+ if (citr == groups.end()) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Failed to expand group \"" << theGroup << "\".";
+ return false;
+ }
+
+ for (nsCitr gni=citr->second->begin(); gni!=citr->second->end(); gni++) {
+ if (groups.find(*gni) == groups.end()) {
+ (*connQuota)[*gni] = theQuota;
+ } else {
+ if (!processQuotaConnGroup(*gni, theQuota))
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ void AclReader::printConnectionQuotas() const {
+ QPID_LOG(debug, "ACL: connection quota: " << (*connQuota).size() << " rules found:");
+ int cnt = 1;
+ for (AclData::quotaRuleSetItr itr=(*connQuota).begin();
+ itr != (*connQuota).end();
+ ++itr,++cnt) {
+ QPID_LOG(debug, "ACL: quota " << cnt << " : " << (*itr).second
+ << " connections for " << (*itr).first)
+ }
+ }
+
+
// 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) {
@@ -459,8 +577,8 @@ namespace acl {
return false;
}
- bool actionAllFlag = toks[3].compare("all") == 0;
- bool userAllFlag = toks[2].compare("all") == 0;
+ bool actionAllFlag = toks[3].compare(AclData::ACL_KEYWORD_ALL) == 0;
+ bool userAllFlag = toks[2].compare(AclData::ACL_KEYWORD_ALL) == 0;
Action action;
if (actionAllFlag) {
@@ -489,7 +607,7 @@ namespace acl {
}
if (toksSize >= 5) { // object name-value pair
- if (toks[4].compare("all") == 0) {
+ if (toks[4].compare(AclData::ACL_KEYWORD_ALL) == 0) {
rule->setObjectTypeAll();
} else {
try {
@@ -523,7 +641,7 @@ namespace acl {
}
}
// Check if name (toks[2]) is group; if not, add as name of individual
- if (toks[2].compare("all") != 0) {
+ if (toks[2].compare(AclData::ACL_KEYWORD_ALL) != 0) {
if (groups.find(toks[2]) == groups.end()) {
addName(toks[2]);
}
diff --git a/cpp/src/qpid/acl/AclReader.h b/cpp/src/qpid/acl/AclReader.h
index 6351c1e509..1fa374c59c 100644
--- a/cpp/src/qpid/acl/AclReader.h
+++ b/cpp/src/qpid/acl/AclReader.h
@@ -28,6 +28,7 @@
#include <sstream>
#include <memory>
#include "qpid/acl/AclData.h"
+#include "qpid/acl/Acl.h"
#include "qpid/broker/AclModule.h"
namespace qpid {
@@ -96,7 +97,7 @@ class AclReader {
std::ostringstream errorStream;
public:
- AclReader();
+ AclReader(uint16_t cliMaxConnPerUser);
virtual ~AclReader();
int read(const std::string& fn, boost::shared_ptr<AclData> d); // return=0 for success
std::string getError();
@@ -116,8 +117,17 @@ class AclReader {
void printRules() const; // debug aid
bool isValidUserName(const std::string& name);
+ bool processQuotaLine(tokList& toks);
+ bool processQuotaConnLine(tokList& toks);
+ bool processQuotaConnGroup(const std::string&, uint16_t);
+ void printConnectionQuotas() const;
+
static bool isValidGroupName(const std::string& name);
static nvPair splitNameValuePair(const std::string& nvpString);
+
+ const uint16_t cliMaxConnPerUser;
+ bool connQuotaRulesExist;
+ boost::shared_ptr<AclData::quotaRuleSet> connQuota;
};
}} // namespace qpid::acl
diff --git a/cpp/src/qpid/acl/AclResourceCounter.cpp b/cpp/src/qpid/acl/AclResourceCounter.cpp
new file mode 100644
index 0000000000..66dfd0777e
--- /dev/null
+++ b/cpp/src/qpid/acl/AclResourceCounter.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 "AclResourceCounter.h"
+#include "Acl.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Mutex.h"
+#include <assert.h>
+#include <sstream>
+
+using namespace qpid::sys;
+
+namespace qpid {
+namespace acl {
+
+//
+// This module approves various resource creation requests:
+// Queues
+//
+
+
+//
+//
+//
+ResourceCounter::ResourceCounter(Acl& a, uint16_t ql) :
+ acl(a), queueLimit(ql) {}
+
+ResourceCounter::~ResourceCounter() {}
+
+
+//
+// limitApproveLH
+//
+// Resource creation approver.
+// If user is under limit increment count and return true.
+// Called with lock held.
+//
+bool ResourceCounter::limitApproveLH(
+ const std::string& theTitle,
+ countsMap_t& theMap,
+ const std::string& theName,
+ uint16_t theLimit,
+ bool emitLog) {
+
+ bool result(true);
+ if (theLimit > 0) {
+ uint16_t count;
+ countsMap_t::iterator eRef = theMap.find(theName);
+ if (eRef != theMap.end()) {
+ count = (uint16_t)(*eRef).second;
+ result = count < theLimit;
+ if (result) {
+ count += 1;
+ (*eRef).second = count;
+ }
+ } else {
+ // Not found
+ theMap[theName] = count = 1;
+ }
+ if (emitLog) {
+ QPID_LOG(trace, theTitle << theName
+ << " limit=" << theLimit
+ << " curValue=" << count
+ << " result=" << (result ? "allow" : "deny"));
+ }
+ }
+ return result;
+}
+
+
+//
+// releaseLH
+//
+// Decrement the name's count in map.
+// called with dataLock already taken
+//
+void ResourceCounter::releaseLH(
+ const std::string& theTitle, countsMap_t& theMap, const std::string& theName, uint16_t theLimit) {
+
+ if (theLimit > 0) {
+ countsMap_t::iterator eRef = theMap.find(theName);
+ if (eRef != theMap.end()) {
+ uint16_t count = (uint16_t) (*eRef).second;
+ assert (count > 0);
+ if (1 == count) {
+ theMap.erase (eRef);
+ } else {
+ (*eRef).second = count - 1;
+ }
+ } else {
+ // User had no connections.
+ QPID_LOG(notice, theTitle << theName
+ << "' not found in resource count pool");
+ }
+ }
+}
+
+
+//
+// approveCreateQueue
+// Count an attempted queue creation by this user.
+// Disapprove if over limit.
+//
+bool ResourceCounter::approveCreateQueue(const std::string& userId, const std::string& queueName)
+{
+ Mutex::ScopedLock locker(dataLock);
+
+ bool okByQ = limitApproveLH("ACL Queue creation approver. userId:", queuePerUserMap, userId, queueLimit, true);
+
+ if (okByQ) {
+ // Queue is owned by this userId
+ queueOwnerMap[queueName] = userId;
+
+ QPID_LOG(trace, "ACL create queue approved for user '" << userId
+ << "' queue '" << queueName << "'");
+ } else {
+
+ QPID_LOG(error, "Client max queue count limit of " << queueLimit
+ << " exceeded by '" << userId << "' creating queue '"
+ << queueName << "'. Queue creation denied.");
+
+ acl.reportQueueLimit(userId, queueName);
+ }
+ return okByQ;
+}
+
+
+//
+// recordDestroyQueue
+// Return a destroyed queue to a user's quota
+//
+void ResourceCounter::recordDestroyQueue(const std::string& queueName)
+{
+ Mutex::ScopedLock locker(dataLock);
+
+ queueOwnerMap_t::iterator eRef = queueOwnerMap.find(queueName);
+ if (eRef != queueOwnerMap.end()) {
+ releaseLH("ACL resource counter: Queue owner for queue '", queuePerUserMap, (*eRef).second, queueLimit);
+
+ queueOwnerMap.erase(eRef);
+ } else {
+ QPID_LOG(notice, "ACL resource counter: Queue '" << queueName
+ << "' not found in queue owner map");
+ }
+}
+
+}} // namespace qpid::acl
diff --git a/cpp/src/qpid/acl/AclResourceCounter.h b/cpp/src/qpid/acl/AclResourceCounter.h
new file mode 100644
index 0000000000..f5995eb961
--- /dev/null
+++ b/cpp/src/qpid/acl/AclResourceCounter.h
@@ -0,0 +1,78 @@
+#ifndef QPID_ACL_RESOURCECOUNTER_H
+#define QPID_ACL_RESOURCECOUNTER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/iterator/iterator_concepts.hpp>
+
+#include <map>
+
+namespace qpid {
+
+namespace acl {
+class Acl;
+
+ /**
+ * Approve or disapprove resource creation requests
+ */
+class ResourceCounter
+{
+private:
+ typedef std::map<std::string, uint32_t> countsMap_t;
+ typedef std::map<std::string, std::string> queueOwnerMap_t;
+
+ Acl& acl;
+ uint16_t queueLimit;
+ qpid::sys::Mutex dataLock;
+
+ /** Records queueName-queueUserId */
+ queueOwnerMap_t queueOwnerMap;
+
+ /** Records queue-by-owner counts */
+ countsMap_t queuePerUserMap;
+
+ /** Return approval for proposed resource creation */
+ bool limitApproveLH(const std::string& theTitle,
+ countsMap_t& theMap,
+ const std::string& theName,
+ uint16_t theLimit,
+ bool emitLog);
+
+ /** Release a connection */
+ void releaseLH(const std::string& theTitle,
+ countsMap_t& theMap,
+ const std::string& theName,
+ uint16_t theLimit);
+
+public:
+ ResourceCounter(Acl& acl, uint16_t ql);
+ ~ResourceCounter();
+
+ // Queue counting
+ bool approveCreateQueue(const std::string& userId, const std::string& queueName);
+ void recordDestroyQueue(const std::string& queueName);
+};
+
+}} // namespace qpid::acl
+
+#endif /*!QPID_ACL_RESOURCECOUNTER_H*/
diff --git a/cpp/src/qpid/acl/AclTopicMatch.h b/cpp/src/qpid/acl/AclTopicMatch.h
index 486c229ad5..654d1d63d4 100644
--- a/cpp/src/qpid/acl/AclTopicMatch.h
+++ b/cpp/src/qpid/acl/AclTopicMatch.h
@@ -30,7 +30,7 @@ namespace qpid {
namespace broker {
// Class for executing topic exchange routing key matching rules in
-// Acl code the allows or denies users publishing to an exchange.
+// Acl code. Allows or denies users publishing to an exchange.
class TopicExchange::TopicExchangeTester {
class boundNode;
diff --git a/cpp/src/qpid/acl/AclValidator.cpp b/cpp/src/qpid/acl/AclValidator.cpp
index 85f0f7c240..73b49b2959 100644
--- a/cpp/src/qpid/acl/AclValidator.cpp
+++ b/cpp/src/qpid/acl/AclValidator.cpp
@@ -94,6 +94,22 @@ namespace acl {
boost::shared_ptr<PropertyType>(
new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+ validators.insert(Validator(acl::SPECPROP_MAXFILESIZELOWERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
+ validators.insert(Validator(acl::SPECPROP_MAXFILESIZEUPPERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
+ validators.insert(Validator(acl::SPECPROP_MAXFILECOUNTLOWERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
+ validators.insert(Validator(acl::SPECPROP_MAXFILECOUNTUPPERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
std::string policyTypes[] = {"ring", "ring_strict", "flow_to_disk", "reject"};
std::vector<std::string> v(policyTypes, policyTypes + sizeof(policyTypes) / sizeof(std::string));
validators.insert(Validator(acl::SPECPROP_POLICYTYPE,
diff --git a/cpp/src/qpid/acl/management-schema.xml b/cpp/src/qpid/acl/management-schema.xml
index f52c251bed..2ac20bb324 100644
--- a/cpp/src/qpid/acl/management-schema.xml
+++ b/cpp/src/qpid/acl/management-schema.xml
@@ -25,8 +25,10 @@
<property name="maxConnections" type="uint16" access="RO" desc="Maximum allowed connections"/>
<property name="maxConnectionsPerIp" type="uint16" access="RO" desc="Maximum allowed connections"/>
<property name="maxConnectionsPerUser" type="uint16" access="RO" desc="Maximum allowed connections"/>
+ <property name="maxQueuesPerUser" type="uint16" access="RO" desc="Maximum allowed queues"/>
<statistic name="aclDenyCount" type="count64" unit="request" desc="Number of ACL requests denied"/>
<statistic name="connectionDenyCount" type="count64" unit="connection" desc="Number of connections denied"/>
+ <statistic name="queueQuotaDenyCount" type="count64" unit="queue" desc="Number of queue creations denied"/>
<method name="reloadACLFile" desc="Reload the ACL file"/>
@@ -70,11 +72,13 @@
<arg name="reason" type="lstr"/>
<arg name="userId" type="sstr"/>
<arg name="clientAddr" type="sstr"/>
+ <arg name="queueName" 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="connectionDeny" sev="notice" args="userId, clientAddr"/>
+ <event name="queueQuotaDeny" sev="notice" args="userId, queueName"/>
<event name="fileLoaded" sev="inform" args="userId"/>
<event name="fileLoadFailed" sev="error" args="userId, reason"/>
diff --git a/cpp/src/qpid/agent/ManagementAgentImpl.cpp b/cpp/src/qpid/agent/ManagementAgentImpl.cpp
index 09b7fa58e9..a48789973a 100644
--- a/cpp/src/qpid/agent/ManagementAgentImpl.cpp
+++ b/cpp/src/qpid/agent/ManagementAgentImpl.cpp
@@ -654,7 +654,10 @@ void ManagementAgentImpl::invokeMethodRequest(const string& body, const string&
void ManagementAgentImpl::handleGetQuery(const string& body, const string& cid, const string& rte, const string& rtk)
{
- moveNewObjectsLH();
+ {
+ sys::Mutex::ScopedLock lock(agentLock);
+ moveNewObjectsLH(lock);
+ }
Variant::Map inMap;
Variant::Map::const_iterator i;
@@ -985,14 +988,37 @@ ManagementAgentImpl::PackageMap::iterator ManagementAgentImpl::findOrAddPackage(
return result.first;
}
-void ManagementAgentImpl::moveNewObjectsLH()
+// note well: caller must hold agentLock when calling this!
+void ManagementAgentImpl::moveNewObjectsLH(const sys::Mutex::ScopedLock& /*agentLock*/)
{
sys::Mutex::ScopedLock lock(addLock);
- for (ObjectMap::iterator iter = newManagementObjects.begin();
- iter != newManagementObjects.end();
- iter++)
- managementObjects[iter->first] = iter->second;
- newManagementObjects.clear();
+ ObjectMap::iterator newObj = newManagementObjects.begin();
+ while (newObj != newManagementObjects.end()) {
+ // before adding a new mgmt object, check for duplicates:
+ ObjectMap::iterator oldObj = managementObjects.find(newObj->first);
+ if (oldObj == managementObjects.end()) {
+ managementObjects[newObj->first] = newObj->second;
+ newManagementObjects.erase(newObj++); // post inc iterator safe!
+ } else {
+ // object exists with same object id. This may be legit, for example, when a
+ // recently deleted object is re-added before the mgmt poll runs.
+ if (newObj->second->isDeleted()) {
+ // @TODO fixme: we missed an add-delete for the new object
+ QPID_LOG(warning, "Mgmt Object deleted before update sent, oid=" << newObj->first);
+ newManagementObjects.erase(newObj++); // post inc iterator safe!
+ } else if (oldObj->second->isDeleted()) {
+ // skip adding newObj, try again later once oldObj has been cleaned up by poll
+ ++newObj;
+ } else {
+ // real bad - two objects exist with same OID. This is a bug in the application
+ QPID_LOG(error, "Detected two Mgmt Objects using the same object id! oid=" << newObj->first
+ << ", this is bad!");
+ // what to do here? Can't erase an active obj - owner has a pointer to it.
+ // for now I punt. Maybe the flood of log messages will get someone's attention :P
+ ++newObj;
+ }
+ }
+ }
}
void ManagementAgentImpl::addClassLocal(uint8_t classKind,
@@ -1060,7 +1086,7 @@ void ManagementAgentImpl::periodicProcessing()
if (!connected)
return;
- moveNewObjectsLH();
+ moveNewObjectsLH(lock);
//
// Clear the been-here flag on all objects in the map.
diff --git a/cpp/src/qpid/agent/ManagementAgentImpl.h b/cpp/src/qpid/agent/ManagementAgentImpl.h
index 53f3c13a91..4c97bc89da 100644
--- a/cpp/src/qpid/agent/ManagementAgentImpl.h
+++ b/cpp/src/qpid/agent/ManagementAgentImpl.h
@@ -92,10 +92,6 @@ class ManagementAgentImpl : public ManagementAgent, public client::MessageListen
uint16_t getInterval() { return interval; }
void periodicProcessing();
- // these next are here to support the hot-wiring of state between clustered brokers
- uint64_t getNextObjectId(void) { return nextObjectId; }
- void setNextObjectId(uint64_t o) { nextObjectId = o; }
-
uint16_t getBootSequence(void) { return bootSequence; }
void setBootSequence(uint16_t b) { bootSequence = b; }
@@ -261,7 +257,7 @@ class ManagementAgentImpl : public ManagementAgent, public client::MessageListen
void storeData(bool requested=false);
void retrieveData(std::string& vendor, std::string& product, std::string& inst);
PackageMap::iterator findOrAddPackage(const std::string& name);
- void moveNewObjectsLH();
+ void moveNewObjectsLH(const sys::Mutex::ScopedLock& agentLock);
void addClassLocal (uint8_t classKind,
PackageMap::iterator pIter,
const std::string& className,
diff --git a/cpp/src/qpid/amqp/CharSequence.cpp b/cpp/src/qpid/amqp/CharSequence.cpp
new file mode 100644
index 0000000000..857ec7e587
--- /dev/null
+++ b/cpp/src/qpid/amqp/CharSequence.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 "CharSequence.h"
+
+namespace qpid {
+namespace amqp {
+
+void CharSequence::init()
+{
+ data = 0;
+ size = 0;
+}
+
+CharSequence::operator bool() const
+{
+ return data && size;
+}
+std::string CharSequence::str() const
+{
+ return std::string(data, size);
+}
+
+CharSequence CharSequence::create(const char* data, size_t size)
+{
+ CharSequence c = {data, size};
+ return c;
+}
+
+}} // namespace qpid::amqp
diff --git a/cpp/src/qpid/amqp/CharSequence.h b/cpp/src/qpid/amqp/CharSequence.h
new file mode 100644
index 0000000000..307a4b1537
--- /dev/null
+++ b/cpp/src/qpid/amqp/CharSequence.h
@@ -0,0 +1,49 @@
+#ifndef QPID_AMQP_CHARSEQUENCE_H
+#define QPID_AMQP_CHARSEQUENCE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <stddef.h>
+#include <string>
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+namespace amqp {
+
+/**
+ * Simple record of a particular sequence of chars/bytes. The memroy
+ * referenced is assumed to be owned by some other entity, this is
+ * merely a pointer into a (segment of) it.
+ */
+struct CharSequence
+{
+ const char* data;
+ size_t size;
+
+ QPID_COMMON_EXTERN operator bool() const;
+ QPID_COMMON_EXTERN std::string str() const;
+ QPID_COMMON_EXTERN void init();
+
+ QPID_COMMON_EXTERN static CharSequence create(const char* data, size_t size);
+};
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_CHARSEQUENCE_H*/
diff --git a/cpp/src/qpid/amqp/Codec.h b/cpp/src/qpid/amqp/Codec.h
new file mode 100644
index 0000000000..c91cd0a96b
--- /dev/null
+++ b/cpp/src/qpid/amqp/Codec.h
@@ -0,0 +1,83 @@
+#ifndef QPID_AMQP_CODEC_H
+#define QPID_AMQP_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.
+ *
+ */
+namespace qpid {
+namespace amqp {
+
+/**
+ *
+ */
+class Codec
+{
+ public:
+
+
+
+ private:
+
+ struct Constructor
+ {
+ uint8_t code;
+ Descriptor descriptor;
+ bool isDescribed;
+ };
+
+ Constructor readConstructor(Decoder decoder, Reader reader)
+ {
+ Constructor result;
+ result.code = decoder.readCode();
+ if (code == DESCRIPTOR) {
+ result.isDescribed = true;
+ result.descriptor = decoder.readDescriptor();
+ result.code = decoder.readCode();
+ } else {
+ result.isDescribed = false;
+ }
+ return result;
+ }
+};
+
+Codec::Descriptor Codec::Decoder::readDescriptor()
+{
+ uint8_t code = decoder.readCode();
+ switch(code) {
+ case SYMBOL8:
+ return Descriptor(readSequence8());
+ case SYMBOL32:
+ return Descriptor(readSequence32());
+ case ULONG:
+ return Descriptor(readULong());
+ case ULONG_SMALL:
+ return Descriptor((uint64_t) readUByte());
+ case ULONG_ZERO:
+ return Descriptor((uint64_t) 0);
+ default:
+ throw qpid::Exception("Expected descriptor of type ulong or symbol; found " << code);
+ }
+}
+
+Codec::Descriptor::Descriptor(uint64_t id) : value.id(id), type(NUMERIC) {}
+Codec::Descriptor::Descriptor(const CharSequence& symbol) : value.symbol(symbol), type(SYMBOLIC) {}
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_CODEC_H*/
diff --git a/cpp/src/qpid/amqp/Constructor.h b/cpp/src/qpid/amqp/Constructor.h
new file mode 100644
index 0000000000..444e455670
--- /dev/null
+++ b/cpp/src/qpid/amqp/Constructor.h
@@ -0,0 +1,42 @@
+#ifndef QPID_AMQP_CONSTRUCTOR_H
+#define QPID_AMQP_CONSTRUCTOR_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/Descriptor.h"
+namespace qpid {
+namespace amqp {
+
+/**
+ * Representation of an AMQP 1.0 type 'constructor' (i.e. a type code
+ * with an optional descriptor)
+ */
+struct Constructor
+{
+ uint8_t code;
+ Descriptor descriptor;
+ bool isDescribed;
+
+ Constructor(uint8_t c) : code(c), descriptor(0), isDescribed(false) {}
+};
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_CONSTRUCTOR_H*/
diff --git a/cpp/src/qpid/amqp/Decoder.cpp b/cpp/src/qpid/amqp/Decoder.cpp
new file mode 100644
index 0000000000..9c577e6c92
--- /dev/null
+++ b/cpp/src/qpid/amqp/Decoder.cpp
@@ -0,0 +1,545 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/amqp/Decoder.h"
+#include "qpid/amqp/CharSequence.h"
+#include "qpid/amqp/Constructor.h"
+#include "qpid/amqp/Descriptor.h"
+#include "qpid/amqp/Reader.h"
+#include "qpid/amqp/typecodes.h"
+#include "qpid/types/Uuid.h"
+#include "qpid/types/Variant.h"
+#include "qpid/log/Statement.h"
+#include "qpid/Exception.h"
+
+namespace qpid {
+namespace amqp {
+
+using namespace qpid::amqp::typecodes;
+
+Decoder::Decoder(const char* d, size_t s) : start(d), size(s), position(0) {}
+
+namespace {
+class MapBuilder : public Reader
+{
+ public:
+ void onNull(const Descriptor*)
+ {
+ qpid::types::Variant v;
+ handle(v, NULL_NAME);
+ }
+ void onBoolean(bool v, const Descriptor*)
+ {
+ handle(v, BOOLEAN_NAME);
+ }
+ void onUByte(uint8_t v, const Descriptor*)
+ {
+ handle(v, UBYTE_NAME);
+ }
+ void onUShort(uint16_t v, const Descriptor*)
+ {
+ handle(v, USHORT_NAME);
+ }
+ void onUInt(uint32_t v, const Descriptor*)
+ {
+ handle(v, UINT_NAME);
+ }
+ void onULong(uint64_t v, const Descriptor*)
+ {
+ handle(v, ULONG_NAME);
+ }
+ void onByte(int8_t v, const Descriptor*)
+ {
+ handle(v, BYTE_NAME);
+ }
+ void onShort(int16_t v, const Descriptor*)
+ {
+ handle(v, SHORT_NAME);
+ }
+ void onInt(int32_t v, const Descriptor*)
+ {
+ handle(v, INT_NAME);
+ }
+ void onLong(int64_t v, const Descriptor*)
+ {
+ handle(v, LONG_NAME);
+ }
+ void onFloat(float v, const Descriptor*)
+ {
+ handle(v, FLOAT_NAME);
+ }
+ void onDouble(double v, const Descriptor*)
+ {
+ handle(v, DOUBLE_NAME);
+ }
+ void onUuid(const CharSequence& v, const Descriptor*)
+ {
+ handle(v, UUID_NAME);
+ }
+ void onTimestamp(int64_t v, const Descriptor*)
+ {
+ handle(v, TIMESTAMP_NAME);
+ }
+ void onBinary(const CharSequence& v, const Descriptor*)
+ {
+ handle(v);
+ }
+ void onString(const CharSequence& v, const Descriptor*)
+ {
+ handle(v);
+ }
+ void onSymbol(const CharSequence& v, const Descriptor*)
+ {
+ handle(v);
+ }
+ MapBuilder(qpid::types::Variant::Map& m) : map(m), state(KEY) {}
+ private:
+ qpid::types::Variant::Map& map;
+ enum {KEY, SKIP, VALUE} state;
+ std::string key;
+
+ template <typename T> void handle(T value, const std::string& name)
+ {
+ switch (state) {
+ case KEY:
+ QPID_LOG(warning, "Ignoring key of type " << name);
+ state = SKIP;
+ break;
+ case VALUE:
+ map[key] = value;
+ case SKIP:
+ state = KEY;
+ break;
+ }
+ }
+ void handle(const CharSequence& value)
+ {
+ switch (state) {
+ case KEY:
+ key = value.str();
+ state = VALUE;
+ break;
+ case VALUE:
+ map[key] = value.str();
+ case SKIP:
+ state = KEY;
+ break;
+ }
+ }
+};
+}
+void Decoder::readMap(qpid::types::Variant::Map& map)
+{
+ MapBuilder builder(map);
+ read(builder);
+}
+
+qpid::types::Variant::Map Decoder::readMap()
+{
+ qpid::types::Variant::Map map;
+ readMap(map);
+ return map;
+}
+
+void Decoder::read(Reader& reader)
+{
+ while (available() && reader.proceed()) {
+ readOne(reader);
+ }
+}
+
+void Decoder::readOne(Reader& reader)
+{
+ const char* temp = start + position;
+ Constructor c = readConstructor();
+ if (c.isDescribed) reader.onDescriptor(c.descriptor, temp);
+ readValue(reader, c.code, c.isDescribed ? &c.descriptor : 0);
+}
+
+void Decoder::readValue(Reader& reader, uint8_t code, const Descriptor* descriptor)
+{
+ switch(code) {
+ case NULL_VALUE:
+ reader.onNull(descriptor);
+ break;
+ case BOOLEAN:
+ reader.onBoolean(readBoolean(), descriptor);
+ break;
+ case BOOLEAN_TRUE:
+ reader.onBoolean(true, descriptor);
+ break;
+ case BOOLEAN_FALSE:
+ reader.onBoolean(false, descriptor);
+ break;
+ case UBYTE:
+ reader.onUByte(readUByte(), descriptor);
+ break;
+ case USHORT:
+ reader.onUShort(readUShort(), descriptor);
+ break;
+ case UINT:
+ reader.onUInt(readUInt(), descriptor);
+ break;
+ case UINT_SMALL:
+ reader.onUInt(readUByte(), descriptor);
+ break;
+ case UINT_ZERO:
+ reader.onUInt(0, descriptor);
+ break;
+ case ULONG:
+ reader.onULong(readULong(), descriptor);
+ break;
+ case ULONG_SMALL:
+ reader.onULong(readUByte(), descriptor);
+ break;
+ case ULONG_ZERO:
+ reader.onULong(0, descriptor);
+ break;
+ case BYTE:
+ reader.onByte(readByte(), descriptor);
+ break;
+ case SHORT:
+ reader.onShort(readShort(), descriptor);
+ break;
+ case INT:
+ reader.onInt(readInt(), descriptor);
+ break;
+ case INT_SMALL:
+ reader.onInt(readByte(), descriptor);
+ break;
+ case LONG:
+ reader.onLong(readLong(), descriptor);
+ break;
+ case LONG_SMALL:
+ reader.onLong(readByte(), descriptor);
+ break;
+ case FLOAT:
+ reader.onFloat(readFloat(), descriptor);
+ break;
+ case DOUBLE:
+ reader.onDouble(readDouble(), descriptor);
+ break;
+ case UUID:
+ reader.onUuid(readRawUuid(), descriptor);
+ break;
+ case TIMESTAMP:
+ reader.onTimestamp(readLong(), descriptor);
+ break;
+
+ case BINARY8:
+ reader.onBinary(readSequence8(), descriptor);
+ break;
+ case BINARY32:
+ reader.onBinary(readSequence32(), descriptor);
+ break;
+ case STRING8:
+ reader.onString(readSequence8(), descriptor);
+ break;
+ case STRING32:
+ reader.onString(readSequence32(), descriptor);
+ break;
+ case SYMBOL8:
+ reader.onSymbol(readSequence8(), descriptor);
+ break;
+ case SYMBOL32:
+ reader.onSymbol(readSequence32(), descriptor);
+ break;
+
+ case LIST0:
+ reader.onStartList(0, CharSequence::create(0, 0), descriptor);
+ reader.onEndList(0, descriptor);
+ break;
+ case LIST8:
+ readList8(reader, descriptor);
+ break;
+ case LIST32:
+ readList32(reader, descriptor);
+ break;
+ case MAP8:
+ readMap8(reader, descriptor);
+ break;
+ case MAP32:
+ readMap32(reader, descriptor);
+ break;
+ case ARRAY8:
+ readArray8(reader, descriptor);
+ break;
+ case ARRAY32:
+ readArray32(reader, descriptor);
+ break;
+ default:
+ break;
+ }
+}
+
+void Decoder::readList8(Reader& reader, const Descriptor* descriptor)
+{
+ uint8_t size = readUByte();
+ uint8_t count = readUByte();
+ readList(reader, size-sizeof(size), count, descriptor);
+}
+
+void Decoder::readList32(Reader& reader, const Descriptor* descriptor)
+{
+ uint32_t size = readUInt();
+ uint32_t count = readUInt();
+ readList(reader, size-sizeof(size), count, descriptor);
+}
+
+void Decoder::readMap8(Reader& reader, const Descriptor* descriptor)
+{
+ uint8_t size = readUByte();
+ uint8_t count = readUByte();
+ readMap(reader, size-sizeof(size), count, descriptor);
+}
+
+void Decoder::readMap32(Reader& reader, const Descriptor* descriptor)
+{
+ uint32_t size = readUInt();
+ uint32_t count = readUInt();
+ readMap(reader, size-sizeof(size), count, descriptor);
+}
+
+void Decoder::readArray8(Reader& reader, const Descriptor* descriptor)
+{
+ uint8_t size = readUByte();
+ uint8_t count = readUByte();
+ readArray(reader, size-sizeof(size), count, descriptor);
+}
+
+void Decoder::readArray32(Reader& reader, const Descriptor* descriptor)
+{
+ uint32_t size = readUInt();
+ uint32_t count = readUInt();
+ readArray(reader, size-sizeof(size), count, descriptor);
+}
+
+void Decoder::readList(Reader& reader, uint32_t size, uint32_t count, const Descriptor* descriptor)
+{
+ if (reader.onStartList(count, CharSequence::create(data(), size), descriptor)) {
+ for (uint32_t i = 0; i < count; ++i) {
+ readOne(reader);
+ }
+ reader.onEndList(count, descriptor);
+ } else {
+ //skip
+ advance(size);
+ }
+}
+void Decoder::readMap(Reader& reader, uint32_t size, uint32_t count, const Descriptor* descriptor)
+{
+ if (reader.onStartMap(count, CharSequence::create(data(), size), descriptor)) {
+ for (uint32_t i = 0; i < count; ++i) {
+ readOne(reader);
+ }
+ reader.onEndMap(count, descriptor);
+ } else {
+ //skip
+ advance(size);
+ }
+}
+
+void Decoder::readArray(Reader& reader, uint32_t size, uint32_t count, const Descriptor* descriptor)
+{
+ size_t temp = position;
+ Constructor constructor = readConstructor();
+ CharSequence raw = CharSequence::create(data(), size-(position-temp));
+ if (reader.onStartArray(count, raw, constructor, descriptor)) {
+ for (uint32_t i = 0; i < count; ++i) {
+ readValue(reader, constructor.code, constructor.isDescribed ? &constructor.descriptor : 0);
+ }
+ reader.onEndArray(count, descriptor);
+ } else {
+ //skip
+ advance(raw.size);
+ }
+}
+
+
+Constructor Decoder::readConstructor()
+{
+ Constructor result(readCode());
+ if (result.code == DESCRIPTOR) {
+ result.isDescribed = true;
+ result.descriptor = readDescriptor();
+ result.code = readCode();
+ } else {
+ result.isDescribed = false;
+ }
+ return result;
+}
+
+Descriptor Decoder::readDescriptor()
+{
+ uint8_t code = readCode();
+ switch(code) {
+ case SYMBOL8:
+ return Descriptor(readSequence8());
+ case SYMBOL32:
+ return Descriptor(readSequence32());
+ case ULONG:
+ return Descriptor(readULong());
+ case ULONG_SMALL:
+ return Descriptor((uint64_t) readUByte());
+ case ULONG_ZERO:
+ return Descriptor((uint64_t) 0);
+ default:
+ throw qpid::Exception(QPID_MSG("Expected descriptor of type ulong or symbol; found " << code));
+ }
+}
+
+void Decoder::advance(size_t n)
+{
+ if (n > available()) throw qpid::Exception(QPID_MSG("Out of Bounds"));
+ position += n;
+}
+
+const char* Decoder::data()
+{
+ return start + position;
+}
+
+size_t Decoder::available()
+{
+ return size - position;
+}
+
+uint8_t Decoder::readCode()
+{
+ return readUByte();
+}
+
+bool Decoder::readBoolean()
+{
+ return readUByte();
+}
+
+uint8_t Decoder::readUByte()
+{
+ return static_cast<uint8_t>(start[position++]);
+}
+
+uint16_t Decoder::readUShort()
+{
+ uint16_t hi = (unsigned char) start[position++];
+ hi = hi << 8;
+ hi |= (unsigned char) start[position++];
+ return hi;
+}
+
+uint32_t Decoder::readUInt()
+{
+ uint32_t a = (unsigned char) start[position++];
+ uint32_t b = (unsigned char) start[position++];
+ uint32_t c = (unsigned char) start[position++];
+ uint32_t d = (unsigned char) start[position++];
+ a = a << 24;
+ a |= b << 16;
+ a |= c << 8;
+ a |= d;
+ return a;
+}
+
+uint64_t Decoder::readULong()
+{
+ uint64_t hi =readUInt();
+ uint64_t lo = readUInt();
+ hi = hi << 32;
+ return hi | lo;
+}
+
+int8_t Decoder::readByte()
+{
+ return (int8_t) readUByte();
+}
+
+int16_t Decoder::readShort()
+{
+ return (int16_t) readUShort();
+}
+
+int32_t Decoder::readInt()
+{
+ return (int32_t) readUInt();
+}
+
+int64_t Decoder::readLong()
+{
+ return (int64_t) readULong();
+}
+
+float Decoder::readFloat()
+{
+ union {
+ uint32_t i;
+ float f;
+ } val;
+ val.i = readUInt();
+ return val.f;
+}
+
+double Decoder::readDouble()
+{
+ union {
+ uint64_t i;
+ double f;
+ } val;
+ val.i = readULong();
+ return val.f;
+}
+
+CharSequence Decoder::readSequence8()
+{
+ CharSequence s;
+ s.size = readUByte();
+ s.data = start + position;
+ advance(s.size);
+ return s;
+}
+
+CharSequence Decoder::readSequence32()
+{
+ CharSequence s;
+ s.size = readUInt();
+ s.data = start + position;
+ advance(s.size);
+ return s;
+}
+
+qpid::types::Uuid Decoder::readUuid()
+{
+ qpid::types::Uuid uuid(start + position);
+ advance(16);
+ return uuid;
+}
+
+CharSequence Decoder::readRawUuid()
+{
+ CharSequence s;
+ s.data = start + position;
+ s.size = 16;
+ advance(s.size);
+ return s;
+}
+
+size_t Decoder::getPosition() const { return position; }
+size_t Decoder::getSize() const { return size; }
+void Decoder::resetSize(size_t s) { size = s; }
+}} // namespace qpid::amqp
diff --git a/cpp/src/qpid/amqp/Decoder.h b/cpp/src/qpid/amqp/Decoder.h
new file mode 100644
index 0000000000..7ddfe0f17f
--- /dev/null
+++ b/cpp/src/qpid/amqp/Decoder.h
@@ -0,0 +1,99 @@
+#ifndef QPID_AMQP_DECODER_H
+#define QPID_AMQP_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/sys/IntegerTypes.h"
+#include "qpid/CommonImportExport.h"
+#include <map>
+#include <string>
+#include <stddef.h>
+
+namespace qpid {
+namespace types {
+class Uuid;
+class Variant;
+}
+namespace amqp {
+struct CharSequence;
+struct Constructor;
+struct Descriptor;
+class Reader;
+
+/**
+ * Class to assist in decoding an AMQP encoded data-stream.
+ */
+class Decoder
+{
+ public:
+ QPID_COMMON_EXTERN Decoder(const char*, size_t);
+
+ QPID_COMMON_EXTERN size_t available();
+ QPID_COMMON_EXTERN uint8_t readCode();
+
+ QPID_COMMON_EXTERN bool readBoolean();
+ QPID_COMMON_EXTERN uint8_t readUByte();
+ QPID_COMMON_EXTERN uint16_t readUShort();
+ QPID_COMMON_EXTERN uint32_t readUInt();
+ QPID_COMMON_EXTERN uint64_t readULong();
+ QPID_COMMON_EXTERN int8_t readByte();
+ QPID_COMMON_EXTERN int16_t readShort();
+ QPID_COMMON_EXTERN int32_t readInt();
+ QPID_COMMON_EXTERN int64_t readLong();
+ QPID_COMMON_EXTERN float readFloat();
+ QPID_COMMON_EXTERN double readDouble();
+ QPID_COMMON_EXTERN qpid::types::Uuid readUuid();
+ QPID_COMMON_EXTERN CharSequence readSequence8();
+ QPID_COMMON_EXTERN CharSequence readSequence32();
+ QPID_COMMON_EXTERN Descriptor readDescriptor();
+ QPID_COMMON_EXTERN void read(Reader& reader);
+
+ QPID_COMMON_EXTERN void readMap(std::map<std::string, qpid::types::Variant>&);
+ QPID_COMMON_EXTERN std::map<std::string, qpid::types::Variant> readMap();
+ QPID_COMMON_EXTERN void advance(size_t);
+ QPID_COMMON_EXTERN size_t getPosition() const;
+ QPID_COMMON_EXTERN void resetSize(size_t size);
+ QPID_COMMON_EXTERN size_t getSize() const;
+
+ private:
+ const char* const start;
+ size_t size;
+ size_t position;
+
+ void readOne(Reader& reader);
+ void readValue(Reader& reader, uint8_t code, const Descriptor* descriptor);
+ void readList(Reader& reader, uint32_t size, uint32_t count, const Descriptor* descriptor);
+ void readMap(Reader& reader, uint32_t size, uint32_t count, const Descriptor* descriptor);
+ void readArray(Reader& reader, uint32_t size, uint32_t count, const Descriptor* descriptor);
+ void readList8(Reader& reader, const Descriptor* descriptor);
+ void readList32(Reader& reader, const Descriptor* descriptor);
+ void readMap8(Reader& reader, const Descriptor* descriptor);
+ void readMap32(Reader& reader, const Descriptor* descriptor);
+ void readArray8(Reader& reader, const Descriptor* descriptor);
+ void readArray32(Reader& reader, const Descriptor* descriptor);
+ CharSequence readRawUuid();
+ Constructor readConstructor();
+ const char* data();
+
+};
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_DECODER_H*/
diff --git a/cpp/src/qpid/amqp/Descriptor.cpp b/cpp/src/qpid/amqp/Descriptor.cpp
new file mode 100644
index 0000000000..087e87c5e6
--- /dev/null
+++ b/cpp/src/qpid/amqp/Descriptor.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 "Descriptor.h"
+
+namespace qpid {
+namespace amqp {
+Descriptor::Descriptor(uint64_t code) : type(NUMERIC) { value.code = code; }
+Descriptor::Descriptor(const CharSequence& symbol) : type(SYMBOLIC) { value.symbol = symbol; }
+bool Descriptor::match(const std::string& symbol, uint64_t code) const
+{
+ switch (type) {
+ case SYMBOLIC:
+ return symbol.compare(0, symbol.size(), value.symbol.data, value.symbol.size) == 0;
+ case NUMERIC:
+ return code == value.code;
+ }
+ return false;
+}
+
+
+std::ostream& operator<<(std::ostream& os, const Descriptor& d)
+{
+ switch (d.type) {
+ case Descriptor::SYMBOLIC:
+ if (d.value.symbol.data && d.value.symbol.size) os << std::string(d.value.symbol.data, d.value.symbol.size);
+ else os << "null";
+ break;
+ case Descriptor::NUMERIC:
+ os << d.value.code;
+ break;
+ }
+ return os;
+}
+}} // namespace qpid::amqp
diff --git a/cpp/src/qpid/amqp/Descriptor.h b/cpp/src/qpid/amqp/Descriptor.h
new file mode 100644
index 0000000000..c36aa38ee3
--- /dev/null
+++ b/cpp/src/qpid/amqp/Descriptor.h
@@ -0,0 +1,54 @@
+#ifndef QPID_AMQP_DESCRIPTOR_H
+#define QPID_AMQP_DESCRIPTOR_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/CharSequence.h"
+#include "qpid/sys/IntegerTypes.h"
+#include <ostream>
+
+namespace qpid {
+namespace amqp {
+
+/**
+ * Representation of an AMQP 1.0 type descriptor.
+ */
+struct Descriptor
+{
+ union {
+ CharSequence symbol;
+ uint64_t code;
+ } value;
+ enum {
+ NUMERIC,
+ SYMBOLIC
+ } type;
+
+ Descriptor(uint64_t code);
+ Descriptor(const CharSequence& symbol);
+ bool match(const std::string&, uint64_t) const;
+};
+
+std::ostream& operator<<(std::ostream& os, const Descriptor& d);
+
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_DESCRIPTOR_H*/
diff --git a/cpp/src/qpid/amqp/Encoder.cpp b/cpp/src/qpid/amqp/Encoder.cpp
new file mode 100644
index 0000000000..6599f70811
--- /dev/null
+++ b/cpp/src/qpid/amqp/Encoder.cpp
@@ -0,0 +1,402 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/Encoder.h"
+#include "qpid/amqp/CharSequence.h"
+#include "qpid/amqp/Descriptor.h"
+#include "qpid/amqp/typecodes.h"
+#include "qpid/types/Uuid.h"
+#include "qpid/log/Statement.h"
+#include "qpid/Exception.h"
+#include <assert.h>
+#include <string.h>
+
+namespace qpid {
+namespace amqp {
+
+namespace {
+template <typename T> size_t encode(char* data, T i);
+template <> size_t encode<uint8_t>(char* data, uint8_t i)
+{
+ *data = i;
+ return 1;
+}
+template <> size_t encode<uint16_t>(char* data, uint16_t i)
+{
+ uint16_t b = i;
+ size_t position(0);
+ data[position++] = (uint8_t) (0xFF & (b >> 8));
+ data[position++] = (uint8_t) (0xFF & b);
+ return position;
+}
+template <> size_t encode<uint32_t>(char* data, uint32_t i)
+{
+ uint32_t b = i;
+ size_t position(0);
+ data[position++] = (uint8_t) (0xFF & (b >> 24));
+ data[position++] = (uint8_t) (0xFF & (b >> 16));
+ data[position++] = (uint8_t) (0xFF & (b >> 8));
+ data[position++] = (uint8_t) (0xFF & b);
+ return position;
+}
+template <> size_t encode<uint64_t>(char* data, uint64_t i)
+{
+ uint32_t hi = i >> 32;
+ uint32_t lo = i;
+ size_t r(0);
+ r += encode(data, hi);
+ r += encode(data + r, lo);
+ return r;
+}
+template<typename T> struct Backfill
+{
+ T size;
+ T count;
+ char* location;
+};
+
+template<typename T> void end(T count, void* token, char* current)
+{
+ Backfill<T> b;
+ b.location = (char*) token;
+ b.size = (T) (current - b.location) - sizeof(b.size);
+ b.count = count;
+ b.location += encode<T>(b.location, b.size);
+ encode<T>(b.location, b.count);
+}
+}
+char* Encoder::skip(size_t n)
+{
+ char* current = data + position;
+ check(n);
+ position += n;
+ return current;
+}
+
+void Encoder::write(bool b)
+{
+ check(sizeof(b));
+ position += encode<uint8_t>(data+position, b ? 1u : 0u);
+}
+void Encoder::write(uint8_t i)
+{
+ check(sizeof(i));
+ position += encode<uint8_t>(data+position, i);
+}
+void Encoder::write(uint16_t i)
+{
+ check(sizeof(i));
+ position += encode<uint16_t>(data+position, i);
+}
+void Encoder::write(uint32_t i)
+{
+ check(sizeof(i));
+ position += encode<uint32_t>(data+position, i);
+}
+void Encoder::write(uint64_t i)
+{
+ check(sizeof(i));
+ position += encode<uint64_t>(data+position, i);
+}
+void Encoder::write(int8_t i)
+{
+ check(sizeof(i));
+ position += encode(data+position, (uint8_t) i);
+}
+void Encoder::write(int16_t i)
+{
+ check(sizeof(i));
+ position += encode(data+position, (uint16_t) i);
+}
+void Encoder::write(int32_t i)
+{
+ check(sizeof(i));
+ position += encode(data+position, (uint32_t) i);
+}
+void Encoder::write(int64_t i)
+{
+ check(sizeof(i));
+ position += encode(data+position, (uint64_t) i);
+}
+void Encoder::write(float f)
+{
+ check(sizeof(f));
+ union {
+ uint32_t i;
+ float f;
+ } val;
+
+ val.f = f;
+ write(val.i);
+}
+void Encoder::write(double d)
+{
+ check(sizeof(d));
+ union {
+ uint64_t i;
+ double d;
+ } val;
+
+ val.d = d;
+ write(val.i);
+}
+void Encoder::write(const qpid::types::Uuid& uuid)
+{
+ writeBytes((const char*) uuid.data(), uuid.size());
+}
+
+void Encoder::writeBytes(const char* bytes, size_t count)
+{
+ check(count);
+ ::memcpy(data + position, bytes, count);
+ position += count;
+}
+
+void Encoder::writeCode(uint8_t code)
+{
+ write(code);
+}
+
+void Encoder::writeNull(const Descriptor* d)
+{
+ if (d) writeDescriptor(*d);
+ writeCode(typecodes::NULL_VALUE);
+}
+void Encoder::writeBoolean(bool b, const Descriptor* d)
+{
+ if (d) writeDescriptor(*d);
+ writeCode(b ? typecodes::BOOLEAN_TRUE : typecodes::BOOLEAN_FALSE);
+}
+void Encoder::writeUByte(uint8_t i, const Descriptor* d)
+{
+ write(i, typecodes::UBYTE, d);
+}
+
+void Encoder::writeUShort(uint16_t i, const Descriptor* d)
+{
+ write(i, typecodes::USHORT, d);
+}
+
+void Encoder::writeUInt(uint32_t i, const Descriptor* d)
+{
+ if (i == 0) {
+ if (d) writeDescriptor(*d);
+ writeCode(typecodes::UINT_ZERO);
+ } else {
+ if (i < 256) {
+ write((uint8_t) i, typecodes::UINT_SMALL, d);
+ } else {
+ write(i, typecodes::UINT, d);
+ }
+ }
+}
+
+void Encoder::writeULong(uint64_t i, const Descriptor* d)
+{
+ if (i == 0) {
+ if (d) writeDescriptor(*d);
+ writeCode(typecodes::ULONG_ZERO);
+ } else {
+ if (i < 256) {
+ write((uint8_t) i, typecodes::ULONG_SMALL, d);
+ } else {
+ write(i, typecodes::ULONG, d);
+ }
+ }
+}
+
+void Encoder::writeByte(int8_t i, const Descriptor* d)
+{
+ write((uint8_t) i, typecodes::LONG, d);
+}
+
+void Encoder::writeShort(int16_t i, const Descriptor* d)
+{
+ write((uint16_t) i, typecodes::SHORT, d);
+}
+
+void Encoder::writeInt(int32_t i, const Descriptor* d)
+{
+ write((uint32_t) i, typecodes::INT, d);
+}
+
+void Encoder::writeLong(int64_t i, const Descriptor* d)
+{
+ write((uint64_t) i, typecodes::LONG, d);
+}
+
+void Encoder::writeFloat(float f, const Descriptor* d)
+{
+ write(f, typecodes::FLOAT, d);
+}
+
+void Encoder::writeDouble(double f, const Descriptor* d)
+{
+ write(f, typecodes::DOUBLE, d);
+}
+
+void Encoder::writeUuid(const qpid::types::Uuid& uuid, const Descriptor* d)
+{
+ write(uuid, typecodes::UUID, d);
+}
+
+void Encoder::write(const CharSequence& v, std::pair<uint8_t, uint8_t> codes, const Descriptor* d)
+{
+ if (d) writeDescriptor(*d);
+ if (v.size < 256) {
+ writeCode(codes.first);
+ write((uint8_t) v.size);
+ } else {
+ writeCode(codes.second);
+ write((uint32_t) v.size);
+ }
+ writeBytes(v.data, v.size);
+}
+
+void Encoder::write(const std::string& v, std::pair<uint8_t, uint8_t> codes, const Descriptor* d)
+{
+ if (d) writeDescriptor(*d);
+ if (v.size() < 256) {
+ writeCode(codes.first);
+ write((uint8_t) v.size());
+ } else {
+ writeCode(codes.second);
+ write((uint32_t) v.size());
+ }
+ writeBytes(v.data(), v.size());
+}
+
+void Encoder::writeSymbol(const CharSequence& v, const Descriptor* d)
+{
+ write(v, typecodes::SYMBOL, d);
+}
+
+void Encoder::writeSymbol(const std::string& v, const Descriptor* d)
+{
+ write(v, typecodes::SYMBOL, d);
+}
+
+void Encoder::writeString(const CharSequence& v, const Descriptor* d)
+{
+ write(v, typecodes::STRING, d);
+}
+
+void Encoder::writeString(const std::string& v, const Descriptor* d)
+{
+ write(v, typecodes::STRING, d);
+}
+
+void Encoder::writeBinary(const CharSequence& v, const Descriptor* d)
+{
+ write(v, typecodes::BINARY, d);
+}
+
+void Encoder::writeBinary(const std::string& v, const Descriptor* d)
+{
+ write(v, typecodes::BINARY, d);
+}
+
+void* Encoder::startList8(const Descriptor* d)
+{
+ return start<uint8_t>(typecodes::LIST8, d);
+}
+
+void* Encoder::startList32(const Descriptor* d)
+{
+ return start<uint32_t>(typecodes::LIST32, d);
+}
+
+void Encoder::endList8(uint8_t count, void* token)
+{
+ end<uint8_t>(count, token, data+position);
+}
+
+void Encoder::endList32(uint32_t count, void* token)
+{
+ end<uint32_t>(count, token, data+position);
+}
+
+void* Encoder::startMap8(const Descriptor* d)
+{
+ return start<uint8_t>(typecodes::MAP8, d);
+}
+
+void* Encoder::startMap32(const Descriptor* d)
+{
+ return start<uint32_t>(typecodes::MAP32, d);
+}
+
+void Encoder::endMap8(uint8_t count, void* token)
+{
+ end<uint8_t>(count, token, data+position);
+}
+
+void Encoder::endMap32(uint32_t count, void* token)
+{
+ end<uint32_t>(count, token, data+position);
+}
+
+
+void* Encoder::startArray8(const Constructor& c, const Descriptor* d)
+{
+ return startArray<uint8_t>(typecodes::ARRAY8, d, c);
+}
+
+void* Encoder::startArray32(const Constructor& c, const Descriptor* d)
+{
+ return startArray<uint8_t>(typecodes::ARRAY32, d, c);
+}
+
+void Encoder::endArray8(size_t count, void* token)
+{
+ end<uint8_t>(count, token, data+position);
+}
+
+void Encoder::endArray32(size_t count, void* token)
+{
+ end<uint32_t>(count, token, data+position);
+}
+
+
+void Encoder::writeDescriptor(const Descriptor& d)
+{
+ writeCode(typecodes::DESCRIPTOR);
+ switch (d.type) {
+ case Descriptor::NUMERIC:
+ writeULong(d.value.code, 0);
+ break;
+ case Descriptor::SYMBOLIC:
+ writeSymbol(d.value.symbol, 0);
+ break;
+ }
+}
+void Encoder::check(size_t s)
+{
+ if (position + s > size) {
+ QPID_LOG(notice, "Buffer overflow for write of size " << s << " to buffer of size " << size << " at position " << position);
+ assert(false);
+ throw qpid::Exception("Buffer overflow in encoder!");
+ }
+}
+Encoder::Encoder(char* d, size_t s) : data(d), size(s), position(0) {}
+size_t Encoder::getPosition() { return position; }
+void Encoder::resetPosition(size_t p) { assert(p <= size); position = p; }
+
+}} // namespace qpid::amqp
diff --git a/cpp/src/qpid/amqp/Encoder.h b/cpp/src/qpid/amqp/Encoder.h
new file mode 100644
index 0000000000..e2938a002a
--- /dev/null
+++ b/cpp/src/qpid/amqp/Encoder.h
@@ -0,0 +1,149 @@
+#ifndef QPID_AMQP_ENCODER_H
+#define QPID_AMQP_ENCODER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/amqp/Constructor.h"
+#include <stddef.h>
+#include <string>
+
+namespace qpid {
+namespace types {
+class Uuid;
+}
+namespace amqp {
+struct CharSequence;
+struct Descriptor;
+
+/**
+ * Class to help create AMQP encoded data.
+ */
+class Encoder
+{
+ public:
+ void writeCode(uint8_t);
+
+ void write(bool);
+ void write(uint8_t);
+ void write(uint16_t);
+ void write(uint32_t);
+ void write(uint64_t);
+ void write(int8_t);
+ void write(int16_t);
+ void write(int32_t);
+ void write(int64_t);
+ void write(float);
+ void write(double);
+ void write(const qpid::types::Uuid&);
+
+ void writeNull(const Descriptor* d=0);
+ void writeBoolean(bool, const Descriptor* d=0);
+ void writeUByte(uint8_t, const Descriptor* d=0);
+ void writeUShort(uint16_t, const Descriptor* d=0);
+ void writeUInt(uint32_t, const Descriptor* d=0);
+ void writeULong(uint64_t, const Descriptor* d=0);
+ void writeByte(int8_t, const Descriptor* d=0);
+ void writeShort(int16_t, const Descriptor* d=0);
+ void writeInt(int32_t, const Descriptor* d=0);
+ void writeLong(int64_t, const Descriptor* d=0);
+ void writeFloat(float, const Descriptor* d=0);
+ void writeDouble(double, const Descriptor* d=0);
+ void writeUuid(const qpid::types::Uuid&, const Descriptor* d=0);
+
+ void writeSymbol(const CharSequence&, const Descriptor* d=0);
+ void writeSymbol(const std::string&, const Descriptor* d=0);
+ void writeString(const CharSequence&, const Descriptor* d=0);
+ void writeString(const std::string&, const Descriptor* d=0);
+ void writeBinary(const CharSequence&, const Descriptor* d=0);
+ void writeBinary(const std::string&, const Descriptor* d=0);
+
+ void* startList8(const Descriptor* d=0);
+ void* startList32(const Descriptor* d=0);
+ void endList8(uint8_t count, void*);
+ void endList32(uint32_t count, void*);
+
+ void* startMap8(const Descriptor* d=0);
+ void* startMap32(const Descriptor* d=0);
+ void endMap8(uint8_t count, void*);
+ void endMap32(uint32_t count, void*);
+
+ void* startArray8(const Constructor&, const Descriptor* d=0);
+ void* startArray32(const Constructor&, const Descriptor* d=0);
+ void endArray8(size_t count, void*);
+ void endArray32(size_t count, void*);
+
+ void writeDescriptor(const Descriptor&);
+ Encoder(char* data, size_t size);
+ size_t getPosition();
+ void resetPosition(size_t p);
+ char* skip(size_t);
+ void writeBytes(const char* bytes, size_t count);
+ virtual ~Encoder() {}
+ private:
+ char* data;
+ size_t size;
+ size_t position;
+
+ void write(const CharSequence& v, std::pair<uint8_t, uint8_t> codes, const Descriptor* d);
+ void write(const std::string& v, std::pair<uint8_t, uint8_t> codes, const Descriptor* d);
+ void check(size_t);
+
+ template<typename T> void write(T value, uint8_t code, const Descriptor* d)
+ {
+ if (d) writeDescriptor(*d);
+ writeCode(code);
+ write(value);
+ }
+
+ template<typename T> void write(T value, std::pair<uint8_t, uint8_t> codes, const Descriptor* d)
+ {
+ if (value < 256) {
+ write((uint8_t) value, codes.first, d);
+ } else {
+ write(value, codes.second, d);
+ }
+ }
+
+ template<typename T> void* start(uint8_t code, const Descriptor* d)
+ {
+ if (d) writeDescriptor(*d);
+ writeCode(code);
+ //skip size and count, will backfill on end
+ return skip(sizeof(T)/*size*/ + sizeof(T)/*count*/);
+ }
+
+ template<typename T> void* startArray(uint8_t code, const Descriptor* d, const Constructor& c)
+ {
+ void* token = start<T>(code, d);
+ if (c.isDescribed) {
+ writeDescriptor(c.descriptor);
+ }
+ check(1);
+ writeCode(c.code);
+ return token;
+ }
+
+};
+
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_ENCODER_H*/
diff --git a/cpp/src/qpid/amqp/ListReader.h b/cpp/src/qpid/amqp/ListReader.h
new file mode 100644
index 0000000000..dce874bf2f
--- /dev/null
+++ b/cpp/src/qpid/amqp/ListReader.h
@@ -0,0 +1,103 @@
+#ifndef QPID_AMQP_LISTREADER_H
+#define QPID_AMQP_LISTREADER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "Reader.h"
+
+namespace qpid {
+namespace amqp {
+
+/**
+ * Utility to assist in reading AMQP encoded lists
+ */
+class ListReader : public Reader
+{
+ public:
+ ListReader() : index(0), level(0) {}
+ virtual ~ListReader() {}
+ virtual void onNull(const Descriptor* descriptor) { getReader().onNull(descriptor); }
+ virtual void onBoolean(bool v, const Descriptor* descriptor) { getReader().onBoolean(v, descriptor); }
+ virtual void onUByte(uint8_t v, const Descriptor* descriptor) { getReader().onUByte(v, descriptor); }
+ virtual void onUShort(uint16_t v, const Descriptor* descriptor) { getReader().onUShort(v, descriptor); }
+ virtual void onUInt(uint32_t v, const Descriptor* descriptor) { getReader().onUInt(v, descriptor); }
+ virtual void onULong(uint64_t v, const Descriptor* descriptor) { getReader().onULong(v, descriptor); }
+ virtual void onByte(int8_t v, const Descriptor* descriptor) { getReader().onByte(v, descriptor); }
+ virtual void onShort(int16_t v, const Descriptor* descriptor) { getReader().onShort(v, descriptor); }
+ virtual void onInt(int32_t v, const Descriptor* descriptor) { getReader().onInt(v, descriptor); }
+ virtual void onLong(int64_t v, const Descriptor* descriptor) { getReader().onLong(v, descriptor); }
+ virtual void onFloat(float v, const Descriptor* descriptor) { getReader().onFloat(v, descriptor); }
+ virtual void onDouble(double v, const Descriptor* descriptor) { getReader().onDouble(v, descriptor); }
+ virtual void onUuid(const CharSequence& v, const Descriptor* descriptor) { getReader().onUuid(v, descriptor); }
+ virtual void onTimestamp(int64_t v, const Descriptor* descriptor) { getReader().onTimestamp(v, descriptor); }
+
+ virtual void onBinary(const CharSequence& v, const Descriptor* descriptor) { getReader().onBinary(v, descriptor); }
+ virtual void onString(const CharSequence& v, const Descriptor* descriptor) { getReader().onString(v, descriptor); }
+ virtual void onSymbol(const CharSequence& v, const Descriptor* descriptor) { getReader().onSymbol(v, descriptor); }
+
+ virtual bool onStartList(uint32_t count, const CharSequence& v, const Descriptor* descriptor)
+ {
+ ++level;
+ getReader().onStartList(count, v, descriptor);
+ return false;
+ }
+ virtual void onEndList(uint32_t count, const Descriptor* descriptor)
+ {
+ --level;
+ getReader().onEndList(count, descriptor);
+ }
+ virtual bool onStartMap(uint32_t count, const CharSequence& v, const Descriptor* descriptor)
+ {
+ ++level;
+ getReader().onStartMap(count, v, descriptor);
+ return false;
+ }
+ virtual void onEndMap(uint32_t count, const Descriptor* descriptor)
+ {
+ --level;
+ getReader().onEndList(count, descriptor);
+ }
+ virtual bool onStartArray(uint32_t count, const CharSequence& v, const Constructor& c, const Descriptor* descriptor)
+ {
+ ++level;
+ getReader().onStartArray(count, v, c, descriptor);
+ return false;
+ }
+ virtual void onEndArray(uint32_t count, const Descriptor* descriptor)
+ {
+ --level;
+ getReader().onEndList(count, descriptor);
+ }
+ private:
+ size_t index;
+ size_t level;
+ Reader& getReader()
+ {
+ Reader& r = getReader(index);
+ if (level == 0) ++index;
+ return r;
+ }
+ protected:
+ virtual Reader& getReader(size_t i) = 0;
+};
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_LISTREADER_H*/
diff --git a/cpp/src/qpid/amqp/LoggingReader.h b/cpp/src/qpid/amqp/LoggingReader.h
new file mode 100644
index 0000000000..ed5cab1cbd
--- /dev/null
+++ b/cpp/src/qpid/amqp/LoggingReader.h
@@ -0,0 +1,64 @@
+#ifndef QPID_AMQP_LOGGINGREADER_H
+#define QPID_AMQP_LOGGINGREADER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "Reader.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace amqp {
+
+class LoggingReader : public Reader
+{
+ public:
+ virtual ~LoggingReader() {}
+ virtual void onNull(const Descriptor*) { if (!ignoreNull()) QPID_LOG(warning, prefix() << "null" << suffix()); }
+ virtual void onBoolean(bool, const Descriptor*) { QPID_LOG(warning, prefix() << "boolean" << suffix()); }
+ virtual void onUByte(uint8_t, const Descriptor*) { QPID_LOG(warning, prefix() << "ubyte" << suffix()); }
+ virtual void onUShort(uint16_t, const Descriptor*) { QPID_LOG(warning, prefix() << "ushort" << suffix()); }
+ virtual void onUInt(uint32_t, const Descriptor*) { QPID_LOG(warning, prefix() << "uint" << suffix()); }
+ virtual void onULong(uint64_t, const Descriptor*) { QPID_LOG(warning, prefix() << "ulong" << suffix()); }
+ virtual void onByte(int8_t, const Descriptor*) { QPID_LOG(warning, prefix() << "byte" << suffix()); }
+ virtual void onShort(int16_t, const Descriptor*) { QPID_LOG(warning, prefix() << "short" << suffix()); }
+ virtual void onInt(int32_t, const Descriptor*) { QPID_LOG(warning, prefix() << "int" << suffix()); }
+ virtual void onLong(int64_t, const Descriptor*) { QPID_LOG(warning, prefix() << "long" << suffix()); }
+ virtual void onFloat(float, const Descriptor*) { QPID_LOG(warning, prefix() << "float" << suffix()); }
+ virtual void onDouble(double, const Descriptor*) { QPID_LOG(warning, prefix() << "double" << suffix()); }
+ virtual void onUuid(const CharSequence&, const Descriptor*) { QPID_LOG(warning, prefix() << "uuid" << suffix()); }
+ virtual void onTimestamp(int64_t, const Descriptor*) { QPID_LOG(warning, prefix() << "timestamp" << suffix()); }
+
+ virtual void onBinary(const CharSequence&, const Descriptor*) { QPID_LOG(warning, prefix() << "binary" << suffix()); }
+ virtual void onString(const CharSequence&, const Descriptor*) { QPID_LOG(warning, prefix() << "string" << suffix()); }
+ virtual void onSymbol(const CharSequence&, const Descriptor*) { QPID_LOG(warning, prefix() << "symbol" << suffix()); }
+
+ virtual bool onStartList(uint32_t, const CharSequence&, const Descriptor*) { QPID_LOG(warning, prefix() << "list" << suffix()); return recursive; }
+ virtual bool onStartMap(uint32_t, const CharSequence&, const Descriptor*) { QPID_LOG(warning, prefix() << "map" << suffix()); return recursive; }
+ virtual bool onStartArray(uint32_t, const CharSequence&, const Constructor&, const Descriptor*) { QPID_LOG(warning, prefix() << "array" << suffix()); return recursive; }
+ protected:
+ virtual bool recursive() { return true; }
+ virtual bool ignoreNull() { return true; }
+ virtual std::string prefix() = 0;
+ virtual std::string suffix() = 0;
+};
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_LOGGINGREADER_H*/
diff --git a/cpp/src/qpid/amqp/MapReader.cpp b/cpp/src/qpid/amqp/MapReader.cpp
new file mode 100644
index 0000000000..2bace74d34
--- /dev/null
+++ b/cpp/src/qpid/amqp/MapReader.cpp
@@ -0,0 +1,289 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/amqp/MapReader.h"
+#include "qpid/Exception.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace amqp {
+
+void MapReader::onNull(const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onNullValue(key, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+void MapReader::onBoolean(bool v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onBooleanValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+void MapReader::onUByte(uint8_t v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onUByteValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+void MapReader::onUShort(uint16_t v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onUShortValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+void MapReader::onUInt(uint32_t v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onUIntValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+void MapReader::onULong(uint64_t v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onULongValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+void MapReader::onByte(int8_t v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onByteValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+void MapReader::onShort(int16_t v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onShortValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+void MapReader::onInt(int32_t v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onIntValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+void MapReader::onLong(int64_t v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onLongValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+void MapReader::onFloat(float v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onFloatValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+void MapReader::onDouble(double v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onDoubleValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+void MapReader::onUuid(const CharSequence& v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onUuidValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+void MapReader::onTimestamp(int64_t v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onTimestampValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+void MapReader::onBinary(const CharSequence& v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onBinaryValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+void MapReader::onString(const CharSequence& v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onStringValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key, got string " << v.str()));
+ }
+}
+
+void MapReader::onSymbol(const CharSequence& v, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onSymbolValue(key, v, d);
+ key.data = 0; key.size = 0;
+ } else {
+ key = v;
+ }
+}
+
+bool MapReader::onStartList(uint32_t count, const CharSequence&, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ bool step = onStartListValue(key, count, d);
+ key.data = 0; key.size = 0;
+ return step;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+ return true;
+}
+
+bool MapReader::onStartMap(uint32_t count, const CharSequence&, const Descriptor* d)
+{
+ if (level++) {
+ if (key) {
+ bool step = onStartMapValue(key, count, d);
+ key.data = 0; key.size = 0;
+ return step;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+ }
+ return true;
+}
+
+bool MapReader::onStartArray(uint32_t count, const CharSequence&, const Constructor& c, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ bool step = onStartArrayValue(key, count, c, d);
+ key.data = 0; key.size = 0;
+ return step;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+ return true;
+}
+
+void MapReader::onEndList(uint32_t count, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onEndListValue(key, count, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+void MapReader::onEndMap(uint32_t count, const Descriptor* d)
+{
+ if (--level) {
+ onEndMapValue(key, count, d);
+ key.data = 0; key.size = 0;
+ }
+}
+
+void MapReader::onEndArray(uint32_t count, const Descriptor* d)
+{
+ if (!level) throw qpid::Exception(QPID_MSG("Expecting map as top level datum"));
+ if (key) {
+ onEndArrayValue(key, count, d);
+ key.data = 0; key.size = 0;
+ } else {
+ throw qpid::Exception(QPID_MSG("Expecting symbol as key"));
+ }
+}
+
+MapReader::MapReader() : level(0)
+{
+ key.data = 0; key.size = 0;
+}
+
+}} // namespace qpid::amqp
diff --git a/cpp/src/qpid/amqp/MapReader.h b/cpp/src/qpid/amqp/MapReader.h
new file mode 100644
index 0000000000..fe8f65b30c
--- /dev/null
+++ b/cpp/src/qpid/amqp/MapReader.h
@@ -0,0 +1,104 @@
+#ifndef QPID_AMQP_MAPREADER_H
+#define QPID_AMQP_MAPREADER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "Reader.h"
+#include "CharSequence.h"
+#include <string>
+
+namespace qpid {
+namespace amqp {
+
+/**
+ * Reading AMQP 1.0 encoded data which is constrained to be a symbol
+ * keyeed map. The keys are assumed never to be described, the values
+ * may be.
+ */
+class MapReader : public Reader
+{
+ public:
+ virtual void onNullValue(const CharSequence& /*key*/, const Descriptor*) {}
+ virtual void onBooleanValue(const CharSequence& /*key*/, bool, const Descriptor*) {}
+ virtual void onUByteValue(const CharSequence& /*key*/, uint8_t, const Descriptor*) {}
+ virtual void onUShortValue(const CharSequence& /*key*/, uint16_t, const Descriptor*) {}
+ virtual void onUIntValue(const CharSequence& /*key*/, uint32_t, const Descriptor*) {}
+ virtual void onULongValue(const CharSequence& /*key*/, uint64_t, const Descriptor*) {}
+ virtual void onByteValue(const CharSequence& /*key*/, int8_t, const Descriptor*) {}
+ virtual void onShortValue(const CharSequence& /*key*/, int16_t, const Descriptor*) {}
+ virtual void onIntValue(const CharSequence& /*key*/, int32_t, const Descriptor*) {}
+ virtual void onLongValue(const CharSequence& /*key*/, int64_t, const Descriptor*) {}
+ virtual void onFloatValue(const CharSequence& /*key*/, float, const Descriptor*) {}
+ virtual void onDoubleValue(const CharSequence& /*key*/, double, const Descriptor*) {}
+ virtual void onUuidValue(const CharSequence& /*key*/, const CharSequence&, const Descriptor*) {}
+ virtual void onTimestampValue(const CharSequence& /*key*/, int64_t, const Descriptor*) {}
+
+ virtual void onBinaryValue(const CharSequence& /*key*/, const CharSequence&, const Descriptor*) {}
+ virtual void onStringValue(const CharSequence& /*key*/, const CharSequence&, const Descriptor*) {}
+ virtual void onSymbolValue(const CharSequence& /*key*/, const CharSequence&, const Descriptor*) {}
+
+ /**
+ * @return true to step into elements of the compound value, false
+ * to skip over it
+ */
+ virtual bool onStartListValue(const CharSequence& /*key*/, uint32_t /*count*/, const Descriptor*) { return true; }
+ virtual bool onStartMapValue(const CharSequence& /*key*/, uint32_t /*count*/, const Descriptor*) { return true; }
+ virtual bool onStartArrayValue(const CharSequence& /*key*/, uint32_t /*count*/, const Constructor&, const Descriptor*) { return true; }
+ virtual void onEndListValue(const CharSequence& /*key*/, uint32_t /*count*/, const Descriptor*) {}
+ virtual void onEndMapValue(const CharSequence& /*key*/, uint32_t /*count*/, const Descriptor*) {}
+ virtual void onEndArrayValue(const CharSequence& /*key*/, uint32_t /*count*/, const Descriptor*) {}
+
+
+ //this class implements the Reader interface, thus acting as a transformer into a more map oriented scheme
+ void onNull(const Descriptor*);
+ void onBoolean(bool, const Descriptor*);
+ void onUByte(uint8_t, const Descriptor*);
+ void onUShort(uint16_t, const Descriptor*);
+ void onUInt(uint32_t, const Descriptor*);
+ void onULong(uint64_t, const Descriptor*);
+ void onByte(int8_t, const Descriptor*);
+ void onShort(int16_t, const Descriptor*);
+ void onInt(int32_t, const Descriptor*);
+ void onLong(int64_t, const Descriptor*);
+ void onFloat(float, const Descriptor*);
+ void onDouble(double, const Descriptor*);
+ void onUuid(const CharSequence&, const Descriptor*);
+ void onTimestamp(int64_t, const Descriptor*);
+
+ void onBinary(const CharSequence&, const Descriptor*);
+ void onString(const CharSequence&, const Descriptor*);
+ void onSymbol(const CharSequence&, const Descriptor*);
+
+ bool onStartList(uint32_t /*count*/, const CharSequence&, const Descriptor*);
+ bool onStartMap(uint32_t /*count*/, const CharSequence&, const Descriptor*);
+ bool onStartArray(uint32_t /*count*/, const CharSequence&, const Constructor&, const Descriptor*);
+ void onEndList(uint32_t /*count*/, const Descriptor*);
+ void onEndMap(uint32_t /*count*/, const Descriptor*);
+ void onEndArray(uint32_t /*count*/, const Descriptor*);
+
+ MapReader();
+ private:
+ CharSequence key;
+ size_t level;
+};
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_MAPREADER_H*/
diff --git a/cpp/src/qpid/amqp/MessageEncoder.cpp b/cpp/src/qpid/amqp/MessageEncoder.cpp
new file mode 100644
index 0000000000..852ad29635
--- /dev/null
+++ b/cpp/src/qpid/amqp/MessageEncoder.cpp
@@ -0,0 +1,313 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/amqp/MessageEncoder.h"
+#include "qpid/amqp/descriptors.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace amqp {
+
+namespace {
+size_t optimisable(const MessageEncoder::Header& msg)
+{
+ if (msg.getDeliveryCount()) return 5;
+ else if (msg.isFirstAcquirer()) return 4;
+ else if (msg.hasTtl()) return 3;
+ else if (msg.getPriority() != 4) return 2;
+ else if (msg.isDurable()) return 1;
+ else return 0;
+}
+
+size_t optimisable(const MessageEncoder::Properties& msg)
+{
+ if (msg.hasReplyToGroupId()) return 13;
+ else if (msg.hasGroupSequence()) return 12;
+ else if (msg.hasGroupId()) return 11;
+ else if (msg.hasCreationTime()) return 10;
+ else if (msg.hasAbsoluteExpiryTime()) return 9;
+ else if (msg.hasContentEncoding()) return 8;
+ else if (msg.hasContentType()) return 7;
+ else if (msg.hasCorrelationId()) return 6;
+ else if (msg.hasReplyTo()) return 5;
+ else if (msg.hasSubject()) return 4;
+ else if (msg.hasTo()) return 3;
+ else if (msg.hasUserId()) return 2;
+ else if (msg.hasMessageId()) return 1;
+ else return 0;
+}
+size_t encodedSize(const std::string& s)
+{
+ size_t total = s.size();
+ if (total > 255) total += 4;
+ else total += 1;
+ return total;
+}
+const std::string BINARY("binary");
+}
+
+void MessageEncoder::writeHeader(const Header& msg)
+{
+ size_t fields(optimise ? optimisable(msg) : 5);
+ if (fields) {
+ void* token = startList8(&qpid::amqp::message::HEADER);
+ writeBoolean(msg.isDurable());
+ if (fields > 1) writeUByte(msg.getPriority());
+
+ if (msg.getTtl()) writeUInt(msg.getTtl());
+ else if (fields > 2) writeNull();
+
+ if (msg.isFirstAcquirer()) writeBoolean(true);
+ else if (fields > 3) writeNull();
+
+ if (msg.getDeliveryCount()) writeUInt(msg.getDeliveryCount());
+ else if (fields > 4) writeNull();
+ endList8(fields, token);
+ }
+}
+
+
+void MessageEncoder::writeProperties(const Properties& msg)
+{
+ size_t fields(optimise ? optimisable(msg) : 13);
+ if (fields) {
+ void* token = startList32(&qpid::amqp::message::PROPERTIES);
+ if (msg.hasMessageId()) writeString(msg.getMessageId());
+ else writeNull();
+
+ if (msg.hasUserId()) writeBinary(msg.getUserId());
+ else if (fields > 1) writeNull();
+
+ if (msg.hasTo()) writeString(msg.getTo());
+ else if (fields > 2) writeNull();
+
+ if (msg.hasSubject()) writeString(msg.getSubject());
+ else if (fields > 3) writeNull();
+
+ if (msg.hasReplyTo()) writeString(msg.getReplyTo());
+ else if (fields > 4) writeNull();
+
+ if (msg.hasCorrelationId()) writeString(msg.getCorrelationId());
+ else if (fields > 5) writeNull();
+
+ if (msg.hasContentType()) writeSymbol(msg.getContentType());
+ else if (fields > 6) writeNull();
+
+ if (msg.hasContentEncoding()) writeSymbol(msg.getContentEncoding());
+ else if (fields > 7) writeNull();
+
+ if (msg.hasAbsoluteExpiryTime()) writeLong(msg.getAbsoluteExpiryTime());
+ else if (fields > 8) writeNull();
+
+ if (msg.hasCreationTime()) writeLong(msg.getCreationTime());
+ else if (fields > 9) writeNull();
+
+ if (msg.hasGroupId()) writeString(msg.getGroupId());
+ else if (fields > 10) writeNull();
+
+ if (msg.hasGroupSequence()) writeUInt(msg.getGroupSequence());
+ else if (fields > 11) writeNull();
+
+ if (msg.hasReplyToGroupId()) writeString(msg.getReplyToGroupId());
+ else if (fields > 12) writeNull();
+
+ endList32(fields, token);
+ }
+}
+
+void MessageEncoder::writeApplicationProperties(const qpid::types::Variant::Map& properties)
+{
+ writeApplicationProperties(properties, !optimise || properties.size()*2 > 255 || getEncodedSizeForElements(properties) > 255);
+}
+
+void MessageEncoder::writeApplicationProperties(const qpid::types::Variant::Map& properties, bool large)
+{
+ writeMap(properties, &qpid::amqp::message::APPLICATION_PROPERTIES, large);
+}
+
+void MessageEncoder::writeMap(const qpid::types::Variant::Map& properties, const Descriptor* d, bool large)
+{
+ void* token = large ? startMap32(d) : startMap8(d);
+ for (qpid::types::Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) {
+ writeString(i->first);
+ switch (i->second.getType()) {
+ case qpid::types::VAR_MAP:
+ case qpid::types::VAR_LIST:
+ //not allowed (TODO: revise, only strictly true for application-properties) whereas this is now a more general method)
+ QPID_LOG(warning, "Ignoring nested map/list; not allowed in application-properties for AMQP 1.0");
+ case qpid::types::VAR_VOID:
+ writeNull();
+ break;
+ case qpid::types::VAR_BOOL:
+ writeBoolean(i->second);
+ break;
+ case qpid::types::VAR_UINT8:
+ writeUByte(i->second);
+ break;
+ case qpid::types::VAR_UINT16:
+ writeUShort(i->second);
+ break;
+ case qpid::types::VAR_UINT32:
+ writeUInt(i->second);
+ break;
+ case qpid::types::VAR_UINT64:
+ writeULong(i->second);
+ break;
+ case qpid::types::VAR_INT8:
+ writeByte(i->second);
+ break;
+ case qpid::types::VAR_INT16:
+ writeShort(i->second);
+ break;
+ case qpid::types::VAR_INT32:
+ writeInt(i->second);
+ break;
+ case qpid::types::VAR_INT64:
+ writeULong(i->second);
+ break;
+ case qpid::types::VAR_FLOAT:
+ writeFloat(i->second);
+ break;
+ case qpid::types::VAR_DOUBLE:
+ writeDouble(i->second);
+ break;
+ case qpid::types::VAR_STRING:
+ if (i->second.getEncoding() == BINARY) {
+ writeBinary(i->second);
+ } else {
+ writeString(i->second);
+ }
+ break;
+ case qpid::types::VAR_UUID:
+ writeUuid(i->second);
+ break;
+ }
+ }
+ if (large) endMap32(properties.size()*2, token);
+ else endMap8(properties.size()*2, token);
+}
+
+size_t MessageEncoder::getEncodedSize(const Header& h, const Properties& p, const qpid::types::Variant::Map& ap, const std::string& d)
+{
+ //NOTE: this does not take optional optimisation into account,
+ //i.e. it is a 'worst case' estimate for required buffer space
+ size_t total(0);
+
+ //header:
+ total += 3/*descriptor*/ + 1/*code*/ + 1/*size*/ + 1/*count*/ + 5/*codes for each field*/;
+ if (h.getPriority() != 4) total += 1;
+ if (h.getDeliveryCount()) total += 4;
+ if (h.hasTtl()) total += 4;
+ return total + getEncodedSize(p, ap, d);
+}
+
+size_t MessageEncoder::getEncodedSize(const Properties& p, const qpid::types::Variant::Map& ap, const std::string& d)
+{
+ //NOTE: this does not take optional optimisation into account,
+ //i.e. it is a 'worst case' estimate for required buffer space
+ size_t total(0);
+
+ //properties:
+ total += 3/*descriptor*/ + 1/*code*/ + 4/*size*/ + 4/*count*/ + 13/*codes for each field*/;
+ if (p.hasMessageId()) total += encodedSize(p.getMessageId());
+ if (p.hasUserId()) total += encodedSize(p.getUserId());
+ if (p.hasTo()) total += encodedSize(p.getTo());
+ if (p.hasSubject()) total += encodedSize(p.getSubject());
+ if (p.hasReplyTo()) total += encodedSize(p.getReplyTo());
+ if (p.hasCorrelationId()) total += encodedSize(p.getCorrelationId());
+ if (p.hasContentType()) total += encodedSize(p.getContentType());
+ if (p.hasContentEncoding()) total += encodedSize(p.getContentEncoding());
+ if (p.hasAbsoluteExpiryTime()) total += 8;
+ if (p.hasCreationTime()) total += 8;
+ if (p.hasGroupId()) total += encodedSize(p.getGroupId());
+ if (p.hasGroupSequence()) total += 4;
+ if (p.hasReplyToGroupId()) total += encodedSize(p.getReplyToGroupId());
+
+
+ //application-properties:
+ total += 3/*descriptor*/ + getEncodedSize(ap, true);
+ //body:
+ if (d.size()) total += 3/*descriptor*/ + 1/*code*/ + encodedSize(d);
+
+ return total;
+}
+
+size_t MessageEncoder::getEncodedSizeForElements(const qpid::types::Variant::Map& map)
+{
+ size_t total = 0;
+ for (qpid::types::Variant::Map::const_iterator i = map.begin(); i != map.end(); ++i) {
+ total += 1/*code*/ + encodedSize(i->first);
+
+ switch (i->second.getType()) {
+ case qpid::types::VAR_MAP:
+ case qpid::types::VAR_LIST:
+ case qpid::types::VAR_VOID:
+ case qpid::types::VAR_BOOL:
+ total += 1;
+ break;
+
+ case qpid::types::VAR_UINT8:
+ case qpid::types::VAR_INT8:
+ total += 2;
+ break;
+
+ case qpid::types::VAR_UINT16:
+ case qpid::types::VAR_INT16:
+ total += 3;
+ break;
+
+ case qpid::types::VAR_UINT32:
+ case qpid::types::VAR_INT32:
+ case qpid::types::VAR_FLOAT:
+ total += 5;
+ break;
+
+ case qpid::types::VAR_UINT64:
+ case qpid::types::VAR_INT64:
+ case qpid::types::VAR_DOUBLE:
+ total += 9;
+ break;
+
+ case qpid::types::VAR_UUID:
+ total += 17;
+ break;
+
+ case qpid::types::VAR_STRING:
+ total += 1/*code*/ + encodedSize(i->second);
+ break;
+ }
+ }
+ return total;
+}
+
+
+size_t MessageEncoder::getEncodedSize(const qpid::types::Variant::Map& map, bool alwaysUseLargeMap)
+{
+ size_t total = getEncodedSizeForElements(map);
+
+ //its not just the count that determines whether we can use a small map, but the aggregate size:
+ if (alwaysUseLargeMap || map.size()*2 > 255 || total > 255) total += 4/*size*/ + 4/*count*/;
+ else total += 1/*size*/ + 1/*count*/;
+
+ total += 1 /*code for map itself*/;
+
+ return total;
+}
+}} // namespace qpid::amqp
diff --git a/cpp/src/qpid/amqp/MessageEncoder.h b/cpp/src/qpid/amqp/MessageEncoder.h
new file mode 100644
index 0000000000..3db0763e8a
--- /dev/null
+++ b/cpp/src/qpid/amqp/MessageEncoder.h
@@ -0,0 +1,100 @@
+#ifndef QPID_AMQP_MESSAGEENCODER_H
+#define QPID_AMQP_MESSAGEENCODER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/Encoder.h"
+#include "qpid/types/Variant.h"
+
+namespace qpid {
+namespace amqp {
+
+/**
+ *
+ */
+class MessageEncoder : public Encoder
+{
+ public:
+ class Header
+ {
+ public:
+ virtual ~Header() {}
+ virtual bool isDurable() const = 0;
+ virtual uint8_t getPriority() const = 0;
+ virtual bool hasTtl() const = 0;
+ virtual uint32_t getTtl() const = 0;
+ virtual bool isFirstAcquirer() const = 0;
+ virtual uint32_t getDeliveryCount() const = 0;
+ };
+
+ class Properties
+ {
+ public:
+ virtual ~Properties() {}
+ virtual bool hasMessageId() const = 0;
+ virtual std::string getMessageId() const = 0;
+ virtual bool hasUserId() const = 0;
+ virtual std::string getUserId() const = 0;
+ virtual bool hasTo() const = 0;
+ virtual std::string getTo() const = 0;
+ virtual bool hasSubject() const = 0;
+ virtual std::string getSubject() const = 0;
+ virtual bool hasReplyTo() const = 0;
+ virtual std::string getReplyTo() const = 0;
+ virtual bool hasCorrelationId() const = 0;
+ virtual std::string getCorrelationId() const = 0;
+ virtual bool hasContentType() const = 0;
+ virtual std::string getContentType() const = 0;
+ virtual bool hasContentEncoding() const = 0;
+ virtual std::string getContentEncoding() const = 0;
+ virtual bool hasAbsoluteExpiryTime() const = 0;
+ virtual int64_t getAbsoluteExpiryTime() const = 0;
+ virtual bool hasCreationTime() const = 0;
+ virtual int64_t getCreationTime() const = 0;
+ virtual bool hasGroupId() const = 0;
+ virtual std::string getGroupId() const = 0;
+ virtual bool hasGroupSequence() const = 0;
+ virtual uint32_t getGroupSequence() const = 0;
+ virtual bool hasReplyToGroupId() const = 0;
+ virtual std::string getReplyToGroupId() const = 0;
+ };
+
+ MessageEncoder(char* d, size_t s, bool o=false) : Encoder(d, s), optimise(o) {}
+ void writeHeader(const Header&);
+ void writeProperties(const Properties&);
+ void writeApplicationProperties(const qpid::types::Variant::Map& properties);
+ void writeApplicationProperties(const qpid::types::Variant::Map& properties, bool useLargeMap);
+
+ void writeMap(const qpid::types::Variant::Map& map, const Descriptor*, bool useLargeMap);
+
+ static size_t getEncodedSize(const Header&, const Properties&, const qpid::types::Variant::Map&, const std::string&);
+ static size_t getEncodedSize(const Properties&, const qpid::types::Variant::Map&, const std::string&);
+ static size_t getEncodedSize(const qpid::types::Variant::Map&, bool useLargeMap);
+ static size_t getEncodedSize(const qpid::types::Variant::Map&);
+
+ private:
+ bool optimise;
+
+ static size_t getEncodedSizeForElements(const qpid::types::Variant::Map&);
+};
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_MESSAGEENCODER_H*/
diff --git a/cpp/src/qpid/amqp/MessageId.cpp b/cpp/src/qpid/amqp/MessageId.cpp
new file mode 100644
index 0000000000..e6f6f4a231
--- /dev/null
+++ b/cpp/src/qpid/amqp/MessageId.cpp
@@ -0,0 +1,69 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/amqp/MessageId.h"
+#include <assert.h>
+#include <boost/lexical_cast.hpp>
+
+namespace qpid {
+namespace amqp {
+
+MessageId::MessageId() : type(BYTES)
+{
+ value.bytes.data = 0;
+ value.bytes.size = 0;
+}
+void MessageId::assign(std::string& s) const
+{
+ switch (type) {
+ case BYTES:
+ if (value.bytes) s.assign(value.bytes.data, value.bytes.size);
+ break;
+ case UUID:
+ s = qpid::types::Uuid(value.bytes).str();
+ break;
+ case ULONG:
+ s = boost::lexical_cast<std::string>(value.ulong);
+ break;
+ }
+}
+
+void MessageId::set(qpid::amqp::CharSequence bytes, qpid::types::VariantType t)
+{
+ switch (t) {
+ case qpid::types::VAR_STRING:
+ type = BYTES;
+ break;
+ case qpid::types::VAR_UUID:
+ type = UUID;
+ assert(bytes.size == 16);
+ break;
+ default:
+ assert(false);
+ }
+ value.bytes = bytes;
+}
+void MessageId::set(uint64_t ulong)
+{
+ type = ULONG;
+ value.ulong = ulong;
+}
+
+}} // namespace qpid::amqp
diff --git a/cpp/src/qpid/amqp/MessageId.h b/cpp/src/qpid/amqp/MessageId.h
new file mode 100644
index 0000000000..ee440f3011
--- /dev/null
+++ b/cpp/src/qpid/amqp/MessageId.h
@@ -0,0 +1,54 @@
+#ifndef QPID_AMQP_MESSAGEID_H
+#define QPID_AMQP_MESSAGEID_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/CharSequence.h"
+#include "qpid/types/Variant.h"
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+namespace amqp {
+
+struct MessageId
+{
+ union
+ {
+ qpid::amqp::CharSequence bytes;
+ uint64_t ulong;
+ } value;
+ enum
+ {
+ BYTES,
+ UUID,
+ ULONG
+ } type;
+
+ QPID_COMMON_EXTERN MessageId();
+ QPID_COMMON_EXTERN void assign(std::string&) const;
+ QPID_COMMON_EXTERN void set(qpid::amqp::CharSequence bytes, qpid::types::VariantType t);
+ QPID_COMMON_EXTERN void set(uint64_t ulong);
+
+};
+
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_MESSAGEID_H*/
diff --git a/cpp/src/qpid/amqp/MessageReader.cpp b/cpp/src/qpid/amqp/MessageReader.cpp
new file mode 100644
index 0000000000..1550fa1977
--- /dev/null
+++ b/cpp/src/qpid/amqp/MessageReader.cpp
@@ -0,0 +1,759 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/MessageReader.h"
+#include "qpid/amqp/Descriptor.h"
+#include "qpid/amqp/descriptors.h"
+#include "qpid/types/Uuid.h"
+#include "qpid/types/Variant.h"
+#include "qpid/log/Statement.h"
+
+using namespace qpid::amqp::message;
+
+namespace qpid {
+namespace amqp {
+namespace {
+
+//header fields:
+const size_t DURABLE(0);
+const size_t PRIORITY(1);
+const size_t TTL(2);
+const size_t FIRST_ACQUIRER(3);
+const size_t DELIVERY_COUNT(4);
+
+//properties fields:
+const size_t MESSAGE_ID(0);
+const size_t USER_ID(1);
+const size_t TO(2);
+const size_t SUBJECT(3);
+const size_t REPLY_TO(4);
+const size_t CORRELATION_ID(5);
+const size_t CONTENT_TYPE(6);
+const size_t CONTENT_ENCODING(7);
+const size_t ABSOLUTE_EXPIRY_TIME(8);
+const size_t CREATION_TIME(9);
+const size_t GROUP_ID(10);
+const size_t GROUP_SEQUENCE(11);
+const size_t REPLY_TO_GROUP_ID(12);
+
+}
+
+/*
+Reader& MessageReader::HeaderReader::getReader(size_t index)
+{
+ switch (index) {
+ case DURABLE: return durableReader;
+ case PRIORITY: return priorityReader;
+ case TTL: return ttlReader;
+ case FIRST_ACQUIRER: return firstAcquirerReader;
+ case DELIVERY_COUNT: return deliveryCountReader;
+ default: return noSuchFieldReader;
+ }
+}
+
+Reader& MessageReader::PropertiesReader::getReader(size_t index)
+{
+ switch (index) {
+ case MESSAGE_ID: return messageIdReader;
+ case USER_ID: return userIdReader;
+ case TO: return toReader;
+ case SUBJECT: return subjectReader;
+ case REPLY_TO: return replyToReader;
+ case CORRELATION_ID: return correlationIdReader;
+ case CONTENT_TYPE: return contentTypeReader;
+ case CONTENT_ENCODING: return contentEncodingReader;
+ case ABSOLUTE_EXPIRY_TIME: return absoluteExpiryTimeReader;
+ case CREATION_TIME: return creationTimeReader;
+ case GROUP_ID: return groupIdReader;
+ case GROUP_SEQUENCE: return groupSequenceReader;
+ case REPLY_TO_GROUP_ID: return replyToGroupIdReader;
+ default: return noSuchFieldReader;
+ }
+}
+*/
+
+MessageReader::HeaderReader::HeaderReader(MessageReader& p) : parent(p), index(0) {}
+void MessageReader::HeaderReader::onBoolean(bool v, const Descriptor*) // durable, first-acquirer
+{
+ if (index == DURABLE) {
+ parent.onDurable(v);
+ } else if (index == FIRST_ACQUIRER) {
+ parent.onFirstAcquirer(v);
+ } else {
+ QPID_LOG(warning, "Unexpected message format, got boolean at index " << index << " of headers");
+ }
+ ++index;
+}
+void MessageReader::HeaderReader::onUByte(uint8_t v, const Descriptor*) // priority
+{
+ if (index == PRIORITY) {
+ parent.onPriority(v);
+ } else {
+ QPID_LOG(warning, "Unexpected message format, got ubyte at index " << index << " of headers");
+ }
+ ++index;
+}
+void MessageReader::HeaderReader::onUInt(uint32_t v, const Descriptor*) // ttl, delivery-count
+{
+ if (index == TTL) {
+ parent.onTtl(v);
+ } else if (index == DELIVERY_COUNT) {
+ parent.onDeliveryCount(v);
+ } else {
+ QPID_LOG(warning, "Unexpected message format, got uint at index " << index << " of headers");
+ }
+ ++index;
+}
+void MessageReader::HeaderReader::onNull(const Descriptor*)
+{
+ ++index;
+}
+
+MessageReader::PropertiesReader::PropertiesReader(MessageReader& p) : parent(p), index(0) {}
+void MessageReader::PropertiesReader::onUuid(const CharSequence& v, const Descriptor*) // message-id, correlation-id
+{
+ if (index == MESSAGE_ID) {
+ parent.onMessageId(v, qpid::types::VAR_UUID);
+ } else if (index == CORRELATION_ID) {
+ parent.onCorrelationId(v);
+ } else {
+ QPID_LOG(warning, "Unexpected message format, got uuid at index " << index << " of properties");
+ }
+ ++index;
+}
+void MessageReader::PropertiesReader::onULong(uint64_t v, const Descriptor*) // message-id, correlation-id
+{
+ if (index == MESSAGE_ID) {
+ parent.onMessageId(v);
+ } else if (index == CORRELATION_ID) {
+ parent.onCorrelationId(v);
+ } else {
+ QPID_LOG(warning, "Unexpected message format, got long at index " << index << " of properties");
+ }
+ ++index;
+}
+void MessageReader::PropertiesReader::onBinary(const CharSequence& v, const Descriptor*) // message-id, correlation-id, user-id
+{
+ if (index == MESSAGE_ID) {
+ parent.onMessageId(v, qpid::types::VAR_STRING);
+ } else if (index == CORRELATION_ID) {
+ parent.onCorrelationId(v);
+ } else if (index == USER_ID) {
+ parent.onUserId(v);
+ } else {
+ QPID_LOG(warning, "Unexpected message format, got binary at index " << index << " of properties");
+ }
+ ++index;
+}
+void MessageReader::PropertiesReader::onString(const CharSequence& v, const Descriptor*) // message-id, correlation-id, group-id, reply-to-group-id, subject, to, reply-to
+{
+ if (index == MESSAGE_ID) {
+ parent.onMessageId(v);
+ } else if (index == CORRELATION_ID) {
+ parent.onCorrelationId(v);
+ } else if (index == GROUP_ID) {
+ parent.onGroupId(v);
+ } else if (index == REPLY_TO_GROUP_ID) {
+ parent.onReplyToGroupId(v);
+ } else if (index == SUBJECT) {
+ parent.onSubject(v);
+ } else if (index == TO) {
+ parent.onTo(v);
+ } else if (index == REPLY_TO) {
+ parent.onReplyTo(v);
+ } else {
+ QPID_LOG(warning, "Unexpected message format, got string at index " << index << " of properties");
+ }
+ ++index;
+}
+void MessageReader::PropertiesReader::onSymbol(const CharSequence& v, const Descriptor*) // content-type, content-encoding
+{
+ if (index == CONTENT_TYPE) {
+ parent.onContentType(v);
+ } else if (index == CONTENT_ENCODING) {
+ parent.onContentEncoding(v);
+ } else {
+ QPID_LOG(warning, "Unexpected message format, got symbol at index " << index << " of properties");
+ }
+ ++index;
+}
+void MessageReader::PropertiesReader::onTimestamp(int64_t v, const Descriptor*) // absolute-expiry-time, creation-time
+{
+ if (index == ABSOLUTE_EXPIRY_TIME) {
+ parent.onAbsoluteExpiryTime(v);
+ } else if (index == CREATION_TIME) {
+ parent.onCreationTime(v);
+ } else {
+ QPID_LOG(warning, "Unexpected message format, got timestamp at index " << index << " of properties");
+ }
+ ++index;
+}
+void MessageReader::PropertiesReader::onUInt(uint32_t v, const Descriptor*) // group-sequence
+{
+ if (index == GROUP_SEQUENCE) {
+ parent.onGroupSequence(v);
+ } else {
+ QPID_LOG(warning, "Unexpected message format, got uint at index " << index << " of properties");
+ }
+ ++index;
+}
+void MessageReader::PropertiesReader::onNull(const Descriptor*)
+{
+ ++index;
+}
+
+/*
+MessageReader::DurableReader::DurableReader(MessageReader& p) : parent(p) {}
+void MessageReader::DurableReader::onBoolean(bool v, const Descriptor*)
+{
+ parent.onDurable(v);
+}
+MessageReader::PriorityReader::PriorityReader(MessageReader& p) : parent(p) {}
+void MessageReader::PriorityReader::onUByte(uint8_t v, const Descriptor*)
+{
+ parent.onPriority(v);
+}
+MessageReader::TtlReader::TtlReader(MessageReader& p) : parent(p) {}
+void MessageReader::TtlReader::onUInt(uint32_t v, const Descriptor*)
+{
+ parent.onTtl(v);
+}
+MessageReader::FirstAcquirerReader::FirstAcquirerReader(MessageReader& p) : parent(p) {}
+void MessageReader::FirstAcquirerReader::onBoolean(bool v, const Descriptor*)
+{
+ parent.onFirstAcquirer(v);
+}
+MessageReader::DeliveryCountReader::DeliveryCountReader(MessageReader& p) : parent(p) {}
+void MessageReader::DeliveryCountReader::onUInt(uint32_t v, const Descriptor*)
+{
+ parent.onDeliveryCount(v);
+}
+MessageReader::MessageIdReader::MessageIdReader(MessageReader& p) : parent(p) {}
+void MessageReader::MessageIdReader::onUuid(const qpid::types::Uuid& v, const Descriptor*)
+{
+ parent.onMessageId(v);
+}
+void MessageReader::MessageIdReader::onULong(uint64_t v, const Descriptor*)
+{
+ parent.onMessageId(v);
+}
+void MessageReader::MessageIdReader::onString(const CharSequence& v, const Descriptor*)
+{
+ parent.onMessageId(v);
+}
+void MessageReader::MessageIdReader::onBinary(const CharSequence& v, const Descriptor*)
+{
+ parent.onMessageId(v);
+}
+MessageReader::UserIdReader::UserIdReader(MessageReader& p) : parent(p) {}
+void MessageReader::UserIdReader::onBinary(const CharSequence& v, const Descriptor*)
+{
+ parent.onUserId(v);
+}
+MessageReader::ToReader::ToReader(MessageReader& p) : parent(p) {}
+void MessageReader::ToReader::onString(const CharSequence& v, const Descriptor*)
+{
+ parent.onTo(v);
+}
+MessageReader::SubjectReader::SubjectReader(MessageReader& p) : parent(p) {}
+void MessageReader::SubjectReader::onString(const CharSequence& v, const Descriptor*)
+{
+ parent.onSubject(v);
+}
+MessageReader::ReplyToReader::ReplyToReader(MessageReader& p) : parent(p) {}
+void MessageReader::ReplyToReader::onString(const CharSequence& v, const Descriptor*)
+{
+ parent.onReplyTo(v);
+}
+MessageReader::CorrelationIdReader::CorrelationIdReader(MessageReader& p) : parent(p) {}
+void MessageReader::CorrelationIdReader::onUuid(const qpid::types::Uuid& v, const Descriptor*)
+{
+ parent.onCorrelationId(v);
+}
+void MessageReader::CorrelationIdReader::onULong(uint64_t v, const Descriptor*)
+{
+ parent.onCorrelationId(v);
+}
+void MessageReader::CorrelationIdReader::onString(const CharSequence& v, const Descriptor*)
+{
+ parent.onCorrelationId(v);
+}
+void MessageReader::CorrelationIdReader::onBinary(const CharSequence& v, const Descriptor*)
+{
+ parent.onCorrelationId(v);
+}
+MessageReader::ContentTypeReader::ContentTypeReader(MessageReader& p) : parent(p) {}
+void MessageReader::ContentTypeReader::onString(const CharSequence& v, const Descriptor*)
+{
+ parent.onContentType(v);
+}
+MessageReader::ContentEncodingReader::ContentEncodingReader(MessageReader& p) : parent(p) {}
+void MessageReader::ContentEncodingReader::onString(const CharSequence& v, const Descriptor*)
+{
+ parent.onContentEncoding(v);
+}
+MessageReader::AbsoluteExpiryTimeReader::AbsoluteExpiryTimeReader(MessageReader& p) : parent(p) {}
+void MessageReader::AbsoluteExpiryTimeReader::onTimestamp(int64_t v, const Descriptor*)
+{
+ parent.onAbsoluteExpiryTime(v);
+}
+MessageReader::CreationTimeReader::CreationTimeReader(MessageReader& p) : parent(p) {}
+void MessageReader::CreationTimeReader::onTimestamp(int64_t v, const Descriptor*)
+{
+ parent.onCreationTime(v);
+}
+MessageReader::GroupIdReader::GroupIdReader(MessageReader& p) : parent(p) {}
+void MessageReader::GroupIdReader::onString(const CharSequence& v, const Descriptor*)
+{
+ parent.onGroupId(v);
+}
+MessageReader::GroupSequenceReader::GroupSequenceReader(MessageReader& p) : parent(p) {}
+void MessageReader::GroupSequenceReader::onUInt(uint32_t v, const Descriptor*)
+{
+ parent.onGroupSequence(v);
+}
+MessageReader::ReplyToGroupIdReader::ReplyToGroupIdReader(MessageReader& p) : parent(p) {}
+void MessageReader::ReplyToGroupIdReader::onString(const CharSequence& v, const Descriptor*)
+{
+ parent.onReplyToGroupId(v);
+}
+*/
+
+//header, properties, amqp-sequence, amqp-value
+bool MessageReader::onStartList(uint32_t count, const CharSequence& raw, const Descriptor* descriptor)
+{
+ if (delegate) {
+ return delegate->onStartList(count, raw, descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got no descriptor for list.");
+ return false;
+ } else if (descriptor->match(HEADER_SYMBOL, HEADER_CODE)) {
+ delegate = &headerReader;
+ return true;
+ } else if (descriptor->match(PROPERTIES_SYMBOL, PROPERTIES_CODE)) {
+ delegate = &propertiesReader;
+ return true;
+ } else if (descriptor->match(AMQP_SEQUENCE_SYMBOL, AMQP_SEQUENCE_CODE) || descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ onBody(raw, *descriptor);
+ return false;
+ } else {
+ QPID_LOG(warning, "Unexpected described list: " << *descriptor);
+ return false;
+ }
+ }
+}
+void MessageReader::onEndList(uint32_t count, const Descriptor* descriptor)
+{
+ if (delegate) {
+ if (descriptor && (descriptor->match(HEADER_SYMBOL, HEADER_CODE) || descriptor->match(PROPERTIES_SYMBOL, PROPERTIES_CODE))) {
+ delegate = 0;
+ } else {
+ delegate->onEndList(count, descriptor);
+ }
+ }
+}
+
+//delivery-annotations, message-annotations, application-properties, amqp-value
+bool MessageReader::onStartMap(uint32_t count, const CharSequence& raw, const Descriptor* descriptor)
+{
+ if (delegate) {
+ return delegate->onStartMap(count, raw, descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got no descriptor for map.");
+ return false;
+ } else if (descriptor->match(DELIVERY_ANNOTATIONS_SYMBOL, DELIVERY_ANNOTATIONS_CODE)) {
+ onDeliveryAnnotations(raw);
+ return false;
+ } else if (descriptor->match(MESSAGE_ANNOTATIONS_SYMBOL, MESSAGE_ANNOTATIONS_CODE)) {
+ onMessageAnnotations(raw);
+ return false;
+ } else if (descriptor->match(FOOTER_SYMBOL, FOOTER_CODE)) {
+ onFooter(raw);
+ return false;
+ } else if (descriptor->match(APPLICATION_PROPERTIES_SYMBOL, APPLICATION_PROPERTIES_CODE)) {
+ onApplicationProperties(raw);
+ return false;
+ } else if (descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ onBody(raw, *descriptor);
+ return false;
+ } else {
+ QPID_LOG(warning, "Unexpected described map: " << *descriptor);
+ return false;
+ }
+ }
+}
+
+void MessageReader::onEndMap(uint32_t count, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onEndMap(count, descriptor);
+ }
+}
+
+//data, amqp-value
+void MessageReader::onBinary(const CharSequence& bytes, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onBinary(bytes, descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got binary value with no descriptor.");
+ } else if (descriptor->match(DATA_SYMBOL, DATA_CODE) || descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ onBody(bytes, *descriptor);
+ } else {
+ QPID_LOG(warning, "Unexpected binary value with descriptor: " << *descriptor);
+ }
+ }
+
+}
+
+//amqp-value
+void MessageReader::onNull(const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onNull(descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ qpid::types::Variant v;
+ onBody(v, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got null value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected null value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+void MessageReader::onString(const CharSequence& v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onString(v, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ onBody(v, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got string value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected string value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+void MessageReader::onSymbol(const CharSequence& v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onSymbol(v, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ onBody(v, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got symbol value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected symbol value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+
+void MessageReader::onBoolean(bool v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onBoolean(v, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ qpid::types::Variant body = v;
+ onBody(body, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got boolean value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected boolean value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+
+void MessageReader::onUByte(uint8_t v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onUByte(v, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ qpid::types::Variant body = v;
+ onBody(body, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got ubyte value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected ubyte value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+
+void MessageReader::onUShort(uint16_t v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onUShort(v, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ qpid::types::Variant body = v;
+ onBody(body, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got ushort value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected ushort value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+
+void MessageReader::onUInt(uint32_t v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onUInt(v, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ qpid::types::Variant body = v;
+ onBody(body, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got uint value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected uint value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+
+void MessageReader::onULong(uint64_t v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onULong(v, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ qpid::types::Variant body = v;
+ onBody(body, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got ulong value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected ulong value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+
+void MessageReader::onByte(int8_t v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onByte(v, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ qpid::types::Variant body = v;
+ onBody(body, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got byte value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected byte value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+
+void MessageReader::onShort(int16_t v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onShort(v, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ qpid::types::Variant body = v;
+ onBody(body, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got short value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected short value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+
+void MessageReader::onInt(int32_t v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onInt(v, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ qpid::types::Variant body = v;
+ onBody(body, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got int value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected int value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+
+void MessageReader::onLong(int64_t v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onLong(v, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ qpid::types::Variant body = v;
+ onBody(body, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got long value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected long value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+
+void MessageReader::onFloat(float v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onFloat(v, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ qpid::types::Variant body = v;
+ onBody(body, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got float value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected float value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+
+void MessageReader::onDouble(double v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onDouble(v, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ qpid::types::Variant body = v;
+ onBody(body, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got double value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected double value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+
+void MessageReader::onUuid(const CharSequence& v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onUuid(v, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ onBody(v, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got uuid value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected uuid value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+
+void MessageReader::onTimestamp(int64_t v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onTimestamp(v, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ qpid::types::Variant body = v;
+ onBody(body, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got timestamp value with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected timestamp value with descriptor: " << *descriptor);
+ }
+ }
+ }
+}
+
+bool MessageReader::onStartArray(uint32_t count, const CharSequence& raw, const Constructor& constructor, const Descriptor* descriptor)
+{
+ if (delegate) {
+ return delegate->onStartArray(count, raw, constructor, descriptor);
+ } else {
+ if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) {
+ onBody(raw, *descriptor);
+ } else {
+ if (!descriptor) {
+ QPID_LOG(warning, "Expected described type but got array with no descriptor.");
+ } else {
+ QPID_LOG(warning, "Unexpected array with descriptor: " << *descriptor);
+ }
+ }
+ return false;
+ }
+}
+
+void MessageReader::onEndArray(uint32_t v, const Descriptor* descriptor)
+{
+ if (delegate) {
+ delegate->onEndArray(v, descriptor);
+ }
+}
+
+MessageReader::MessageReader() : headerReader(*this), propertiesReader(*this), delegate(0)
+{
+ bare.init();
+}
+
+void MessageReader::onDescriptor(const Descriptor& descriptor, const char* position)
+{
+ if (bare.data) {
+ if (descriptor.match(FOOTER_SYMBOL, FOOTER_CODE)) {
+ bare.size = position - bare.data;
+ }
+ } else {
+ if (descriptor.match(PROPERTIES_SYMBOL, PROPERTIES_CODE) || descriptor.match(APPLICATION_PROPERTIES_SYMBOL, APPLICATION_PROPERTIES_CODE)
+ || descriptor.match(AMQP_SEQUENCE_SYMBOL, AMQP_SEQUENCE_CODE) || descriptor.match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE) || descriptor.match(DATA_SYMBOL, DATA_CODE)) {
+ bare.data = position;
+ }
+ }
+}
+
+CharSequence MessageReader::getBareMessage() const { return bare; }
+
+}} // namespace qpid::amqp
diff --git a/cpp/src/qpid/amqp/MessageReader.h b/cpp/src/qpid/amqp/MessageReader.h
new file mode 100644
index 0000000000..5d26b288f5
--- /dev/null
+++ b/cpp/src/qpid/amqp/MessageReader.h
@@ -0,0 +1,301 @@
+#ifndef QPID_AMQP_MESSAGEREADER_H
+#define QPID_AMQP_MESSAGEREADER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/CharSequence.h"
+#include "qpid/amqp/Reader.h"
+#include "qpid/amqp/ListReader.h"
+#include "qpid/types/Variant.h"
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+namespace amqp {
+
+/**
+ * Reader for an AMQP 1.0 message
+ */
+class MessageReader : public Reader
+{
+ public:
+ QPID_COMMON_EXTERN MessageReader();
+
+ //header, properties, amqp-sequence, amqp-value
+ QPID_COMMON_EXTERN bool onStartList(uint32_t, const CharSequence&, const Descriptor*);
+ QPID_COMMON_EXTERN void onEndList(uint32_t, const Descriptor*);
+
+ //delivery-annotations, message-annotations, application-headers, amqp-value
+ QPID_COMMON_EXTERN bool onStartMap(uint32_t, const CharSequence&, const Descriptor*);
+ QPID_COMMON_EXTERN void onEndMap(uint32_t, const Descriptor*);
+
+ //data, amqp-value
+ QPID_COMMON_EXTERN void onBinary(const CharSequence&, const Descriptor*);
+
+ //amqp-value
+ QPID_COMMON_EXTERN void onNull(const Descriptor*);
+ QPID_COMMON_EXTERN void onString(const CharSequence&, const Descriptor*);
+ QPID_COMMON_EXTERN void onSymbol(const CharSequence&, const Descriptor*);
+ QPID_COMMON_EXTERN void onBoolean(bool, const Descriptor*);
+ QPID_COMMON_EXTERN void onUByte(uint8_t, const Descriptor*);
+ QPID_COMMON_EXTERN void onUShort(uint16_t, const Descriptor*);
+ QPID_COMMON_EXTERN void onUInt(uint32_t, const Descriptor*);
+ QPID_COMMON_EXTERN void onULong(uint64_t, const Descriptor*);
+ QPID_COMMON_EXTERN void onByte(int8_t, const Descriptor*);
+ QPID_COMMON_EXTERN void onShort(int16_t, const Descriptor*);
+ QPID_COMMON_EXTERN void onInt(int32_t, const Descriptor*);
+ QPID_COMMON_EXTERN void onLong(int64_t, const Descriptor*);
+ QPID_COMMON_EXTERN void onFloat(float, const Descriptor*);
+ QPID_COMMON_EXTERN void onDouble(double, const Descriptor*);
+ QPID_COMMON_EXTERN void onUuid(const CharSequence&, const Descriptor*);
+ QPID_COMMON_EXTERN void onTimestamp(int64_t, const Descriptor*);
+ QPID_COMMON_EXTERN bool onStartArray(uint32_t, const CharSequence&, const Constructor&, const Descriptor*);
+ QPID_COMMON_EXTERN void onEndArray(uint32_t, const Descriptor*);
+ QPID_COMMON_EXTERN void onDescriptor(const Descriptor&, const char*);
+
+ //header:
+ virtual void onDurable(bool) = 0;
+ virtual void onPriority(uint8_t) = 0;
+ virtual void onTtl(uint32_t) = 0;
+ virtual void onFirstAcquirer(bool) = 0;
+ virtual void onDeliveryCount(uint32_t) = 0;
+
+ //properties:
+ virtual void onMessageId(uint64_t) = 0;
+ virtual void onMessageId(const CharSequence&, qpid::types::VariantType) = 0;
+ virtual void onUserId(const CharSequence&) = 0;
+ virtual void onTo(const CharSequence&) = 0;
+ virtual void onSubject(const CharSequence&) = 0;
+ virtual void onReplyTo(const CharSequence&) = 0;
+ virtual void onCorrelationId(uint64_t) = 0;
+ virtual void onCorrelationId(const CharSequence&, qpid::types::VariantType) = 0;
+ virtual void onContentType(const CharSequence&) = 0;
+ virtual void onContentEncoding(const CharSequence&) = 0;
+ virtual void onAbsoluteExpiryTime(int64_t) = 0;
+ virtual void onCreationTime(int64_t) = 0;
+ virtual void onGroupId(const CharSequence&) = 0;
+ virtual void onGroupSequence(uint32_t) = 0;
+ virtual void onReplyToGroupId(const CharSequence&) = 0;
+
+ virtual void onApplicationProperties(const CharSequence&) = 0;
+ virtual void onDeliveryAnnotations(const CharSequence&) = 0;
+ virtual void onMessageAnnotations(const CharSequence&) = 0;
+ virtual void onBody(const CharSequence&, const Descriptor&) = 0;
+ virtual void onBody(const qpid::types::Variant&, const Descriptor&) = 0;
+ virtual void onFooter(const CharSequence&) = 0;
+
+ QPID_COMMON_EXTERN CharSequence getBareMessage() const;
+
+ private:
+ /*
+ class DurableReader : public Reader
+ {
+ public:
+ DurableReader(MessageReader&);
+ void onBoolean(bool v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class PriorityReader : public Reader
+ {
+ public:
+ PriorityReader(MessageReader&);
+ void onUByte(uint8_t v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class TtlReader : public Reader
+ {
+ public:
+ TtlReader(MessageReader&);
+ void onUInt(uint32_t v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class FirstAcquirerReader : public Reader
+ {
+ public:
+ FirstAcquirerReader(MessageReader&);
+ void onBoolean(bool v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class DeliveryCountReader : public Reader
+ {
+ public:
+ DeliveryCountReader(MessageReader&);
+ void onUInt(uint32_t v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+
+ class MessageIdReader : public Reader
+ {
+ public:
+ MessageIdReader(MessageReader&);
+ void onUuid(const qpid::types::Uuid& v, const Descriptor*);
+ void onULong(uint64_t v, const Descriptor*);
+ void onString(const CharSequence& v, const Descriptor*);
+ void onBinary(const CharSequence& v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class UserIdReader : public Reader
+ {
+ public:
+ UserIdReader(MessageReader&);
+ void onBinary(const CharSequence& v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class ToReader : public Reader
+ {
+ public:
+ ToReader(MessageReader&);
+ void onString(const CharSequence& v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class SubjectReader : public Reader
+ {
+ public:
+ SubjectReader(MessageReader&);
+ void onString(const CharSequence& v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class ReplyToReader : public Reader
+ {
+ public:
+ ReplyToReader(MessageReader&);
+ void onString(const CharSequence& v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class CorrelationIdReader : public Reader
+ {
+ public:
+ CorrelationIdReader(MessageReader&);
+ void onUuid(const qpid::types::Uuid& v, const Descriptor*);
+ void onULong(uint64_t v, const Descriptor*);
+ void onString(const CharSequence& v, const Descriptor*);
+ void onBinary(const CharSequence& v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class ContentTypeReader : public Reader
+ {
+ public:
+ ContentTypeReader(MessageReader&);
+ void onString(const CharSequence& v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class ContentEncodingReader : public Reader
+ {
+ public:
+ ContentEncodingReader(MessageReader&);
+ void onString(const CharSequence& v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class AbsoluteExpiryTimeReader : public Reader
+ {
+ public:
+ AbsoluteExpiryTimeReader(MessageReader&);
+ void onTimestamp(int64_t v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class CreationTimeReader : public Reader
+ {
+ public:
+ CreationTimeReader(MessageReader&);
+ void onTimestamp(int64_t v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class GroupIdReader : public Reader
+ {
+ public:
+ GroupIdReader(MessageReader&);
+ void onString(const CharSequence& v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class GroupSequenceReader : public Reader
+ {
+ public:
+ GroupSequenceReader(MessageReader&);
+ void onUInt(uint32_t v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ class ReplyToGroupIdReader : public Reader
+ {
+ public:
+ ReplyToGroupIdReader(MessageReader&);
+ void onString(const CharSequence& v, const Descriptor*);
+ private:
+ MessageReader& parent;
+ };
+ */
+
+ class HeaderReader : public Reader //public ListReader
+ {
+ public:
+ //Reader& getReader(size_t index);
+
+ HeaderReader(MessageReader&);
+ void onBoolean(bool v, const Descriptor*); // durable, first-acquirer
+ void onUByte(uint8_t v, const Descriptor*); // priority
+ void onUInt(uint32_t v, const Descriptor*); // ttl, delivery-count
+ void onNull(const Descriptor*);
+ private:
+ MessageReader& parent;
+ size_t index;
+ };
+ class PropertiesReader : public Reader //public ListReader
+ {
+ public:
+ //Reader& getReader(size_t index);
+
+ PropertiesReader(MessageReader&);
+ void onUuid(const CharSequence& v, const Descriptor*); // message-id, correlation-id
+ void onULong(uint64_t v, const Descriptor*); // message-id, correlation-id
+ void onBinary(const CharSequence& v, const Descriptor*); // message-id, correlation-id, user-id
+ void onString(const CharSequence& v, const Descriptor*); // message-id, correlation-id, group-id, reply-to-group-id, subject, to, reply-to
+ void onSymbol(const CharSequence& v, const Descriptor*); // content-type, content-encoding
+ void onTimestamp(int64_t v, const Descriptor*); // absolute-expiry-time, creation-time
+ void onUInt(uint32_t v, const Descriptor*); // group-sequence
+ void onNull(const Descriptor*);
+ private:
+ MessageReader& parent;
+ size_t index;
+ };
+ HeaderReader headerReader;
+ PropertiesReader propertiesReader;
+ Reader* delegate;
+ CharSequence bare;
+};
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_MESSAGEREADER_H*/
diff --git a/cpp/src/qpid/amqp/Reader.h b/cpp/src/qpid/amqp/Reader.h
new file mode 100644
index 0000000000..64019d1521
--- /dev/null
+++ b/cpp/src/qpid/amqp/Reader.h
@@ -0,0 +1,80 @@
+#ifndef QPID_AMQP_READER_H
+#define QPID_AMQP_READER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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 <stddef.h>
+
+namespace qpid {
+namespace amqp {
+struct CharSequence;
+struct Constructor;
+struct Descriptor;
+
+/**
+ * Allows an event-driven, callback-based approach to processing an
+ * AMQP encoded data stream. By sublassing and implementing the
+ * methods of interest, readers can be constructed for different
+ * contexts.
+ */
+class Reader
+{
+ public:
+ virtual ~Reader() {}
+ virtual void onNull(const Descriptor*) {}
+ virtual void onBoolean(bool, const Descriptor*) {}
+ virtual void onUByte(uint8_t, const Descriptor*) {}
+ virtual void onUShort(uint16_t, const Descriptor*) {}
+ virtual void onUInt(uint32_t, const Descriptor*) {}
+ virtual void onULong(uint64_t, const Descriptor*) {}
+ virtual void onByte(int8_t, const Descriptor*) {}
+ virtual void onShort(int16_t, const Descriptor*) {}
+ virtual void onInt(int32_t, const Descriptor*) {}
+ virtual void onLong(int64_t, const Descriptor*) {}
+ virtual void onFloat(float, const Descriptor*) {}
+ virtual void onDouble(double, const Descriptor*) {}
+ virtual void onUuid(const CharSequence&, const Descriptor*) {}
+ virtual void onTimestamp(int64_t, const Descriptor*) {}
+
+ virtual void onBinary(const CharSequence&, const Descriptor*) {}
+ virtual void onString(const CharSequence&, const Descriptor*) {}
+ virtual void onSymbol(const CharSequence&, const Descriptor*) {}
+
+ /**
+ * @return true to get elements of the compound value, false
+ * to skip over it
+ */
+ virtual bool onStartList(uint32_t /*count*/, const CharSequence&, const Descriptor*) { return true; }
+ virtual bool onStartMap(uint32_t /*count*/, const CharSequence&, const Descriptor*) { return true; }
+ virtual bool onStartArray(uint32_t /*count*/, const CharSequence&, const Constructor&, const Descriptor*) { return true; }
+ virtual void onEndList(uint32_t /*count*/, const Descriptor*) {}
+ virtual void onEndMap(uint32_t /*count*/, const Descriptor*) {}
+ virtual void onEndArray(uint32_t /*count*/, const Descriptor*) {}
+
+ virtual void onDescriptor(const Descriptor&, const char*) {}
+
+ virtual bool proceed() { return true; }
+ private:
+};
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_READER_H*/
diff --git a/cpp/src/qpid/amqp/Sasl.cpp b/cpp/src/qpid/amqp/Sasl.cpp
new file mode 100644
index 0000000000..7b0779fe94
--- /dev/null
+++ b/cpp/src/qpid/amqp/Sasl.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/amqp/Sasl.h"
+#include "qpid/amqp/Decoder.h"
+#include "qpid/amqp/Descriptor.h"
+#include "qpid/amqp/Encoder.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/ProtocolVersion.h"
+#include "qpid/framing/ProtocolInitiation.h"
+#include <string.h>
+
+namespace qpid {
+namespace amqp {
+
+Sasl::Sasl(const std::string& i) : id(i), buffer(2*512/*AMQP 1.0's MAX_MIN_FRAME_SIZE - is this enough though?*/), encoder(&buffer[0], buffer.size()) {}
+Sasl::~Sasl() {}
+
+void* Sasl::startFrame()
+{
+ //write sasl frame header, leaving 4 bytes for total size
+ char* start = encoder.skip(4);
+ encoder.write((uint8_t) 0x02);//data offset
+ encoder.write((uint8_t) 0x01);//frame type
+ encoder.write((uint16_t) 0x0000);//ignored
+ return start;
+}
+
+void Sasl::endFrame(void* frame)
+{
+ //now backfill the frame size
+ char* start = (char*) frame;
+ char* current = &buffer[encoder.getPosition()];
+ uint32_t frameSize = current - start;
+ Encoder backfill(start, 4);
+ backfill.write(frameSize);
+ QPID_LOG(trace, "Completed encoding of frame of " << frameSize << " bytes");
+}
+
+
+std::size_t Sasl::read(const char* data, size_t available)
+{
+ size_t consumed = 0;
+ while (available - consumed > 4/*framesize*/) {
+ Decoder decoder(data+consumed, available-consumed);
+ //read frame-header
+ uint32_t frameSize = decoder.readUInt();
+ if (frameSize > decoder.getSize()) break;//don't have all the data for this frame yet
+
+ QPID_LOG(trace, "Reading SASL frame of size " << frameSize);
+ decoder.resetSize(frameSize);
+ uint8_t dataOffset = decoder.readUByte();
+ uint8_t frameType = decoder.readUByte();
+ if (frameType != 0x01) {
+ QPID_LOG(error, "Expected SASL frame; got type " << frameType);
+ }
+ uint16_t ignored = decoder.readUShort();
+ if (ignored) {
+ QPID_LOG(info, "Got non null bytes at end of SASL frame header");
+ }
+
+ //body is at offset 4*dataOffset from the start
+ size_t skip = dataOffset*4 - 8;
+ if (skip) {
+ QPID_LOG(info, "Offset for sasl frame was not as expected");
+ decoder.advance(skip);
+ }
+ decoder.read(*this);
+ consumed += decoder.getPosition();
+ }
+ return consumed;
+}
+
+std::size_t Sasl::write(char* data, size_t size)
+{
+ size_t available = encoder.getPosition();
+ if (available) {
+ size_t encoded = available > size ? size : available;
+ ::memcpy(data, &buffer[0], encoded);
+ size_t remainder = encoder.getPosition() - encoded;
+ if (remainder) {
+ //shuffle
+ ::memcpy(&buffer[0], &buffer[size], remainder);
+ }
+ encoder.resetPosition(remainder);
+ return encoded;
+ } else {
+ return 0;
+ }
+}
+
+std::size_t Sasl::readProtocolHeader(const char* buffer, std::size_t size)
+{
+ framing::ProtocolInitiation pi(qpid::framing::ProtocolVersion(1,0,qpid::framing::ProtocolVersion::SASL));
+ if (size >= pi.encodedSize()) {
+ qpid::framing::Buffer out(const_cast<char*>(buffer), size);
+ pi.decode(out);
+ QPID_LOG_CAT(debug, protocol, id << " read protocol header: " << pi);
+ return pi.encodedSize();
+ } else {
+ return 0;
+ }
+}
+std::size_t Sasl::writeProtocolHeader(char* buffer, std::size_t size)
+{
+ framing::ProtocolInitiation pi(qpid::framing::ProtocolVersion(1,0,qpid::framing::ProtocolVersion::SASL));
+ if (size >= pi.encodedSize()) {
+ QPID_LOG_CAT(debug, protocol, id << " writing protocol header: " << pi);
+ qpid::framing::Buffer out(buffer, size);
+ pi.encode(out);
+ return pi.encodedSize();
+ } else {
+ QPID_LOG_CAT(warning, protocol, id << " insufficient buffer for protocol header: " << size)
+ return 0;
+ }
+}
+
+}} // namespace qpid::amqp
diff --git a/cpp/src/qpid/amqp/Sasl.h b/cpp/src/qpid/amqp/Sasl.h
new file mode 100644
index 0000000000..558f6071fc
--- /dev/null
+++ b/cpp/src/qpid/amqp/Sasl.h
@@ -0,0 +1,54 @@
+#ifndef QPID_AMQP_SASL_H
+#define QPID_AMQP_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 "qpid/amqp/Encoder.h"
+#include "qpid/amqp/Reader.h"
+#include <string>
+#include <vector>
+
+namespace qpid {
+namespace amqp {
+
+/**
+ * Base for SASL client and server utilities
+ */
+class Sasl : protected Reader
+{
+ public:
+ Sasl(const std::string& id);
+ virtual ~Sasl();
+ std::size_t read(const char* data, size_t available);
+ std::size_t write(char* data, size_t available);
+ std::size_t readProtocolHeader(const char* buffer, std::size_t size);
+ std::size_t writeProtocolHeader(char* buffer, std::size_t size);
+ protected:
+ const std::string id;
+ std::vector<char> buffer;
+ Encoder encoder;
+
+ void* startFrame();
+ void endFrame(void*);
+};
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_SASL_H*/
diff --git a/cpp/src/qpid/amqp/SaslClient.cpp b/cpp/src/qpid/amqp/SaslClient.cpp
new file mode 100644
index 0000000000..69660e9294
--- /dev/null
+++ b/cpp/src/qpid/amqp/SaslClient.cpp
@@ -0,0 +1,154 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/SaslClient.h"
+#include "qpid/amqp/Decoder.h"
+#include "qpid/amqp/Descriptor.h"
+#include "qpid/amqp/descriptors.h"
+#include "qpid/amqp/Encoder.h"
+#include "qpid/log/Statement.h"
+
+using namespace qpid::amqp::sasl;
+
+namespace qpid {
+namespace amqp {
+
+SaslClient::SaslClient(const std::string& id) : Sasl(id) {}
+SaslClient::~SaslClient() {}
+void SaslClient::init(const std::string& mechanism, const std::string* response, const std::string* hostname)
+{
+ void* frame = startFrame();
+
+ void* token = encoder.startList32(&SASL_INIT);
+ encoder.writeSymbol(mechanism);
+ if (response) encoder.writeBinary(*response);
+ else encoder.writeNull();
+ if (hostname) encoder.writeString(*hostname);
+ else encoder.writeNull();
+ encoder.endList32(3, token);
+
+ endFrame(frame);
+ QPID_LOG_CAT(debug, protocol, id << " Sent SASL-INIT(" << mechanism << ", " << (response ? *response : "null") << ", " << (hostname ? *hostname : "null") << ")");
+}
+void SaslClient::response(const std::string* r)
+{
+ void* frame = startFrame();
+
+ void* token = encoder.startList32(&SASL_RESPONSE);
+ if (r) encoder.writeBinary(*r);
+ else encoder.writeNull();
+ encoder.endList32(1, token);
+
+ endFrame(frame);
+ QPID_LOG_CAT(debug, protocol, id << " Sent SASL-RESPONSE(" << (r ? *r : "null") << ")");
+}
+
+
+namespace {
+const std::string SPACE(" ");
+class SaslMechanismsReader : public Reader
+{
+ public:
+ SaslMechanismsReader(SaslClient& c) : client(c), expected(0) {}
+ void onSymbol(const CharSequence& mechanism, const Descriptor*)
+ {
+ if (expected) {
+ mechanisms << mechanism.str() << SPACE;
+ } else {
+ client.mechanisms(mechanism.str());
+ }
+ }
+ bool onStartArray(uint32_t count, const CharSequence&, const Constructor&, const Descriptor*)
+ {
+ expected = count;
+ return true;
+ }
+ void onEndArray(uint32_t, const Descriptor*)
+ {
+ client.mechanisms(mechanisms.str());
+ }
+ private:
+ SaslClient& client;
+ uint32_t expected;
+ std::stringstream mechanisms;
+};
+class SaslChallengeReader : public Reader
+{
+ public:
+ SaslChallengeReader(SaslClient& c) : client(c) {}
+ void onNull(const Descriptor*) { client.challenge(); }
+ void onBinary(const CharSequence& c, const Descriptor*) { client.challenge(c.str()); }
+ private:
+ SaslClient& client;
+};
+class SaslOutcomeReader : public Reader
+{
+ public:
+ SaslOutcomeReader(SaslClient& c, bool e) : client(c), expectExtraData(e) {}
+ void onUByte(uint8_t c, const Descriptor*)
+ {
+ if (expectExtraData) code = c;
+ else client.outcome(c);
+ }
+ void onBinary(const CharSequence& extra, const Descriptor*) { client.outcome(code, extra.str()); }
+ void onNull(const Descriptor*) { client.outcome(code); }
+ private:
+ SaslClient& client;
+ bool expectExtraData;
+ uint8_t code;
+};
+}
+
+bool SaslClient::onStartList(uint32_t count, const CharSequence& arguments, const Descriptor* descriptor)
+{
+ if (!descriptor) {
+ QPID_LOG(error, "Expected described type in SASL negotiation but got no descriptor");
+ } else if (descriptor->match(SASL_MECHANISMS_SYMBOL, SASL_MECHANISMS_CODE)) {
+ QPID_LOG(trace, "Reading SASL-MECHANISMS");
+ Decoder decoder(arguments.data, arguments.size);
+ if (count != 1) QPID_LOG(error, "Invalid SASL-MECHANISMS frame; exactly one field expected, got " << count);
+ SaslMechanismsReader reader(*this);
+ decoder.read(reader);
+ } else if (descriptor->match(SASL_CHALLENGE_SYMBOL, SASL_CHALLENGE_CODE)) {
+ QPID_LOG(trace, "Reading SASL-CHALLENGE");
+ Decoder decoder(arguments.data, arguments.size);
+ if (count != 1) QPID_LOG(error, "Invalid SASL-CHALLENGE frame; exactly one field expected, got " << count);
+ SaslChallengeReader reader(*this);
+ decoder.read(reader);
+ } else if (descriptor->match(SASL_OUTCOME_SYMBOL, SASL_OUTCOME_CODE)) {
+ QPID_LOG(trace, "Reading SASL-OUTCOME");
+ Decoder decoder(arguments.data, arguments.size);
+ if (count == 1) {
+ SaslOutcomeReader reader(*this, false);
+ decoder.read(reader);
+ } else if (count == 2) {
+ SaslOutcomeReader reader(*this, true);
+ decoder.read(reader);
+ } else {
+ QPID_LOG(error, "Invalid SASL-OUTCOME frame; got " << count << " fields");
+ }
+ } else {
+ QPID_LOG(error, "Unexpected descriptor in SASL negotiation: " << *descriptor);
+ }
+ return false;
+}
+
+
+}} // namespace qpid::amqp
diff --git a/cpp/src/qpid/amqp/SaslClient.h b/cpp/src/qpid/amqp/SaslClient.h
new file mode 100644
index 0000000000..9f3eefadc2
--- /dev/null
+++ b/cpp/src/qpid/amqp/SaslClient.h
@@ -0,0 +1,54 @@
+#ifndef QPID_AMQP_SASLCLIENT_H
+#define QPID_AMQP_SASLCLIENT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/Sasl.h"
+
+namespace qpid {
+namespace amqp {
+
+/**
+ * Utility for decoding and encoding SASL frames by the peer acting as
+ * the SASL client.
+ */
+class SaslClient : public Sasl
+{
+ public:
+ SaslClient(const std::string& id);
+ virtual ~SaslClient();
+ virtual void mechanisms(const std::string&) = 0;
+ virtual void challenge(const std::string&) = 0;
+ virtual void challenge() = 0; //null != empty string
+ virtual void outcome(uint8_t result, const std::string&) = 0;
+ virtual void outcome(uint8_t result) = 0;
+
+ void init(const std::string& mechanism, const std::string* response, const std::string* hostname);
+ void response(const std::string*);
+
+ private:
+ bool onStartList(uint32_t count, const CharSequence& arguments, const Descriptor* descriptor);
+
+};
+
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_SASLCLIENT_H*/
diff --git a/cpp/src/qpid/amqp/SaslServer.cpp b/cpp/src/qpid/amqp/SaslServer.cpp
new file mode 100644
index 0000000000..403730ad69
--- /dev/null
+++ b/cpp/src/qpid/amqp/SaslServer.cpp
@@ -0,0 +1,183 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/amqp/SaslServer.h"
+#include "qpid/amqp/Constructor.h"
+#include "qpid/amqp/Decoder.h"
+#include "qpid/amqp/Descriptor.h"
+#include "qpid/amqp/descriptors.h"
+#include "qpid/amqp/Encoder.h"
+#include "qpid/amqp/typecodes.h"
+#include "qpid/log/Statement.h"
+#include "qpid/StringUtils.h"
+#include <limits>
+#include <vector>
+
+using namespace qpid::amqp::sasl;
+using namespace qpid::amqp::typecodes;
+
+namespace qpid {
+namespace amqp {
+namespace {
+const std::string SPACE(" ");
+const std::string NULL_("NULL");
+}
+
+SaslServer::SaslServer(const std::string& id) : Sasl(id) {}
+SaslServer::~SaslServer() {}
+
+void SaslServer::mechanisms(const std::string& mechanisms)
+{
+ void* frameToken = startFrame();
+
+ std::vector<std::string> parts = split(mechanisms, SPACE);
+ void* listToken = encoder.startList32(&SASL_MECHANISMS);
+ if (parts.size() > 1) {
+ void* arrayToken = encoder.startArray8(Constructor(SYMBOL8));
+ for (std::vector<std::string>::const_iterator i = parts.begin();i != parts.end(); ++i) {
+ uint8_t size = i->size() > std::numeric_limits<uint8_t>::max() ? std::numeric_limits<uint8_t>::max() : i->size();
+ encoder.write(size);
+ encoder.writeBytes(i->data(), size);
+ }
+ encoder.endArray8(parts.size(), arrayToken);
+ } else {
+ encoder.writeSymbol(mechanisms);
+ }
+ encoder.endList32(1, listToken);
+
+ endFrame(frameToken);
+ QPID_LOG_CAT(debug, protocol, id << " Sent SASL-MECHANISMS(" << mechanisms << ") " << encoder.getPosition());
+}
+void SaslServer::challenge(const std::string* c)
+{
+ void* frameToken = startFrame();
+
+ void* listToken = encoder.startList32(&SASL_CHALLENGE);
+ if (c) encoder.writeBinary(*c);
+ else encoder.writeNull();
+ encoder.endList32(1, listToken);
+
+ endFrame(frameToken);
+ QPID_LOG_CAT(debug, protocol, id << " Sent SASL-CHALLENGE(" << (c ? *c : NULL_) << ") " << encoder.getPosition());
+}
+void SaslServer::completed(bool succeeded)
+{
+ void* frameToken = startFrame();
+
+ void* listToken = encoder.startList8(&SASL_OUTCOME);
+ encoder.writeUByte(succeeded ? 0 : 1);
+ encoder.endList8(1, listToken);
+
+ endFrame(frameToken);
+ QPID_LOG_CAT(debug, protocol, id << " Sent SASL-OUTCOME(" << (succeeded ? 0 : 1) << ") " << encoder.getPosition());
+}
+
+namespace {
+class SaslInitReader : public Reader
+{
+ public:
+ SaslInitReader(SaslServer& s, uint32_t e) : server(s), expected(e), hasResponse(false), index(0) {}
+ void onNull(const Descriptor*)
+ {
+ ++index;
+ if (index == 2) {
+ if (--expected == 0) {
+ server.init(mechanism, 0, 0);
+ }
+ } else if (index == 3) {
+ server.init(mechanism, hasResponse ? &response : 0, 0);
+ } else {
+ QPID_LOG(warning, "Unexpected sequence of fields for SASL-INIT: got null for field " << index);
+ }
+ }
+ void onBinary(const CharSequence& r, const Descriptor*)
+ {
+ if (++index != 2) QPID_LOG(warning, "Unexpected sequence of fields for SASL-INIT: got binary for field " << index);
+ response = r.str();
+ hasResponse = true;
+ if (--expected == 0) {
+ server.init(mechanism, &response, 0);
+ }
+ }
+ void onString(const CharSequence& h, const Descriptor*)
+ {
+ if (--expected || ++index != 3) {
+ QPID_LOG(warning, "Unexpected sequence of fields for SASL-INIT: got string for field " << index);
+ } else {
+ std::string hostname = h.str();
+ server.init(mechanism, hasResponse ? &response : 0, &hostname);
+ }
+ }
+ void onSymbol(const CharSequence& m, const Descriptor*)
+ {
+ if (++index != 1) QPID_LOG(warning, "Unexpected sequence of fields for SASL-INIT: got symbol for field " << index);
+ if (--expected) {
+ mechanism = m.str();
+ } else {
+ server.init(m.str(), 0, 0);
+ }
+ }
+ private:
+ SaslServer& server;
+ uint32_t expected;
+ std::string mechanism;
+ std::string response;
+ bool hasResponse;
+ uint32_t index;
+};
+
+class SaslResponseReader : public Reader
+{
+ public:
+ SaslResponseReader(SaslServer& s) : server(s) {}
+ void onNull(const Descriptor*) { server.response(0); }
+ void onBinary(const CharSequence& r, const Descriptor*)
+ {
+ std::string s = r.str();
+ server.response(&s);
+ }
+ private:
+ SaslServer& server;
+};
+}
+
+bool SaslServer::onStartList(uint32_t count, const CharSequence& arguments, const Descriptor* descriptor)
+{
+ if (!descriptor) {
+ QPID_LOG(error, "Expected described type in SASL negotiation but got no descriptor");
+ } else if (descriptor->match(SASL_INIT_SYMBOL, SASL_INIT_CODE)) {
+ QPID_LOG(trace, "Reading SASL-INIT");
+ Decoder decoder(arguments.data, arguments.size);
+ if (count < 1 || count > 3) QPID_LOG(error, "Invalid SASL-INIT frame; got " << count << " fields");
+ SaslInitReader reader(*this, count);
+ decoder.read(reader);
+ } else if (descriptor->match(SASL_RESPONSE_SYMBOL, SASL_RESPONSE_CODE)) {
+ QPID_LOG(trace, "Reading SASL-RESPONSE (" << std::string(arguments.data, arguments.size) << ") " << count << " elements");
+ Decoder decoder(arguments.data, arguments.size);
+ if (count != 1) QPID_LOG(error, "Invalid SASL-RESPONSE frame; exactly one field expected, got " << count);
+ SaslResponseReader reader(*this);
+ decoder.read(reader);
+ } else {
+ QPID_LOG(error, "Unexpected descriptor in SASL negotiation: " << *descriptor);
+ }
+ return false;
+}
+
+}} // namespace qpid::amqp
diff --git a/cpp/src/qpid/amqp/SaslServer.h b/cpp/src/qpid/amqp/SaslServer.h
new file mode 100644
index 0000000000..43b960454f
--- /dev/null
+++ b/cpp/src/qpid/amqp/SaslServer.h
@@ -0,0 +1,50 @@
+#ifndef QPID_AMQP_SASLSERVER_H
+#define QPID_AMQP_SASLSERVER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/Sasl.h"
+
+namespace qpid {
+namespace amqp {
+
+/**
+ * Utility for decoding and encoding SASL frames by the peer acting as
+ * the SASL server.
+ */
+class SaslServer : public Sasl
+{
+ public:
+ SaslServer(const std::string& id);
+ virtual ~SaslServer();
+ virtual void init(const std::string& mechanism, const std::string* response, const std::string* hostname) = 0;
+ virtual void response(const std::string*) = 0;
+
+ void mechanisms(const std::string& mechanisms);
+ void challenge(const std::string*);
+ void completed(bool succeeded);
+
+ private:
+ bool onStartList(uint32_t count, const CharSequence& arguments, const Descriptor* descriptor);
+};
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_SASLSERVER_H*/
diff --git a/cpp/src/qpid/amqp/descriptors.h b/cpp/src/qpid/amqp/descriptors.h
new file mode 100644
index 0000000000..19a8985433
--- /dev/null
+++ b/cpp/src/qpid/amqp/descriptors.h
@@ -0,0 +1,88 @@
+#ifndef QPID_AMQP_DESCRIPTORS_H
+#define QPID_AMQP_DESCRIPTORS_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "Descriptor.h"
+
+namespace qpid {
+namespace amqp {
+
+namespace message {
+const std::string HEADER_SYMBOL("amqp:header:list");
+const std::string PROPERTIES_SYMBOL("amqp:properties:list");
+const std::string DELIVERY_ANNOTATIONS_SYMBOL("amqp:delivery-annotations:map");
+const std::string MESSAGE_ANNOTATIONS_SYMBOL("amqp:message-annotations:map");
+const std::string APPLICATION_PROPERTIES_SYMBOL("amqp:application-properties:map");
+const std::string AMQP_SEQUENCE_SYMBOL("amqp:amqp-sequence:list");
+const std::string AMQP_VALUE_SYMBOL("amqp:amqp-sequence:*");
+const std::string DATA_SYMBOL("amqp:data:binary");
+const std::string FOOTER_SYMBOL("amqp:footer:map");
+
+const uint64_t HEADER_CODE(0x70);
+const uint64_t DELIVERY_ANNOTATIONS_CODE(0x71);
+const uint64_t MESSAGE_ANNOTATIONS_CODE(0x72);
+const uint64_t PROPERTIES_CODE(0x73);
+const uint64_t APPLICATION_PROPERTIES_CODE(0x74);
+const uint64_t DATA_CODE(0x75);
+const uint64_t AMQP_SEQUENCE_CODE(0x76);
+const uint64_t AMQP_VALUE_CODE(0x77);
+const uint64_t FOOTER_CODE(0x78);
+
+const Descriptor HEADER(HEADER_CODE);
+const Descriptor DELIVERY_ANNOTATIONS(DELIVERY_ANNOTATIONS_CODE);
+const Descriptor MESSAGE_ANNOTATIONS(MESSAGE_ANNOTATIONS_CODE);
+const Descriptor PROPERTIES(PROPERTIES_CODE);
+const Descriptor APPLICATION_PROPERTIES(APPLICATION_PROPERTIES_CODE);
+const Descriptor DATA(DATA_CODE);
+}
+
+namespace sasl {
+const std::string SASL_MECHANISMS_SYMBOL("amqp:sasl-mechanisms:list");
+const std::string SASL_INIT_SYMBOL("amqp:sasl-init:list");
+const std::string SASL_CHALLENGE_SYMBOL("amqp:sasl-challenge:list");
+const std::string SASL_RESPONSE_SYMBOL("amqp:sasl-response:list");
+const std::string SASL_OUTCOME_SYMBOL("amqp:sasl-outcome:list");
+
+const uint64_t SASL_MECHANISMS_CODE(0x40);
+const uint64_t SASL_INIT_CODE(0x41);
+const uint64_t SASL_CHALLENGE_CODE(0x42);
+const uint64_t SASL_RESPONSE_CODE(0x43);
+const uint64_t SASL_OUTCOME_CODE(0x44);
+
+const Descriptor SASL_MECHANISMS(SASL_MECHANISMS_CODE);
+const Descriptor SASL_INIT(SASL_INIT_CODE);
+const Descriptor SASL_CHALLENGE(SASL_CHALLENGE_CODE);
+const Descriptor SASL_RESPONSE(SASL_RESPONSE_CODE);
+const Descriptor SASL_OUTCOME(SASL_OUTCOME_CODE);
+}
+
+namespace filters {
+const std::string LEGACY_DIRECT_FILTER_SYMBOL("apache.org:legacy-amqp-direct-binding:string");
+const std::string LEGACY_TOPIC_FILTER_SYMBOL("apache.org:legacy-amqp-direct-binding:string");
+
+const uint64_t LEGACY_DIRECT_FILTER_CODE(0x0000468C00000000ULL);
+const uint64_t LEGACY_TOPIC_FILTER_CODE(0x0000468C00000001ULL);
+}
+
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_DESCRIPTORS_H*/
diff --git a/cpp/src/qpid/amqp/typecodes.h b/cpp/src/qpid/amqp/typecodes.h
new file mode 100644
index 0000000000..3c6bd17b97
--- /dev/null
+++ b/cpp/src/qpid/amqp/typecodes.h
@@ -0,0 +1,115 @@
+#ifndef QPID_AMQP_TYPECODES_H
+#define QPID_AMQP_TYPECODES_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+namespace qpid {
+namespace amqp {
+
+namespace typecodes
+{
+const uint8_t DESCRIPTOR(0x0);
+
+const uint8_t NULL_VALUE(0x40);
+
+const uint8_t BOOLEAN(0x56);
+const uint8_t BOOLEAN_TRUE(0x41);
+const uint8_t BOOLEAN_FALSE(0x42);
+
+const uint8_t UBYTE(0x50);
+const uint8_t USHORT(0x60);
+const uint8_t UINT(0x70);
+const uint8_t UINT_SMALL(0x52);
+const uint8_t UINT_ZERO(0x43);
+const uint8_t ULONG(0x80);
+const uint8_t ULONG_SMALL(0x53);
+const uint8_t ULONG_ZERO(0x44);
+
+const uint8_t BYTE(0x51);
+const uint8_t SHORT(0x61);
+const uint8_t INT(0x71);
+const uint8_t INT_SMALL(0x54);
+const uint8_t LONG(0x81);
+const uint8_t LONG_SMALL(0x55);
+
+const uint8_t FLOAT(0x72);
+const uint8_t DOUBLE(0x82);
+
+const uint8_t DECIMAL32(0x74);
+const uint8_t DECIMAL64(0x84);
+const uint8_t DECIMAL128(0x94);
+
+const uint8_t CHAR_UTF32(0x73);
+const uint8_t TIMESTAMP(0x83);
+const uint8_t UUID(0x98);
+
+const uint8_t BINARY8(0xa0);
+const uint8_t BINARY32(0xb0);
+const uint8_t STRING8(0xa1);
+const uint8_t STRING32(0xb1);
+const uint8_t SYMBOL8(0xa3);
+const uint8_t SYMBOL32(0xb3);
+
+typedef std::pair<uint8_t, uint8_t> CodePair;
+const CodePair SYMBOL(SYMBOL8, SYMBOL32);
+const CodePair STRING(STRING8, STRING32);
+const CodePair BINARY(BINARY8, BINARY32);
+
+const uint8_t LIST0(0x45);
+const uint8_t LIST8(0xc0);
+const uint8_t LIST32(0xd0);
+const uint8_t MAP8(0xc1);
+const uint8_t MAP32(0xd1);
+const uint8_t ARRAY8(0xe0);
+const uint8_t ARRAY32(0xf0);
+
+
+const std::string NULL_NAME("null");
+const std::string BOOLEAN_NAME("name");
+
+const std::string UBYTE_NAME("ubyte");
+const std::string USHORT_NAME("ushort");
+const std::string UINT_NAME("uint");
+const std::string ULONG_NAME("ulong");
+
+const std::string BYTE_NAME("byte");
+const std::string SHORT_NAME("short");
+const std::string INT_NAME("int");
+const std::string LONG_NAME("long");
+
+const std::string FLOAT_NAME("float");
+const std::string DOUBLE_NAME("double");
+
+const std::string TIMESTAMP_NAME("timestamp");
+const std::string UUID_NAME("uuid");
+
+const std::string BINARY_NAME("binary");
+const std::string STRING_NAME("string");
+const std::string SYMBOL_NAME("symbol");
+
+const std::string LIST_NAME("list");
+const std::string MAP_NAME("map");
+const std::string ARRAY_NAME("array");
+}
+
+}} // namespace qpid::amqp
+
+#endif /*!QPID_AMQP_TYPECODES_H*/
diff --git a/cpp/src/qpid/amqp_0_10/Connection.cpp b/cpp/src/qpid/amqp_0_10/Connection.cpp
index ceeaadf70c..5312c40f2a 100644
--- a/cpp/src/qpid/amqp_0_10/Connection.cpp
+++ b/cpp/src/qpid/amqp_0_10/Connection.cpp
@@ -74,14 +74,14 @@ bool Connection::isClosed() const {
return pushClosed && popClosed;
}
-size_t Connection::encode(const char* buffer, size_t size) {
+size_t Connection::encode(char* buffer, size_t size) {
{ // Swap frameQueue data into workQueue to avoid holding lock while we encode.
Mutex::ScopedLock l(frameQueueLock);
if (popClosed) return 0; // Can't pop any more frames.
assert(workQueue.empty());
workQueue.swap(frameQueue);
}
- framing::Buffer out(const_cast<char*>(buffer), size);
+ framing::Buffer out(buffer, size);
if (!isClient && !initialized) {
framing::ProtocolInitiation pi(getVersion());
pi.encode(out);
@@ -119,7 +119,6 @@ size_t Connection::encode(const char* buffer, size_t size) {
void Connection::abort() { output.abort(); }
void Connection::activateOutput() { output.activateOutput(); }
-void Connection::giveReadCredit(int32_t credit) { output.giveReadCredit(credit); }
void Connection::close() {
// No more frames can be pushed onto the queue.
@@ -146,10 +145,6 @@ framing::ProtocolVersion Connection::getVersion() const {
return version;
}
-void Connection::setVersion(const framing::ProtocolVersion& v) {
- version = v;
-}
-
size_t Connection::getBuffered() const {
Mutex::ScopedLock l(frameQueueLock);
return buffered;
diff --git a/cpp/src/qpid/amqp_0_10/Connection.h b/cpp/src/qpid/amqp_0_10/Connection.h
index 995d824796..92ae5a3dd3 100644
--- a/cpp/src/qpid/amqp_0_10/Connection.h
+++ b/cpp/src/qpid/amqp_0_10/Connection.h
@@ -61,21 +61,16 @@ class Connection : public sys::ConnectionCodec,
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);
+ size_t encode(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/asyncStore/OperationQueue.cpp b/cpp/src/qpid/asyncStore/OperationQueue.cpp
index dff5387827..9dea055223 100644
--- a/cpp/src/qpid/asyncStore/OperationQueue.cpp
+++ b/cpp/src/qpid/asyncStore/OperationQueue.cpp
@@ -51,7 +51,7 @@ OperationQueue::handle(const OperationQueue::OpQueue::Batch& e) {
try {
for (OpQueue::Batch::const_iterator i = e.begin(); i != e.end(); ++i) {
// DEBUG: kpvdr
-std::cout << "#### OperationQueue::handle(): op=" << (*i)->getOpStr() << std::endl << std::flush;
+//std::cout << "#### OperationQueue::handle(): op=" << (*i)->getOpStr() << std::endl << std::flush;
(*i)->executeOp(); // Do store work here
}
} catch (const std::exception& e) {
diff --git a/cpp/src/qpid/asyncStore/Plugin.cpp b/cpp/src/qpid/asyncStore/Plugin.cpp
index 7395fc9b87..666db2120f 100644
--- a/cpp/src/qpid/asyncStore/Plugin.cpp
+++ b/cpp/src/qpid/asyncStore/Plugin.cpp
@@ -54,13 +54,8 @@ Plugin::initialize(Target& target) {
Broker* broker = dynamic_cast<Broker*>(&target);
if (!broker || !m_store) return;
- // Not done in earlyInitialize as the Broker::isInCluster test won't work there.
- if (broker->isInCluster()) {
- QPID_LOG(info, "asyncStore: Part of cluster: Disabling management instrumentation");
- } else {
- QPID_LOG(info, "asyncStore: Enabling management instrumentation");
- m_store->initManagement(broker);
- }
+ QPID_LOG(info, "asyncStore: Enabling management instrumentation");
+ m_store->initManagement(broker);
}
void
diff --git a/cpp/src/qpid/broker/AclModule.h b/cpp/src/qpid/broker/AclModule.h
index 4caf8ed3ce..c01697ace9 100644
--- a/cpp/src/qpid/broker/AclModule.h
+++ b/cpp/src/qpid/broker/AclModule.h
@@ -79,6 +79,8 @@ namespace acl {
PROP_POLICYTYPE,
PROP_MAXQUEUESIZE,
PROP_MAXQUEUECOUNT,
+ PROP_MAXFILESIZE,
+ PROP_MAXFILECOUNT,
PROPERTYSIZE // PROPERTYSIZE must be last in list
};
@@ -102,7 +104,11 @@ namespace acl {
SPECPROP_MAXQUEUESIZELOWERLIMIT,
SPECPROP_MAXQUEUESIZEUPPERLIMIT,
SPECPROP_MAXQUEUECOUNTLOWERLIMIT,
- SPECPROP_MAXQUEUECOUNTUPPERLIMIT };
+ SPECPROP_MAXQUEUECOUNTUPPERLIMIT,
+ SPECPROP_MAXFILESIZELOWERLIMIT,
+ SPECPROP_MAXFILESIZEUPPERLIMIT,
+ SPECPROP_MAXFILECOUNTLOWERLIMIT,
+ SPECPROP_MAXFILECOUNTUPPERLIMIT };
// AclResult shared between ACL spec and ACL authorise interface
enum AclResult {
@@ -126,6 +132,8 @@ namespace broker {
// doTransferAcl pervents time consuming ACL calls on a per-message basis.
virtual bool doTransferAcl()=0;
+ virtual uint16_t getMaxConnectTotal()=0;
+
virtual bool authorise(
const std::string& id,
const acl::Action& action,
@@ -147,9 +155,10 @@ namespace broker {
*/
virtual bool approveConnection (const Connection& connection)=0;
- /** Change connection's counted userId
+ /** Approve queue creation by counting per-user.
*/
- virtual void setUserId(const Connection& connection, const std::string& username)=0;
+ virtual bool approveCreateQueue(const std::string& userId, const std::string& queueName)=0;
+ virtual void recordDestroyQueue(const std::string& queueName)=0;
virtual ~AclModule() {};
};
@@ -222,6 +231,8 @@ namespace acl {
if (str.compare("policytype") == 0) return PROP_POLICYTYPE;
if (str.compare("maxqueuesize") == 0) return PROP_MAXQUEUESIZE;
if (str.compare("maxqueuecount") == 0) return PROP_MAXQUEUECOUNT;
+ if (str.compare("maxfilesize") == 0) return PROP_MAXFILESIZE;
+ if (str.compare("maxfilecount") == 0) return PROP_MAXFILECOUNT;
throw qpid::Exception(str);
}
static inline std::string getPropertyStr(const Property p) {
@@ -240,6 +251,8 @@ namespace acl {
case PROP_POLICYTYPE: return "policytype";
case PROP_MAXQUEUESIZE: return "maxqueuesize";
case PROP_MAXQUEUECOUNT: return "maxqueuecount";
+ case PROP_MAXFILESIZE: return "maxfilesize";
+ case PROP_MAXFILECOUNT: return "maxfilecount";
default: assert(false); // should never get here
}
return "";
@@ -261,6 +274,10 @@ namespace acl {
if (str.compare("queuemaxsizeupperlimit") == 0) return SPECPROP_MAXQUEUESIZEUPPERLIMIT;
if (str.compare("queuemaxcountlowerlimit") == 0) return SPECPROP_MAXQUEUECOUNTLOWERLIMIT;
if (str.compare("queuemaxcountupperlimit") == 0) return SPECPROP_MAXQUEUECOUNTUPPERLIMIT;
+ if (str.compare("filemaxsizelowerlimit") == 0) return SPECPROP_MAXFILESIZELOWERLIMIT;
+ if (str.compare("filemaxsizeupperlimit") == 0) return SPECPROP_MAXFILESIZEUPPERLIMIT;
+ if (str.compare("filemaxcountlowerlimit") == 0) return SPECPROP_MAXFILECOUNTLOWERLIMIT;
+ if (str.compare("filemaxcountupperlimit") == 0) return SPECPROP_MAXFILECOUNTUPPERLIMIT;
// Allow old names in ACL file as aliases for newly-named properties
if (str.compare("maxqueuesize") == 0) return SPECPROP_MAXQUEUESIZEUPPERLIMIT;
if (str.compare("maxqueuecount") == 0) return SPECPROP_MAXQUEUECOUNTUPPERLIMIT;
@@ -284,6 +301,10 @@ namespace acl {
case SPECPROP_MAXQUEUESIZEUPPERLIMIT: return "queuemaxsizeupperlimit";
case SPECPROP_MAXQUEUECOUNTLOWERLIMIT: return "queuemaxcountlowerlimit";
case SPECPROP_MAXQUEUECOUNTUPPERLIMIT: return "queuemaxcountupperlimit";
+ case SPECPROP_MAXFILESIZELOWERLIMIT: return "filemaxsizelowerlimit";
+ case SPECPROP_MAXFILESIZEUPPERLIMIT: return "filemaxsizeupperlimit";
+ case SPECPROP_MAXFILECOUNTLOWERLIMIT: return "filemaxcountlowerlimit";
+ case SPECPROP_MAXFILECOUNTUPPERLIMIT: return "filemaxcountupperlimit";
default: assert(false); // should never get here
}
return "";
diff --git a/cpp/src/qpid/broker/Bridge.cpp b/cpp/src/qpid/broker/Bridge.cpp
index 4604ac643f..75aba70ae8 100644
--- a/cpp/src/qpid/broker/Bridge.cpp
+++ b/cpp/src/qpid/broker/Bridge.cpp
@@ -19,6 +19,8 @@
*
*/
#include "qpid/broker/Bridge.h"
+
+#include "qpid/broker/Broker.h"
#include "qpid/broker/FedOps.h"
#include "qpid/broker/ConnectionState.h"
#include "qpid/broker/Connection.h"
@@ -49,6 +51,11 @@ using qpid::management::ManagementAgent;
using std::string;
namespace _qmf = qmf::org::apache::qpid::broker;
+namespace {
+const std::string QPID_REPLICATE("qpid.replicate");
+const std::string NONE("none");
+}
+
namespace qpid {
namespace broker {
@@ -60,7 +67,7 @@ void Bridge::PushHandler::handle(framing::AMQFrame& frame)
Bridge::Bridge(const std::string& _name, Link* _link, framing::ChannelId _id,
CancellationListener l, const _qmf::ArgsLinkBridge& _args,
InitializeCallback init, const std::string& _queueName, const string& ae) :
- link(_link), channel(_id), args(_args), mgmtObject(0),
+ link(_link), channel(_id), args(_args),
listener(l), name(_name),
queueName(_queueName.empty() ? "qpid.bridge_queue_" + name + "_" + link->getBroker()->getFederationTag()
: _queueName),
@@ -71,10 +78,10 @@ Bridge::Bridge(const std::string& _name, Link* _link, framing::ChannelId _id,
{
ManagementAgent* agent = link->getBroker()->getManagementAgent();
if (agent != 0) {
- mgmtObject = new _qmf::Bridge
+ mgmtObject = _qmf::Bridge::shared_ptr(new _qmf::Bridge
(agent, this, link, name, 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);
+ args.i_tag, args.i_excludes, args.i_dynamic, args.i_sync));
mgmtObject->set_channelId(channel);
agent->addObject(mgmtObject);
}
@@ -121,28 +128,30 @@ void Bridge::create(Connection& c, AsyncStore* const store)
peer->getMessage().flow(args.i_dest, 1, 0xFFFFFFFF);
QPID_LOG(debug, "Activated bridge " << name << " for route from queue " << args.i_src << " to " << args.i_dest);
} else {
- FieldTable queueSettings;
-
- if (args.i_tag.size()) {
- queueSettings.setString("qpid.trace.id", args.i_tag);
- } else {
- const string& peerTag = c.getFederationPeerTag();
- if (peerTag.size())
- queueSettings.setString("qpid.trace.id", peerTag);
+ if (!useExistingQueue) {
+ FieldTable queueSettings;
+
+ if (args.i_tag.size()) {
+ queueSettings.setString("qpid.trace.id", args.i_tag);
+ } else {
+ const string& peerTag = c.getFederationPeerTag();
+ if (peerTag.size())
+ queueSettings.setString("qpid.trace.id", peerTag);
+ }
+
+ if (args.i_excludes.size()) {
+ queueSettings.setString("qpid.trace.exclude", args.i_excludes);
+ } else {
+ const string& localTag = link->getBroker()->getFederationTag();
+ if (localTag.size())
+ queueSettings.setString("qpid.trace.exclude", localTag);
+ }
+
+ bool durable = false;//should this be an arg, or would we use srcIsQueue for durable queues?
+ bool exclusive = true; // only exclusive if the queue is owned by the bridge
+ bool autoDelete = exclusive && !durable;//auto delete transient queues?
+ peer->getQueue().declare(queueName, altEx, false, durable, exclusive, autoDelete, queueSettings);
}
-
- 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 exclusive = !useExistingQueue; // only exclusive if the queue is owned by the bridge
- bool autoDelete = exclusive && !durable;//auto delete transient queues?
- peer->getQueue().declare(queueName, altEx, false, durable, exclusive, autoDelete, queueSettings);
if (!args.i_dynamic)
peer->getExchange().bind(queueName, args.i_src, args.i_key, FieldTable());
peer->getMessage().subscribe(queueName, args.i_dest, (useExistingQueue && args.i_sync) ? 0 : 1, 0, false, "", 0, options);
@@ -296,9 +305,9 @@ uint32_t Bridge::encodedSize() const
+ 2; // sync
}
-management::ManagementObject* Bridge::GetManagementObject (void) const
+management::ManagementObject::shared_ptr Bridge::GetManagementObject(void) const
{
- return (management::ManagementObject*) mgmtObject;
+ return mgmtObject;
}
management::Manageable::status_t Bridge::ManagementMethod(uint32_t methodId,
@@ -331,6 +340,7 @@ void Bridge::propagateBinding(const string& key, const string& tagList,
}
string newTagList(tagList + string(tagList.empty() ? "" : ",") + localTag);
+ bindArgs.setString(QPID_REPLICATE, NONE);
bindArgs.setString(qpidFedOp, op);
bindArgs.setString(qpidFedTags, newTagList);
if (origin.empty())
@@ -366,8 +376,7 @@ void Bridge::ioThreadPropagateBinding(const string& queue, const string& exchang
if (resetProxy()) {
peer->getExchange().bind(queue, exchange, key, args);
} else {
- QPID_LOG(error, "Cannot propagate binding for dynamic bridge as session has been detached, deleting dynamic bridge");
- close();
+ // link's periodic maintenance visit will attempt to recover
}
}
diff --git a/cpp/src/qpid/broker/Bridge.h b/cpp/src/qpid/broker/Bridge.h
index 04ac585d80..893febdf92 100644
--- a/cpp/src/qpid/broker/Bridge.h
+++ b/cpp/src/qpid/broker/Bridge.h
@@ -65,6 +65,7 @@ class Bridge : public PersistableConfig,
QPID_BROKER_EXTERN void close();
bool isDurable() { return args.i_durable; }
+ framing::ChannelId getChannel() const { return channel; }
Link *getLink() const { return link; }
const std::string getSrc() const { return args.i_src; }
const std::string getDest() const { return args.i_dest; }
@@ -72,7 +73,7 @@ class Bridge : public PersistableConfig,
bool isDetached() const { return detached; }
- management::ManagementObject* GetManagementObject() const;
+ management::ManagementObject::shared_ptr GetManagementObject() const;
management::Manageable::status_t ManagementMethod(uint32_t methodId,
management::Args& args,
std::string& text);
@@ -128,7 +129,7 @@ class Bridge : public PersistableConfig,
Link* const link;
const framing::ChannelId channel;
qmf::org::apache::qpid::broker::ArgsLinkBridge args;
- qmf::org::apache::qpid::broker::Bridge* mgmtObject;
+ qmf::org::apache::qpid::broker::Bridge::shared_ptr mgmtObject;
CancellationListener listener;
std::string name;
std::string queueName;
diff --git a/cpp/src/qpid/broker/Broker.cpp b/cpp/src/qpid/broker/Broker.cpp
index 08606516d4..49e80689fc 100644
--- a/cpp/src/qpid/broker/Broker.cpp
+++ b/cpp/src/qpid/broker/Broker.cpp
@@ -20,6 +20,8 @@
*/
#include "qpid/broker/Broker.h"
+
+#include "qpid/broker/AclModule.h"
#include "qpid/broker/AsyncResultHandle.h"
#include "qpid/broker/ConfigAsyncContext.h"
#include "qpid/broker/ConfigHandle.h"
@@ -27,9 +29,7 @@
#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/RecoveryAsyncContext.h"
+#include "qpid/broker/NameGenerator.h"
#include "qpid/broker/RecoveryManagerImpl.h"
#include "qpid/broker/SaslAuthenticator.h"
#include "qpid/broker/SecureConnectionFactory.h"
@@ -43,6 +43,7 @@
#include "qpid/broker/MessageGroupManager.h"
#include "qmf/org/apache/qpid/broker/Package.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerConnect.h"
#include "qmf/org/apache/qpid/broker/ArgsBrokerCreate.h"
#include "qmf/org/apache/qpid/broker/ArgsBrokerDelete.h"
#include "qmf/org/apache/qpid/broker/ArgsBrokerQuery.h"
@@ -50,12 +51,12 @@
#include "qmf/org/apache/qpid/broker/ArgsBrokerGetLogLevel.h"
#include "qmf/org/apache/qpid/broker/ArgsBrokerQueueMoveMessages.h"
#include "qmf/org/apache/qpid/broker/ArgsBrokerSetLogLevel.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerGetLogHiresTimestamp.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerSetLogHiresTimestamp.h"
#include "qmf/org/apache/qpid/broker/ArgsBrokerSetTimestampConfig.h"
#include "qmf/org/apache/qpid/broker/ArgsBrokerGetTimestampConfig.h"
#include "qmf/org/apache/qpid/broker/EventExchangeDeclare.h"
#include "qmf/org/apache/qpid/broker/EventExchangeDelete.h"
-#include "qmf/org/apache/qpid/broker/EventQueueDeclare.h"
-#include "qmf/org/apache/qpid/broker/EventQueueDelete.h"
#include "qmf/org/apache/qpid/broker/EventBind.h"
#include "qmf/org/apache/qpid/broker/EventUnbind.h"
#include "qpid/amqp_0_10/Codecs.h"
@@ -75,6 +76,7 @@
#include "qpid/sys/Dispatcher.h"
#include "qpid/sys/Thread.h"
#include "qpid/sys/Time.h"
+#include "qpid/sys/Timer.h"
#include "qpid/sys/ConnectionInputHandler.h"
#include "qpid/sys/ConnectionInputHandlerFactory.h"
#include "qpid/sys/TimeoutHandler.h"
@@ -110,6 +112,17 @@ namespace _qmf = qmf::org::apache::qpid::broker;
namespace qpid {
namespace broker {
+const std::string empty;
+const std::string amq_direct("amq.direct");
+const std::string amq_topic("amq.topic");
+const std::string amq_fanout("amq.fanout");
+const std::string amq_match("amq.match");
+const std::string qpid_management("qpid.management");
+const std::string knownHostsNone("none");
+
+// static
+Broker* Broker::thisBroker;
+
Broker::Options::Options(const std::string& name) :
qpid::Options(name),
noDataDir(0),
@@ -127,6 +140,7 @@ Broker::Options::Options(const std::string& name) :
queueLimit(100*1048576/*100M default limit*/),
tcpNoDelay(false),
requireEncrypted(false),
+ knownHosts(knownHostsNone),
qmf2Support(true),
qmf1Support(true),
queueFlowStopRatio(80),
@@ -136,7 +150,7 @@ Broker::Options::Options(const std::string& name) :
timestampRcvMsgs(false), // set the 0.10 timestamp delivery property
linkMaintenanceInterval(2),
linkHeartbeatInterval(120),
- maxNegotiateTime(2000) // 2s
+ maxNegotiateTime(10000) // 10s
{
int c = sys::SystemInfo::concurrency();
workerThreads=c+1;
@@ -152,6 +166,7 @@ Broker::Options::Options(const std::string& name) :
("data-dir", optValue(dataDir,"DIR"), "Directory to contain persistent data generated by the broker")
("no-data-dir", optValue(noDataDir), "Don't use a data directory. No persistent configuration will be loaded or stored")
("port,p", optValue(port,"PORT"), "Tells the broker to listen on PORT")
+ ("interface", optValue(listenInterfaces, "<interface name>|<interface address>"), "Which network interfaces to use to listen for incoming connections")
("worker-threads", optValue(workerThreads, "N"), "Sets the broker thread pool size")
("connection-backlog", optValue(connectionBacklog, "N"), "Sets the connection backlog limit for the server socket")
("mgmt-enable,m", optValue(enableMgmt,"yes|no"), "Enable Management")
@@ -175,23 +190,27 @@ Broker::Options::Options(const std::string& name) :
("default-event-threshold-ratio", optValue(queueThresholdEventRatio, "%age of limit"), "The ratio of any specified queue limit at which an event will be raised")
("default-message-group", optValue(defaultMsgGroup, "GROUP-IDENTIFER"), "Group identifier to assign to messages delivered to a message group queue that do not contain an identifier.")
("enable-timestamp", optValue(timestampRcvMsgs, "yes|no"), "Add current time to each received message.")
- ("link-maintenace-interval", optValue(linkMaintenanceInterval, "SECONDS"))
- ("link-heartbeat-interval", optValue(linkHeartbeatInterval, "SECONDS"))
- ("max-negotiate-time", optValue(maxNegotiateTime, "MilliSeconds"), "Maximum time a connection can take to send the initial protocol negotiation")
+ ("link-maintenance-interval", optValue(linkMaintenanceInterval, "SECONDS"),
+ "Interval to check link health and re-connect if need be")
+ ("link-heartbeat-interval", optValue(linkHeartbeatInterval, "SECONDS"),
+ "Heartbeat interval for a federation link")
+ ("max-negotiate-time", optValue(maxNegotiateTime, "MILLISECONDS"), "Maximum time a connection can take to send the initial protocol negotiation")
("federation-tag", optValue(fedTag, "NAME"), "Override the federation tag")
;
}
-const std::string empty;
-const std::string amq_direct("amq.direct");
-const std::string amq_topic("amq.topic");
-const std::string amq_fanout("amq.fanout");
-const std::string amq_match("amq.match");
-const std::string qpid_management("qpid.management");
-const std::string knownHostsNone("none");
+namespace {
+// Arguments to declare a non-replicated exchange.
+framing::FieldTable noReplicateArgs() {
+ framing::FieldTable args;
+ args.setString("qpid.replicate", "none");
+ return args;
+}
+}
Broker::Broker(const Broker::Options& conf) :
poller(new Poller),
+ timer(new qpid::sys::Timer),
config(conf),
managementAgent(conf.enableMgmt ? new ManagementAgent(conf.qmf1Support,
conf.qmf2Support)
@@ -205,21 +224,18 @@ Broker::Broker(const Broker::Options& conf) :
exchanges(this),
links(this),
factory(new SecureConnectionFactory(*this)),
- dtxManager(timer),
+ dtxManager(*timer.get()),
sessionManager(
qpid::SessionState::Configuration(
conf.replayFlushLimit*1024, // convert kb to bytes.
conf.replayHardLimit*1024),
*this),
- mgmtObject(0),
- queueCleaner(queues, &timer),
- recovery(true),
- inCluster(false),
- clusterUpdatee(false),
+ queueCleaner(queues, timer.get()),
+ recoveryInProgress(false),
expiryPolicy(new ExpiryPolicy),
- getKnownBrokers(boost::bind(&Broker::getKnownBrokersImpl, this)),
- deferDelivery(boost::bind(&Broker::deferDeliveryImpl, this, _1, _2))
+ getKnownBrokers(boost::bind(&Broker::getKnownBrokersImpl, this))
{
+ thisBroker = this;
try {
if (conf.enableMgmt) {
QPID_LOG(info, "Management enabled");
@@ -231,7 +247,7 @@ Broker::Broker(const Broker::Options& conf) :
System* system = new System (dataDir.isEnabled() ? dataDir.getPath() : string(), this);
systemObject = System::shared_ptr(system);
- mgmtObject = new _qmf::Broker(managementAgent.get(), this, system, "amqp-broker");
+ mgmtObject = _qmf::Broker::shared_ptr(new _qmf::Broker(managementAgent.get(), this, system, "amqp-broker"));
mgmtObject->set_systemRef(system->GetManagementObject()->getObjectId());
mgmtObject->set_port(conf.port);
mgmtObject->set_workerThreads(conf.workerThreads);
@@ -281,28 +297,26 @@ Broker::Broker(const Broker::Options& conf) :
// if (NullMessageStore::isNullStore(store.get()))
// setStore();
- exchanges.declare(empty, DirectExchange::typeName); // Default exchange.
+ framing::FieldTable args;
+
+ // Default exchnge is not replicated.
+ exchanges.declare(empty, DirectExchange::typeName, false, noReplicateArgs());
// if (store.get() != 0) {
if (asyncStore.get() != 0) {
- // The cluster plug-in will setRecovery(false) on all but the first
- // broker to join a cluster.
- if (getRecovery()) {
QPID_LOG(info, "Store recovery starting")
- RecoveryManagerImpl recoverer(queues, exchanges, links, dtxManager);
+ RecoveryManagerImpl recoverer(queues, exchanges, links, dtxManager, protocolRegistry);
RecoveryHandle rh = asyncStore->createRecoveryHandle();
- boost::shared_ptr<RecoveryAsyncContext> rac(new RecoveryAsyncContext(recoverer, &recoverComplete, &asyncResultQueue));
+ boost::shared_ptr<RecoveryAsyncContext> rac(new RecoveryAsyncContext(recoverer, &recoverCompleteCb, &asyncResultQueue));
asyncStore->submitRecover(rh, rac);
-// 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
- asyncStore->initialize(true, true);
- }
+// RecoveryManagerImpl recoverer(
+// queues, exchanges, links, dtxManager, protocolRegistry);
+// recoveryInProgress = true;
+// store->recover(recoverer);
+// recoveryInProgress = false;
}
// debug
- else QPID_LOG(info, ">>>> No store!!!!")
+// else QPID_LOG(info, ">>>> No store!!!!")
//ensure standard exchanges exist (done after recovery from store)
declareStandardExchange(amq_direct, DirectExchange::typeName);
@@ -311,7 +325,7 @@ Broker::Broker(const Broker::Options& conf) :
declareStandardExchange(amq_match, HeadersExchange::typeName);
if(conf.enableMgmt) {
- exchanges.declare(qpid_management, ManagementTopicExchange::typeName);
+ exchanges.declare(qpid_management, ManagementTopicExchange::typeName, false, noReplicateArgs());
Exchange::shared_ptr mExchange = exchanges.get(qpid_management);
Exchange::shared_ptr dExchange = exchanges.get(amq_direct);
managementAgent->setExchange(mExchange, dExchange);
@@ -320,8 +334,10 @@ Broker::Broker(const Broker::Options& conf) :
std::string qmfTopic("qmf.default.topic");
std::string qmfDirect("qmf.default.direct");
- std::pair<Exchange::shared_ptr, bool> topicPair(exchanges.declare(qmfTopic, ManagementTopicExchange::typeName));
- std::pair<Exchange::shared_ptr, bool> directPair(exchanges.declare(qmfDirect, ManagementDirectExchange::typeName));
+ std::pair<Exchange::shared_ptr, bool> topicPair(
+ exchanges.declare(qmfTopic, ManagementTopicExchange::typeName, false, noReplicateArgs()));
+ std::pair<Exchange::shared_ptr, bool> directPair(
+ exchanges.declare(qmfDirect, ManagementDirectExchange::typeName, false, noReplicateArgs()));
boost::dynamic_pointer_cast<ManagementDirectExchange>(directPair.first)->setManagmentAgent(managementAgent.get(), 2);
boost::dynamic_pointer_cast<ManagementTopicExchange>(topicPair.first)->setManagmentAgent(managementAgent.get(), 2);
@@ -349,19 +365,17 @@ Broker::Broker(const Broker::Options& conf) :
// Initialize plugins
Plugin::initializeAll(*this);
- if (managementAgent.get()) managementAgent->pluginsInitialized();
+ if(conf.enableMgmt) {
+ if (getAcl()) {
+ mgmtObject->set_maxConns(getAcl()->getMaxConnectTotal());
+ }
+ }
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) {
+ if (!conf.knownHosts.empty() && conf.knownHosts != knownHostsNone) {
knownBrokers.push_back(Url(conf.knownHosts));
}
@@ -375,11 +389,14 @@ void Broker::declareStandardExchange(const std::string& name, const std::string&
{
// bool storeEnabled = store.get() != NULL;
bool storeEnabled = asyncStore.get() != NULL;
- std::pair<Exchange::shared_ptr, bool> status = exchanges.declare(name, type, storeEnabled);
+ framing::FieldTable args;
+ // Standard exchanges are not replicated.
+ std::pair<Exchange::shared_ptr, bool> status =
+ exchanges.declare(name, type, storeEnabled, noReplicateArgs());
if (status.second && storeEnabled) {
// store->create(*status.first, framing::FieldTable ());
ConfigHandle ch = asyncStore->createConfigHandle();
- boost::shared_ptr<BrokerAsyncContext> bc(new ConfigAsyncContext(&configureComplete, &asyncResultQueue));
+ boost::shared_ptr<BrokerAsyncContext> bc(new ConfigAsyncContext(&configureCompleteCb, &asyncResultQueue));
asyncStore->submitCreate(ch, status.first.get(), bc);
}
}
@@ -420,11 +437,20 @@ void Broker::setStore () {
}
// static
+void Broker::recoverCompleteCb(const AsyncResultHandle* const arh) {
+ thisBroker->recoverComplete(arh);
+}
+
void Broker::recoverComplete(const AsyncResultHandle* const arh) {
+ recoveryInProgress = false;
std::cout << "@@@@ Broker: Recover complete: err=" << arh->getErrNo() << "; msg=\"" << arh->getErrMsg() << "\"" << std::endl;
}
// static
+void Broker::configureCompleteCb(const AsyncResultHandle* const arh) {
+ thisBroker->configureComplete(arh);
+}
+
void Broker::configureComplete(const AsyncResultHandle* const arh) {
std::cout << "@@@@ Broker: Configure complete: err=" << arh->getErrNo() << "; msg=\"" << arh->getErrMsg() << "\"" << std::endl;
}
@@ -464,13 +490,13 @@ Broker::~Broker() {
finalize(); // Finalize any plugins.
if (config.auth)
SaslAuthenticator::fini();
- timer.stop();
+ timer->stop();
QPID_LOG(notice, "Shut down");
}
-ManagementObject* Broker::GetManagementObject(void) const
+ManagementObject::shared_ptr Broker::GetManagementObject(void) const
{
- return (ManagementObject*) mgmtObject;
+ return mgmtObject;
}
Manageable* Broker::GetVhostObject(void) const
@@ -536,7 +562,7 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId,
_qmf::ArgsBrokerQueueMoveMessages& moveArgs=
dynamic_cast<_qmf::ArgsBrokerQueueMoveMessages&>(args);
QPID_LOG (debug, "Broker::queueMoveMessages()");
- if (queueMoveMessages(moveArgs.i_srcQueue, moveArgs.i_destQueue, moveArgs.i_qty, moveArgs.i_filter))
+ if (queueMoveMessages(moveArgs.i_srcQueue, moveArgs.i_destQueue, moveArgs.i_qty, moveArgs.i_filter) >= 0)
status = Manageable::STATUS_OK;
else
return Manageable::STATUS_PARAMETER_INVALID;
@@ -584,7 +610,22 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId,
status = setTimestampConfig(a.i_receive, getManagementExecutionContext());
break;
}
- default:
+
+ case _qmf::Broker::METHOD_GETLOGHIRESTIMESTAMP:
+ {
+ dynamic_cast<_qmf::ArgsBrokerGetLogHiresTimestamp&>(args).o_logHires = getLogHiresTimestamp();
+ QPID_LOG (debug, "Broker::getLogHiresTimestamp()");
+ status = Manageable::STATUS_OK;
+ break;
+ }
+ case _qmf::Broker::METHOD_SETLOGHIRESTIMESTAMP:
+ {
+ setLogHiresTimestamp(dynamic_cast<_qmf::ArgsBrokerSetLogHiresTimestamp&>(args).i_logHires);
+ QPID_LOG (debug, "Broker::setLogHiresTimestamp()");
+ status = Manageable::STATUS_OK;
+ break;
+ }
+ default:
QPID_LOG (debug, "Broker ManagementMethod not implemented: id=" << methodId << "]");
status = Manageable::STATUS_NOT_IMPLEMENTED;
break;
@@ -750,7 +791,7 @@ void Broker::createObject(const std::string& type, const std::string& name,
else extensions[i->first] = i->second;
}
framing::FieldTable arguments;
- amqp_0_10::translate(extensions, arguments);
+ qpid::amqp_0_10::translate(extensions, arguments);
try {
std::pair<boost::shared_ptr<Exchange>, bool> result =
@@ -772,7 +813,7 @@ void Broker::createObject(const std::string& type, const std::string& name,
else extensions[i->first] = i->second;
}
framing::FieldTable arguments;
- amqp_0_10::translate(extensions, arguments);
+ qpid::amqp_0_10::translate(extensions, arguments);
bind(binding.queue, binding.exchange, binding.key, arguments, userId, connectionId);
@@ -1008,6 +1049,18 @@ std::string Broker::getLogLevel()
return level;
}
+void Broker::setLogHiresTimestamp(bool enabled)
+{
+ QPID_LOG(notice, "Changing log hires timestamp to " << enabled);
+ qpid::log::Logger::instance().setHiresTimestamp(enabled);
+}
+
+bool Broker::getLogHiresTimestamp()
+{
+ return qpid::log::Logger::instance().getHiresTimestamp();
+}
+
+
boost::shared_ptr<ProtocolFactory> Broker::getProtocolFactory(const std::string& name) const {
ProtocolFactoryMap::const_iterator i
= name.empty() ? protocolFactories.begin() : protocolFactories.find(name);
@@ -1036,39 +1089,29 @@ void Broker::accept() {
}
void Broker::connect(
+ const std::string& name,
const std::string& host, const std::string& port, const std::string& transport,
- boost::function2<void, int, std::string> failed,
- sys::ConnectionCodec::Factory* f)
+ boost::function2<void, int, std::string> failed)
{
boost::shared_ptr<ProtocolFactory> pf = getProtocolFactory(transport);
- if (pf) pf->connect(poller, host, port, f ? f : factory.get(), failed);
+ if (pf) pf->connect(poller, name, host, port, factory.get(), failed);
else throw NoSuchTransportException(QPID_MSG("Unsupported transport type: " << transport));
}
-void Broker::connect(
- const Url& url,
- boost::function2<void, int, std::string> failed,
- sys::ConnectionCodec::Factory* f)
-{
- url.throwIfEmpty();
- const Address& addr=url[0];
- connect(addr.host, boost::lexical_cast<std::string>(addr.port), addr.protocol, failed, f);
-}
-
-uint32_t Broker::queueMoveMessages(
+int32_t Broker::queueMoveMessages(
const std::string& srcQueue,
const std::string& destQueue,
uint32_t qty,
const Variant::Map& filter)
{
- 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, &filter);
+ Queue::shared_ptr src_queue = queues.find(srcQueue);
+ if (!src_queue)
+ return -1;
+ Queue::shared_ptr dest_queue = queues.find(destQueue);
+ if (!dest_queue)
+ return -1;
+
+ return (int32_t) src_queue->move(dest_queue, qty, &filter);
}
@@ -1083,12 +1126,6 @@ Broker::getKnownBrokersImpl()
bool Broker::deferDeliveryImpl(const std::string&, const Message&)
{ return false; }
-void Broker::setClusterTimer(std::auto_ptr<sys::Timer> t) {
- clusterTimer = t;
- queueCleaner.setTimer(clusterTimer.get());
- dtxManager.setTimer(*clusterTimer.get());
-}
-
const std::string Broker::TCP_TRANSPORT("tcp");
@@ -1109,9 +1146,14 @@ std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue(
params.insert(make_pair(acl::PROP_POLICYTYPE, settings.dropMessagesAtLimit ? "ring" : "reject"));
params.insert(make_pair(acl::PROP_MAXQUEUECOUNT, boost::lexical_cast<string>(settings.maxDepth.getCount())));
params.insert(make_pair(acl::PROP_MAXQUEUESIZE, boost::lexical_cast<string>(settings.maxDepth.getSize())));
+ params.insert(make_pair(acl::PROP_MAXFILECOUNT, boost::lexical_cast<string>(settings.maxFileCount)));
+ params.insert(make_pair(acl::PROP_MAXFILESIZE, boost::lexical_cast<string>(settings.maxFileSize)));
if (!acl->authorise(userId,acl::ACT_CREATE,acl::OBJ_QUEUE,name,&params) )
throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << userId));
+
+ if (!acl->approveCreateQueue(userId,name) )
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << userId));
}
Exchange::shared_ptr alternate;
@@ -1120,21 +1162,12 @@ std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue(
if (!alternate) throw framing::NotFoundException(QPID_MSG("Alternate exchange does not exist: " << alternateExchange));
}
- std::pair<Queue::shared_ptr, bool> result = queues.declare(name, settings, alternate);
+ std::pair<Queue::shared_ptr, bool> result =
+ queues.declare(name, settings, alternate, false/*recovering*/,
+ owner, connectionId, userId);
if (result.second) {
//add default binding:
result.first->bind(exchanges.getDefault(), name, qpid::framing::FieldTable());
-
- if (managementAgent.get()) {
- //TODO: debatable whether we should raise an event here for
- //create when this is a 'declare' event; ideally add a create
- //event instead?
- managementAgent->raiseEvent(
- _qmf::EventQueueDeclare(connectionId, userId, name,
- settings.durable, owner, settings.autodelete, alternateExchange,
- settings.asMap(),
- "created"));
- }
QPID_LOG_CAT(debug, model, "Create queue. name:" << name
<< " user:" << userId
<< " rhost:" << connectionId
@@ -1149,6 +1182,10 @@ std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue(
void Broker::deleteQueue(const std::string& name, const std::string& userId,
const std::string& connectionId, QueueFunctor check)
{
+ QPID_LOG_CAT(debug, model, "Deleting queue. name:" << name
+ << " user:" << userId
+ << " rhost:" << connectionId
+ );
if (acl && !acl->authorise(userId,acl::ACT_DELETE,acl::OBJ_QUEUE,name,NULL)) {
throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied queue delete request from " << userId));
}
@@ -1156,19 +1193,13 @@ void Broker::deleteQueue(const std::string& name, const std::string& userId,
Queue::shared_ptr queue = queues.find(name);
if (queue) {
if (check) check(queue);
- queues.destroy(name);
+ if (acl)
+ acl->recordDestroyQueue(name);
+ queues.destroy(name, connectionId, userId);
queue->destroyed();
} else {
throw framing::NotFoundException(QPID_MSG("Delete failed. No such queue: " << name));
}
-
- if (managementAgent.get())
- managementAgent->raiseEvent(_qmf::EventQueueDelete(connectionId, userId, name));
- QPID_LOG_CAT(debug, model, "Delete queue. name:" << name
- << " user:" << userId
- << " rhost:" << connectionId
- );
-
}
std::pair<Exchange::shared_ptr, bool> Broker::createExchange(
@@ -1196,33 +1227,16 @@ std::pair<Exchange::shared_ptr, bool> Broker::createExchange(
}
std::pair<Exchange::shared_ptr, bool> result;
- result = exchanges.declare(name, type, durable, arguments);
+ result = exchanges.declare(
+ name, type, durable, arguments, alternate, connectionId, userId);
if (result.second) {
- if (alternate) {
- result.first->setAlternate(alternate);
- alternate->incAlternateUsers();
- }
if (durable) {
// store->create(*result.first, arguments);
ConfigHandle ch = asyncStore->createConfigHandle();
result.first->setHandle(ch);
- boost::shared_ptr<BrokerAsyncContext> bc(new ConfigAsyncContext(&configureComplete, &asyncResultQueue));
+ boost::shared_ptr<BrokerAsyncContext> bc(new ConfigAsyncContext(&configureCompleteCb, &asyncResultQueue));
asyncStore->submitCreate(ch, result.first.get(), bc);
}
- if (managementAgent.get()) {
- //TODO: debatable whether we should raise an event here for
- //create when this is a 'declare' event; ideally add a create
- //event instead?
- managementAgent->raiseEvent(_qmf::EventExchangeDeclare(connectionId,
- userId,
- name,
- type,
- alternateExchange,
- durable,
- false,
- ManagementAgent::toMap(arguments),
- "created"));
- }
QPID_LOG_CAT(debug, model, "Create exchange. name:" << name
<< " user:" << userId
<< " rhost:" << connectionId
@@ -1236,6 +1250,9 @@ std::pair<Exchange::shared_ptr, bool> Broker::createExchange(
void Broker::deleteExchange(const std::string& name, const std::string& userId,
const std::string& connectionId)
{
+ QPID_LOG_CAT(debug, model, "Deleting exchange. name:" << name
+ << " user:" << userId
+ << " rhost:" << connectionId);
if (acl) {
if (!acl->authorise(userId,acl::ACT_DELETE,acl::OBJ_EXCHANGE,name,NULL) )
throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange delete request from " << userId));
@@ -1246,21 +1263,15 @@ void Broker::deleteExchange(const std::string& name, const std::string& userId,
}
Exchange::shared_ptr exchange(exchanges.get(name));
if (!exchange) throw framing::NotFoundException(QPID_MSG("Delete failed. No such exchange: " << name));
- if (exchange->inUseAsAlternate()) throw framing::NotAllowedException(QPID_MSG("Exchange in use as alternate-exchange."));
+ if (exchange->inUseAsAlternate()) throw framing::NotAllowedException(QPID_MSG("Cannot delete " << name <<", in use as alternate-exchange."));
// if (exchange->isDurable()) store->destroy(*exchange);
if (exchange->isDurable()) {
- boost::shared_ptr<BrokerAsyncContext> bc(new ConfigAsyncContext(&configureComplete, &asyncResultQueue));
+ boost::shared_ptr<BrokerAsyncContext> bc(new ConfigAsyncContext(&configureCompleteCb, &asyncResultQueue));
asyncStore->submitDestroy(exchange->getHandle(), bc);
exchange->resetHandle();
}
if (exchange->getAlternate()) exchange->getAlternate()->decAlternateUsers();
- exchanges.destroy(name);
-
- if (managementAgent.get())
- managementAgent->raiseEvent(_qmf::EventExchangeDelete(connectionId, userId, name));
- QPID_LOG_CAT(debug, model, "Delete exchange. name:" << name
- << " user:" << userId
- << " rhost:" << connectionId);
+ exchanges.destroy(name, connectionId, userId);
}
void Broker::bind(const std::string& queueName,
@@ -1298,6 +1309,7 @@ void Broker::bind(const std::string& queueName,
QPID_LOG_CAT(debug, model, "Create binding. exchange:" << exchangeName
<< " queue:" << queueName
<< " key:" << key
+ << " arguments:" << arguments
<< " user:" << userId
<< " rhost:" << connectionId);
}
diff --git a/cpp/src/qpid/broker/Broker.h b/cpp/src/qpid/broker/Broker.h
index 698d446bca..468da8983a 100644
--- a/cpp/src/qpid/broker/Broker.h
+++ b/cpp/src/qpid/broker/Broker.h
@@ -25,40 +25,30 @@
#include "qpid/broker/AsyncResultQueueImpl.h"
#include "qpid/broker/AsyncStore.h"
#include "qpid/broker/BrokerImportExport.h"
-#include "qpid/broker/ConnectionFactory.h"
-#include "qpid/broker/ConnectionToken.h"
-#include "qpid/broker/DirectExchange.h"
+
+#include "qpid/DataDir.h"
+#include "qpid/Options.h"
+#include "qpid/Plugin.h"
#include "qpid/broker/DtxManager.h"
#include "qpid/broker/ExchangeRegistry.h"
//#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/Protocol.h"
#include "qpid/broker/QueueRegistry.h"
#include "qpid/broker/LinkRegistry.h"
#include "qpid/broker/SessionManager.h"
#include "qpid/broker/QueueCleaner.h"
#include "qpid/broker/Vhost.h"
#include "qpid/broker/System.h"
-#include "qpid/broker/ExpiryPolicy.h"
#include "qpid/broker/ConsumerFactory.h"
#include "qpid/broker/ConnectionObservers.h"
#include "qpid/broker/ConfigurationObservers.h"
#include "qpid/management/Manageable.h"
-#include "qpid/management/ManagementAgent.h"
-#include "qmf/org/apache/qpid/broker/Broker.h"
-#include "qmf/org/apache/qpid/broker/ArgsBrokerConnect.h"
-#include "qpid/Options.h"
-#include "qpid/Plugin.h"
-#include "qpid/DataDir.h"
-#include "qpid/framing/FrameHandler.h"
-#include "qpid/framing/OutputHandler.h"
-#include "qpid/framing/ProtocolInitiation.h"
-#include "qpid/sys/Runnable.h"
-#include "qpid/sys/Timer.h"
-#include "qpid/types/Variant.h"
-#include "qpid/RefCounted.h"
-#include "qpid/broker/AclModule.h"
+#include "qpid/sys/ConnectionCodec.h"
#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Runnable.h"
#include <boost/intrusive_ptr.hpp>
+
#include <string>
#include <vector>
@@ -67,12 +57,14 @@ namespace qpid {
namespace sys {
class ProtocolFactory;
class Poller;
+class Timer;
}
struct Url;
namespace broker {
+class AclModule;
class ConnectionState;
class ExpiryPolicy;
class Message;
@@ -103,6 +95,7 @@ class Broker : public sys::Runnable, public Plugin::Target,
bool noDataDir;
std::string dataDir;
uint16_t port;
+ std::vector<std::string> listenInterfaces;
int workerThreads;
int connectionBacklog;
bool enableMgmt;
@@ -139,10 +132,15 @@ class Broker : public sys::Runnable, public Plugin::Target,
void declareStandardExchange(const std::string& name, const std::string& type);
void setStore ();
- static void recoverComplete(const AsyncResultHandle* const);
- static void configureComplete(const AsyncResultHandle* const);
+ static void recoverCompleteCb(const AsyncResultHandle* const);
+ static void configureCompleteCb(const AsyncResultHandle* const);
+ static Broker* thisBroker;
+ void recoverComplete(const AsyncResultHandle* const arh);
+ void configureComplete(const AsyncResultHandle* const arh);
void setLogLevel(const std::string& level);
std::string getLogLevel();
+ void setLogHiresTimestamp(bool enabled);
+ bool getLogHiresTimestamp();
void createObject(const std::string& type, const std::string& name,
const qpid::types::Variant::Map& properties, bool strict, const ConnectionState* context);
void deleteObject(const std::string& type, const std::string& name,
@@ -158,8 +156,7 @@ class Broker : public sys::Runnable, public Plugin::Target,
Manageable::status_t setTimestampConfig(const bool receive,
const ConnectionState* context);
boost::shared_ptr<sys::Poller> poller;
- sys::Timer timer;
- std::auto_ptr<sys::Timer> clusterTimer;
+ std::auto_ptr<sys::Timer> timer;
Options config;
std::auto_ptr<management::ManagementAgent> managementAgent;
ProtocolFactoryMap protocolFactories;
@@ -178,7 +175,7 @@ class Broker : public sys::Runnable, public Plugin::Target,
boost::shared_ptr<sys::ConnectionCodec::Factory> factory;
DtxManager dtxManager;
SessionManager sessionManager;
- qmf::org::apache::qpid::broker::Broker* mgmtObject;
+ qmf::org::apache::qpid::broker::Broker::shared_ptr mgmtObject;
Vhost::shared_ptr vhostObject;
System::shared_ptr systemObject;
QueueCleaner queueCleaner;
@@ -187,10 +184,10 @@ class Broker : public sys::Runnable, public Plugin::Target,
bool deferDeliveryImpl(const std::string& queue,
const Message& msg);
std::string federationTag;
- bool recovery;
- bool inCluster, clusterUpdatee;
+ bool recoveryInProgress;
boost::intrusive_ptr<ExpiryPolicy> expiryPolicy;
ConsumerFactories consumerFactories;
+ ProtocolRegistry protocolRegistry;
mutable sys::Mutex linkClientPropertiesLock;
framing::FieldTable linkClientProperties;
@@ -233,6 +230,7 @@ class Broker : public sys::Runnable, public Plugin::Target,
DataDir& getDataDir() { return dataDir; }
Options& getOptions() { return config; }
AsyncResultQueueImpl& getAsyncResultQueue() { return asyncResultQueue; }
+ ProtocolRegistry& getProtocolRegistry() { return protocolRegistry; }
void setExpiryPolicy(const boost::intrusive_ptr<ExpiryPolicy>& e) { expiryPolicy = e; }
boost::intrusive_ptr<ExpiryPolicy> getExpiryPolicy() { return expiryPolicy; }
@@ -240,7 +238,7 @@ class Broker : public sys::Runnable, public Plugin::Target,
SessionManager& getSessionManager() { return sessionManager; }
const std::string& getFederationTag() const { return federationTag; }
- QPID_BROKER_EXTERN management::ManagementObject* GetManagementObject() const;
+ QPID_BROKER_EXTERN management::ManagementObject::shared_ptr GetManagementObject() const;
QPID_BROKER_EXTERN management::Manageable* GetVhostObject() const;
QPID_BROKER_EXTERN management::Manageable::status_t ManagementMethod(
uint32_t methodId, management::Args& args, std::string& text);
@@ -253,19 +251,17 @@ class Broker : public sys::Runnable, public Plugin::Target,
QPID_BROKER_EXTERN void accept();
/** Create a connection to another broker. */
- void connect(const std::string& host, const std::string& port,
+ void connect(const std::string& name,
+ const std::string& host, const std::string& port,
const std::string& transport,
- boost::function2<void, int, std::string> failed,
- sys::ConnectionCodec::Factory* =0);
- /** Create a connection to another broker. */
- void connect(const Url& url,
- boost::function2<void, int, std::string> failed,
- sys::ConnectionCodec::Factory* =0);
+ boost::function2<void, int, std::string> failed);
/** Move messages from one queue to another.
A zero quantity means to move all messages
+ Return -1 if one of the queues does not exist, otherwise
+ the number of messages moved.
*/
- QPID_BROKER_EXTERN uint32_t queueMoveMessages(
+ QPID_BROKER_EXTERN int32_t queueMoveMessages(
const std::string& srcQueue,
const std::string& destQueue,
uint32_t qty,
@@ -277,46 +273,17 @@ class Broker : public sys::Runnable, public Plugin::Target,
/** Expose poller so plugins can register their descriptors. */
QPID_BROKER_EXTERN boost::shared_ptr<sys::Poller> getPoller();
- boost::shared_ptr<sys::ConnectionCodec::Factory> getConnectionFactory() { return factory; }
- void setConnectionFactory(boost::shared_ptr<sys::ConnectionCodec::Factory> f) { factory = f; }
-
/** Timer for local tasks affecting only this broker */
- sys::Timer& getTimer() { return timer; }
-
- /** Timer for tasks that must be synchronized if we are in a cluster */
- sys::Timer& getClusterTimer() { return clusterTimer.get() ? *clusterTimer : timer; }
- QPID_BROKER_EXTERN void setClusterTimer(std::auto_ptr<sys::Timer>);
+ 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; }
-
- /** True of this broker is part of a cluster.
- * Only valid after early initialization of plugins is complete.
- */
- bool isInCluster() const { return inCluster; }
- void setInCluster(bool set) { inCluster = set; }
-
- /** True if this broker is joining a cluster and in the process of
- * receiving a state update.
- */
- bool isClusterUpdatee() const { return clusterUpdatee; }
- void setClusterUpdatee(bool set) { clusterUpdatee = set; }
+ bool inRecovery() const { return recoveryInProgress; }
management::ManagementAgent* getManagementAgent() { return managementAgent.get(); }
- /**
- * Never true in a stand-alone broker. In a cluster, return true
- * to defer delivery of messages deliveredg in a cluster-unsafe
- * context.
- *@return true if delivery of a message should be deferred.
- */
- boost::function<bool (const std::string& queue,
- const Message& msg)> deferDelivery;
-
bool isAuthenticating ( ) { return config.auth; }
bool isTimestamping() { return config.timestampRcvMsgs; }
@@ -371,8 +338,10 @@ class Broker : public sys::Runnable, public Plugin::Target,
QPID_BROKER_EXTERN framing::FieldTable getLinkClientProperties() const;
QPID_BROKER_EXTERN void setLinkClientProperties(const framing::FieldTable&);
+ QPID_BROKER_EXTERN uint16_t getLinkHearbeatInterval() { return config.linkHeartbeatInterval; }
/** Information identifying this system */
boost::shared_ptr<const System> getSystem() const { return systemObject; }
+ friend class StatusCheckThread;
};
}}
diff --git a/cpp/src/qpid/broker/ConfigurationObserver.h b/cpp/src/qpid/broker/ConfigurationObserver.h
index 701043db40..789490e08c 100644
--- a/cpp/src/qpid/broker/ConfigurationObserver.h
+++ b/cpp/src/qpid/broker/ConfigurationObserver.h
@@ -38,6 +38,10 @@ class Exchange;
/**
* Observer for changes to configuration (aka wiring)
+ *
+ * NOTE: create and destroy functions are called with
+ * the registry lock held. This is necessary to ensure
+ * they are called in the correct sequence.
*/
class ConfigurationObserver
{
diff --git a/cpp/src/qpid/broker/Connection.cpp b/cpp/src/qpid/broker/Connection.cpp
index e68c906cc2..df1a23f882 100644
--- a/cpp/src/qpid/broker/Connection.cpp
+++ b/cpp/src/qpid/broker/Connection.cpp
@@ -25,9 +25,9 @@
#include "qpid/broker/Bridge.h"
#include "qpid/broker/Broker.h"
#include "qpid/broker/Queue.h"
-#include "qpid/broker/AclModule.h"
+#include "qpid/management/ManagementAgent.h"
#include "qpid/sys/SecuritySettings.h"
-#include "qpid/sys/ClusterSafe.h"
+#include "qpid/sys/Timer.h"
#include "qpid/log/Statement.h"
#include "qpid/ptr_map.h"
@@ -86,20 +86,14 @@ Connection::Connection(ConnectionOutputHandler* out_,
std::string& mgmtId_,
const qpid::sys::SecuritySettings& external,
bool link_,
- uint64_t objectId_,
- bool shadow_,
- bool delayManagement,
- bool authenticated_
+ uint64_t objectId_
) :
ConnectionState(out_, broker_),
securitySettings(external),
- shadow(shadow_),
- authenticated(authenticated_),
adapter(*this, link_),
link(link_),
mgmtClosing(false),
mgmtId(mgmtId_),
- mgmtObject(0),
links(broker_.getLinks()),
agent(0),
timer(broker_.getTimer()),
@@ -109,11 +103,6 @@ Connection::Connection(ConnectionOutputHandler* out_,
{
outboundTracker.wrap(out);
broker.getConnectionObservers().connection(*this);
- // In a cluster, allow adding the management object to be delayed.
- if (!delayManagement) addManagementObject();
-}
-
-void Connection::addManagementObject() {
assert(agent == 0);
assert(mgmtObject == 0);
Manageable* parent = broker.GetVhostObject();
@@ -121,8 +110,7 @@ void Connection::addManagementObject() {
agent = broker.getManagementAgent();
if (agent != 0) {
// TODO set last bool true if system connection
- mgmtObject = new _qmf::Connection(agent, this, parent, mgmtId, !link, false);
- mgmtObject->set_shadow(shadow);
+ mgmtObject = _qmf::Connection::shared_ptr(new _qmf::Connection(agent, this, parent, mgmtId, !link, false, "AMQP 0-10"));
agent->addObject(mgmtObject, objectId);
}
ConnectionState::setUrl(mgmtId);
@@ -139,13 +127,11 @@ void Connection::requestIOProcessing(boost::function0<void> callback)
Connection::~Connection()
{
if (mgmtObject != 0) {
- mgmtObject->resourceDestroy();
- // In a cluster, Connections destroyed during shutdown are in
- // a cluster-unsafe context. Don't raise an event in that case.
- if (!link && isClusterSafe())
- agent->raiseEvent(_qmf::EventClientDisconnect(mgmtId, ConnectionState::getUserId()));
+ if (!link)
+ agent->raiseEvent(_qmf::EventClientDisconnect(mgmtId, ConnectionState::getUserId(), mgmtObject->get_remoteProperties()));
QPID_LOG_CAT(debug, model, "Delete connection. user:" << ConnectionState::getUserId()
<< " rhost:" << mgmtId );
+ mgmtObject->resourceDestroy();
}
broker.getConnectionObservers().closed(*this);
@@ -188,8 +174,7 @@ bool isMessage(const AMQMethodBody* method)
void Connection::recordFromServer(const framing::AMQFrame& frame)
{
- // Don't record management stats in cluster-unsafe contexts
- if (mgmtObject != 0 && isClusterSafe())
+ if (mgmtObject != 0)
{
qmf::org::apache::qpid::broker::Connection::PerThreadStats *cStats = mgmtObject->getStatistics();
cStats->framesToClient += 1;
@@ -203,8 +188,7 @@ void Connection::recordFromServer(const framing::AMQFrame& frame)
void Connection::recordFromClient(const framing::AMQFrame& frame)
{
- // Don't record management stats in cluster-unsafe contexts
- if (mgmtObject != 0 && isClusterSafe())
+ if (mgmtObject != 0)
{
qmf::org::apache::qpid::broker::Connection::PerThreadStats *cStats = mgmtObject->getStatistics();
cStats->framesFromClient += 1;
@@ -279,28 +263,7 @@ void Connection::notifyConnectionForced(const string& text)
void Connection::setUserId(const string& userId)
{
- // Account for changing userId
- AclModule* acl = broker.getAcl();
- if (acl)
- {
- acl->setUserId(*this, userId);
- }
-
ConnectionState::setUserId(userId);
- // In a cluster, the cluster code will raise the connect event
- // when the connection is replicated to the cluster.
- if (!broker.isInCluster()) raiseConnectEvent();
-}
-
-void Connection::raiseConnectEvent() {
- if (mgmtObject != 0) {
- mgmtObject->set_authIdentity(userId);
- agent->raiseEvent(_qmf::EventClientConnect(mgmtId, userId));
- }
-
- QPID_LOG_CAT(debug, model, "Create connection. user:" << userId
- << " rhost:" << mgmtId );
-
}
void Connection::setUserProxyAuth(bool b)
@@ -327,19 +290,6 @@ void Connection::close(connection::CloseCode code, const string& text)
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();
- if (linkHeartbeatTimer) {
- linkHeartbeatTimer->cancel();
- }
- adapter.close(connection::CLOSE_CODE_NORMAL, "OK");
- getOutput().close();
-}
-
void Connection::idleOut(){}
void Connection::idleIn(){}
@@ -364,9 +314,6 @@ void Connection::closed(){ // Physically closed, suspend open sessions.
void Connection::doIoCallbacks() {
if (!isOpen()) return; // Don't process IO callbacks until we are open.
ScopedLock<Mutex> l(ioCallbackLock);
- // Although IO callbacks execute in the connection thread context, they are
- // not cluster safe because they are queued for execution in non-IO threads.
- ClusterUnsafeScope cus;
while (!ioCallbacks.empty()) {
boost::function0<void> cb = ioCallbacks.front();
ioCallbacks.pop();
@@ -413,9 +360,9 @@ SessionHandler& Connection::getChannel(ChannelId id) {
return *ptr_map_ptr(i);
}
-ManagementObject* Connection::GetManagementObject(void) const
+ManagementObject::shared_ptr Connection::GetManagementObject(void) const
{
- return (ManagementObject*) mgmtObject;
+ return mgmtObject;
}
Manageable::status_t Connection::ManagementMethod(uint32_t methodId, Args&, string&)
@@ -499,7 +446,7 @@ void Connection::abort()
void Connection::setHeartbeatInterval(uint16_t heartbeat)
{
setHeartbeat(heartbeat);
- if (heartbeat > 0 && !isShadow()) {
+ if (heartbeat > 0) {
if (!heartbeatTimer) {
heartbeatTimer = new ConnectionHeartbeatTask(heartbeat, timer, *this);
timer.add(heartbeatTimer);
@@ -535,7 +482,6 @@ void Connection::OutboundFrameTracker::close() { next->close(); }
size_t Connection::OutboundFrameTracker::getBuffered() const { return next->getBuffered(); }
void Connection::OutboundFrameTracker::abort() { next->abort(); }
void Connection::OutboundFrameTracker::activateOutput() { next->activateOutput(); }
-void Connection::OutboundFrameTracker::giveReadCredit(int32_t credit) { next->giveReadCredit(credit); }
void Connection::OutboundFrameTracker::send(framing::AMQFrame& f)
{
next->send(f);
diff --git a/cpp/src/qpid/broker/Connection.h b/cpp/src/qpid/broker/Connection.h
index d01599ce54..5c4d7132be 100644
--- a/cpp/src/qpid/broker/Connection.h
+++ b/cpp/src/qpid/broker/Connection.h
@@ -30,24 +30,13 @@
#include "qpid/broker/BrokerImportExport.h"
#include "qpid/broker/ConnectionHandler.h"
#include "qpid/broker/ConnectionState.h"
-#include "qpid/broker/SessionHandler.h"
-#include "qmf/org/apache/qpid/broker/Connection.h"
-#include "qpid/Exception.h"
-#include "qpid/RefCounted.h"
-#include "qpid/framing/AMQFrame.h"
-#include "qpid/framing/AMQP_ClientProxy.h"
-#include "qpid/framing/AMQP_ServerOperations.h"
-#include "qpid/framing/ProtocolVersion.h"
-#include "qpid/management/ManagementAgent.h"
-#include "qpid/management/Manageable.h"
-#include "qpid/ptr_map.h"
-#include "qpid/sys/AggregateOutput.h"
#include "qpid/sys/ConnectionInputHandler.h"
-#include "qpid/sys/ConnectionOutputHandler.h"
#include "qpid/sys/SecuritySettings.h"
-#include "qpid/sys/Socket.h"
-#include "qpid/sys/TimeoutHandler.h"
#include "qpid/sys/Mutex.h"
+#include "qpid/RefCounted.h"
+#include "qpid/ptr_map.h"
+
+#include "qmf/org/apache/qpid/broker/Connection.h"
#include <boost/ptr_container/ptr_map.hpp>
#include <boost/bind.hpp>
@@ -55,11 +44,17 @@
#include <algorithm>
namespace qpid {
+namespace sys {
+class Timer;
+class TimerTask;
+}
namespace broker {
class Broker;
class LinkRegistry;
+class Queue;
class SecureConnection;
+class SessionHandler;
struct ConnectionTimeoutTask;
class Connection : public sys::ConnectionInputHandler,
@@ -83,10 +78,7 @@ class Connection : public sys::ConnectionInputHandler,
const std::string& mgmtId,
const qpid::sys::SecuritySettings&,
bool isLink = false,
- uint64_t objectId = 0,
- bool shadow=false,
- bool delayManagement = false,
- bool authenticated=true);
+ uint64_t objectId = 0);
~Connection ();
@@ -112,7 +104,7 @@ class Connection : public sys::ConnectionInputHandler,
void closeChannel(framing::ChannelId channel);
// Manageable entry points
- management::ManagementObject* GetManagementObject (void) const;
+ management::ManagementObject::shared_ptr GetManagementObject(void) const;
management::Manageable::status_t
ManagementMethod (uint32_t methodId, management::Args& args, std::string&);
@@ -130,7 +122,6 @@ class Connection : public sys::ConnectionInputHandler,
void notifyConnectionForced(const std::string& text);
void setUserId(const std::string& uid);
- void raiseConnectEvent();
// credentials for connected client
const std::string& getUserId() const { return ConnectionState::getUserId(); }
@@ -144,27 +135,14 @@ class Connection : public sys::ConnectionInputHandler,
void setHeartbeatInterval(uint16_t heartbeat);
void sendHeartbeat();
void restartTimeout();
-
+
template <class F> void eachSessionHandler(F f) {
for (ChannelMap::iterator i = channels.begin(); i != channels.end(); ++i)
f(*ptr_map_ptr(i));
}
- void sendClose();
void setSecureConnection(SecureConnection* secured);
- /** True if this is a shadow connection in a cluster. */
- bool isShadow() const { return shadow; }
-
- /** True if this connection is authenticated */
- bool isAuthenticated() const { return authenticated; }
-
- // Used by cluster to update connection status
- sys::AggregateOutput& getOutputTasks() { return outputTasks; }
-
- /** Cluster delays adding management object in the constructor then calls this. */
- void addManagementObject();
-
const qpid::sys::SecuritySettings& getExternalSecuritySettings() const
{
return securitySettings;
@@ -176,9 +154,6 @@ class Connection : public sys::ConnectionInputHandler,
bool isLink() { return link; }
void startLinkHeartbeatTimeoutTask();
- // Used by cluster during catch-up, see cluster::OutputInterceptor
- void doIoCallbacks();
-
void setClientProperties(const framing::FieldTable& cp) { clientProperties = cp; }
const framing::FieldTable& getClientProperties() const { return clientProperties; }
@@ -188,15 +163,13 @@ class Connection : public sys::ConnectionInputHandler,
ChannelMap channels;
qpid::sys::SecuritySettings securitySettings;
- bool shadow;
- bool authenticated;
ConnectionHandler adapter;
const bool link;
bool mgmtClosing;
const std::string mgmtId;
sys::Mutex ioCallbackLock;
std::queue<boost::function0<void> > ioCallbacks;
- qmf::org::apache::qpid::broker::Connection* mgmtObject;
+ qmf::org::apache::qpid::broker::Connection::shared_ptr mgmtObject;
LinkRegistry& links;
management::ManagementAgent* agent;
sys::Timer& timer;
@@ -218,7 +191,6 @@ class Connection : public sys::ConnectionInputHandler,
size_t getBuffered() const;
void abort();
void activateOutput();
- void giveReadCredit(int32_t credit);
void send(framing::AMQFrame&);
void wrap(sys::ConnectionOutputHandlerPtr&);
private:
@@ -228,10 +200,11 @@ class Connection : public sys::ConnectionInputHandler,
OutboundFrameTracker outboundTracker;
void sent(const framing::AMQFrame& f);
+ void doIoCallbacks();
public:
- qmf::org::apache::qpid::broker::Connection* getMgmtObject() { return mgmtObject; }
+ qmf::org::apache::qpid::broker::Connection::shared_ptr getMgmtObject() { return mgmtObject; }
};
}}
diff --git a/cpp/src/qpid/broker/ConnectionFactory.cpp b/cpp/src/qpid/broker/ConnectionFactory.cpp
deleted file mode 100644
index d5d24ca629..0000000000
--- a/cpp/src/qpid/broker/ConnectionFactory.cpp
+++ /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.
- *
- */
-#include "qpid/broker/ConnectionFactory.h"
-#include "qpid/framing/ProtocolVersion.h"
-#include "qpid/amqp_0_10/Connection.h"
-#include "qpid/broker/Connection.h"
-#include "qpid/sys/SecuritySettings.h"
-#include "qpid/log/Statement.h"
-
-namespace qpid {
-namespace broker {
-
-using framing::ProtocolVersion;
-using qpid::sys::SecuritySettings;
-typedef std::auto_ptr<amqp_0_10::Connection> ConnectionPtr;
-typedef std::auto_ptr<sys::ConnectionInputHandler> InputPtr;
-
-ConnectionFactory::ConnectionFactory(Broker& b) : broker(b) {}
-
-ConnectionFactory::~ConnectionFactory() {}
-
-sys::ConnectionCodec*
-ConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id,
- const SecuritySettings& external) {
- if (v == ProtocolVersion(0, 10)) {
- ConnectionPtr c(new amqp_0_10::Connection(out, id, false));
- c->setInputHandler(InputPtr(new broker::Connection(c.get(), broker, id, external, false)));
- return c.release();
- }
- return 0;
-}
-
-sys::ConnectionCodec*
-ConnectionFactory::create(sys::OutputControl& out, const std::string& id,
- const SecuritySettings& external) {
- // used to create connections from one broker to another
- ConnectionPtr c(new amqp_0_10::Connection(out, id, true));
- c->setInputHandler(InputPtr(new broker::Connection(c.get(), broker, id, external, true)));
- return c.release();
-}
-
-
-}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/ConnectionHandler.cpp b/cpp/src/qpid/broker/ConnectionHandler.cpp
index 06f442a47f..977c706ebd 100644
--- a/cpp/src/qpid/broker/ConnectionHandler.cpp
+++ b/cpp/src/qpid/broker/ConnectionHandler.cpp
@@ -20,8 +20,10 @@
*
*/
-#include "qpid/SaslFactory.h"
#include "qpid/broker/ConnectionHandler.h"
+
+#include "qpid/SaslFactory.h"
+#include "qpid/broker/Broker.h"
#include "qpid/broker/Connection.h"
#include "qpid/broker/SecureConnection.h"
#include "qpid/Url.h"
@@ -30,8 +32,10 @@
#include "qpid/framing/enum.h"
#include "qpid/framing/FieldValue.h"
#include "qpid/log/Statement.h"
+#include "qpid/management/ManagementAgent.h"
#include "qpid/sys/SecurityLayer.h"
#include "qpid/broker/AclModule.h"
+#include "qpid/amqp_0_10/Codecs.h"
#include "qmf/org/apache/qpid/broker/EventClientConnectFail.h"
using namespace qpid;
@@ -148,6 +152,24 @@ void ConnectionHandler::Handler::startOk(const framing::FieldTable& /*clientProp
void ConnectionHandler::Handler::startOk(const ConnectionStartOkBody& body)
{
+ const framing::FieldTable& clientProperties = body.getClientProperties();
+ qmf::org::apache::qpid::broker::Connection::shared_ptr mgmtObject = connection.getMgmtObject();
+
+ if (mgmtObject != 0) {
+ string procName = clientProperties.getAsString(CLIENT_PROCESS_NAME);
+ uint32_t pid = clientProperties.getAsInt(CLIENT_PID);
+ uint32_t ppid = clientProperties.getAsInt(CLIENT_PPID);
+
+ types::Variant::Map properties;
+ qpid::amqp_0_10::translate(clientProperties, properties);
+ mgmtObject->set_remoteProperties(properties);
+ if (!procName.empty())
+ mgmtObject->set_remoteProcessName(procName);
+ if (pid != 0)
+ mgmtObject->set_remotePid(pid);
+ if (ppid != 0)
+ mgmtObject->set_remoteParentPid(ppid);
+ }
try {
authenticator->start(body.getMechanism(), body.hasResponse() ? &body.getResponse() : 0);
} catch (std::exception& /*e*/) {
@@ -160,8 +182,9 @@ void ConnectionHandler::Handler::startOk(const ConnectionStartOkBody& body)
string uid;
authenticator->getError(error);
authenticator->getUid(uid);
- if (agent) {
- agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error));
+ if (agent && mgmtObject) {
+ agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error,
+ mgmtObject->get_remoteProperties()));
}
QPID_LOG_CAT(debug, model, "Failed connection. rhost:" << connection.getMgmtId()
<< " user:" << uid
@@ -169,9 +192,8 @@ void ConnectionHandler::Handler::startOk(const ConnectionStartOkBody& body)
}
throw;
}
- const framing::FieldTable& clientProperties = body.getClientProperties();
- connection.setClientProperties(clientProperties);
+ connection.setClientProperties(clientProperties);
connection.setFederationLink(clientProperties.get(QPID_FED_LINK));
if (clientProperties.isSet(QPID_FED_TAG)) {
connection.setFederationPeerTag(clientProperties.getAsString(QPID_FED_TAG));
@@ -187,19 +209,6 @@ void ConnectionHandler::Handler::startOk(const ConnectionStartOkBody& body)
}
QPID_LOG(info, "Connection is a federation link");
}
-
- 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)
@@ -216,8 +225,9 @@ void ConnectionHandler::Handler::secureOk(const string& response)
string uid;
authenticator->getError(error);
authenticator->getUid(uid);
- if (agent) {
- agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error));
+ if (agent && connection.getMgmtObject()) {
+ agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error,
+ connection.getMgmtObject()->get_remoteProperties()));
}
QPID_LOG_CAT(debug, model, "Failed connection. rhost:" << connection.getMgmtId()
<< " user:" << uid
diff --git a/cpp/src/qpid/sys/alloca.h b/cpp/src/qpid/broker/ConnectionState.cpp
index b3f59b7c3f..c6a8317c2b 100644
--- a/cpp/src/qpid/sys/alloca.h
+++ b/cpp/src/qpid/broker/ConnectionState.cpp
@@ -1,7 +1,5 @@
-#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
@@ -21,22 +19,20 @@
*
*/
-#if (defined(_WINDOWS) || defined (WIN32))
-# include <malloc.h>
+#include "qpid/broker/ConnectionState.h"
+
+#include "qpid/broker/Broker.h"
-# if defined(_MSC_VER)
-# ifdef alloc
-# undef alloc
-# endif
-# define alloc _alloc
-# ifdef alloca
-# undef alloca
-# endif
-# define alloca _alloca
-# endif
-#endif
-#if !defined _WINDOWS && !defined WIN32
-# include <alloca.h>
-#endif
+namespace qpid {
+namespace broker {
-#endif /*!QPID_SYS_ALLOCA_H*/
+void ConnectionState::setUserId(const std::string& uid) {
+ userId = uid;
+ size_t at = userId.find('@');
+ userName = userId.substr(0, at);
+ isDefaultRealm = (
+ at!= std::string::npos &&
+ getBroker().getOptions().realm == userId.substr(at+1,userId.size()));
+}
+
+}}
diff --git a/cpp/src/qpid/broker/ConnectionState.h b/cpp/src/qpid/broker/ConnectionState.h
index 4dfd86fd8e..a8d6e82210 100644
--- a/cpp/src/qpid/broker/ConnectionState.h
+++ b/cpp/src/qpid/broker/ConnectionState.h
@@ -21,18 +21,22 @@
#ifndef _ConnectionState_
#define _ConnectionState_
-#include <vector>
-
+#include "qpid/broker/ConnectionToken.h"
#include "qpid/sys/AggregateOutput.h"
#include "qpid/sys/ConnectionOutputHandlerPtr.h"
#include "qpid/framing/ProtocolVersion.h"
#include "qpid/management/Manageable.h"
#include "qpid/Url.h"
-#include "qpid/broker/Broker.h"
+
+#include <boost/function.hpp>
+#include <vector>
+
namespace qpid {
namespace broker {
+class Broker;
+
class ConnectionState : public ConnectionToken, public management::Manageable
{
protected:
@@ -46,9 +50,8 @@ class ConnectionState : public ConnectionToken, public management::Manageable
framemax(65535),
heartbeat(0),
heartbeatmax(120),
- userProxyAuth(false), // Can proxy msgs with non-matching auth ids when true (used by federation links & clustering)
+ userProxyAuth(false), // Can proxy msgs with non-matching auth ids when true (used by federation links)
federationLink(true),
- clusterOrderOut(0),
isDefaultRealm(false)
{}
@@ -62,14 +65,7 @@ class ConnectionState : public ConnectionToken, public management::Manageable
void setHeartbeat(uint16_t hb) { heartbeat = hb; }
void setHeartbeatMax(uint16_t hbm) { heartbeatmax = hbm; }
- virtual void setUserId(const std::string& uid) {
- userId = uid;
- size_t at = userId.find('@');
- userName = userId.substr(0, at);
- isDefaultRealm = (
- at!= std::string::npos &&
- getBroker().getOptions().realm == userId.substr(at+1,userId.size()));
- }
+ virtual void setUserId(const std::string& uid);
const std::string& getUserId() const { return userId; }
@@ -102,15 +98,6 @@ class ConnectionState : public ConnectionToken, public management::Manageable
framing::ProtocolVersion getVersion() const { return version; }
void setOutputHandler(qpid::sys::ConnectionOutputHandler* o) { out.set(o); }
- /**
- * If the broker is part of a cluster, this is a handler provided
- * by cluster code. It ensures consistent ordering of commands
- * that are sent based on criteria that are not predictably
- * ordered cluster-wide, e.g. a timer firing.
- */
- framing::FrameHandler* getClusterOrderOutput() { return clusterOrderOut; }
- void setClusterOrderOutput(framing::FrameHandler& fh) { clusterOrderOut = &fh; }
-
virtual void requestIOProcessing (boost::function0<void>) = 0;
protected:
@@ -124,7 +111,6 @@ class ConnectionState : public ConnectionToken, public management::Manageable
bool federationLink;
std::string federationPeerTag;
std::vector<Url> knownHosts;
- framing::FrameHandler* clusterOrderOut;
std::string userName;
bool isDefaultRealm;
};
diff --git a/cpp/src/qpid/broker/Consumer.h b/cpp/src/qpid/broker/Consumer.h
index 64fc4288af..662b0f937d 100644
--- a/cpp/src/qpid/broker/Consumer.h
+++ b/cpp/src/qpid/broker/Consumer.h
@@ -79,6 +79,15 @@ class Consumer : public QueueCursor {
*/
virtual bool hideDeletedError() { return false; }
+ /** If false, the consumer is not counted for purposes of auto-deletion or
+ * immediate messages. This is used for "system" consumers that are created
+ * by the broker for internal purposes as opposed to consumers that are
+ * created by normal clients.
+ */
+ virtual bool isCounted() { return true; }
+
+ QueueCursor getCursor() const { return *this; }
+ void setCursor(const QueueCursor& qc) { static_cast<QueueCursor&>(*this) = qc; }
protected:
//framing::SequenceNumber position;
diff --git a/cpp/src/qpid/broker/ConsumerFactory.h b/cpp/src/qpid/broker/ConsumerFactory.h
index abd39fb3f8..1c0f2571e2 100644
--- a/cpp/src/qpid/broker/ConsumerFactory.h
+++ b/cpp/src/qpid/broker/ConsumerFactory.h
@@ -25,11 +25,14 @@
// TODO aconway 2011-11-25: it's ugly exposing SemanticState::ConsumerImpl in public.
// Refactor to use a more abstract interface.
-#include "qpid/broker/SemanticState.h"
+#include <boost/shared_ptr.hpp>
namespace qpid {
namespace broker {
+class SemanticState;
+class SemanticStateConsumerImpl;
+
/**
* Base class for consumer factoires. Plugins can register a
* ConsumerFactory via Broker:: getConsumerFactories() Each time a
@@ -41,7 +44,7 @@ class ConsumerFactory
public:
virtual ~ConsumerFactory() {}
- virtual boost::shared_ptr<SemanticState::ConsumerImpl> create(
+ virtual boost::shared_ptr<SemanticStateConsumerImpl> create(
SemanticState* parent,
const std::string& name, boost::shared_ptr<Queue> queue,
bool ack, bool acquire, bool exclusive, const std::string& tag,
diff --git a/cpp/src/qpid/broker/DeliverableMessage.cpp b/cpp/src/qpid/broker/DeliverableMessage.cpp
index be4b7f0796..31823709ce 100644
--- a/cpp/src/qpid/broker/DeliverableMessage.cpp
+++ b/cpp/src/qpid/broker/DeliverableMessage.cpp
@@ -36,8 +36,3 @@ Message& DeliverableMessage::getMessage()
{
return msg;
}
-
-uint64_t DeliverableMessage::contentSize()
-{
- return msg.getContentSize();
-}
diff --git a/cpp/src/qpid/broker/DeliverableMessage.h b/cpp/src/qpid/broker/DeliverableMessage.h
index d6d6bf5265..6e8275770d 100644
--- a/cpp/src/qpid/broker/DeliverableMessage.h
+++ b/cpp/src/qpid/broker/DeliverableMessage.h
@@ -36,7 +36,6 @@ namespace qpid {
QPID_BROKER_EXTERN DeliverableMessage(const Message& msg, TxBuffer* txn);
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
deleted file mode 100644
index e69de29bb2..0000000000
--- a/cpp/src/qpid/broker/DeliveryAdapter.h
+++ /dev/null
diff --git a/cpp/src/qpid/broker/DirectExchange.cpp b/cpp/src/qpid/broker/DirectExchange.cpp
index b1130c3ec0..6aef1c2168 100644
--- a/cpp/src/qpid/broker/DirectExchange.cpp
+++ b/cpp/src/qpid/broker/DirectExchange.cpp
@@ -70,7 +70,7 @@ bool DirectExchange::bind(Queue::shared_ptr queue, const string& routingKey, con
if (args == 0 || fedOp.empty() || fedOp == fedOpBind) {
Mutex::ScopedLock l(lock);
- Binding::shared_ptr b(new Binding(routingKey, queue, this, FieldTable(), fedOrigin));
+ Binding::shared_ptr b(new Binding(routingKey, queue, this, args ? *args : FieldTable(), fedOrigin));
BoundKey& bk = bindings[routingKey];
if (exclusiveBinding) bk.queues.clear();
diff --git a/cpp/src/qpid/broker/DtxManager.cpp b/cpp/src/qpid/broker/DtxManager.cpp
index c55771c4e6..3b23ea2900 100644
--- a/cpp/src/qpid/broker/DtxManager.cpp
+++ b/cpp/src/qpid/broker/DtxManager.cpp
@@ -27,6 +27,9 @@
#include "qpid/ptr_map.h"
#include <boost/format.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
#include <iostream>
using boost::intrusive_ptr;
@@ -35,6 +38,30 @@ using qpid::ptr_map_ptr;
using namespace qpid::broker;
using namespace qpid::framing;
+namespace {
+ typedef boost::function0<void> FireFunction;
+ struct DtxCleanup : public qpid::sys::TimerTask
+ {
+ FireFunction fireFunction;
+
+ DtxCleanup(uint32_t timeout, FireFunction f);
+ void fire();
+ };
+
+ DtxCleanup::DtxCleanup(uint32_t _timeout, FireFunction f)
+ : TimerTask(qpid::sys::Duration(_timeout * qpid::sys::TIME_SEC),"DtxCleanup"), fireFunction(f){}
+
+ void DtxCleanup::fire()
+ {
+ try {
+ fireFunction();
+ } catch (qpid::ConnectionException& /*e*/) {
+ //assume it was explicitly cleaned up after a call to prepare, commit or rollback
+ }
+ }
+
+}
+
//DtxManager::DtxManager(qpid::sys::Timer& t) : store(0), timer(&t) {}
DtxManager::DtxManager(qpid::sys::Timer& t) : asyncTxnStore(0), timer(&t) {}
@@ -158,19 +185,7 @@ void DtxManager::timedout(const std::string& xid)
} else {
ptr_map_ptr(i)->timedout();
//TODO: do we want to have a timed task to cleanup, or can we rely on an explicit completion?
- //timer.add(intrusive_ptr<TimerTask>(new DtxCleanup(60*30/*30 mins*/, *this, xid)));
- }
-}
-
-DtxManager::DtxCleanup::DtxCleanup(uint32_t _timeout, DtxManager& _mgr, const std::string& _xid)
- : TimerTask(qpid::sys::Duration(_timeout * qpid::sys::TIME_SEC),"DtxCleanup"), mgr(_mgr), xid(_xid) {}
-
-void DtxManager::DtxCleanup::fire()
-{
- try {
- mgr.remove(xid);
- } catch (ConnectionException& /*e*/) {
- //assume it was explicitly cleaned up after a call to prepare, commit or rollback
+ //timer->add(new DtxCleanup(60*30/*30 mins*/, boost::bind(&DtxManager::remove, this, xid)));
}
}
diff --git a/cpp/src/qpid/broker/DtxManager.h b/cpp/src/qpid/broker/DtxManager.h
index cbc66d6391..82f7ebb9ac 100644
--- a/cpp/src/qpid/broker/DtxManager.h
+++ b/cpp/src/qpid/broker/DtxManager.h
@@ -32,20 +32,15 @@
#include "qpid/ptr_map.h"
namespace qpid {
+namespace sys {
+class Timer;
+}
+
namespace broker {
class DtxManager{
typedef boost::ptr_map<std::string, DtxWorkRecord> WorkMap;
- struct DtxCleanup : public sys::TimerTask
- {
- DtxManager& mgr;
- const std::string& xid;
-
- DtxCleanup(uint32_t timeout, DtxManager& mgr, const std::string& xid);
- void fire();
- };
-
WorkMap work;
// TransactionalStore* store;
AsyncTransactionalStore* asyncTxnStore;
@@ -71,11 +66,6 @@ public:
void setStore(AsyncTransactionalStore* const ats);
void setTimer(sys::Timer& t) { timer = &t; }
- // Used by cluster for replication.
- template<class F> void each(F f) const {
- for (WorkMap::const_iterator i = work.begin(); i != work.end(); ++i)
- f(*ptr_map_ptr(i));
- }
DtxWorkRecord* getWork(const std::string& xid);
bool exists(const std::string& xid);
static std::string convert(const framing::Xid& xid);
diff --git a/cpp/src/qpid/broker/DtxWorkRecord.cpp b/cpp/src/qpid/broker/DtxWorkRecord.cpp
index 924f953eb8..13eaf4e568 100644
--- a/cpp/src/qpid/broker/DtxWorkRecord.cpp
+++ b/cpp/src/qpid/broker/DtxWorkRecord.cpp
@@ -20,7 +20,10 @@
*/
#include "qpid/broker/DtxWorkRecord.h"
#include "qpid/broker/DtxManager.h"
+#include "qpid/broker/DtxTimeout.h"
#include "qpid/framing/reply_exceptions.h"
+#include "qpid/sys/Timer.h"
+
#include <boost/format.hpp>
#include <boost/mem_fn.hpp>
using boost::mem_fn;
@@ -39,6 +42,12 @@ DtxWorkRecord::~DtxWorkRecord()
}
}
+void DtxWorkRecord::setTimeout(boost::intrusive_ptr<DtxTimeout> t)
+{ timeout = t; }
+
+boost::intrusive_ptr<DtxTimeout> DtxWorkRecord::getTimeout()
+{ return timeout; }
+
bool DtxWorkRecord::prepare()
{
Mutex::ScopedLock locker(lock);
@@ -182,17 +191,3 @@ void DtxWorkRecord::timedout()
}
abort();
}
-
-size_t DtxWorkRecord::indexOf(const DtxBuffer::shared_ptr& buf) {
- Work::iterator i = std::find(work.begin(), work.end(), buf);
- if (i == work.end()) throw NotFoundException(
- QPID_MSG("Can't find DTX buffer for xid: " << buf->getXid()));
- return i - work.begin();
-}
-
-DtxBuffer::shared_ptr DtxWorkRecord::operator[](size_t i) const {
- if (i > work.size())
- throw NotFoundException(
- QPID_MSG("Can't find DTX buffer " << i << " for xid: " << xid));
- return work[i];
-}
diff --git a/cpp/src/qpid/broker/DtxWorkRecord.h b/cpp/src/qpid/broker/DtxWorkRecord.h
index 579579df2d..84344533d9 100644
--- a/cpp/src/qpid/broker/DtxWorkRecord.h
+++ b/cpp/src/qpid/broker/DtxWorkRecord.h
@@ -24,7 +24,6 @@
#include "qpid/broker/AsyncStore.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"
@@ -39,6 +38,8 @@
namespace qpid {
namespace broker {
+struct DtxTimeout;
+
/**
* Represents the work done under a particular distributed transaction
* across potentially multiple channels. Identified by a xid. Allows
@@ -74,19 +75,13 @@ public:
QPID_BROKER_EXTERN void add(DtxBuffer::shared_ptr ops);
void recover(std::auto_ptr<TPCTransactionContext> txn, DtxBuffer::shared_ptr ops);
void timedout();
- void setTimeout(boost::intrusive_ptr<DtxTimeout> t) { timeout = t; }
- boost::intrusive_ptr<DtxTimeout> getTimeout() { return timeout; }
+ void setTimeout(boost::intrusive_ptr<DtxTimeout> t);
+ boost::intrusive_ptr<DtxTimeout> getTimeout();
std::string getXid() const { return xid; }
bool isCompleted() const { return completed; }
bool isRolledback() const { return rolledback; }
bool isPrepared() const { return prepared; }
bool isExpired() const { return expired; }
-
- // Used by cluster update;
- size_t size() const { return work.size(); }
- DtxBuffer::shared_ptr operator[](size_t i) const;
- uint32_t getTimeout() const { return timeout? timeout->timeout : 0; }
- size_t indexOf(const DtxBuffer::shared_ptr&);
};
}} // qpid::broker
diff --git a/cpp/src/qpid/broker/Exchange.cpp b/cpp/src/qpid/broker/Exchange.cpp
index 2414981481..855a69af98 100644
--- a/cpp/src/qpid/broker/Exchange.cpp
+++ b/cpp/src/qpid/broker/Exchange.cpp
@@ -167,19 +167,19 @@ void Exchange::routeIVE(){
Exchange::Exchange (const string& _name, Manageable* parent, Broker* b) :
name(_name), durable(false), alternateUsers(0), persistenceId(0), sequence(false),
- sequenceNo(0), ive(false), mgmtExchange(0), brokerMgmtObject(0), broker(b), destroyed(false)
+ sequenceNo(0), ive(false), broker(b), destroyed(false)
{
if (parent != 0 && broker != 0)
{
ManagementAgent* agent = broker->getManagementAgent();
if (agent != 0)
{
- mgmtExchange = new _qmf::Exchange (agent, this, parent, _name);
+ mgmtExchange = _qmf::Exchange::shared_ptr(new _qmf::Exchange (agent, this, parent, _name));
mgmtExchange->set_durable(durable);
mgmtExchange->set_autoDelete(false);
agent->addObject(mgmtExchange, 0, durable);
if (broker)
- brokerMgmtObject = (qmf::org::apache::qpid::broker::Broker*) broker->GetManagementObject();
+ brokerMgmtObject = boost::dynamic_pointer_cast<qmf::org::apache::qpid::broker::Broker>(broker->GetManagementObject());
}
}
}
@@ -187,20 +187,20 @@ Exchange::Exchange (const string& _name, Manageable* parent, Broker* b) :
Exchange::Exchange(const string& _name, bool _durable, const qpid::framing::FieldTable& _args,
Manageable* parent, Broker* b)
: name(_name), durable(_durable), alternateUsers(0), persistenceId(0),
- args(_args), sequence(false), sequenceNo(0), ive(false), mgmtExchange(0), brokerMgmtObject(0), broker(b), destroyed(false)
+ args(_args), sequence(false), sequenceNo(0), ive(false), broker(b), destroyed(false)
{
if (parent != 0 && broker != 0)
{
ManagementAgent* agent = broker->getManagementAgent();
if (agent != 0)
{
- mgmtExchange = new _qmf::Exchange (agent, this, parent, _name);
+ mgmtExchange = _qmf::Exchange::shared_ptr(new _qmf::Exchange (agent, this, parent, _name));
mgmtExchange->set_durable(durable);
mgmtExchange->set_autoDelete(false);
mgmtExchange->set_arguments(ManagementAgent::toMap(args));
agent->addObject(mgmtExchange, 0, durable);
if (broker)
- brokerMgmtObject = (qmf::org::apache::qpid::broker::Broker*) broker->GetManagementObject();
+ brokerMgmtObject = boost::dynamic_pointer_cast<qmf::org::apache::qpid::broker::Broker>(broker->GetManagementObject());
}
}
@@ -212,8 +212,6 @@ Exchange::Exchange(const string& _name, bool _durable, const qpid::framing::Fiel
ive = _args.get(qpidIVE);
if (ive) {
- if (broker && broker->isInCluster())
- throw framing::NotImplementedException("Cannot use Initial Value Exchanges in a cluster");
QPID_LOG(debug, "Configured exchange " << _name << " with Initial Value");
}
}
@@ -227,6 +225,7 @@ Exchange::~Exchange ()
void Exchange::setAlternate(Exchange::shared_ptr _alternate)
{
alternate = _alternate;
+ alternate->incAlternateUsers();
if (mgmtExchange != 0) {
if (alternate.get() != 0)
mgmtExchange->set_altExchange(alternate->GetManagementObject()->getObjectId());
@@ -296,9 +295,9 @@ void Exchange::recoveryComplete(ExchangeRegistry& exchanges)
}
}
-ManagementObject* Exchange::GetManagementObject (void) const
+ManagementObject::shared_ptr Exchange::GetManagementObject (void) const
{
- return (ManagementObject*) mgmtExchange;
+ return mgmtExchange;
}
void Exchange::registerDynamicBridge(DynamicBridge* db, AsyncStore* const store)
@@ -347,16 +346,16 @@ void Exchange::propagateFedOp(const string& routingKey, const string& tags, cons
Exchange::Binding::Binding(const string& _key, Queue::shared_ptr _queue, Exchange* _parent,
FieldTable _args, const string& _origin, ConfigHandle _cfgHandle)
- : parent(_parent), queue(_queue), key(_key), args(_args), origin(_origin), cfgHandle(_cfgHandle), mgmtBinding(0)
+ : parent(_parent), queue(_queue), key(_key), args(_args), origin(_origin), cfgHandle(_cfgHandle)
{
}
Exchange::Binding::~Binding ()
{
if (mgmtBinding != 0) {
- ManagementObject* mo = queue->GetManagementObject();
+ _qmf::Queue::shared_ptr mo = boost::dynamic_pointer_cast<_qmf::Queue>(queue->GetManagementObject());
if (mo != 0)
- static_cast<_qmf::Queue*>(mo)->dec_bindingCount();
+ mo->dec_bindingCount();
mgmtBinding->resourceDestroy ();
}
}
@@ -369,25 +368,25 @@ void Exchange::Binding::startManagement()
if (broker != 0) {
ManagementAgent* agent = broker->getManagementAgent();
if (agent != 0) {
- ManagementObject* mo = queue->GetManagementObject();
+ _qmf::Queue::shared_ptr mo = boost::dynamic_pointer_cast<_qmf::Queue>(queue->GetManagementObject());
if (mo != 0) {
management::ObjectId queueId = mo->getObjectId();
- mgmtBinding = new _qmf::Binding
- (agent, this, (Manageable*) parent, queueId, key, ManagementAgent::toMap(args));
+ mgmtBinding = _qmf::Binding::shared_ptr(new _qmf::Binding
+ (agent, this, (Manageable*) parent, queueId, key, ManagementAgent::toMap(args)));
if (!origin.empty())
mgmtBinding->set_origin(origin);
agent->addObject(mgmtBinding);
- static_cast<_qmf::Queue*>(mo)->inc_bindingCount();
+ mo->inc_bindingCount();
}
}
}
}
}
-ManagementObject* Exchange::Binding::GetManagementObject () const
+ManagementObject::shared_ptr Exchange::Binding::GetManagementObject () const
{
- return (ManagementObject*) mgmtBinding;
+ return mgmtBinding;
}
uint64_t Exchange::Binding::getSize() { return 0; } // TODO: kpvdr: implement persistence
@@ -434,5 +433,10 @@ bool Exchange::routeWithAlternate(Deliverable& msg)
return msg.delivered;
}
+void Exchange::setArgs(const framing::FieldTable& newArgs) {
+ args = newArgs;
+ if (mgmtExchange) mgmtExchange->set_arguments(ManagementAgent::toMap(args));
+}
+
}}
diff --git a/cpp/src/qpid/broker/Exchange.h b/cpp/src/qpid/broker/Exchange.h
index df6d5d05a4..2c85e826ab 100644
--- a/cpp/src/qpid/broker/Exchange.h
+++ b/cpp/src/qpid/broker/Exchange.h
@@ -55,14 +55,14 @@ public:
const framing::FieldTable args;
std::string origin;
ConfigHandle cfgHandle;
- qmf::org::apache::qpid::broker::Binding* mgmtBinding;
+ qmf::org::apache::qpid::broker::Binding::shared_ptr mgmtBinding;
Binding(const std::string& key, boost::shared_ptr<Queue> queue, Exchange* parent = 0,
framing::FieldTable args = framing::FieldTable(), const std::string& origin = std::string(),
ConfigHandle cfgHandle = ConfigHandle());
~Binding();
void startManagement();
- management::ManagementObject* GetManagementObject() const;
+ management::ManagementObject::shared_ptr GetManagementObject() const;
// DataSource implementation - allows for persistence
uint64_t getSize();
@@ -170,8 +170,8 @@ protected:
}
};
- qmf::org::apache::qpid::broker::Exchange* mgmtExchange;
- qmf::org::apache::qpid::broker::Broker* brokerMgmtObject;
+ qmf::org::apache::qpid::broker::Exchange::shared_ptr mgmtExchange;
+ qmf::org::apache::qpid::broker::Broker::shared_ptr brokerMgmtObject;
public:
typedef boost::shared_ptr<Exchange> shared_ptr;
@@ -184,7 +184,8 @@ public:
const std::string& getName() const { return name; }
bool isDurable() { return durable; }
- qpid::framing::FieldTable& getArgs() { return args; }
+ QPID_BROKER_EXTERN const qpid::framing::FieldTable& getArgs() const { return args; }
+ QPID_BROKER_EXTERN void setArgs(const framing::FieldTable&);
QPID_BROKER_EXTERN Exchange::shared_ptr getAlternate() { return alternate; }
QPID_BROKER_EXTERN void setAlternate(Exchange::shared_ptr _alternate);
@@ -221,7 +222,7 @@ public:
static QPID_BROKER_EXTERN Exchange::shared_ptr decode(ExchangeRegistry& exchanges, framing::Buffer& buffer);
// Manageable entry points
- QPID_BROKER_EXTERN management::ManagementObject* GetManagementObject(void) const;
+ QPID_BROKER_EXTERN management::ManagementObject::shared_ptr GetManagementObject(void) const;
// Federation hooks
class DynamicBridge {
diff --git a/cpp/src/qpid/broker/ExchangeRegistry.cpp b/cpp/src/qpid/broker/ExchangeRegistry.cpp
index b31c7bd7b8..645918d526 100644
--- a/cpp/src/qpid/broker/ExchangeRegistry.cpp
+++ b/cpp/src/qpid/broker/ExchangeRegistry.cpp
@@ -29,20 +29,26 @@
#include "qpid/management/ManagementDirectExchange.h"
#include "qpid/management/ManagementTopicExchange.h"
#include "qpid/framing/reply_exceptions.h"
+#include "qmf/org/apache/qpid/broker/EventExchangeDeclare.h"
+#include "qmf/org/apache/qpid/broker/EventExchangeDelete.h"
using namespace qpid::broker;
using namespace qpid::sys;
using std::pair;
using std::string;
using qpid::framing::FieldTable;
+using qpid::management::ManagementAgent;
+namespace _qmf = qmf::org::apache::qpid::broker;
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){
+pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(
+ const string& name, const string& type, bool durable, const FieldTable& args,
+ Exchange::shared_ptr alternate, const string& connectionId, const string& userId)
+{
Exchange::shared_ptr exchange;
std::pair<Exchange::shared_ptr, bool> result;
{
@@ -73,31 +79,55 @@ pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, c
}
exchanges[name] = exchange;
result = std::pair<Exchange::shared_ptr, bool>(exchange, true);
+ if (alternate) exchange->setAlternate(alternate);
+ // Call exchangeCreate inside the lock to ensure correct ordering.
+ if (broker) broker->getConfigurationObservers().exchangeCreate(exchange);
} else {
result = std::pair<Exchange::shared_ptr, bool>(i->second, false);
}
+ if (broker && broker->getManagementAgent()) {
+ // Call raiseEvent inside the lock to ensure correct ordering.
+ broker->getManagementAgent()->raiseEvent(
+ _qmf::EventExchangeDeclare(
+ connectionId,
+ userId,
+ name,
+ type,
+ alternate ? alternate->getName() : string(),
+ durable,
+ false,
+ ManagementAgent::toMap(result.first->getArgs()),
+ result.second ? "created" : "existing"));
+ }
}
- if (broker && exchange) broker->getConfigurationObservers().exchangeCreate(exchange);
return result;
}
-void ExchangeRegistry::destroy(const string& name){
+void ExchangeRegistry::destroy(
+ const string& name, const string& connectionId, const string& userId)
+{
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 << "'"));
- Exchange::shared_ptr exchange;
{
RWlock::ScopedWlock locker(lock);
ExchangeMap::iterator i = exchanges.find(name);
if (i != exchanges.end()) {
- exchange = i->second;
+ if (broker) {
+ // Call exchangeDestroy and raiseEvent inside the lock to ensure
+ // correct ordering.
+ broker->getConfigurationObservers().exchangeDestroy(i->second);
+ if (broker->getManagementAgent())
+ broker->getManagementAgent()->raiseEvent(
+ _qmf::EventExchangeDelete(connectionId, userId, name));
+ }
i->second->destroy();
exchanges.erase(i);
+
}
}
- if (broker && exchange) broker->getConfigurationObservers().exchangeDestroy(exchange);
}
Exchange::shared_ptr ExchangeRegistry::find(const string& name){
diff --git a/cpp/src/qpid/broker/ExchangeRegistry.h b/cpp/src/qpid/broker/ExchangeRegistry.h
index c5d2483a23..df4c24da39 100644
--- a/cpp/src/qpid/broker/ExchangeRegistry.h
+++ b/cpp/src/qpid/broker/ExchangeRegistry.h
@@ -46,14 +46,23 @@ class ExchangeRegistry{
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 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(),
+ Exchange::shared_ptr alternate = Exchange::shared_ptr(),
+ const std::string& connectionId = std::string(),
+ const std::string& userId = std::string());
+
+ QPID_BROKER_EXTERN void destroy(
+ const std::string& name,
+ const std::string& connectionId = std::string(),
+ const std::string& userId = std::string());
+
QPID_BROKER_EXTERN Exchange::shared_ptr getDefault();
/**
diff --git a/cpp/src/qpid/broker/FanOutExchange.cpp b/cpp/src/qpid/broker/FanOutExchange.cpp
index 941d909778..4ccb44ce53 100644
--- a/cpp/src/qpid/broker/FanOutExchange.cpp
+++ b/cpp/src/qpid/broker/FanOutExchange.cpp
@@ -54,7 +54,7 @@ bool FanOutExchange::bind(Queue::shared_ptr queue, const string& /*key*/, const
bool propagate = false;
if (args == 0 || fedOp.empty() || fedOp == fedOpBind) {
- Binding::shared_ptr binding (new Binding ("", queue, this, FieldTable(), fedOrigin));
+ Binding::shared_ptr binding (new Binding ("", queue, this, args ? *args : FieldTable(), fedOrigin));
if (bindings.add_unless(binding, MatchQueue(queue))) {
binding->startManagement();
propagate = fedBinding.addOrigin(queue->getName(), fedOrigin);
diff --git a/cpp/src/qpid/broker/HeadersExchange.cpp b/cpp/src/qpid/broker/HeadersExchange.cpp
index 76ffa7a922..7edd54bc24 100644
--- a/cpp/src/qpid/broker/HeadersExchange.cpp
+++ b/cpp/src/qpid/broker/HeadersExchange.cpp
@@ -48,6 +48,7 @@ namespace {
const std::string empty;
// federation related args and values
+ const std::string QPID_RESERVED("qpid.");
const std::string qpidFedOp("qpid.fed.op");
const std::string qpidFedTags("qpid.fed.tags");
const std::string qpidFedOrigin("qpid.fed.origin");
@@ -200,8 +201,8 @@ bool HeadersExchange::bind(Queue::shared_ptr queue, const string& bindingKey, co
//matching (they are internally added properties
//controlling binding propagation but not relevant to
//actual routing)
- Binding::shared_ptr binding (new Binding (bindingKey, queue, this, extra_args));
- BoundKey bk(binding);
+ Binding::shared_ptr binding (new Binding (bindingKey, queue, this, args ? *args : FieldTable()));
+ BoundKey bk(binding, extra_args);
if (bindings.add_unless(bk, MatchArgs(queue, &extra_args))) {
binding->startManagement();
propagate = bk.fedBinding.addOrigin(queue->getName(), fedOrigin);
@@ -282,7 +283,7 @@ void HeadersExchange::route(Deliverable& msg)
Bindings::ConstPtr p = bindings.snapshot();
if (p.get()) {
for (std::vector<BoundKey>::const_iterator i = p->begin(); i != p->end(); ++i) {
- Matcher matcher(i->binding->args);
+ Matcher matcher(i->args);
msg.getMessage().processProperties(matcher);
if (matcher.matches()) {
b->push_back(i->binding);
@@ -298,7 +299,7 @@ bool HeadersExchange::isBound(Queue::shared_ptr queue, const string* const, cons
Bindings::ConstPtr p = bindings.snapshot();
if (p.get()){
for (std::vector<BoundKey>::const_iterator i = p->begin(); i != p->end(); ++i) {
- if ( (!args || equal((*i).binding->args, *args)) && (!queue || (*i).binding->queue == queue)) {
+ if ( (!args || equal((*i).args, *args)) && (!queue || (*i).binding->queue == queue)) {
return true;
}
}
@@ -315,10 +316,7 @@ void HeadersExchange::getNonFedArgs(const FieldTable* args, FieldTable& nonFedAr
for (qpid::framing::FieldTable::ValueMap::const_iterator i=args->begin(); i != args->end(); ++i)
{
- const string & name(i->first);
- if (name == qpidFedOp ||
- name == qpidFedTags ||
- name == qpidFedOrigin)
+ if (i->first.find(QPID_RESERVED) == 0)
{
continue;
}
diff --git a/cpp/src/qpid/broker/HeadersExchange.h b/cpp/src/qpid/broker/HeadersExchange.h
index 10bf9c8c0b..93e3929563 100644
--- a/cpp/src/qpid/broker/HeadersExchange.h
+++ b/cpp/src/qpid/broker/HeadersExchange.h
@@ -38,8 +38,9 @@ class HeadersExchange : public virtual Exchange {
struct BoundKey
{
Binding::shared_ptr binding;
+ qpid::framing::FieldTable args;
FedBinding fedBinding;
- BoundKey(Binding::shared_ptr binding_) : binding(binding_) {}
+ BoundKey(Binding::shared_ptr binding_, const qpid::framing::FieldTable& args_) : binding(binding_), args(args_) {}
};
struct MatchArgs
diff --git a/cpp/src/qpid/broker/LegacyLVQ.cpp b/cpp/src/qpid/broker/LegacyLVQ.cpp
deleted file mode 100644
index e69de29bb2..0000000000
--- a/cpp/src/qpid/broker/LegacyLVQ.cpp
+++ /dev/null
diff --git a/cpp/src/qpid/broker/LegacyLVQ.h b/cpp/src/qpid/broker/LegacyLVQ.h
deleted file mode 100644
index e69de29bb2..0000000000
--- a/cpp/src/qpid/broker/LegacyLVQ.h
+++ /dev/null
diff --git a/cpp/src/qpid/broker/Link.cpp b/cpp/src/qpid/broker/Link.cpp
index 9727040c9b..bb5d5cb678 100644
--- a/cpp/src/qpid/broker/Link.cpp
+++ b/cpp/src/qpid/broker/Link.cpp
@@ -30,8 +30,10 @@
#include "qpid/log/Statement.h"
#include "qpid/framing/enum.h"
#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/amqp_types.h"
#include "qpid/broker/AclModule.h"
#include "qpid/broker/Exchange.h"
+#include "qpid/broker/NameGenerator.h"
#include "qpid/UrlArray.h"
namespace qpid {
@@ -148,12 +150,12 @@ Link::Link(const string& _name,
host(_host), port(_port), transport(_transport),
durable(_durable),
authMechanism(_authMechanism), username(_username), password(_password),
- persistenceId(0), mgmtObject(0), broker(_broker), state(0),
+ persistenceId(0), broker(_broker), state(0),
visitCount(0),
currentInterval(1),
- closing(false),
reconnectNext(0), // Index of next address for reconnecting in url.
- channelCounter(1),
+ nextFreeChannel(1),
+ freeChannels(1, framing::CHANNEL_MAX),
connection(0),
agent(0),
listener(l),
@@ -166,19 +168,15 @@ Link::Link(const string& _name,
agent = broker->getManagementAgent();
if (agent != 0)
{
- mgmtObject = new _qmf::Link(agent, this, parent, name, durable);
+ mgmtObject = _qmf::Link::shared_ptr(new _qmf::Link(agent, this, parent, name, durable));
mgmtObject->set_host(host);
mgmtObject->set_port(port);
mgmtObject->set_transport(transport);
agent->addObject(mgmtObject, 0, durable);
}
}
- if (links->isPassive()) {
- setStateLH(STATE_PASSIVE);
- } else {
- setStateLH(STATE_WAITING);
- startConnectionLH();
- }
+ setStateLH(STATE_WAITING);
+ startConnectionLH();
broker->getTimer().add(timerTask);
if (failover) {
@@ -212,9 +210,6 @@ void Link::setStateLH (int newState)
state = newState;
- if (hideManagement())
- return;
-
switch (state)
{
case STATE_WAITING : mgmtObject->set_state("Waiting"); break;
@@ -222,7 +217,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;
+ case STATE_CLOSING : mgmtObject->set_state("Closing"); break;
}
}
@@ -233,40 +228,39 @@ 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, boost::lexical_cast<std::string>(port), transport,
+ broker->connect (name, host, boost::lexical_cast<std::string>(port), transport,
boost::bind (&Link::closed, this, _1, _2));
- QPID_LOG (debug, "Inter-broker link connecting to " << host << ":" << port);
+ QPID_LOG (info, "Inter-broker link connecting to " << host << ":" << port);
} catch(const std::exception& e) {
QPID_LOG(error, "Link connection to " << host << ":" << port << " failed: "
<< e.what());
setStateLH(STATE_WAITING);
- if (!hideManagement())
- mgmtObject->set_lastError (e.what());
+ mgmtObject->set_lastError (e.what());
}
}
void Link::established(Connection* c)
{
- if (state == STATE_PASSIVE) return;
stringstream addr;
addr << host << ":" << port;
QPID_LOG (info, "Inter-broker link established to " << addr.str());
- if (!hideManagement() && agent)
+ if (agent)
agent->raiseEvent(_qmf::EventBrokerLinkUp(addr.str()));
- bool isClosing = false;
+ bool isClosing = true;
{
Mutex::ScopedLock mutex(lock);
- setStateLH(STATE_OPERATIONAL);
- currentInterval = 1;
- visitCount = 0;
- connection = c;
- isClosing = closing;
+ if (state != STATE_CLOSING) {
+ isClosing = false;
+ setStateLH(STATE_OPERATIONAL);
+ currentInterval = 1;
+ visitCount = 0;
+ connection = c;
+ c->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this));
+ }
}
if (isClosing)
destroy();
- else // Process any IO tasks bridges added before established.
- c->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this));
}
@@ -291,11 +285,12 @@ class DetachedCallback : public SessionHandler::ErrorListener {
};
}
-void Link::opened() {
+void Link::opened()
+{
Mutex::ScopedLock mutex(lock);
- if (!connection) return;
+ if (!connection || state != STATE_OPERATIONAL) return;
- if (!hideManagement() && connection->GetManagementObject()) {
+ if (connection->GetManagementObject()) {
mgmtObject->set_connectionRef(connection->GetManagementObject()->getObjectId());
}
@@ -350,37 +345,43 @@ void Link::opened() {
}
}
+
+// called when connection attempt fails (see startConnectionLH)
void Link::closed(int, std::string text)
{
- Mutex::ScopedLock mutex(lock);
QPID_LOG (info, "Inter-broker link disconnected from " << host << ":" << port << " " << text);
- connection = 0;
+ bool isClosing = false;
+ {
+ Mutex::ScopedLock mutex(lock);
+
+ connection = 0;
- if (!hideManagement()) {
mgmtObject->set_connectionRef(qpid::management::ObjectId());
if (state == STATE_OPERATIONAL && agent) {
stringstream addr;
addr << host << ":" << port;
agent->raiseEvent(_qmf::EventBrokerLinkDown(addr.str()));
}
- }
- for (Bridges::iterator i = active.begin(); i != active.end(); i++) {
- (*i)->closed();
- created.push_back(*i);
- }
- active.clear();
+ for (Bridges::iterator i = active.begin(); i != active.end(); i++) {
+ (*i)->closed();
+ created.push_back(*i);
+ }
+ active.clear();
- if (state != STATE_FAILED && state != STATE_PASSIVE)
- {
- setStateLH(STATE_WAITING);
- if (!hideManagement())
+ if (state == STATE_CLOSING) {
+ isClosing = true;
+ } else if (state != STATE_FAILED) {
+ setStateLH(STATE_WAITING);
mgmtObject->set_lastError (text);
+ }
}
+ if (isClosing) destroy();
}
-// Called in connection IO thread, cleans up the connection before destroying Link
+// Cleans up the connection before destroying Link. Must be called in connection thread
+// if the connection is active. Caller Note well: may call "delete this"!
void Link::destroy ()
{
Bridges toDelete;
@@ -410,7 +411,9 @@ void Link::destroy ()
for (Bridges::iterator i = toDelete.begin(); i != toDelete.end(); i++)
(*i)->close();
toDelete.clear();
- listener(this); // notify LinkRegistry that this Link has been destroyed
+ // notify LinkRegistry that this Link has been destroyed. Will result in "delete
+ // this" if LinkRegistry is holding the last shared pointer to *this
+ listener(this);
}
void Link::add(Bridge::shared_ptr bridge)
@@ -452,7 +455,7 @@ void Link::ioThreadProcessing()
{
Mutex::ScopedLock mutex(lock);
- if (state != STATE_OPERATIONAL || closing)
+ if (state != STATE_OPERATIONAL)
return;
// check for bridge session errors and recover
@@ -489,9 +492,9 @@ void Link::ioThreadProcessing()
void Link::maintenanceVisit ()
{
Mutex::ScopedLock mutex(lock);
- if (closing) return;
- if (state == STATE_WAITING)
- {
+
+ switch (state) {
+ case STATE_WAITING:
visitCount++;
if (visitCount >= currentInterval)
{
@@ -504,11 +507,17 @@ void Link::maintenanceVisit ()
startConnectionLH();
}
}
+ break;
+
+ case STATE_OPERATIONAL:
+ if ((!active.empty() || !created.empty() || !cancellations.empty()) &&
+ connection && connection->isOpen())
+ connection->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this));
+ break;
+
+ default: // no-op for all other states
+ break;
}
- else if (state == STATE_OPERATIONAL &&
- (!active.empty() || !created.empty() || !cancellations.empty()) &&
- connection && connection->isOpen())
- connection->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this));
}
void Link::reconnectLH(const Address& a)
@@ -517,14 +526,13 @@ void Link::reconnectLH(const Address& a)
port = a.port;
transport = a.protocol;
- if (!hideManagement()) {
- stringstream errorString;
- errorString << "Failing over to " << a;
- mgmtObject->set_lastError(errorString.str());
- mgmtObject->set_host(host);
- mgmtObject->set_port(port);
- mgmtObject->set_transport(transport);
- }
+ stringstream errorString;
+ errorString << "Failing over to " << a;
+ mgmtObject->set_lastError(errorString.str());
+ mgmtObject->set_host(host);
+ mgmtObject->set_port(port);
+ mgmtObject->set_transport(transport);
+
startConnectionLH();
}
@@ -541,26 +549,56 @@ bool Link::tryFailoverLH() {
return false;
}
-// Management updates for a link are inconsistent in a cluster, so they are
-// suppressed.
-bool Link::hideManagement() const {
- return !mgmtObject || ( broker && broker->isInCluster());
+// Allocate channel from link free pool
+framing::ChannelId Link::nextChannel()
+{
+ Mutex::ScopedLock mutex(lock);
+ if (!freeChannels.empty()) {
+ // A free channel exists.
+ for (framing::ChannelId i = 1; i <= framing::CHANNEL_MAX; i++)
+ {
+ // extract proposed free channel
+ framing::ChannelId c = nextFreeChannel;
+ // calculate next free channel
+ if (framing::CHANNEL_MAX == nextFreeChannel)
+ nextFreeChannel = 1;
+ else
+ nextFreeChannel += 1;
+ // if proposed channel is free, use it
+ if (freeChannels.contains(c))
+ {
+ freeChannels -= c;
+ QPID_LOG(debug, "Link " << name << " allocates channel: " << c);
+ return c;
+ }
+ }
+ assert (false);
+ }
+
+ throw Exception(Msg() << "Link " << name << " channel pool is empty");
}
-uint Link::nextChannel()
+// Return channel to link free pool
+void Link::returnChannel(framing::ChannelId c)
{
Mutex::ScopedLock mutex(lock);
- if (channelCounter >= framing::CHANNEL_MAX)
- channelCounter = 1;
- return channelCounter++;
+ QPID_LOG(debug, "Link " << name << " frees channel: " << c);
+ freeChannels += c;
}
void Link::notifyConnectionForced(const string text)
{
- Mutex::ScopedLock mutex(lock);
- setStateLH(STATE_FAILED);
- if (!hideManagement())
- mgmtObject->set_lastError(text);
+ bool isClosing = false;
+ {
+ Mutex::ScopedLock mutex(lock);
+ if (state == STATE_CLOSING) {
+ isClosing = true;
+ } else {
+ setStateLH(STATE_FAILED);
+ mgmtObject->set_lastError(text);
+ }
+ }
+ if (isClosing) destroy();
}
void Link::setPersistenceId(uint64_t id) const
@@ -643,21 +681,32 @@ uint32_t Link::encodedSize() const
+ password.size() + 1;
}
-ManagementObject* Link::GetManagementObject (void) const
+ManagementObject::shared_ptr Link::GetManagementObject(void) const
{
- return (ManagementObject*) mgmtObject;
+ return mgmtObject;
}
void Link::close() {
QPID_LOG(debug, "Link::close(), link=" << name );
- Mutex::ScopedLock mutex(lock);
- 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));
+ bool destroy_now = false;
+ {
+ Mutex::ScopedLock mutex(lock);
+ if (state != STATE_CLOSING) {
+ int old_state = state;
+ setStateLH(STATE_CLOSING);
+ if (connection) {
+ //connection can only be closed on the connections own IO processing thread
+ connection->requestIOProcessing(boost::bind(&Link::destroy, this));
+ } else if (old_state == STATE_CONNECTING) {
+ // cannot destroy Link now since a connection request is outstanding.
+ // destroy the link after we get a response (see Link::established,
+ // Link::closed, Link::notifyConnectionForced, etc).
+ } else {
+ destroy_now = true;
+ }
}
}
+ if (destroy_now) destroy();
}
@@ -701,22 +750,6 @@ Manageable::status_t Link::ManagementMethod (uint32_t op, Args& args, string& te
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 "
- << host << ":" << port);
- }
- }
-}
-
-
/** utility to clean up connection resources correctly */
void Link::closeConnection( const std::string& reason)
{
@@ -752,28 +785,6 @@ namespace {
const std::string FAILOVER_INDEX("failover-index");
}
-void Link::getState(framing::FieldTable& state) const
-{
- state.clear();
- Mutex::ScopedLock mutex(lock);
- if (!url.empty()) {
- state.setString(FAILOVER_ADDRESSES, url.str());
- state.setInt(FAILOVER_INDEX, reconnectNext);
- }
-}
-
-void Link::setState(const framing::FieldTable& state)
-{
- Mutex::ScopedLock mutex(lock);
- if (state.isSet(FAILOVER_ADDRESSES)) {
- Url failovers(state.getAsString(FAILOVER_ADDRESSES));
- setUrl(failovers);
- }
- if (state.isSet(FAILOVER_INDEX)) {
- reconnectNext = state.getAsInt(FAILOVER_INDEX);
- }
-}
-
std::string Link::createName(const std::string& transport,
const std::string& host,
uint16_t port)
@@ -784,14 +795,6 @@ std::string Link::createName(const std::string& transport,
return linkName.str();
}
-
-bool Link::pendingConnection(const std::string& _host, uint16_t _port) const
-{
- Mutex::ScopedLock mutex(lock);
- return (isConnecting() && _port == port && _host == host);
-}
-
-
const std::string Link::exchangeTypeName("qpid.LinkExchange");
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/Link.h b/cpp/src/qpid/broker/Link.h
index f0cb90e73b..01ddc68d97 100644
--- a/cpp/src/qpid/broker/Link.h
+++ b/cpp/src/qpid/broker/Link.h
@@ -69,12 +69,11 @@ class Link : public PersistableConfig, public management::Manageable {
std::string username;
std::string password;
mutable uint64_t persistenceId;
- qmf::org::apache::qpid::broker::Link* mgmtObject;
+ qmf::org::apache::qpid::broker::Link::shared_ptr mgmtObject;
Broker* broker;
int state;
uint32_t visitCount;
uint32_t currentInterval;
- bool closing;
Url url; // URL can contain many addresses.
size_t reconnectNext; // Index for next re-connect attempt
@@ -82,7 +81,8 @@ class Link : public PersistableConfig, public management::Manageable {
Bridges created; // Bridges pending creation
Bridges active; // Bridges active
Bridges cancellations; // Bridges pending cancellation
- uint channelCounter;
+ framing::ChannelId nextFreeChannel;
+ RangeSet<framing::ChannelId> freeChannels;
Connection* connection;
management::ManagementAgent* agent;
boost::function<void(Link*)> listener;
@@ -97,7 +97,7 @@ class Link : public PersistableConfig, public management::Manageable {
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 int STATE_CLOSING = 6; // Waiting for outstanding connect to complete first
static const uint32_t MAX_INTERVAL = 32;
@@ -106,7 +106,6 @@ class Link : public PersistableConfig, public management::Manageable {
void destroy(); // Cleanup connection before link goes away
void ioThreadProcessing(); // Called on connection's IO thread by request
bool tryFailoverLH(); // Called during maintenance visit
- bool hideManagement() const;
void reconnectLH(const Address&); //called by LinkRegistry
// connection management (called by LinkRegistry)
@@ -115,7 +114,6 @@ class Link : public PersistableConfig, public management::Manageable {
void closed(int, std::string); // Called when connection goes away
void notifyConnectionForced(const std::string text);
void closeConnection(const std::string& reason);
- bool pendingConnection(const std::string& host, uint16_t port) const; // is Link trying to connect to this remote?
friend class LinkRegistry; // to call established, opened, closed
@@ -151,7 +149,8 @@ class Link : public PersistableConfig, public management::Manageable {
bool isDurable() { return durable; }
void maintenanceVisit ();
- uint nextChannel();
+ framing::ChannelId nextChannel(); // allocate channel from link free pool
+ void returnChannel(framing::ChannelId); // return channel to link free pool
void add(Bridge::shared_ptr);
void cancel(Bridge::shared_ptr);
@@ -165,7 +164,6 @@ class Link : public PersistableConfig, public management::Manageable {
std::string getPassword() { return password; }
Broker* getBroker() { return broker; }
- void setPassive(bool p);
bool isConnecting() const { return state == STATE_CONNECTING; }
// PersistableConfig:
@@ -181,17 +179,13 @@ class Link : public PersistableConfig, public management::Manageable {
static bool isEncodedLink(const std::string& key);
// Manageable entry points
- management::ManagementObject* GetManagementObject(void) const;
+ management::ManagementObject::shared_ptr GetManagementObject(void) const;
management::Manageable::status_t ManagementMethod(uint32_t, management::Args&, std::string&);
// manage the exchange owned by this link
static const std::string exchangeTypeName;
static boost::shared_ptr<Exchange> linkExchangeFactory(const std::string& name);
- // replicate internal state of this Link for clustering
- void getState(framing::FieldTable& state) const;
- void setState(const framing::FieldTable& state);
-
/** create a name for a link (if none supplied by user config) */
static std::string createName(const std::string& transport,
const std::string& host,
diff --git a/cpp/src/qpid/broker/LinkRegistry.cpp b/cpp/src/qpid/broker/LinkRegistry.cpp
index a79081b8ed..d048b9c05f 100644
--- a/cpp/src/qpid/broker/LinkRegistry.cpp
+++ b/cpp/src/qpid/broker/LinkRegistry.cpp
@@ -19,8 +19,10 @@
*
*/
#include "qpid/broker/LinkRegistry.h"
-#include "qpid/broker/Link.h"
+
+#include "qpid/broker/Broker.h"
#include "qpid/broker/Connection.h"
+#include "qpid/broker/Link.h"
#include "qpid/log/Statement.h"
#include <iostream>
#include <boost/format.hpp>
@@ -42,8 +44,8 @@ namespace _qmf = qmf::org::apache::qpid::broker;
// factored: The persistence element should be factored separately
LinkRegistry::LinkRegistry () :
broker(0),
-// parent(0), store(0), passive(false),
- parent(0), asyncStore(0), passive(false),
+// parent(0), store(0),
+ parent(0), asyncStore(0),
realm("")
{
}
@@ -60,7 +62,8 @@ class LinkRegistryConnectionObserver : public ConnectionObserver {
LinkRegistry::LinkRegistry (Broker* _broker) :
broker(_broker),
- parent(0), asyncStore(0), passive(false),
+// parent(0), store(0),
+ parent(0), asyncStore(0),
realm(broker->getOptions().realm)
{
broker->getConnectionObservers().add(
@@ -118,10 +121,9 @@ pair<Link::shared_ptr, bool> LinkRegistry::declare(const string& name,
boost::bind(&LinkRegistry::linkDestroyed, this, _1),
durable, authMechanism, username, password, broker,
parent, failover));
-// if (durable && store) store->create(*link);
- if (durable && asyncStore) {
-// store->create(*link);
- // TODO: kpvdr: async create config (link)
+ if (durable && asyncStore && !broker->inRecovery()) {
+ //store->create(*link);
+ // TODO: kpvdr: async create config (link)
}
links[name] = link;
pendingLinks[name] = link;
@@ -218,9 +220,8 @@ pair<Bridge::shared_ptr, bool> LinkRegistry::declare(const std::string& name,
args, init, queueName, altExchange));
bridges[name] = bridge;
link.add(bridge);
-// if (durable && store)
- if (durable && asyncStore) {
-// store->create(*bridge);
+ if (durable && asyncStore && !broker->inRecovery()) {
+ //store->create(*bridge);
// TODO: kpvdr: Async create config (bridge)
}
@@ -264,6 +265,7 @@ void LinkRegistry::destroyBridge(Bridge *bridge)
Link *link = b->second->getLink();
if (link) {
link->cancel(b->second);
+ link->returnChannel( bridge->getChannel() );
}
// if (b->second->isDurable())
if (b->second->isDurable()) {
@@ -283,38 +285,6 @@ AsyncStore* LinkRegistry::getStore() const {
return asyncStore;
}
-namespace {
- void extractHostPort(const std::string& connId, std::string *host, uint16_t *port)
- {
- // Extract host and port of remote broker from connection id string.
- //
- // TODO aconway 2011-02-01: centralize code that constructs/parses connection
- // management IDs. Currently sys:: protocol factories and IO plugins construct the
- // IDs and LinkRegistry parses them.
- // KAG: current connection id format assumed:
- // "localhost:port-remotehost:port". In the case of IpV6, the host addresses are
- // contained within brackets "[...]", example:
- // connId="[::1]:36859-[::1]:48603". Liberal use of "asserts" provided to alert us
- // if this assumption changes!
- size_t separator = connId.find('-');
- assert(separator != std::string::npos);
- std::string remote = connId.substr(separator+1, std::string::npos);
- separator = remote.rfind(":");
- assert(separator != std::string::npos);
- *host = remote.substr(0, separator);
- // IPv6 - host is bracketed by "[]", strip them
- if ((*host)[0] == '[' && (*host)[host->length() - 1] == ']') {
- *host = host->substr(1, host->length() - 2);
- }
- try {
- *port = boost::lexical_cast<uint16_t>(remote.substr(separator+1, std::string::npos));
- } catch (const boost::bad_lexical_cast&) {
- QPID_LOG(error, "Invalid format for connection identifier! '" << connId << "'");
- assert(false);
- }
- }
-}
-
/** find the Link that corresponds to the given connection */
Link::shared_ptr LinkRegistry::findLink(const std::string& connId)
{
@@ -334,19 +304,15 @@ void LinkRegistry::notifyConnection(const std::string& key, Connection* c)
// create a mapping from connection id to link
QPID_LOG(debug, "LinkRegistry::notifyConnection(); key=" << key );
std::string host;
- uint16_t port = 0;
- extractHostPort( key, &host, &port );
Link::shared_ptr link;
{
Mutex::ScopedLock locker(lock);
- for (LinkMap::iterator l = pendingLinks.begin(); l != pendingLinks.end(); ++l) {
- if (l->second->pendingConnection(host, port)) {
- link = l->second;
- pendingLinks.erase(l);
- connections[key] = link->getName();
- QPID_LOG(debug, "LinkRegistry:: found pending =" << link->getName());
- break;
- }
+ LinkMap::iterator l = pendingLinks.find(key);
+ if (l != pendingLinks.end()) {
+ link = l->second;
+ pendingLinks.erase(l);
+ connections[key] = link->getName();
+ QPID_LOG(debug, "LinkRegistry:: found pending =" << link->getName());
}
}
@@ -461,26 +427,4 @@ std::string LinkRegistry::getAuthIdentity(const std::string& key)
return link->getUsername();
}
-
-void LinkRegistry::setPassive(bool p)
-{
- Mutex::ScopedLock locker(lock);
- passive = p;
- 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);
- }
-}
-
-void LinkRegistry::eachLink(boost::function<void(boost::shared_ptr<Link>)> f) {
- Mutex::ScopedLock locker(lock);
- for (LinkMap::iterator i = links.begin(); i != links.end(); ++i) f(i->second);
-}
-
-void LinkRegistry::eachBridge(boost::function<void(boost::shared_ptr<Bridge>)> f) {
- Mutex::ScopedLock locker(lock);
- for (BridgeMap::iterator i = bridges.begin(); i != bridges.end(); ++i) f(i->second);
-}
-
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/LinkRegistry.h b/cpp/src/qpid/broker/LinkRegistry.h
index 17f45a60c8..6689ceda0f 100644
--- a/cpp/src/qpid/broker/LinkRegistry.h
+++ b/cpp/src/qpid/broker/LinkRegistry.h
@@ -54,7 +54,6 @@ namespace broker {
management::Manageable* parent;
// MessageStore* store;
AsyncStore* asyncStore;
- bool passive;
std::string realm;
boost::shared_ptr<Link> findLink(const std::string& key);
@@ -147,20 +146,6 @@ namespace broker {
QPID_BROKER_EXTERN std::string getPassword (const std::string& key);
QPID_BROKER_EXTERN std::string getHost (const std::string& key);
QPID_BROKER_EXTERN uint16_t getPort (const std::string& key);
-
- /**
- * 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.
- */
- QPID_BROKER_EXTERN void setPassive(bool);
- QPID_BROKER_EXTERN bool isPassive() { return passive; }
-
- /** Iterate over each link in the registry. Used for cluster updates. */
- QPID_BROKER_EXTERN void eachLink(boost::function<void(boost::shared_ptr<Link>)> f);
- /** Iterate over each bridge in the registry. Used for cluster updates. */
- QPID_BROKER_EXTERN void eachBridge(boost::function<void(boost::shared_ptr< Bridge>)> f);
};
}
}
diff --git a/cpp/src/qpid/broker/Lvq.cpp b/cpp/src/qpid/broker/Lvq.cpp
index f5e66c8a74..9d6923e560 100644
--- a/cpp/src/qpid/broker/Lvq.cpp
+++ b/cpp/src/qpid/broker/Lvq.cpp
@@ -20,7 +20,6 @@
*/
#include "Lvq.h"
#include "MessageMap.h"
-#include "qpid/sys/ClusterSafe.h"
#include "qpid/sys/Monitor.h"
namespace qpid {
@@ -35,7 +34,6 @@ Lvq::Lvq(const std::string& n, std::auto_ptr<MessageMap> m, const QueueSettings&
void Lvq::push(Message& message, bool isRecovery)
{
- qpid::sys::assertClusterSafe();
QueueListeners::NotificationSet copy;
Message old;
bool removed;
diff --git a/cpp/src/qpid/broker/Message.cpp b/cpp/src/qpid/broker/Message.cpp
index c48e9bcfa4..431f4291ff 100644
--- a/cpp/src/qpid/broker/Message.cpp
+++ b/cpp/src/qpid/broker/Message.cpp
@@ -131,6 +131,18 @@ uint64_t Message::getTtl() const
}
}
+bool Message::getTtl(uint64_t ttl) const
+{
+ if (encoding->getTtl(ttl) && expiration < FAR_FUTURE) {
+ sys::Duration remaining(sys::AbsTime::now(), getExpiration());
+ // convert from ns to ms; set to 0 if expired
+ ttl = (int64_t(remaining) >= 1000000 ? int64_t(remaining)/1000000 : 0);
+ return true;
+ } else {
+ return false;
+ }
+}
+
void Message::computeExpiration(const boost::intrusive_ptr<ExpiryPolicy>& e)
{
//TODO: this is still quite 0-10 specific...
diff --git a/cpp/src/qpid/broker/Message.h b/cpp/src/qpid/broker/Message.h
index 599819d7b6..b999a7d5a3 100644
--- a/cpp/src/qpid/broker/Message.h
+++ b/cpp/src/qpid/broker/Message.h
@@ -61,7 +61,6 @@ public:
virtual std::string getPropertyAsString(const std::string& key) const = 0;
virtual std::string getAnnotationAsString(const std::string& key) const = 0;
virtual bool getTtl(uint64_t&) const = 0;
- virtual bool hasExpiration() const = 0;
virtual std::string getContent() const = 0;
virtual void processProperties(MapHandler&) const = 0;
virtual std::string getUserId() const = 0;
@@ -92,6 +91,7 @@ public:
sys::AbsTime getExpiration() const { return expiration; }
void setExpiration(sys::AbsTime exp) { expiration = exp; }
uint64_t getTtl() const;
+ bool getTtl(uint64_t) const;
/** set the timestamp delivery property to the current time-of-day */
QPID_BROKER_EXTERN void setTimestamp();
@@ -125,7 +125,7 @@ public:
QPID_BROKER_EXTERN const qpid::types::Variant::Map& getAnnotations() const;
std::string getUserId() const;
- QPID_BROKER_EXTERN std::string getContent() const;//TODO: may be better to get rid of this...
+ QPID_BROKER_EXTERN std::string getContent() const;//Used for ha, management, when content needs to be decoded
QPID_BROKER_EXTERN boost::intrusive_ptr<AsyncCompletion> getIngressCompletion() const;
QPID_BROKER_EXTERN boost::intrusive_ptr<PersistableMessage> getPersistentContext() const;
diff --git a/cpp/src/qpid/broker/MessageGroupManager.cpp b/cpp/src/qpid/broker/MessageGroupManager.cpp
index 47e40a4794..c083e4ee0f 100644
--- a/cpp/src/qpid/broker/MessageGroupManager.cpp
+++ b/cpp/src/qpid/broker/MessageGroupManager.cpp
@@ -302,19 +302,6 @@ void MessageGroupManager::setDefaults(const std::string& groupId) // static
defaultGroupId = groupId;
}
-/** Cluster replication:
-
- state map format:
-
- { "group-state": [ {"name": <group-name>,
- "owner": <consumer-name>-or-empty,
- "acquired-ct": <acquired count>,
- "positions": [Seqnumbers, ... ]},
- {...}
- ]
- }
-*/
-
namespace {
const std::string GROUP_NAME("name");
const std::string GROUP_OWNER("owner");
@@ -324,100 +311,3 @@ namespace {
const std::string GROUP_STATE("group-state");
}
-
-/** Runs on UPDATER to snapshot current state */
-void MessageGroupManager::getState(qpid::framing::FieldTable& state ) const
-{
- using namespace qpid::framing;
- state.clear();
- framing::Array groupState(TYPE_CODE_MAP);
- for (GroupMap::const_iterator g = messageGroups.begin();
- g != messageGroups.end(); ++g) {
-
- framing::FieldTable group;
- group.setString(GROUP_NAME, g->first);
- group.setString(GROUP_OWNER, g->second.owner);
- group.setInt(GROUP_ACQUIRED_CT, g->second.acquired);
- framing::Array positions(TYPE_CODE_UINT32);
- framing::Array acquiredMsgs(TYPE_CODE_BOOLEAN);
- for (GroupState::MessageFifo::const_iterator p = g->second.members.begin();
- p != g->second.members.end(); ++p) {
- positions.push_back(framing::Array::ValuePtr(new IntegerValue( p->position )));
- acquiredMsgs.push_back(framing::Array::ValuePtr(new BoolValue( p->acquired )));
- }
- group.setArray(GROUP_POSITIONS, positions);
- group.setArray(GROUP_ACQUIRED_MSGS, acquiredMsgs);
- groupState.push_back(framing::Array::ValuePtr(new FieldTableValue(group)));
- }
- state.setArray(GROUP_STATE, groupState);
-
- QPID_LOG(debug, "Queue \"" << qName << "\": replicating message group state, key=" << groupIdHeader);
-}
-
-
-/** called on UPDATEE to set state from snapshot */
-void MessageGroupManager::setState(const qpid::framing::FieldTable& state)
-{
- using namespace qpid::framing;
- messageGroups.clear();
- freeGroups.clear();
- cachedGroup = 0;
-
- framing::Array groupState(TYPE_CODE_MAP);
-
- bool ok = state.getArray(GROUP_STATE, groupState);
- if (!ok) {
- QPID_LOG(error, "Unable to find message group state information for queue \"" <<
- qName << "\": cluster inconsistency error!");
- return;
- }
-
- for (framing::Array::const_iterator g = groupState.begin();
- g != groupState.end(); ++g) {
- framing::FieldTable group;
- ok = framing::getEncodedValue<FieldTable>(*g, group);
- if (!ok) {
- QPID_LOG(error, "Invalid message group state information for queue \"" <<
- qName << "\": table encoding error!");
- return;
- }
- MessageGroupManager::GroupState state;
- if (!group.isSet(GROUP_NAME) || !group.isSet(GROUP_OWNER) || !group.isSet(GROUP_ACQUIRED_CT)) {
- QPID_LOG(error, "Invalid message group state information for queue \"" <<
- qName << "\": fields missing error!");
- return;
- }
- state.group = group.getAsString(GROUP_NAME);
- state.owner = group.getAsString(GROUP_OWNER);
- state.acquired = group.getAsInt(GROUP_ACQUIRED_CT);
- framing::Array positions(TYPE_CODE_UINT32);
- ok = group.getArray(GROUP_POSITIONS, positions);
- if (!ok) {
- QPID_LOG(error, "Invalid message group state information for queue \"" <<
- qName << "\": position encoding error!");
- return;
- }
- framing::Array acquiredMsgs(TYPE_CODE_BOOLEAN);
- ok = group.getArray(GROUP_ACQUIRED_MSGS, acquiredMsgs);
- if (!ok || positions.count() != acquiredMsgs.count()) {
- QPID_LOG(error, "Invalid message group state information for queue \"" <<
- qName << "\": acquired flag encoding error!");
- return;
- }
-
- Array::const_iterator a = acquiredMsgs.begin();
- for (Array::const_iterator p = positions.begin(); p != positions.end(); ++p) {
- GroupState::MessageState mState((*p)->getIntegerValue<uint32_t, 4>());
- mState.acquired = (*a++)->getIntegerValue<bool>();
- state.members.push_back(mState);
- }
-
- messageGroups[state.group] = state;
- if (!state.owned()) {
- assert(state.members.size());
- freeGroups[state.members.front().position] = &messageGroups[state.group];
- }
- }
-
- QPID_LOG(debug, "Queue \"" << qName << "\": message group state replicated, key =" << groupIdHeader)
-}
diff --git a/cpp/src/qpid/broker/MessageGroupManager.h b/cpp/src/qpid/broker/MessageGroupManager.h
index fe39e007b5..bf45e776c8 100644
--- a/cpp/src/qpid/broker/MessageGroupManager.h
+++ b/cpp/src/qpid/broker/MessageGroupManager.h
@@ -25,11 +25,12 @@
/* for managing message grouping on Queues */
#include "qpid/broker/BrokerImportExport.h"
-#include "qpid/broker/StatefulQueueObserver.h"
+#include "qpid/broker/QueueObserver.h"
#include "qpid/broker/MessageDistributor.h"
#include "qpid/framing/SequenceNumber.h"
#include "qpid/sys/unordered_map.h"
+#include "boost/shared_ptr.hpp"
#include <deque>
namespace qpid {
@@ -39,8 +40,9 @@ class QueueObserver;
struct QueueSettings;
class MessageDistributor;
class Messages;
+class Consumer;
-class MessageGroupManager : public StatefulQueueObserver, public MessageDistributor
+class MessageGroupManager : public QueueObserver, public MessageDistributor
{
static std::string defaultGroupId; // assigned if no group id header present
@@ -101,10 +103,10 @@ class MessageGroupManager : public StatefulQueueObserver, public MessageDistribu
MessageGroupManager(const std::string& header, const std::string& _qName,
Messages& container, unsigned int _timestamp=0 )
- : StatefulQueueObserver(std::string("MessageGroupManager:") + header),
- groupIdHeader( header ), timestamp(_timestamp), messages(container), qName(_qName),
- hits(0), misses(0),
- lastMsg(0), cachedGroup(0) {}
+ : groupIdHeader( header ), timestamp(_timestamp), messages(container),
+ qName(_qName),
+ hits(0), misses(0),
+ lastMsg(0), cachedGroup(0) {}
virtual ~MessageGroupManager();
// QueueObserver iface
@@ -114,8 +116,6 @@ class MessageGroupManager : public StatefulQueueObserver, public MessageDistribu
void dequeued( const Message& qm );
void consumerAdded( const Consumer& ) {};
void consumerRemoved( const Consumer& ) {};
- void getState(qpid::framing::FieldTable& state ) const;
- void setState(const qpid::framing::FieldTable&);
// MessageDistributor iface
bool acquire(const std::string& c, Message& );
diff --git a/cpp/src/qpid/broker/MessageStore.h b/cpp/src/qpid/broker/MessageStore.h
index 5e339574fd..d6a7cea18b 100644
--- a/cpp/src/qpid/broker/MessageStore.h
+++ b/cpp/src/qpid/broker/MessageStore.h
@@ -49,20 +49,6 @@ class MessageStore : public TransactionalStore, public Recoverable {
public:
/**
- * If called after initialization but before recovery, will discard the database
- * content and reinitialize as though it were a new installation. If the parameter
- * saveStoreContent is true, the content of the store will be saved in such a way
- * that the truncate can be reversed. This is used when cluster nodes recover and
- * must get their content from a cluster sync rather than directly from the store.
- *
- * @param saveStoreContent If true, will move content of the store to a backup
- * location where they may be restored later if needed. It is
- * not necessary to save more than one prior version of the
- * store.
- */
- virtual void truncateInit(const bool saveStoreContent = false) = 0;
-
- /**
* Record the existence of a durable queue
*/
virtual void create(PersistableQueue& queue,
diff --git a/cpp/src/qpid/broker/MessageStoreModule.cpp b/cpp/src/qpid/broker/MessageStoreModule.cpp
index 4309ee8524..9214f5a732 100644
--- a/cpp/src/qpid/broker/MessageStoreModule.cpp
+++ b/cpp/src/qpid/broker/MessageStoreModule.cpp
@@ -44,11 +44,6 @@ MessageStoreModule::~MessageStoreModule()
bool MessageStoreModule::init(const Options*) { return true; }
-void MessageStoreModule::truncateInit(const bool pushDownStoreFiles)
-{
- TRANSFER_EXCEPTION(store->truncateInit(pushDownStoreFiles));
-}
-
void MessageStoreModule::create(PersistableQueue& queue, const FieldTable& args)
{
TRANSFER_EXCEPTION(store->create(queue, args));
diff --git a/cpp/src/qpid/broker/MessageStoreModule.h b/cpp/src/qpid/broker/MessageStoreModule.h
index e5c271f4fa..89d6ebdecb 100644
--- a/cpp/src/qpid/broker/MessageStoreModule.h
+++ b/cpp/src/qpid/broker/MessageStoreModule.h
@@ -44,7 +44,6 @@ class MessageStoreModule : public MessageStore
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);
diff --git a/cpp/src/qpid/broker/Messages.h b/cpp/src/qpid/broker/Messages.h
index a94ac7e0bf..cd846a4973 100644
--- a/cpp/src/qpid/broker/Messages.h
+++ b/cpp/src/qpid/broker/Messages.h
@@ -91,13 +91,6 @@ class Messages
virtual Message* find(const QueueCursor&) = 0;
/**
- * Add an already acquired message to the queue.
- * Used by a cluster updatee to replicate acquired messages from the updater.
- * Only need be implemented by subclasses that keep track of
- * acquired messages.
- */
- //virtual void updateAcquired(const QueuedMessage&) { }
- /**
* Apply, the functor to each message held
*/
virtual void foreach(Functor) = 0;
diff --git a/cpp/src/qpid/broker/NullMessageStore.cpp b/cpp/src/qpid/broker/NullMessageStore.cpp
index 209941875a..a038c4db63 100644
--- a/cpp/src/qpid/broker/NullMessageStore.cpp
+++ b/cpp/src/qpid/broker/NullMessageStore.cpp
@@ -54,8 +54,6 @@ NullMessageStore::NullMessageStore() : nextPersistenceId(1) {}
bool NullMessageStore::init(const Options* /*options*/) {return true;}
-void NullMessageStore::truncateInit(const bool /*pushDownStoreFiles*/) {}
-
void NullMessageStore::create(PersistableQueue& queue, const framing::FieldTable& /*args*/)
{
queue.setPersistenceId(nextPersistenceId++);
diff --git a/cpp/src/qpid/broker/NullMessageStore.h b/cpp/src/qpid/broker/NullMessageStore.h
index 799bf6b368..4b108e0331 100644
--- a/cpp/src/qpid/broker/NullMessageStore.h
+++ b/cpp/src/qpid/broker/NullMessageStore.h
@@ -47,7 +47,6 @@ class QPID_BROKER_CLASS_EXTERN NullMessageStore : public MessageStore
QPID_BROKER_EXTERN NullMessageStore();
QPID_BROKER_EXTERN virtual bool init(const Options* options);
- QPID_BROKER_EXTERN virtual void truncateInit(const bool pushDownStoreFiles = false);
QPID_BROKER_EXTERN virtual std::auto_ptr<TransactionContext> begin();
QPID_BROKER_EXTERN virtual std::auto_ptr<TPCTransactionContext> begin(const std::string& xid);
QPID_BROKER_EXTERN virtual void prepare(TPCTransactionContext& txn);
diff --git a/cpp/src/qpid/broker/Protocol.cpp b/cpp/src/qpid/broker/Protocol.cpp
new file mode 100644
index 0000000000..e236698142
--- /dev/null
+++ b/cpp/src/qpid/broker/Protocol.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 "Protocol.h"
+#include "qpid/sys/ConnectionCodec.h"
+#include "qpid/broker/amqp_0_10/MessageTransfer.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace broker {
+
+qpid::sys::ConnectionCodec* ProtocolRegistry::create(const qpid::framing::ProtocolVersion& v, qpid::sys::OutputControl& o, const std::string& id, const qpid::sys::SecuritySettings& s)
+{
+ qpid::sys::ConnectionCodec* codec = 0;
+ for (Protocols::const_iterator i = protocols.begin(); !codec && i != protocols.end(); ++i) {
+ codec = i->second->create(v, o, id, s);
+ }
+ return codec;
+}
+boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> ProtocolRegistry::translate(const Message& m)
+{
+ boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> transfer;
+ const qpid::broker::amqp_0_10::MessageTransfer* ptr = dynamic_cast<const qpid::broker::amqp_0_10::MessageTransfer*>(&m.getEncoding());
+ if (ptr) transfer = boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer>(ptr);
+ for (Protocols::const_iterator i = protocols.begin(); !transfer && i != protocols.end(); ++i) {
+ transfer = i->second->translate(m);
+ }
+ if (!transfer) throw new Exception("Could not convert message into 0-10");
+ return transfer;
+}
+boost::shared_ptr<RecoverableMessage> ProtocolRegistry::recover(qpid::framing::Buffer& b)
+{
+ boost::shared_ptr<RecoverableMessage> msg;
+ for (Protocols::const_iterator i = protocols.begin(); !msg && i != protocols.end(); ++i) {
+ msg = i->second->recover(b);
+ }
+ return msg;
+}
+
+ProtocolRegistry::~ProtocolRegistry()
+{
+ for (Protocols::const_iterator i = protocols.begin(); i != protocols.end(); ++i) {
+ delete i->second;
+ }
+ protocols.clear();
+}
+void ProtocolRegistry::add(const std::string& key, Protocol* protocol)
+{
+ protocols[key] = protocol;
+ QPID_LOG(info, "Loaded protocol " << key);
+}
+
+}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/Protocol.h b/cpp/src/qpid/broker/Protocol.h
new file mode 100644
index 0000000000..2f268748fb
--- /dev/null
+++ b/cpp/src/qpid/broker/Protocol.h
@@ -0,0 +1,82 @@
+#ifndef QPID_BROKER_PROTOCOL_H
+#define QPID_BROKER_PROTOCOL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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 <boost/shared_ptr.hpp>
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+namespace sys {
+class ConnectionCodec;
+class OutputControl;
+struct SecuritySettings;
+}
+namespace framing {
+class Buffer;
+class ProtocolVersion;
+}
+namespace broker {
+class Message;
+class RecoverableMessage;
+namespace amqp_0_10 {
+class MessageTransfer;
+}
+
+/**
+ * A simple abstraction allowing pluggable protocol(s)
+ * (versions). AMQP 0-10 is considered the default. Alternatives must
+ * provide a ConnectionCodec for encoding/decoding the protocol in
+ * full, a means of translating the native message format of that
+ * protocol into AMQP 0-10 and a means of recovering durable messages
+ * from disk.
+ */
+class Protocol
+{
+ public:
+ virtual ~Protocol() {}
+ virtual qpid::sys::ConnectionCodec* create(const qpid::framing::ProtocolVersion&, qpid::sys::OutputControl&, const std::string&, const qpid::sys::SecuritySettings&) = 0;
+ virtual boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> translate(const Message&) = 0;
+ virtual boost::shared_ptr<RecoverableMessage> recover(qpid::framing::Buffer&) = 0;
+
+ private:
+};
+
+class ProtocolRegistry : public Protocol
+{
+ public:
+ qpid::sys::ConnectionCodec* create(const qpid::framing::ProtocolVersion&, qpid::sys::OutputControl&, const std::string&, const qpid::sys::SecuritySettings&);
+ boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> translate(const Message&);
+ boost::shared_ptr<RecoverableMessage> recover(qpid::framing::Buffer&);
+
+ ~ProtocolRegistry();
+ void add(const std::string&, Protocol*);
+ private:
+ //name may be useful for descriptive purposes or even for some
+ //limited manipulation of ordering
+ typedef std::map<std::string, Protocol*> Protocols;
+ Protocols protocols;
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_PROTOCOL_H*/
diff --git a/cpp/src/qpid/broker/Queue.cpp b/cpp/src/qpid/broker/Queue.cpp
index f595b81724..bab6f2ea55 100644
--- a/cpp/src/qpid/broker/Queue.cpp
+++ b/cpp/src/qpid/broker/Queue.cpp
@@ -44,9 +44,9 @@
#include "qpid/framing/reply_exceptions.h"
#include "qpid/framing/FieldTable.h"
#include "qpid/framing/FieldValue.h"
-#include "qpid/sys/ClusterSafe.h"
#include "qpid/sys/Monitor.h"
#include "qpid/sys/Time.h"
+#include "qpid/sys/Timer.h"
#include "qpid/types/Variant.h"
#include "qmf/org/apache/qpid/broker/ArgsQueuePurge.h"
#include "qmf/org/apache/qpid/broker/ArgsQueueReroute.h"
@@ -79,8 +79,8 @@ namespace
{
inline void mgntEnqStats(const Message& msg,
- _qmf::Queue* mgmtObject,
- _qmf::Broker* brokerMgmtObject)
+ _qmf::Queue::shared_ptr mgmtObject,
+ _qmf::Broker::shared_ptr brokerMgmtObject)
{
if (mgmtObject != 0) {
_qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics();
@@ -103,8 +103,8 @@ inline void mgntEnqStats(const Message& msg,
}
inline void mgntDeqStats(const Message& msg,
- _qmf::Queue* mgmtObject,
- _qmf::Broker* brokerMgmtObject)
+ _qmf::Queue::shared_ptr mgmtObject,
+ _qmf::Broker::shared_ptr brokerMgmtObject)
{
if (mgmtObject != 0){
_qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics();
@@ -166,13 +166,11 @@ void Queue::TxPublish::rollback() throw()
}
Queue::Queue(const string& _name, const QueueSettings& _settings,
-// MessageStore* const _store,
AsyncStore* const _asyncStore,
Manageable* parent,
Broker* b) :
name(_name),
-// store(_store),
asyncStore(_asyncStore),
owner(0),
consumerCount(0),
@@ -183,8 +181,6 @@ Queue::Queue(const string& _name, const QueueSettings& _settings,
messages(new MessageDeque()),
persistenceId(0),
settings(b ? merge(_settings, b->getOptions()) : _settings),
- mgmtObject(0),
- brokerMgmtObject(0),
eventMode(0),
broker(b),
deleted(false),
@@ -199,27 +195,24 @@ Queue::Queue(const string& _name, const QueueSettings& _settings,
qpid::amqp_0_10::translate(settings.asMap(), encodableSettings);
if (parent != 0 && broker != 0) {
ManagementAgent* agent = broker->getManagementAgent();
-
if (agent != 0) {
-// mgmtObject = new _qmf::Queue(agent, this, parent, _name, _store != 0, settings.autodelete);
- mgmtObject = new _qmf::Queue(agent, this, parent, _name, _asyncStore != 0, settings.autodelete);
+ mgmtObject = _qmf::Queue::shared_ptr(
+ new _qmf::Queue(agent, this, parent, _name, _asyncStore != 0, settings.autodelete));
mgmtObject->set_arguments(settings.asMap());
-// agent->addObject(mgmtObject, 0, store != 0);
agent->addObject(mgmtObject, 0, asyncStore != 0);
- brokerMgmtObject = (qmf::org::apache::qpid::broker::Broker*) broker->GetManagementObject();
+ brokerMgmtObject = boost::dynamic_pointer_cast<_qmf::Broker>(broker->GetManagementObject());
if (brokerMgmtObject)
brokerMgmtObject->inc_queueCount();
}
}
+
+ if ( settings.isBrowseOnly ) {
+ QPID_LOG ( info, "Queue " << name << " is browse-only." );
+ }
}
Queue::~Queue()
{
- if (mgmtObject != 0) {
- mgmtObject->resourceDestroy();
- if (brokerMgmtObject)
- brokerMgmtObject->dec_queueCount();
- }
}
bool isLocalTo(const OwnershipToken* token, const Message& msg)
@@ -246,9 +239,6 @@ void Queue::deliver(Message msg, TxBuffer* txn){
//'link' for whatever protocol is used; that would let protocol
//specific stuff be kept out the queue
- // Check for deferred delivery in a cluster.
- if (broker && broker->deferDelivery(name, msg))
- return;
if (broker::amqp_0_10::MessageTransfer::isImmediateDeliveryRequired(msg) && getConsumerCount() == 0) {
if (alternateExchange) {
DeliverableMessage deliverable(msg, 0);
@@ -307,7 +297,6 @@ void Queue::process(Message& msg)
void Queue::release(const QueueCursor& position, bool markRedelivered)
{
- assertClusterSafe();
QueueListeners::NotificationSet copy;
{
Mutex::ScopedLock locker(messageLock);
@@ -333,7 +322,6 @@ bool Queue::dequeueMessageAt(const SequenceNumber& position)
boost::intrusive_ptr<PersistableMessage> pmsg;
{
Mutex::ScopedLock locker(messageLock);
- assertClusterSafe();
QPID_LOG(debug, "Attempting to dequeue message at " << position);
QueueCursor cursor;
Message* msg = messages->find(position, &cursor);
@@ -353,7 +341,6 @@ bool Queue::dequeueMessageAt(const SequenceNumber& position)
bool Queue::acquire(const QueueCursor& position, const std::string& consumer)
{
Mutex::ScopedLock locker(messageLock);
- assertClusterSafe();
Message* msg;
msg = messages->find(position);
@@ -375,12 +362,13 @@ bool Queue::acquire(const QueueCursor& position, const std::string& consumer)
bool Queue::getNextMessage(Message& m, Consumer::shared_ptr& c)
{
- checkNotDeleted(c);
+ if (!checkNotDeleted(c)) return false;
QueueListeners::NotificationSet set;
while (true) {
//TODO: reduce lock scope
Mutex::ScopedLock locker(messageLock);
- Message* msg = messages->next(*c);
+ QueueCursor cursor = c->getCursor(); // Save current position.
+ Message* msg = messages->next(*c); // Advances c.
if (msg) {
if (msg->hasExpired()) {
QPID_LOG(debug, "Message expired from queue '" << name << "'");
@@ -419,6 +407,7 @@ bool Queue::getNextMessage(Message& m, Consumer::shared_ptr& c)
} else {
//message(s) are available but consumer hasn't got enough credit
QPID_LOG(debug, "Consumer can't currently accept message from '" << name << "'");
+ c->setCursor(cursor); // Restore cursor, will try again with credit
if (c->preAcquires()) {
//let someone else try
listeners.populate(set);
@@ -480,7 +469,6 @@ bool Queue::find(SequenceNumber pos, Message& msg) const
void Queue::consume(Consumer::shared_ptr c, bool requestExclusive)
{
- assertClusterSafe();
{
Mutex::ScopedLock locker(messageLock);
// NOTE: consumerCount is actually a count of all
@@ -488,6 +476,11 @@ void Queue::consume(Consumer::shared_ptr c, bool requestExclusive)
// Check for exclusivity of acquiring consumers.
size_t acquiringConsumers = consumerCount - browserCount;
if (c->preAcquires()) {
+ if(settings.isBrowseOnly) {
+ throw NotAllowedException(
+ QPID_MSG("Queue " << name << " is browse only. Refusing acquiring consumer."));
+ }
+
if(exclusive) {
throw ResourceLockedException(
QPID_MSG("Queue " << getName()
@@ -502,22 +495,29 @@ void Queue::consume(Consumer::shared_ptr c, bool requestExclusive)
}
}
}
- else
+ else if(c->isCounted()) {
browserCount++;
- consumerCount++;
- //reset auto deletion timer if necessary
- if (settings.autoDeleteDelay && autoDeleteTask) {
- autoDeleteTask->cancel();
}
- observeConsumerAdd(*c, locker);
+ if(c->isCounted()) {
+ consumerCount++;
+
+ //reset auto deletion timer if necessary
+ if (settings.autoDeleteDelay && autoDeleteTask) {
+ autoDeleteTask->cancel();
+ }
+
+ observeConsumerAdd(*c, locker);
+ }
+ }
+ if (mgmtObject != 0 && c->isCounted()) {
+ mgmtObject->inc_consumerCount();
}
- if (mgmtObject != 0)
- mgmtObject->inc_consumerCount ();
}
void Queue::cancel(Consumer::shared_ptr c)
{
removeListener(c);
+ if(c->isCounted())
{
Mutex::ScopedLock locker(messageLock);
consumerCount--;
@@ -525,8 +525,9 @@ void Queue::cancel(Consumer::shared_ptr c)
if(exclusive) exclusive = 0;
observeConsumerRemove(*c, locker);
}
- if (mgmtObject != 0)
- mgmtObject->dec_consumerCount ();
+ if (mgmtObject != 0 && c->isCounted()) {
+ mgmtObject->dec_consumerCount();
+ }
}
/**
@@ -733,7 +734,6 @@ uint32_t Queue::move(const Queue::shared_ptr destq, uint32_t qty,
void Queue::push(Message& message, bool /*isRecovery*/)
{
- assertClusterSafe();
QueueListeners::NotificationSet copy;
{
Mutex::ScopedLock locker(messageLock);
@@ -1101,8 +1101,16 @@ void Queue::destroyed()
notifyDeleted();
{
Mutex::ScopedLock lock(messageLock);
+ for_each(observers.begin(), observers.end(),
+ boost::bind(&QueueObserver::destroy, _1));
observers.clear();
}
+
+ if (mgmtObject != 0) {
+ mgmtObject->resourceDestroy();
+ if (brokerMgmtObject)
+ brokerMgmtObject->dec_queueCount();
+ }
}
void Queue::notifyDeleted()
@@ -1136,7 +1144,7 @@ void Queue::setPersistenceId(uint64_t _persistenceId) const
{
if (mgmtObject != 0 && persistenceId == 0 && externalQueueStore)
{
- ManagementObject* childObj = externalQueueStore->GetManagementObject();
+ ManagementObject::shared_ptr childObj = externalQueueStore->GetManagementObject();
if (childObj != 0)
childObj->setReference(mgmtObject->getObjectId());
}
@@ -1180,6 +1188,7 @@ Queue::shared_ptr Queue::restore( QueueRegistry& queues, Buffer& buffer )
void Queue::setAlternateExchange(boost::shared_ptr<Exchange> exchange)
{
alternateExchange = exchange;
+ alternateExchange->incAlternateUsers();
if (mgmtObject) {
if (exchange.get() != 0)
mgmtObject->set_altExchange(exchange->GetManagementObject()->getObjectId());
@@ -1197,14 +1206,10 @@ void tryAutoDeleteImpl(Broker& broker, Queue::shared_ptr queue, const std::strin
{
if (broker.getQueues().destroyIf(queue->getName(),
boost::bind(boost::mem_fn(&Queue::canAutoDelete), queue))) {
- QPID_LOG(debug, "Auto-deleting " << queue->getName());
- queue->destroyed();
-
- if (broker.getManagementAgent())
- broker.getManagementAgent()->raiseEvent(_qmf::EventQueueDelete(connectionId, userId, queue->getName()));
- QPID_LOG_CAT(debug, model, "Delete queue. name:" << queue->getName()
+ QPID_LOG_CAT(debug, model, "Auto-delete queue: " << queue->getName()
<< " user:" << userId
<< " rhost:" << connectionId );
+ queue->destroyed();
}
}
@@ -1233,7 +1238,7 @@ void Queue::tryAutoDelete(Broker& broker, Queue::shared_ptr queue, const std::st
if (queue->settings.autoDeleteDelay && queue->canAutoDelete()) {
AbsTime time(now(), Duration(queue->settings.autoDeleteDelay * TIME_SEC));
queue->autoDeleteTask = boost::intrusive_ptr<qpid::sys::TimerTask>(new AutoDeleteTask(broker, queue, connectionId, userId, time));
- broker.getClusterTimer().add(queue->autoDeleteTask);
+ broker.getTimer().add(queue->autoDeleteTask);
QPID_LOG(debug, "Timed auto-delete for " << queue->getName() << " initiated");
} else {
tryAutoDeleteImpl(broker, queue, connectionId, userId);
@@ -1290,7 +1295,7 @@ void Queue::setExternalQueueStore(ExternalQueueStore* inst) {
externalQueueStore = inst;
if (inst) {
- ManagementObject* childObj = inst->GetManagementObject();
+ ManagementObject::shared_ptr childObj = inst->GetManagementObject();
if (childObj != 0 && mgmtObject != 0)
childObj->setReference(mgmtObject->getObjectId());
}
@@ -1378,9 +1383,9 @@ void Queue::countLoadedFromDisk(uint64_t size) const
}
-ManagementObject* Queue::GetManagementObject (void) const
+ManagementObject::shared_ptr Queue::GetManagementObject(void) const
{
- return (ManagementObject*) mgmtObject;
+ return mgmtObject;
}
Manageable::status_t Queue::ManagementMethod (uint32_t methodId, Args& args, string& etext)
@@ -1459,6 +1464,16 @@ SequenceNumber Queue::getPosition() {
return sequence;
}
+void Queue::getRange(framing::SequenceNumber& front, framing::SequenceNumber& back,
+ SubscriptionType type)
+{
+ Mutex::ScopedLock locker(messageLock);
+ QueueCursor cursor(type);
+ back = sequence;
+ Message* message = messages->next(cursor);
+ front = message ? message->getSequence() : back+1;
+}
+
int Queue::getEventMode() { return eventMode; }
void Queue::recoveryComplete(ExchangeRegistry& exchanges)
@@ -1493,20 +1508,11 @@ void Queue::observeEnqueue(const Message& m, const Mutex::ScopedLock&)
mgntEnqStats(m, mgmtObject, brokerMgmtObject);
}
-// Note: accessing listeners outside of lock is dangerous. Caller must ensure the queue's
-// state is not changed while listeners is referenced.
-QueueListeners& Queue::getListeners() { return listeners; }
-
-// Note: accessing messages outside of lock is dangerous. Caller must ensure the queue's
-// state is not changed while messages is referenced.
-Messages& Queue::getMessages() { return *messages; }
-const Messages& Queue::getMessages() const { return *messages; }
-
-void Queue::checkNotDeleted(const Consumer::shared_ptr& c)
+bool Queue::checkNotDeleted(const Consumer::shared_ptr& c)
{
- if (deleted && !c->hideDeletedError()) {
+ if (deleted && !c->hideDeletedError())
throw ResourceDeletedException(QPID_MSG("Queue " << getName() << " has been deleted."));
- }
+ return !deleted;
}
void Queue::addObserver(boost::shared_ptr<QueueObserver> observer)
@@ -1641,7 +1647,7 @@ Queue::UsageBarrier::UsageBarrier(Queue& q) : parent(q), count(0) {}
bool Queue::UsageBarrier::acquire()
{
- Monitor::ScopedLock l(parent.messageLock); /** @todo: use a dedicated lock instead of messageLock */
+ Monitor::ScopedLock l(usageLock);
if (parent.deleted) {
return false;
} else {
@@ -1652,15 +1658,20 @@ bool Queue::UsageBarrier::acquire()
void Queue::UsageBarrier::release()
{
- Monitor::ScopedLock l(parent.messageLock);
- if (--count == 0) parent.messageLock.notifyAll();
+ Monitor::ScopedLock l(usageLock);
+ if (--count == 0) usageLock.notifyAll();
}
void Queue::UsageBarrier::destroy()
{
- Monitor::ScopedLock l(parent.messageLock);
+ Monitor::ScopedLock l(usageLock);
parent.deleted = true;
- while (count) parent.messageLock.wait();
+ while (count) usageLock.wait();
+}
+
+void Queue::addArgument(const string& key, const types::Variant& value) {
+ settings.original.insert(types::Variant::Map::value_type(key, value));
+ if (mgmtObject != 0) mgmtObject->set_arguments(settings.asMap());
}
}}
diff --git a/cpp/src/qpid/broker/Queue.h b/cpp/src/qpid/broker/Queue.h
index 1294f813aa..bb713eba2b 100644
--- a/cpp/src/qpid/broker/Queue.h
+++ b/cpp/src/qpid/broker/Queue.h
@@ -38,7 +38,6 @@
#include "qpid/framing/SequenceNumber.h"
#include "qpid/sys/AtomicValue.h"
#include "qpid/sys/Monitor.h"
-#include "qpid/sys/Timer.h"
#include "qpid/management/Manageable.h"
#include "qmf/org/apache/qpid/broker/Queue.h"
#include "qmf/org/apache/qpid/broker/Broker.h"
@@ -56,6 +55,9 @@
#include <algorithm>
namespace qpid {
+namespace sys {
+class TimerTask;
+}
namespace broker {
class Broker;
class Exchange;
@@ -83,6 +85,7 @@ class Queue : public boost::enable_shared_from_this<Queue>,
{
Queue& parent;
uint count;
+ qpid::sys::Monitor usageLock;
UsageBarrier(Queue&);
bool acquire();
@@ -142,18 +145,18 @@ class Queue : public boost::enable_shared_from_this<Queue>,
* o consumerCount (TBD: move under separate lock)
* o Queue::UsageBarrier (TBD: move under separate lock)
*/
- mutable qpid::sys::Monitor messageLock;
+ mutable qpid::sys::Mutex messageLock;
mutable qpid::sys::Mutex ownershipLock;
mutable uint64_t persistenceId;
- const QueueSettings settings;
+ QueueSettings settings;
qpid::framing::FieldTable encodableSettings;
QueueDepth current;
QueueBindings bindings;
std::string alternateExchangeName;
boost::shared_ptr<Exchange> alternateExchange;
framing::SequenceNumber sequence;
- qmf::org::apache::qpid::broker::Queue* mgmtObject;
- qmf::org::apache::qpid::broker::Broker* brokerMgmtObject;
+ qmf::org::apache::qpid::broker::Queue::shared_ptr mgmtObject;
+ qmf::org::apache::qpid::broker::Broker::shared_ptr brokerMgmtObject;
sys::AtomicValue<uint32_t> dequeueSincePurge; // Count dequeues since last purge.
int eventMode;
Observers observers;
@@ -189,7 +192,7 @@ class Queue : public boost::enable_shared_from_this<Queue>,
int getEventMode();
void dequeueFromStore(boost::intrusive_ptr<PersistableMessage>);
void abandoned(const Message& message);
- void checkNotDeleted(const Consumer::shared_ptr&);
+ bool checkNotDeleted(const Consumer::shared_ptr&);
void notifyDeleted();
uint32_t remove(uint32_t maxCount, MessagePredicate, MessageFunctor, SubscriptionType);
virtual bool checkDepth(const QueueDepth& increment, const Message&);
@@ -338,7 +341,7 @@ class Queue : public boost::enable_shared_from_this<Queue>,
* exclusive owner
*/
static Queue::shared_ptr restore(QueueRegistry& queues, framing::Buffer& buffer);
- static void tryAutoDelete(Broker& broker, Queue::shared_ptr, const std::string& connectionId, const std::string& userId);
+ QPID_BROKER_EXTERN static void tryAutoDelete(Broker& broker, Queue::shared_ptr, const std::string& connectionId, const std::string& userId);
virtual void setExternalQueueStore(ExternalQueueStore* inst);
@@ -352,7 +355,7 @@ class Queue : public boost::enable_shared_from_this<Queue>,
QPID_BROKER_EXTERN void countLoadedFromDisk(uint64_t size) const;
// Manageable entry points
- QPID_BROKER_EXTERN management::ManagementObject* GetManagementObject (void) const;
+ QPID_BROKER_EXTERN management::ManagementObject::shared_ptr GetManagementObject(void) const;
management::Manageable::status_t
QPID_BROKER_EXTERN ManagementMethod (uint32_t methodId, management::Args& args, std::string& text);
QPID_BROKER_EXTERN void query(::qpid::types::Variant::Map&) const;
@@ -382,15 +385,30 @@ class Queue : public boost::enable_shared_from_this<Queue>,
*
* The _caller_ must ensure that any messages after pos have been dequeued.
*
- * Used by HA/cluster code for queue replication.
+ * Used by HA code for queue replication.
*/
QPID_BROKER_EXTERN void setPosition(framing::SequenceNumber pos);
/**
*@return sequence number for the back of the queue. The next message pushed
- * will be at getPosition+1
+ * will be at getPosition()+1
*/
QPID_BROKER_EXTERN framing::SequenceNumber getPosition();
+
+ /**
+ * Set front and back.
+ * If the queue is empty then front = back+1 (the first message to
+ * consume will be the next message pushed.)
+ *
+ *@param front = Position of first message to consume.
+ *@param back = getPosition(), next message pushed will be getPosition()+1
+ *@param type Subscription type to use to determine the front.
+ */
+ QPID_BROKER_EXTERN void getRange(
+ framing::SequenceNumber& front, framing::SequenceNumber& back,
+ SubscriptionType type=CONSUMER
+ );
+
QPID_BROKER_EXTERN void addObserver(boost::shared_ptr<QueueObserver>);
QPID_BROKER_EXTERN void removeObserver(boost::shared_ptr<QueueObserver>);
QPID_BROKER_EXTERN void insertSequenceNumbers(const std::string& key);
@@ -399,11 +417,6 @@ class Queue : public boost::enable_shared_from_this<Queue>,
*/
QPID_BROKER_EXTERN void recoveryComplete(ExchangeRegistry& exchanges);
- // For cluster update
- QPID_BROKER_EXTERN QueueListeners& getListeners();
- QPID_BROKER_EXTERN Messages& getMessages();
- QPID_BROKER_EXTERN const Messages& getMessages() const;
-
/**
* Reserve space in policy for an enqueued message that
* has been recovered in the prepared state (dtx only)
@@ -420,6 +433,10 @@ class Queue : public boost::enable_shared_from_this<Queue>,
uint32_t getDequeueSincePurge() { return dequeueSincePurge.get(); }
QPID_BROKER_EXTERN void setDequeueSincePurge(uint32_t value);
+
+ /** Add an argument to be included in management messages about this queue. */
+ QPID_BROKER_EXTERN void addArgument(const std::string& key, const types::Variant& value);
+
friend class QueueFactory;
};
}
diff --git a/cpp/src/qpid/broker/QueueCleaner.cpp b/cpp/src/qpid/broker/QueueCleaner.cpp
index 838bc28be8..8d9e3f43dd 100644
--- a/cpp/src/qpid/broker/QueueCleaner.cpp
+++ b/cpp/src/qpid/broker/QueueCleaner.cpp
@@ -18,15 +18,36 @@
* under the License.
*
*/
-#include "qpid/broker/Queue.h"
#include "qpid/broker/QueueCleaner.h"
#include "qpid/broker/Broker.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/sys/Timer.h"
+
+#include <boost/function.hpp>
#include <boost/bind.hpp>
namespace qpid {
namespace broker {
+namespace {
+ typedef boost::function0<void> FireFunction;
+ class Task : public sys::TimerTask
+ {
+ public:
+ Task(FireFunction f, sys::Duration duration);
+ void fire();
+ private:
+ FireFunction fireFunction;
+ };
+
+ Task::Task(FireFunction f, qpid::sys::Duration d) : sys::TimerTask(d,"QueueCleaner"), fireFunction(f) {}
+
+ void Task::fire()
+ {
+ fireFunction();
+ }
+}
QueueCleaner::QueueCleaner(QueueRegistry& q, sys::Timer* t) : queues(q), timer(t) {}
QueueCleaner::~QueueCleaner()
@@ -37,7 +58,7 @@ QueueCleaner::~QueueCleaner()
void QueueCleaner::start(qpid::sys::Duration p)
{
period = p;
- task = new Task(*this, p);
+ task = new Task(boost::bind(&QueueCleaner::fired, this), p);
timer->add(task);
}
@@ -45,14 +66,6 @@ void QueueCleaner::setTimer(qpid::sys::Timer* timer) {
this->timer = timer;
}
-
-QueueCleaner::Task::Task(QueueCleaner& p, qpid::sys::Duration d) : sys::TimerTask(d,"QueueCleaner"), parent(p) {}
-
-void QueueCleaner::Task::fire()
-{
- parent.fired();
-}
-
namespace {
struct CollectQueues
{
diff --git a/cpp/src/qpid/broker/QueueCleaner.h b/cpp/src/qpid/broker/QueueCleaner.h
index ffebfe3e1b..896af1dcd5 100644
--- a/cpp/src/qpid/broker/QueueCleaner.h
+++ b/cpp/src/qpid/broker/QueueCleaner.h
@@ -23,9 +23,17 @@
*/
#include "qpid/broker/BrokerImportExport.h"
-#include "qpid/sys/Timer.h"
+#include "qpid/sys/Time.h"
+
+#include <boost/intrusive_ptr.hpp>
namespace qpid {
+
+namespace sys {
+ class Timer;
+ class TimerTask;
+}
+
namespace broker {
class QueueRegistry;
@@ -39,16 +47,8 @@ class QueueCleaner
QPID_BROKER_EXTERN ~QueueCleaner();
QPID_BROKER_EXTERN void start(sys::Duration period);
QPID_BROKER_EXTERN void setTimer(sys::Timer* timer);
- private:
- class Task : public sys::TimerTask
- {
- public:
- Task(QueueCleaner& parent, sys::Duration duration);
- void fire();
- private:
- QueueCleaner& parent;
- };
+ private:
boost::intrusive_ptr<sys::TimerTask> task;
QueueRegistry& queues;
sys::Timer* timer;
diff --git a/cpp/src/qpid/broker/QueueFlowLimit.cpp b/cpp/src/qpid/broker/QueueFlowLimit.cpp
index 11b9cbae63..9b2e31c925 100644
--- a/cpp/src/qpid/broker/QueueFlowLimit.cpp
+++ b/cpp/src/qpid/broker/QueueFlowLimit.cpp
@@ -29,7 +29,6 @@
#include "qpid/log/Statement.h"
#include "qpid/sys/Mutex.h"
#include "qpid/broker/SessionState.h"
-#include "qpid/sys/ClusterSafe.h"
#include "qmf/org/apache/qpid/broker/Queue.h"
@@ -66,10 +65,10 @@ namespace {
QueueFlowLimit::QueueFlowLimit(Queue *_queue,
uint32_t _flowStopCount, uint32_t _flowResumeCount,
uint64_t _flowStopSize, uint64_t _flowResumeSize)
- : StatefulQueueObserver(std::string("QueueFlowLimit")), queue(_queue), queueName("<unknown>"),
+ : queue(_queue), queueName("<unknown>"),
flowStopCount(_flowStopCount), flowResumeCount(_flowResumeCount),
flowStopSize(_flowStopSize), flowResumeSize(_flowResumeSize),
- flowStopped(false), count(0), size(0), queueMgmtObj(0), broker(0)
+ flowStopped(false), count(0), size(0), broker(0)
{
uint32_t maxCount(0);
uint64_t maxSize(0);
@@ -79,7 +78,7 @@ QueueFlowLimit::QueueFlowLimit(Queue *_queue,
if (queue->getSettings().maxDepth.hasCount()) maxCount = queue->getSettings().maxDepth.getCount();
if (queue->getSettings().maxDepth.hasCount()) maxSize = queue->getSettings().maxDepth.getSize();
broker = queue->getBroker();
- queueMgmtObj = dynamic_cast<_qmfBroker::Queue*> (queue->GetManagementObject());
+ queueMgmtObj = boost::dynamic_pointer_cast<_qmfBroker::Queue> (queue->GetManagementObject());
if (queueMgmtObj) {
queueMgmtObj->set_flowStopped(isFlowControlActive());
}
@@ -130,11 +129,6 @@ void QueueFlowLimit::enqueued(const Message& msg)
}
if (flowStopped || !index.empty()) {
- // ignore flow control if we are populating the queue due to cluster replication:
- if (broker && broker->isClusterUpdatee()) {
- QPID_LOG(trace, "Queue \"" << queueName << "\": ignoring flow control for msg pos=" << msg.getSequence());
- return;
- }
QPID_LOG(trace, "Queue \"" << queueName << "\": setting flow control for msg pos=" << msg.getSequence());
msg.getPersistentContext()->getIngressCompletion().startCompleter(); // don't complete until flow resumes
bool unique;
@@ -297,79 +291,8 @@ QueueFlowLimit *QueueFlowLimit::createLimit(Queue *queue, const QueueSettings& s
return 0;
}
-/* Cluster replication */
-
-namespace {
- /** pack a set of sequence number ranges into a framing::Array */
- void buildSeqRangeArray(qpid::framing::Array *seqs,
- const qpid::framing::SequenceNumber& first,
- const qpid::framing::SequenceNumber& last)
- {
- seqs->push_back(qpid::framing::Array::ValuePtr(new Unsigned32Value(first)));
- seqs->push_back(qpid::framing::Array::ValuePtr(new Unsigned32Value(last)));
- }
-}
-
-/** Runs on UPDATER to snapshot current state */
-void QueueFlowLimit::getState(qpid::framing::FieldTable& state ) const
-{
- sys::Mutex::ScopedLock l(indexLock);
- state.clear();
-
- framing::SequenceSet ss;
- if (!index.empty()) {
- /* replicate the set of messages pending flow control */
- for (std::map<framing::SequenceNumber, Message >::const_iterator itr = index.begin();
- itr != index.end(); ++itr) {
- ss.add(itr->first);
- }
- framing::Array seqs(TYPE_CODE_UINT32);
- typedef boost::function<void(framing::SequenceNumber, framing::SequenceNumber)> arrayBuilder;
- ss.for_each((arrayBuilder)boost::bind(&buildSeqRangeArray, &seqs, _1, _2));
- state.setArray("pendingMsgSeqs", seqs);
- }
- QPID_LOG(debug, "Queue \"" << queueName << "\": flow limit replicating pending msgs, range=" << ss);
-}
-
-
-/** called on UPDATEE to set state from snapshot */
-void QueueFlowLimit::setState(const qpid::framing::FieldTable& state)
-{
- sys::Mutex::ScopedLock l(indexLock);
- index.clear();
-
- framing::SequenceSet fcmsg;
- framing::Array seqArray(TYPE_CODE_UINT32);
- if (state.getArray("pendingMsgSeqs", seqArray)) {
- assert((seqArray.count() & 0x01) == 0); // must be even since they are sequence ranges
- framing::Array::const_iterator i = seqArray.begin();
- while (i != seqArray.end()) {
- framing::SequenceNumber first((*i)->getIntegerValue<uint32_t, 4>());
- ++i;
- framing::SequenceNumber last((*i)->getIntegerValue<uint32_t, 4>());
- ++i;
- fcmsg.add(first, last);
- for (SequenceNumber seq = first; seq <= last; ++seq) {
- Message msg;
- queue->find(seq, msg); // fyi: may not be found if msg is acquired & unacked
- bool unique;
- unique = index.insert(std::pair<framing::SequenceNumber, Message >(seq, msg)).second;
- // Like this to avoid tripping up unused variable warning when NDEBUG set
- if (!unique) assert(unique);
- }
- }
- }
-
- flowStopped = index.size() != 0;
- if (queueMgmtObj) {
- queueMgmtObj->set_flowStopped(isFlowControlActive());
- }
- QPID_LOG(debug, "Queue \"" << queueName << "\": flow limit replicated the pending msgs, range=" << fcmsg)
-}
-
-
namespace qpid {
- namespace broker {
+namespace broker {
std::ostream& operator<<(std::ostream& out, const QueueFlowLimit& f)
{
@@ -378,6 +301,6 @@ std::ostream& operator<<(std::ostream& out, const QueueFlowLimit& f)
return out;
}
- }
+}
}
diff --git a/cpp/src/qpid/broker/QueueFlowLimit.h b/cpp/src/qpid/broker/QueueFlowLimit.h
index 1bcc388ceb..b9aa09ec3a 100644
--- a/cpp/src/qpid/broker/QueueFlowLimit.h
+++ b/cpp/src/qpid/broker/QueueFlowLimit.h
@@ -26,19 +26,13 @@
#include <iostream>
#include <memory>
#include "qpid/broker/BrokerImportExport.h"
-#include "qpid/broker/StatefulQueueObserver.h"
+#include "qpid/broker/QueueObserver.h"
#include "qpid/framing/FieldTable.h"
#include "qpid/framing/SequenceNumber.h"
#include "qpid/sys/AtomicValue.h"
#include "qpid/sys/Mutex.h"
+#include "qmf/org/apache/qpid/broker/Queue.h"
-namespace qmf {
-namespace org {
-namespace apache {
-namespace qpid {
-namespace broker {
- class Queue;
-}}}}}
namespace _qmfBroker = qmf::org::apache::qpid::broker;
namespace qpid {
@@ -46,6 +40,7 @@ namespace broker {
class Broker;
class Queue;
+class Message;
struct QueueSettings;
/**
@@ -55,7 +50,7 @@ struct QueueSettings;
* passing _either_ level may turn flow control ON, but _both_ must be
* below level before flow control will be turned OFF.
*/
- class QueueFlowLimit : public StatefulQueueObserver
+ class QueueFlowLimit : public QueueObserver
{
static uint64_t defaultMaxSize;
static uint defaultFlowStopRatio;
@@ -90,10 +85,6 @@ struct QueueSettings;
QPID_BROKER_EXTERN void acquired(const Message&) {};
QPID_BROKER_EXTERN void requeued(const Message&) {};
- /** for clustering: */
- QPID_BROKER_EXTERN void getState(qpid::framing::FieldTable&) const;
- QPID_BROKER_EXTERN void setState(const qpid::framing::FieldTable&);
-
uint32_t getFlowStopCount() const { return flowStopCount; }
uint32_t getFlowResumeCount() const { return flowResumeCount; }
uint64_t getFlowStopSize() const { return flowStopSize; }
@@ -118,7 +109,7 @@ struct QueueSettings;
std::map<framing::SequenceNumber, Message > index;
mutable qpid::sys::Mutex indexLock;
- _qmfBroker::Queue *queueMgmtObj;
+ _qmfBroker::Queue::shared_ptr queueMgmtObj;
const Broker *broker;
diff --git a/cpp/src/qpid/broker/QueueObserver.h b/cpp/src/qpid/broker/QueueObserver.h
index 29e867253e..2ba98f6945 100644
--- a/cpp/src/qpid/broker/QueueObserver.h
+++ b/cpp/src/qpid/broker/QueueObserver.h
@@ -69,6 +69,7 @@ class QueueObserver
virtual void requeued(const Message&) = 0;
virtual void consumerAdded( const Consumer& ) {};
virtual void consumerRemoved( const Consumer& ) {};
+ virtual void destroy() {};
private:
};
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/QueueRegistry.cpp b/cpp/src/qpid/broker/QueueRegistry.cpp
index eb525b6727..2fdcf3b8e6 100644
--- a/cpp/src/qpid/broker/QueueRegistry.cpp
+++ b/cpp/src/qpid/broker/QueueRegistry.cpp
@@ -23,10 +23,14 @@
#include "qpid/broker/QueueRegistry.h"
#include "qpid/broker/Exchange.h"
#include "qpid/log/Statement.h"
+#include "qpid/management/ManagementAgent.h"
#include "qpid/framing/reply_exceptions.h"
+#include "qmf/org/apache/qpid/broker/EventQueueDeclare.h"
+#include "qmf/org/apache/qpid/broker/EventQueueDelete.h"
#include <sstream>
#include <assert.h>
+namespace _qmf = qmf::org::apache::qpid::broker;
using namespace qpid::broker;
using namespace qpid::sys;
using std::string;
@@ -44,7 +48,10 @@ QueueRegistry::declare(const string& name, const QueueSettings& settings,
bool recovering/*true if this declare is a
result of recovering queue
definition from persistent
- record*/)
+ record*/,
+ const OwnershipToken* owner,
+ std::string connectionId,
+ std::string userId)
{
std::pair<Queue::shared_ptr, bool> result;
{
@@ -53,25 +60,38 @@ QueueRegistry::declare(const string& name, const QueueSettings& settings,
if (i == queues.end()) {
Queue::shared_ptr queue = create(name, settings);
//Move this to factory also?
- if (alternate) {
+ if (alternate)
queue->setAlternateExchange(alternate);//need to do this *before* create
- alternate->incAlternateUsers();
- }
if (!recovering) {
//create persistent record if required
queue->create();
}
queues[name] = queue;
+ // NOTE: raiseEvent and queueCreate must be called with the lock held in
+ // order to ensure events are generated in the correct order.
+ // Call queueCreate before raiseEvents so it can add arguments that
+ // will be included in the management event.
+ if (getBroker()) getBroker()->getConfigurationObservers().queueCreate(queue);
result = std::pair<Queue::shared_ptr, bool>(queue, true);
} else {
result = std::pair<Queue::shared_ptr, bool>(i->second, false);
}
+ if (getBroker() && getBroker()->getManagementAgent()) {
+ getBroker()->getManagementAgent()->raiseEvent(
+ _qmf::EventQueueDeclare(
+ connectionId, userId, name,
+ settings.durable, owner, settings.autodelete,
+ alternate ? alternate->getName() : string(),
+ result.first->getSettings().asMap(),
+ result.second ? "created" : "existing"));
+ }
}
- if (getBroker() && result.second) getBroker()->getConfigurationObservers().queueCreate(result.first);
return result;
}
-void QueueRegistry::destroy(const string& name) {
+void QueueRegistry::destroy(
+ const string& name, const string& connectionId, const string& userId)
+{
Queue::shared_ptr q;
{
qpid::sys::RWlock::ScopedWlock locker(lock);
@@ -79,9 +99,17 @@ void QueueRegistry::destroy(const string& name) {
if (i != queues.end()) {
q = i->second;
queues.erase(i);
+ if (getBroker()) {
+ // NOTE: queueDestroy and raiseEvent must be called with the
+ // lock held in order to ensure events are generated
+ // in the correct order.
+ getBroker()->getConfigurationObservers().queueDestroy(q);
+ if (getBroker()->getManagementAgent())
+ getBroker()->getManagementAgent()->raiseEvent(
+ _qmf::EventQueueDelete(connectionId, userId, name));
+ }
}
}
- if (getBroker() && q) getBroker()->getConfigurationObservers().queueDestroy(q);
}
Queue::shared_ptr QueueRegistry::find(const string& name){
diff --git a/cpp/src/qpid/broker/QueueRegistry.h b/cpp/src/qpid/broker/QueueRegistry.h
index ada76f9cca..0170c441b3 100644
--- a/cpp/src/qpid/broker/QueueRegistry.h
+++ b/cpp/src/qpid/broker/QueueRegistry.h
@@ -59,7 +59,9 @@ class QueueRegistry : QueueFactory {
const std::string& name,
const QueueSettings& settings,
boost::shared_ptr<Exchange> alternateExchange = boost::shared_ptr<Exchange>(),
- bool recovering = false);
+ bool recovering = false,
+ const OwnershipToken* owner = 0,
+ std::string connectionId=std::string(), std::string userId=std::string());
/**
* Destroy the named queue.
@@ -73,7 +75,11 @@ class QueueRegistry : QueueFactory {
* subsequent calls to find or declare with the same name.
*
*/
- QPID_BROKER_EXTERN void destroy(const std::string& name);
+ QPID_BROKER_EXTERN void destroy(
+ const std::string& name,
+ const std::string& connectionId=std::string(),
+ const std::string& userId=std::string());
+
template <class Test> bool destroyIf(const std::string& name, Test test)
{
if (test()) {
diff --git a/cpp/src/qpid/broker/QueueSettings.cpp b/cpp/src/qpid/broker/QueueSettings.cpp
index 91616636f1..b92a81bcf3 100644
--- a/cpp/src/qpid/broker/QueueSettings.cpp
+++ b/cpp/src/qpid/broker/QueueSettings.cpp
@@ -33,10 +33,13 @@ namespace broker {
namespace {
const std::string MAX_COUNT("qpid.max_count");
const std::string MAX_SIZE("qpid.max_size");
+const std::string MAX_FILE_COUNT("qpid.file_count");
+const std::string MAX_FILE_SIZE("qpid.file_size");
const std::string POLICY_TYPE("qpid.policy_type");
const std::string POLICY_TYPE_REJECT("reject");
const std::string POLICY_TYPE_RING("ring");
const std::string NO_LOCAL("no-local");
+const std::string BROWSE_ONLY("qpid.browse-only");
const std::string TRACE_ID("qpid.trace.id");
const std::string TRACE_EXCLUDES("qpid.trace.exclude");
const std::string LVQ_KEY("qpid.last_value_queue_key");
@@ -74,12 +77,14 @@ const QueueSettings::Aliases QueueSettings::aliases;
QueueSettings::QueueSettings(bool d, bool a) :
durable(d),
autodelete(a),
+ isTemporary(false),
priorities(0),
defaultFairshare(0),
shareGroups(false),
addTimestamp(false),
dropMessagesAtLimit(false),
noLocal(false),
+ isBrowseOnly(false),
autoDeleteDelay(0),
alertRepeatInterval(60)
{}
@@ -106,6 +111,9 @@ bool QueueSettings::handle(const std::string& key, const qpid::types::Variant& v
} else if (key == NO_LOCAL) {
noLocal = value;
return true;
+ } else if (key == BROWSE_ONLY) {
+ isBrowseOnly = value;
+ return true;
} else if (key == TRACE_ID) {
traceId = value.asString();
return true;
@@ -163,6 +171,12 @@ bool QueueSettings::handle(const std::string& key, const qpid::types::Variant& v
} else if (key == ALERT_SIZE) {
alertThreshold.setSize(value);
return true;
+ } else if (key == MAX_FILE_COUNT && value.asUint64() > 0) {
+ maxFileCount = value.asUint64();
+ return false; // 'handle' here and also pass to store
+ } else if (key == MAX_FILE_SIZE && value.asUint64() > 0) {
+ maxFileSize = value.asUint64();
+ return false; // 'handle' here and also pass to store
} else {
return false;
}
diff --git a/cpp/src/qpid/broker/QueueSettings.h b/cpp/src/qpid/broker/QueueSettings.h
index 2443624615..62d34db5cb 100644
--- a/cpp/src/qpid/broker/QueueSettings.h
+++ b/cpp/src/qpid/broker/QueueSettings.h
@@ -43,6 +43,7 @@ struct QueueSettings
bool durable;
bool autodelete;
+ bool isTemporary;
//basic queue types:
std::string lvqKey;
@@ -59,6 +60,7 @@ struct QueueSettings
bool dropMessagesAtLimit;//aka ring queue policy
bool noLocal;
+ bool isBrowseOnly;
std::string traceId;
std::string traceExcludes;
uint64_t autoDeleteDelay;//queueTtl?
@@ -71,6 +73,10 @@ struct QueueSettings
QueueDepth alertThreshold;
int64_t alertRepeatInterval;
+ //file limits checked by Acl and shared with storeSettings
+ uint64_t maxFileSize;
+ uint64_t maxFileCount;
+
//yuck, yuck
qpid::framing::FieldTable storeSettings;
std::map<std::string, qpid::types::Variant> original;
diff --git a/cpp/src/qpid/broker/RecoverableExchange.h b/cpp/src/qpid/broker/RecoverableExchange.h
index 6bda1e2617..2302a7d925 100644
--- a/cpp/src/qpid/broker/RecoverableExchange.h
+++ b/cpp/src/qpid/broker/RecoverableExchange.h
@@ -45,6 +45,9 @@ public:
const std::string& routingKey,
qpid::framing::FieldTable&,
AsyncStore* const store) = 0;
+
+ virtual std::string getName() const = 0;
+
virtual ~RecoverableExchange() {};
};
diff --git a/cpp/src/qpid/broker/RecoverableMessage.h b/cpp/src/qpid/broker/RecoverableMessage.h
index c98857ceb0..aafcd756d5 100644
--- a/cpp/src/qpid/broker/RecoverableMessage.h
+++ b/cpp/src/qpid/broker/RecoverableMessage.h
@@ -22,12 +22,14 @@
*
*/
+#include <boost/intrusive_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include "qpid/framing/amqp_types.h"
#include "qpid/framing/Buffer.h"
namespace qpid {
namespace broker {
+class ExpiryPolicy;
/**
* The interface through which messages are reloaded on recovery.
@@ -38,6 +40,7 @@ public:
typedef boost::shared_ptr<RecoverableMessage> shared_ptr;
virtual void setPersistenceId(uint64_t id) = 0;
virtual void setRedelivered() = 0;
+ virtual void computeExpiration(const boost::intrusive_ptr<ExpiryPolicy>& e) = 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/RecoverableMessageImpl.h b/cpp/src/qpid/broker/RecoverableMessageImpl.h
new file mode 100644
index 0000000000..a46f5a3676
--- /dev/null
+++ b/cpp/src/qpid/broker/RecoverableMessageImpl.h
@@ -0,0 +1,49 @@
+#ifndef QPID_BROKER_RECOVERABLEMESSAGEIMPL_H
+#define QPID_BROKER_RECOVERABLEMESSAGEIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "RecoverableMessage.h"
+
+namespace qpid {
+namespace broker {
+class DtxBuffer;
+class Message;
+class Queue;
+
+class RecoverableMessageImpl : public RecoverableMessage
+{
+ Message msg;
+public:
+ RecoverableMessageImpl(const Message& _msg);
+ ~RecoverableMessageImpl() {};
+ void setPersistenceId(uint64_t id);
+ void setRedelivered();
+ void computeExpiration(const boost::intrusive_ptr<ExpiryPolicy>& ep);
+ bool loadContent(uint64_t available);
+ void decodeContent(framing::Buffer& buffer);
+ void recover(boost::shared_ptr<Queue> queue);
+ void enqueue(boost::shared_ptr<DtxBuffer> buffer, boost::shared_ptr<Queue> queue);
+ void dequeue(boost::shared_ptr<DtxBuffer> buffer, boost::shared_ptr<Queue> queue);
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_RECOVERABLEMESSAGEIMPL_H*/
diff --git a/cpp/src/qpid/broker/RecoveryManagerImpl.cpp b/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
index f3e1639ca5..901b3ad15a 100644
--- a/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
+++ b/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
@@ -25,6 +25,8 @@
#include "qpid/broker/Queue.h"
#include "qpid/broker/Link.h"
#include "qpid/broker/Bridge.h"
+#include "qpid/broker/Protocol.h"
+#include "qpid/broker/RecoverableMessageImpl.h"
#include "qpid/broker/RecoveredEnqueue.h"
#include "qpid/broker/RecoveredDequeue.h"
#include "qpid/broker/amqp_0_10/MessageTransfer.h"
@@ -38,26 +40,11 @@ namespace qpid {
namespace broker {
RecoveryManagerImpl::RecoveryManagerImpl(QueueRegistry& _queues, ExchangeRegistry& _exchanges, LinkRegistry& _links,
- DtxManager& _dtxMgr)
- : queues(_queues), exchanges(_exchanges), links(_links), dtxMgr(_dtxMgr) {}
+ DtxManager& _dtxMgr, ProtocolRegistry& p)
+ : queues(_queues), exchanges(_exchanges), links(_links), dtxMgr(_dtxMgr), protocols(p) {}
RecoveryManagerImpl::~RecoveryManagerImpl() {}
-class RecoverableMessageImpl : public RecoverableMessage
-{
- Message msg;
-public:
- RecoverableMessageImpl(const Message& _msg);
- ~RecoverableMessageImpl() {};
- void setPersistenceId(uint64_t id);
- void setRedelivered();
- bool loadContent(uint64_t available);
- void decodeContent(framing::Buffer& buffer);
- void recover(Queue::shared_ptr queue);
- void enqueue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue);
- void dequeue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue);
-};
-
class RecoverableQueueImpl : public RecoverableQueue
{
Queue::shared_ptr queue;
@@ -82,6 +69,7 @@ public:
RecoverableExchangeImpl(Exchange::shared_ptr _exchange, QueueRegistry& _queues) : exchange(_exchange), queues(_queues) {}
void setPersistenceId(uint64_t id);
void bind(const std::string& queue, const std::string& routingKey, qpid::framing::FieldTable& args, AsyncStore* const store);
+ string getName() const { return exchange->getName(); }
};
class RecoverableConfigImpl : public RecoverableConfig
@@ -130,10 +118,15 @@ RecoverableQueue::shared_ptr RecoveryManagerImpl::recoverQueue(framing::Buffer&
RecoverableMessage::shared_ptr RecoveryManagerImpl::recoverMessage(framing::Buffer& buffer)
{
- //TODO: determine encoding/version actually used
- boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> transfer(new qpid::broker::amqp_0_10::MessageTransfer());
- transfer->decodeHeader(buffer);
- return RecoverableMessage::shared_ptr(new RecoverableMessageImpl(Message(transfer, transfer)));
+ framing::Buffer sniffer(buffer.getPointer(), buffer.available());
+ RecoverableMessage::shared_ptr m = protocols.recover(sniffer);
+ if (m) {
+ return m;
+ } else {
+ boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> transfer(new qpid::broker::amqp_0_10::MessageTransfer());
+ transfer->decodeHeader(buffer);
+ return RecoverableMessage::shared_ptr(new RecoverableMessageImpl(Message(transfer, transfer)));
+ }
}
RecoverableTransaction::shared_ptr RecoveryManagerImpl::recoverTransaction(const std::string& xid,
@@ -193,6 +186,11 @@ void RecoverableMessageImpl::setRedelivered()
msg.deliver();//increment delivery count (but at present that isn't recorded durably)
}
+void RecoverableMessageImpl::computeExpiration(const boost::intrusive_ptr<ExpiryPolicy>& ep)
+{
+ msg.computeExpiration(ep);
+}
+
void RecoverableQueueImpl::recover(RecoverableMessage::shared_ptr msg)
{
dynamic_pointer_cast<RecoverableMessageImpl>(msg)->recover(queue);
diff --git a/cpp/src/qpid/broker/RecoveryManagerImpl.h b/cpp/src/qpid/broker/RecoveryManagerImpl.h
index 7fca0be194..f824f4a540 100644
--- a/cpp/src/qpid/broker/RecoveryManagerImpl.h
+++ b/cpp/src/qpid/broker/RecoveryManagerImpl.h
@@ -30,15 +30,17 @@
namespace qpid {
namespace broker {
+class ProtocolRegistry;
class RecoveryManagerImpl : public RecoveryManager{
QueueRegistry& queues;
ExchangeRegistry& exchanges;
LinkRegistry& links;
DtxManager& dtxMgr;
+ ProtocolRegistry& protocols;
public:
RecoveryManagerImpl(QueueRegistry& queues, ExchangeRegistry& exchanges, LinkRegistry& links,
- DtxManager& dtxMgr);
+ DtxManager& dtxMgr, ProtocolRegistry&);
~RecoveryManagerImpl();
RecoverableExchange::shared_ptr recoverExchange(framing::Buffer& buffer);
diff --git a/cpp/src/qpid/broker/SaslAuthenticator.cpp b/cpp/src/qpid/broker/SaslAuthenticator.cpp
index 2d7c820b63..8211657e04 100644
--- a/cpp/src/qpid/broker/SaslAuthenticator.cpp
+++ b/cpp/src/qpid/broker/SaslAuthenticator.cpp
@@ -24,6 +24,7 @@
#endif
#include "qpid/broker/AclModule.h"
+#include "qpid/broker/Broker.h"
#include "qpid/broker/Connection.h"
#include "qpid/log/Statement.h"
#include "qpid/framing/reply_exceptions.h"
@@ -169,14 +170,8 @@ void SaslAuthenticator::fini(void)
std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connection& c )
{
if (c.getBroker().getOptions().auth) {
- // The cluster creates non-authenticated connections for internal shadow connections
- // that are never connected to an external client.
- if ( !c.isAuthenticated() )
- return std::auto_ptr<SaslAuthenticator>(
- new NullAuthenticator(c, c.getBroker().getOptions().requireEncrypted));
- else
- return std::auto_ptr<SaslAuthenticator>(
- new CyrusAuthenticator(c, c.getBroker().getOptions().requireEncrypted));
+ return std::auto_ptr<SaslAuthenticator>(
+ new CyrusAuthenticator(c, c.getBroker().getOptions().requireEncrypted));
} else {
QPID_LOG(debug, "SASL: No Authentication Performed");
return std::auto_ptr<SaslAuthenticator>(new NullAuthenticator(c, c.getBroker().getOptions().requireEncrypted));
@@ -424,7 +419,7 @@ void CyrusAuthenticator::start(const string& mechanism, const string* response)
&challenge, &challenge_len);
processAuthenticationStep(code, challenge, challenge_len);
- qmf::org::apache::qpid::broker::Connection* cnxMgmt = connection.getMgmtObject();
+ qmf::org::apache::qpid::broker::Connection::shared_ptr cnxMgmt = connection.getMgmtObject();
if ( cnxMgmt )
cnxMgmt->set_saslMechanism(mechanism);
}
@@ -505,9 +500,9 @@ std::auto_ptr<SecurityLayer> CyrusAuthenticator::getSecurityLayer(uint16_t maxFr
uint ssf = *(reinterpret_cast<const unsigned*>(value));
std::auto_ptr<SecurityLayer> securityLayer;
if (ssf) {
- securityLayer = std::auto_ptr<SecurityLayer>(new CyrusSecurityLayer(sasl_conn, maxFrameSize));
+ securityLayer = std::auto_ptr<SecurityLayer>(new CyrusSecurityLayer(sasl_conn, maxFrameSize, ssf));
}
- qmf::org::apache::qpid::broker::Connection* cnxMgmt = connection.getMgmtObject();
+ qmf::org::apache::qpid::broker::Connection::shared_ptr cnxMgmt = connection.getMgmtObject();
if ( cnxMgmt )
cnxMgmt->set_saslSsf(ssf);
return securityLayer;
diff --git a/cpp/src/qpid/broker/SecureConnection.cpp b/cpp/src/qpid/broker/SecureConnection.cpp
index 5c1ebf3e8b..59ac9ef132 100644
--- a/cpp/src/qpid/broker/SecureConnection.cpp
+++ b/cpp/src/qpid/broker/SecureConnection.cpp
@@ -43,7 +43,7 @@ size_t SecureConnection::decode(const char* buffer, size_t size)
}
}
-size_t SecureConnection::encode(const char* buffer, size_t size)
+size_t SecureConnection::encode(char* buffer, size_t size)
{
if (secured) {
return securityLayer->encode(buffer, size);
diff --git a/cpp/src/qpid/broker/SecureConnection.h b/cpp/src/qpid/broker/SecureConnection.h
index 1547faae1e..632087350e 100644
--- a/cpp/src/qpid/broker/SecureConnection.h
+++ b/cpp/src/qpid/broker/SecureConnection.h
@@ -43,7 +43,7 @@ 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);
+ size_t encode(char* buffer, size_t size);
bool canEncode();
void closed();
bool isClosed() const;
diff --git a/cpp/src/qpid/broker/SecureConnectionFactory.cpp b/cpp/src/qpid/broker/SecureConnectionFactory.cpp
index 757f6efc59..7bc2c94d1c 100644
--- a/cpp/src/qpid/broker/SecureConnectionFactory.cpp
+++ b/cpp/src/qpid/broker/SecureConnectionFactory.cpp
@@ -19,19 +19,21 @@
*
*/
#include "qpid/broker/SecureConnectionFactory.h"
-#include "qpid/framing/ProtocolVersion.h"
+
#include "qpid/amqp_0_10/Connection.h"
+#include "qpid/broker/Broker.h"
#include "qpid/broker/Connection.h"
#include "qpid/broker/SecureConnection.h"
-#include "qpid/sys/SecuritySettings.h"
+#include "qpid/framing/ProtocolVersion.h"
#include "qpid/log/Statement.h"
+#include "qpid/sys/SecuritySettings.h"
namespace qpid {
namespace broker {
using framing::ProtocolVersion;
using qpid::sys::SecuritySettings;
-typedef std::auto_ptr<amqp_0_10::Connection> CodecPtr;
+typedef std::auto_ptr<qpid::amqp_0_10::Connection> CodecPtr;
typedef std::auto_ptr<SecureConnection> SecureConnectionPtr;
typedef std::auto_ptr<Connection> ConnectionPtr;
typedef std::auto_ptr<sys::ConnectionInputHandler> InputPtr;
@@ -43,12 +45,14 @@ SecureConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, cons
const SecuritySettings& external) {
if (v == ProtocolVersion(0, 10)) {
SecureConnectionPtr sc(new SecureConnection());
- CodecPtr c(new amqp_0_10::Connection(out, id, false));
+ CodecPtr c(new qpid::amqp_0_10::Connection(out, id, false));
ConnectionPtr i(new broker::Connection(c.get(), broker, id, external, false));
i->setSecureConnection(sc.get());
c->setInputHandler(InputPtr(i.release()));
sc->setCodec(std::auto_ptr<sys::ConnectionCodec>(c));
return sc.release();
+ } else {
+ return broker.getProtocolRegistry().create(v, out, id, external);
}
return 0;
}
@@ -58,7 +62,7 @@ SecureConnectionFactory::create(sys::OutputControl& out, const std::string& id,
const SecuritySettings& external) {
// used to create connections from one broker to another
SecureConnectionPtr sc(new SecureConnection());
- CodecPtr c(new amqp_0_10::Connection(out, id, true));
+ CodecPtr c(new qpid::amqp_0_10::Connection(out, id, true));
ConnectionPtr i(new broker::Connection(c.get(), broker, id, external, true ));
i->setSecureConnection(sc.get());
c->setInputHandler(InputPtr(i.release()));
diff --git a/cpp/src/qpid/broker/SemanticState.cpp b/cpp/src/qpid/broker/SemanticState.cpp
index 5fc9a1a932..751b3ff709 100644
--- a/cpp/src/qpid/broker/SemanticState.cpp
+++ b/cpp/src/qpid/broker/SemanticState.cpp
@@ -20,6 +20,8 @@
*/
#include "qpid/broker/SessionState.h"
+
+#include "qpid/broker/Broker.h"
#include "qpid/broker/Connection.h"
#include "qpid/broker/DeliverableMessage.h"
#include "qpid/broker/DtxAck.h"
@@ -35,12 +37,14 @@
#include "qpid/framing/SequenceSet.h"
#include "qpid/framing/IsInSequenceSet.h"
#include "qpid/log/Statement.h"
-#include "qpid/sys/ClusterSafe.h"
+#include "qpid/management/ManagementAgent.h"
#include "qpid/ptr_map.h"
#include "qpid/broker/AclModule.h"
+#include "qpid/broker/FedOps.h"
#include <boost/bind.hpp>
#include <boost/format.hpp>
+#include <boost/tuple/tuple_comparison.hpp>
#include <iostream>
#include <sstream>
@@ -49,6 +53,11 @@
#include <assert.h>
+namespace {
+const std::string X_SCOPE("x-scope");
+const std::string SESSION("session");
+}
+
namespace qpid {
namespace broker {
@@ -88,6 +97,7 @@ void SemanticState::closed() {
if (dtxBuffer.get()) {
dtxBuffer->fail();
}
+ unbindSessionBindings();
requeue();
//now unsubscribe, which may trigger queue deletion and thus
@@ -277,7 +287,7 @@ void SemanticState::record(const DeliveryRecord& delivery)
const std::string QPID_SYNC_FREQUENCY("qpid.sync_frequency");
-SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent,
+SemanticStateConsumerImpl::SemanticStateConsumerImpl(SemanticState* _parent,
const string& _name,
Queue::shared_ptr _queue,
bool ack,
@@ -303,7 +313,7 @@ Consumer(_name, type),
notifyEnabled(true),
syncFrequency(_arguments.getAsInt(QPID_SYNC_FREQUENCY)),
deliveryCount(0),
- mgmtObject(0)
+ protocols(parent->getSession().getBroker().getProtocolRegistry())
{
if (parent != 0 && queue.get() != 0 && queue->GetManagementObject() !=0)
{
@@ -312,20 +322,20 @@ Consumer(_name, type),
if (agent != 0)
{
- mgmtObject = new _qmf::Subscription(agent, this, ms , queue->GetManagementObject()->getObjectId(), getTag(),
- !acquire, ackExpected, exclusive, ManagementAgent::toMap(arguments));
+ mgmtObject = _qmf::Subscription::shared_ptr(new _qmf::Subscription(agent, this, ms , queue->GetManagementObject()->getObjectId(), getTag(),
+ !acquire, ackExpected, exclusive, ManagementAgent::toMap(arguments)));
agent->addObject (mgmtObject);
mgmtObject->set_creditMode("WINDOW");
}
}
}
-ManagementObject* SemanticState::ConsumerImpl::GetManagementObject (void) const
+ManagementObject::shared_ptr SemanticStateConsumerImpl::GetManagementObject (void) const
{
- return (ManagementObject*) mgmtObject;
+ return mgmtObject;
}
-Manageable::status_t SemanticState::ConsumerImpl::ManagementMethod (uint32_t methodId, Args&, string&)
+Manageable::status_t SemanticStateConsumerImpl::ManagementMethod (uint32_t methodId, Args&, string&)
{
Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
@@ -335,24 +345,23 @@ Manageable::status_t SemanticState::ConsumerImpl::ManagementMethod (uint32_t met
}
-OwnershipToken* SemanticState::ConsumerImpl::getSession()
+OwnershipToken* SemanticStateConsumerImpl::getSession()
{
return &(parent->session);
}
-bool SemanticState::ConsumerImpl::deliver(const QueueCursor& cursor, const Message& msg)
+bool SemanticStateConsumerImpl::deliver(const QueueCursor& cursor, const Message& msg)
{
return deliver(cursor, msg, shared_from_this());
}
-bool SemanticState::ConsumerImpl::deliver(const QueueCursor& cursor, const Message& msg, boost::shared_ptr<Consumer> consumer)
+bool SemanticStateConsumerImpl::deliver(const QueueCursor& cursor, const Message& msg, boost::shared_ptr<Consumer> consumer)
{
- assertClusterSafe();
allocateCredit(msg);
+ boost::intrusive_ptr<const amqp_0_10::MessageTransfer> transfer = protocols.translate(msg);
DeliveryRecord record(cursor, msg.getSequence(), queue, getTag(),
- consumer, acquire, !ackExpected, credit.isWindowMode(), amqp_0_10::MessageTransfer::getRequiredCredit(msg));
+ consumer, acquire, !ackExpected, credit.isWindowMode(), transfer->getRequiredCredit());
bool sync = syncFrequency && ++deliveryCount >= syncFrequency;
if (sync) deliveryCount = 0;//reset
- const amqp_0_10::MessageTransfer* transfer = dynamic_cast<const amqp_0_10::MessageTransfer*>(&msg.getEncoding());
record.setId(parent->session.deliver(*transfer, getTag(), msg.isRedelivered(), msg.getTtl(), msg.getTimestamp(),
ackExpected ? message::ACCEPT_MODE_EXPLICIT : message::ACCEPT_MODE_NONE,
@@ -370,27 +379,26 @@ bool SemanticState::ConsumerImpl::deliver(const QueueCursor& cursor, const Messa
return true;
}
-bool SemanticState::ConsumerImpl::filter(const Message&)
+bool SemanticStateConsumerImpl::filter(const Message&)
{
return true;
}
-bool SemanticState::ConsumerImpl::accept(const Message& msg)
+bool SemanticStateConsumerImpl::accept(const Message& msg)
{
- assertClusterSafe();
// TODO 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));
+ blocked = !checkCredit(msg);
return !blocked;
}
namespace {
struct ConsumerName {
- const SemanticState::ConsumerImpl& consumer;
- ConsumerName(const SemanticState::ConsumerImpl& ci) : consumer(ci) {}
+ const SemanticStateConsumerImpl& consumer;
+ ConsumerName(const SemanticStateConsumerImpl& ci) : consumer(ci) {}
};
ostream& operator<<(ostream& o, const ConsumerName& pc) {
@@ -399,26 +407,27 @@ ostream& operator<<(ostream& o, const ConsumerName& pc) {
}
}
-void SemanticState::ConsumerImpl::allocateCredit(const Message& msg)
+void SemanticStateConsumerImpl::allocateCredit(const Message& msg)
{
- assertClusterSafe();
Credit original = credit;
- credit.consume(1, qpid::broker::amqp_0_10::MessageTransfer::getRequiredCredit(msg));
+ boost::intrusive_ptr<const amqp_0_10::MessageTransfer> transfer = protocols.translate(msg);
+ credit.consume(1, transfer->getRequiredCredit());
QPID_LOG(debug, "Credit allocated for " << ConsumerName(*this)
<< ", was " << original << " now " << credit);
}
-bool SemanticState::ConsumerImpl::checkCredit(const Message& msg)
+bool SemanticStateConsumerImpl::checkCredit(const Message& msg)
{
- bool enoughCredit = credit.check(1, qpid::broker::amqp_0_10::MessageTransfer::getRequiredCredit(msg));
+ boost::intrusive_ptr<const amqp_0_10::MessageTransfer> transfer = protocols.translate(msg);
+ bool enoughCredit = credit.check(1, transfer->getRequiredCredit());
QPID_LOG(debug, "Subscription " << ConsumerName(*this) << " has " << (enoughCredit ? "sufficient " : "insufficient")
- << " credit for message of " << qpid::broker::amqp_0_10::MessageTransfer::getRequiredCredit(msg) << " bytes: "
+ << " credit for message of " << transfer->getRequiredCredit() << " bytes: "
<< credit);
return enoughCredit;
}
-SemanticState::ConsumerImpl::~ConsumerImpl()
+SemanticStateConsumerImpl::~SemanticStateConsumerImpl()
{
if (mgmtObject != 0)
mgmtObject->resourceDestroy ();
@@ -437,9 +446,9 @@ void SemanticState::cancel(ConsumerImpl::shared_ptr c)
Queue::shared_ptr queue = c->getQueue();
if(queue) {
queue->cancel(c);
- if (queue->canAutoDelete() && !queue->hasExclusiveOwner()) {
+ // Only run auto-delete for counted consumers.
+ if (c->isCounted() && queue->canAutoDelete() && !queue->hasExclusiveOwner())
Queue::tryAutoDelete(session.getBroker(), queue, connectionId, userID);
- }
}
c->cancel();
}
@@ -491,9 +500,8 @@ void SemanticState::requestDispatch()
i->second->requestDispatch();
}
-void SemanticState::ConsumerImpl::requestDispatch()
+void SemanticStateConsumerImpl::requestDispatch()
{
- assertClusterSafe();
if (blocked) {
parent->session.getConnection().outputTasks.addOutputTask(this);
parent->session.getConnection().outputTasks.activateOutput();
@@ -510,7 +518,7 @@ bool SemanticState::complete(DeliveryRecord& delivery)
return delivery.isRedundant();
}
-void SemanticState::ConsumerImpl::complete(DeliveryRecord& delivery)
+void SemanticStateConsumerImpl::complete(DeliveryRecord& delivery)
{
if (!delivery.isComplete()) {
delivery.complete();
@@ -535,7 +543,7 @@ SessionContext& SemanticState::getSession() { return session; }
const SessionContext& SemanticState::getSession() const { return session; }
-const SemanticState::ConsumerImpl::shared_ptr SemanticState::find(const std::string& destination) const
+const SemanticStateConsumerImpl::shared_ptr SemanticState::find(const std::string& destination) const
{
ConsumerImpl::shared_ptr consumer;
if (!find(destination, consumer)) {
@@ -592,37 +600,33 @@ void SemanticState::stop(const std::string& destination)
find(destination)->stop();
}
-void SemanticState::ConsumerImpl::setWindowMode()
+void SemanticStateConsumerImpl::setWindowMode()
{
- assertClusterSafe();
credit.setWindowMode(true);
if (mgmtObject){
mgmtObject->set_creditMode("WINDOW");
}
}
-void SemanticState::ConsumerImpl::setCreditMode()
+void SemanticStateConsumerImpl::setCreditMode()
{
- assertClusterSafe();
credit.setWindowMode(false);
if (mgmtObject){
mgmtObject->set_creditMode("CREDIT");
}
}
-void SemanticState::ConsumerImpl::addByteCredit(uint32_t value)
+void SemanticStateConsumerImpl::addByteCredit(uint32_t value)
{
- assertClusterSafe();
credit.addByteCredit(value);
}
-void SemanticState::ConsumerImpl::addMessageCredit(uint32_t value)
+void SemanticStateConsumerImpl::addMessageCredit(uint32_t value)
{
- assertClusterSafe();
credit.addMessageCredit(value);
}
-bool SemanticState::ConsumerImpl::haveCredit()
+bool SemanticStateConsumerImpl::haveCredit()
{
if (credit) {
return true;
@@ -632,21 +636,20 @@ bool SemanticState::ConsumerImpl::haveCredit()
}
}
-bool SemanticState::ConsumerImpl::doDispatch()
+bool SemanticStateConsumerImpl::doDispatch()
{
return queue->dispatch(shared_from_this());
}
-void SemanticState::ConsumerImpl::flush()
+void SemanticStateConsumerImpl::flush()
{
while(haveCredit() && doDispatch())
;
credit.cancel();
}
-void SemanticState::ConsumerImpl::stop()
+void SemanticStateConsumerImpl::stop()
{
- assertClusterSafe();
credit.cancel();
}
@@ -700,7 +703,7 @@ void SemanticState::reject(DeliveryId first, DeliveryId last)
getSession().setUnackedCount(unacked.size());
}
-bool SemanticState::ConsumerImpl::doOutput()
+bool SemanticStateConsumerImpl::doOutput()
{
try {
return haveCredit() && doDispatch();
@@ -709,28 +712,26 @@ bool SemanticState::ConsumerImpl::doOutput()
}
}
-void SemanticState::ConsumerImpl::enableNotify()
+void SemanticStateConsumerImpl::enableNotify()
{
Mutex::ScopedLock l(lock);
- assertClusterSafe();
notifyEnabled = true;
}
-void SemanticState::ConsumerImpl::disableNotify()
+void SemanticStateConsumerImpl::disableNotify()
{
Mutex::ScopedLock l(lock);
notifyEnabled = false;
}
-bool SemanticState::ConsumerImpl::isNotifyEnabled() const {
+bool SemanticStateConsumerImpl::isNotifyEnabled() const {
Mutex::ScopedLock l(lock);
return notifyEnabled;
}
-void SemanticState::ConsumerImpl::notify()
+void SemanticStateConsumerImpl::notify()
{
Mutex::ScopedLock l(lock);
- assertClusterSafe();
if (notifyEnabled) {
parent->session.getConnection().outputTasks.addOutputTask(this);
parent->session.getConnection().outputTasks.activateOutput();
@@ -755,7 +756,6 @@ isInSequenceSetAnd(const SequenceSet& s, Predicate p) {
}
void SemanticState::accepted(const SequenceSet& commands) {
- assertClusterSafe();
if (txBuffer.get()) {
//in transactional mode, don't dequeue or remove, just
//maintain set of acknowledged messages:
@@ -815,4 +815,63 @@ void SemanticState::detached()
}
}
+void SemanticState::addBinding(const string& queueName, const string& exchangeName,
+ const string& routingKey, const framing::FieldTable& arguments)
+{
+ QPID_LOG (debug, "SemanticState::addBinding ["
+ << "queue=" << queueName << ", "
+ << "exchange=" << exchangeName << ", "
+ << "key=" << routingKey << ", "
+ << "args=" << arguments << "]");
+ std::string fedOp = arguments.getAsString(qpidFedOp);
+ if ((arguments.isSet(qpidFedOp)) && (fedOp.empty())) {
+ fedOp = fedOpBind;
+ }
+ std::string fedOrigin = arguments.getAsString(qpidFedOrigin);
+ if ((arguments.getAsString(X_SCOPE) == SESSION) || (fedOp == fedOpBind)) {
+ bindings.insert(boost::make_tuple(queueName, exchangeName, routingKey, fedOrigin));
+ }
+ else if (fedOp == fedOpUnbind) {
+ bindings.erase(boost::make_tuple(queueName, exchangeName, routingKey, fedOrigin));
+ }
+}
+
+void SemanticState::removeBinding(const string& queueName, const string& exchangeName,
+ const string& routingKey)
+{
+ QPID_LOG (debug, "SemanticState::removeBinding ["
+ << "queue=" << queueName << ", "
+ << "exchange=" << exchangeName << ", "
+ << "key=" << routingKey)
+ bindings.erase(boost::make_tuple(queueName, exchangeName, routingKey, ""));
+}
+
+void SemanticState::unbindSessionBindings()
+{
+ //unbind session-scoped bindings
+ for (Bindings::iterator i = bindings.begin(); i != bindings.end(); i++) {
+ QPID_LOG (debug, "SemanticState::unbindSessionBindings ["
+ << "queue=" << i->get<0>() << ", "
+ << "exchange=" << i->get<1>()<< ", "
+ << "key=" << i->get<2>() << ", "
+ << "fedOrigin=" << i->get<3>() << "]");
+ try {
+ std::string fedOrigin = i->get<3>();
+ if (!fedOrigin.empty()) {
+ framing::FieldTable fedArguments;
+ fedArguments.setString(qpidFedOp, fedOpUnbind);
+ fedArguments.setString(qpidFedOrigin, fedOrigin);
+ session.getBroker().bind(i->get<0>(), i->get<1>(), i->get<2>(), fedArguments,
+ userID, connectionId);
+ } else {
+ session.getBroker().unbind(i->get<0>(), i->get<1>(), i->get<2>(),
+ userID, connectionId);
+ }
+ }
+ catch (...) {
+ }
+ }
+ bindings.clear();
+}
+
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/SemanticState.h b/cpp/src/qpid/broker/SemanticState.h
index 9add663e24..ec48ca4753 100644
--- a/cpp/src/qpid/broker/SemanticState.h
+++ b/cpp/src/qpid/broker/SemanticState.h
@@ -46,10 +46,12 @@
#include <list>
#include <map>
+#include <set>
#include <vector>
#include <boost/enable_shared_from_this.hpp>
#include <boost/cast.hpp>
+#include <boost/tuple/tuple.hpp>
namespace qpid {
namespace broker {
@@ -57,6 +59,7 @@ namespace broker {
class Exchange;
//class MessageStore;
class AsyncStore;
+class ProtocolRegistry;
class SessionContext;
class SessionState;
@@ -74,104 +77,18 @@ class SessionState;
* called when a client's socket is ready to write data.
*
*/
+class SemanticStateConsumerImpl;
class SemanticState : private boost::noncopyable {
- public:
- class ConsumerImpl : public Consumer, public sys::OutputTask,
- public boost::enable_shared_from_this<ConsumerImpl>,
- public management::Manageable
- {
- protected:
- mutable qpid::sys::Mutex lock;
- SemanticState* const parent;
- private:
- const boost::shared_ptr<Queue> queue;
- const bool ackExpected;
- const bool acquire;
- bool blocked;
- bool exclusive;
- std::string resumeId;
- const std::string tag; // <destination> from AMQP 0-10 Message.subscribe command
- uint64_t resumeTtl;
- framing::FieldTable arguments;
- Credit credit;
- bool notifyEnabled;
- const int syncFrequency;
- int deliveryCount;
- qmf::org::apache::qpid::broker::Subscription* mgmtObject;
-
- bool checkCredit(const Message& msg);
- void allocateCredit(const Message& msg);
- bool haveCredit();
-
- protected:
- QPID_BROKER_EXTERN virtual bool doDispatch();
- size_t unacked() { return parent->unacked.size(); }
- QPID_BROKER_EXTERN bool deliver(const QueueCursor&, const Message&, boost::shared_ptr<Consumer>);
-
- public:
- typedef boost::shared_ptr<ConsumerImpl> shared_ptr;
-
- QPID_BROKER_EXTERN ConsumerImpl(SemanticState* parent,
- const std::string& name, boost::shared_ptr<Queue> queue,
- bool ack, SubscriptionType type, bool exclusive,
- const std::string& tag, const std::string& resumeId,
- uint64_t resumeTtl, const framing::FieldTable& arguments);
- QPID_BROKER_EXTERN ~ConsumerImpl();
- QPID_BROKER_EXTERN OwnershipToken* getSession();
- QPID_BROKER_EXTERN bool deliver(const QueueCursor&, const Message&);
- QPID_BROKER_EXTERN bool filter(const Message&);
- QPID_BROKER_EXTERN bool accept(const Message&);
- QPID_BROKER_EXTERN void cancel() {}
-
- QPID_BROKER_EXTERN void disableNotify();
- QPID_BROKER_EXTERN void enableNotify();
- QPID_BROKER_EXTERN void notify();
- QPID_BROKER_EXTERN bool isNotifyEnabled() const;
-
- QPID_BROKER_EXTERN void requestDispatch();
-
- QPID_BROKER_EXTERN void setWindowMode();
- QPID_BROKER_EXTERN void setCreditMode();
- QPID_BROKER_EXTERN void addByteCredit(uint32_t value);
- QPID_BROKER_EXTERN void addMessageCredit(uint32_t value);
- QPID_BROKER_EXTERN void flush();
- QPID_BROKER_EXTERN void stop();
- QPID_BROKER_EXTERN void complete(DeliveryRecord&);
- boost::shared_ptr<Queue> getQueue() const { return queue; }
- bool isBlocked() const { return blocked; }
- bool setBlocked(bool set) { std::swap(set, blocked); return set; }
-
- QPID_BROKER_EXTERN bool doOutput();
-
- Credit& getCredit() { return credit; }
- const Credit& getCredit() const { return credit; }
- bool isAckExpected() const { return ackExpected; }
- bool isAcquire() const { return acquire; }
- bool isExclusive() const { return exclusive; }
- std::string getResumeId() const { return resumeId; };
- const std::string& getTag() const { return tag; }
- uint64_t getResumeTtl() const { return resumeTtl; }
- uint32_t getDeliveryCount() const { return deliveryCount; }
- void setDeliveryCount(uint32_t _deliveryCount) { deliveryCount = _deliveryCount; }
- const framing::FieldTable& getArguments() const { return arguments; }
-
- SemanticState& getParent() { return *parent; }
- const SemanticState& getParent() const { return *parent; }
-
- void acknowledged(const DeliveryRecord&) {}
-
- // manageable entry points
- QPID_BROKER_EXTERN management::ManagementObject*
- GetManagementObject(void) const;
-
- QPID_BROKER_EXTERN management::Manageable::status_t
- ManagementMethod(uint32_t methodId, management::Args& args, std::string& text);
- };
+ friend class SemanticStateConsumerImpl;
+ public:
+ typedef SemanticStateConsumerImpl ConsumerImpl;
typedef std::map<std::string, DtxBuffer::shared_ptr> DtxBufferMap;
private:
- typedef std::map<std::string, ConsumerImpl::shared_ptr> ConsumerImplMap;
+ typedef std::map<std::string, boost::shared_ptr<ConsumerImpl> > ConsumerImplMap;
+ typedef boost::tuple<std::string, std::string, std::string, std::string> Binding;
+ typedef std::set<Binding> Bindings;
SessionState& session;
ConsumerImplMap consumers;
@@ -189,13 +106,16 @@ class SemanticState : private boost::noncopyable {
//needed for queue delete events in auto-delete:
const std::string connectionId;
+ Bindings bindings;
+
void checkDtxTimeout();
bool complete(DeliveryRecord&);
AckRange findRange(DeliveryId first, DeliveryId last);
void requestDispatch();
- void cancel(ConsumerImpl::shared_ptr);
- void disable(ConsumerImpl::shared_ptr);
+ void cancel(boost::shared_ptr<ConsumerImpl>);
+ void disable(boost::shared_ptr<ConsumerImpl>);
+ void unbindSessionBindings();
public:
@@ -205,8 +125,8 @@ class SemanticState : private boost::noncopyable {
SessionContext& getSession();
const SessionContext& getSession() const;
- const ConsumerImpl::shared_ptr find(const std::string& destination) const;
- bool find(const std::string& destination, ConsumerImpl::shared_ptr&) const;
+ const boost::shared_ptr<ConsumerImpl> find(const std::string& destination) const;
+ bool find(const std::string& destination, boost::shared_ptr<ConsumerImpl>&) const;
/**
* Get named queue, never returns 0.
@@ -257,11 +177,6 @@ class SemanticState : private boost::noncopyable {
void detached();
void closed();
- // Used by cluster to re-create sessions
- template <class F> void eachConsumer(F f) {
- for(ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); ++i)
- f(i->second);
- }
DeliveryRecords& getUnacked() { return unacked; }
framing::SequenceSet getAccumulatedAck() const { return accumulatedAck; }
TxBuffer::shared_ptr getTxBuffer() const { return txBuffer; }
@@ -271,6 +186,104 @@ class SemanticState : private boost::noncopyable {
void setAccumulatedAck(const framing::SequenceSet& s) { accumulatedAck = s; }
void record(const DeliveryRecord& delivery);
DtxBufferMap& getSuspendedXids() { return suspendedXids; }
+
+ void addBinding(const std::string& queueName, const std::string& exchangeName,
+ const std::string& routingKey, const framing::FieldTable& arguments);
+ void removeBinding(const std::string& queueName, const std::string& exchangeName,
+ const std::string& routingKey);
+};
+
+class SemanticStateConsumerImpl : public Consumer, public sys::OutputTask,
+ public boost::enable_shared_from_this<SemanticStateConsumerImpl>,
+ public management::Manageable
+{
+ protected:
+ mutable qpid::sys::Mutex lock;
+ SemanticState* const parent;
+ private:
+ const boost::shared_ptr<Queue> queue;
+ const bool ackExpected;
+ const bool acquire;
+ bool blocked;
+ bool exclusive;
+ std::string resumeId;
+ const std::string tag; // <destination> from AMQP 0-10 Message.subscribe command
+ uint64_t resumeTtl;
+ framing::FieldTable arguments;
+ Credit credit;
+ bool notifyEnabled;
+ const int syncFrequency;
+ int deliveryCount;
+ qmf::org::apache::qpid::broker::Subscription::shared_ptr mgmtObject;
+ ProtocolRegistry& protocols;
+
+ bool checkCredit(const Message& msg);
+ void allocateCredit(const Message& msg);
+ bool haveCredit();
+
+ protected:
+ QPID_BROKER_EXTERN virtual bool doDispatch();
+ size_t unacked() { return parent->unacked.size(); }
+ QPID_BROKER_EXTERN bool deliver(const QueueCursor&, const Message&, boost::shared_ptr<Consumer>);
+
+ public:
+ typedef boost::shared_ptr<SemanticStateConsumerImpl> shared_ptr;
+
+ QPID_BROKER_EXTERN SemanticStateConsumerImpl(SemanticState* parent,
+ const std::string& name, boost::shared_ptr<Queue> queue,
+ bool ack, SubscriptionType type, bool exclusive,
+ const std::string& tag, const std::string& resumeId,
+ uint64_t resumeTtl, const framing::FieldTable& arguments);
+ QPID_BROKER_EXTERN ~SemanticStateConsumerImpl();
+ QPID_BROKER_EXTERN OwnershipToken* getSession();
+ QPID_BROKER_EXTERN bool deliver(const QueueCursor&, const Message&);
+ QPID_BROKER_EXTERN bool filter(const Message&);
+ QPID_BROKER_EXTERN bool accept(const Message&);
+ QPID_BROKER_EXTERN void cancel() {}
+
+ QPID_BROKER_EXTERN void disableNotify();
+ QPID_BROKER_EXTERN void enableNotify();
+ QPID_BROKER_EXTERN void notify();
+ QPID_BROKER_EXTERN bool isNotifyEnabled() const;
+
+ QPID_BROKER_EXTERN void requestDispatch();
+
+ QPID_BROKER_EXTERN void setWindowMode();
+ QPID_BROKER_EXTERN void setCreditMode();
+ QPID_BROKER_EXTERN void addByteCredit(uint32_t value);
+ QPID_BROKER_EXTERN void addMessageCredit(uint32_t value);
+ QPID_BROKER_EXTERN void flush();
+ QPID_BROKER_EXTERN void stop();
+ QPID_BROKER_EXTERN void complete(DeliveryRecord&);
+ boost::shared_ptr<Queue> getQueue() const { return queue; }
+ bool isBlocked() const { return blocked; }
+ bool setBlocked(bool set) { std::swap(set, blocked); return set; }
+
+ QPID_BROKER_EXTERN bool doOutput();
+
+ Credit& getCredit() { return credit; }
+ const Credit& getCredit() const { return credit; }
+ bool isAckExpected() const { return ackExpected; }
+ bool isAcquire() const { return acquire; }
+ bool isExclusive() const { return exclusive; }
+ std::string getResumeId() const { return resumeId; };
+ const std::string& getTag() const { return tag; }
+ uint64_t getResumeTtl() const { return resumeTtl; }
+ uint32_t getDeliveryCount() const { return deliveryCount; }
+ void setDeliveryCount(uint32_t _deliveryCount) { deliveryCount = _deliveryCount; }
+ const framing::FieldTable& getArguments() const { return arguments; }
+
+ SemanticState& getParent() { return *parent; }
+ const SemanticState& getParent() const { return *parent; }
+
+ void acknowledged(const DeliveryRecord&) {}
+
+ // manageable entry points
+ QPID_BROKER_EXTERN management::ManagementObject::shared_ptr
+ GetManagementObject(void) const;
+
+ QPID_BROKER_EXTERN management::Manageable::status_t
+ ManagementMethod(uint32_t methodId, management::Args& args, std::string& text);
};
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/SessionAdapter.cpp b/cpp/src/qpid/broker/SessionAdapter.cpp
index cb2fe15b58..1ea18ea472 100644
--- a/cpp/src/qpid/broker/SessionAdapter.cpp
+++ b/cpp/src/qpid/broker/SessionAdapter.cpp
@@ -16,7 +16,10 @@
*
*/
#include "qpid/broker/SessionAdapter.h"
+
+#include "qpid/broker/Broker.h"
#include "qpid/broker/Connection.h"
+#include "qpid/broker/DtxTimeout.h"
#include "qpid/broker/Queue.h"
#include "qpid/Exception.h"
#include "qpid/framing/reply_exceptions.h"
@@ -98,17 +101,6 @@ void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const
//exchange already there, not created
checkType(response.first, type);
checkAlternate(response.first, alternate);
- ManagementAgent* agent = getBroker().getManagementAgent();
- if (agent)
- agent->raiseEvent(_qmf::EventExchangeDeclare(getConnection().getUrl(),
- getConnection().getUserId(),
- exchange,
- type,
- alternateExchange,
- durable,
- false,
- ManagementAgent::toMap(args),
- "existing"));
QPID_LOG_CAT(debug, model, "Create exchange. name:" << exchange
<< " user:" << getConnection().getUserId()
<< " rhost:" << getConnection().getUrl()
@@ -165,12 +157,14 @@ void SessionAdapter::ExchangeHandlerImpl::bind(const string& queueName,
{
getBroker().bind(queueName, exchangeName, routingKey, arguments,
getConnection().getUserId(), getConnection().getUrl());
+ state.addBinding(queueName, exchangeName, routingKey, arguments);
}
void SessionAdapter::ExchangeHandlerImpl::unbind(const string& queueName,
const string& exchangeName,
const string& routingKey)
{
+ state.removeBinding(queueName, exchangeName, routingKey);
getBroker().unbind(queueName, exchangeName, routingKey,
getConnection().getUserId(), getConnection().getUrl());
}
@@ -300,6 +294,8 @@ void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string&
} catch (const qpid::types::Exception& e) {
throw InvalidArgumentException(e.what());
}
+ // Identify queues that won't survive a failover.
+ settings.isTemporary = exclusive && autoDelete && !settings.autoDeleteDelay;
std::pair<Queue::shared_ptr, bool> queue_created =
getBroker().createQueue(name, settings,
@@ -318,11 +314,6 @@ void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string&
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, alternateExchange, ManagementAgent::toMap(arguments),
- "existing"));
QPID_LOG_CAT(debug, model, "Create queue. name:" << name
<< " user:" << getConnection().getUserId()
<< " rhost:" << getConnection().getUrl()
@@ -422,6 +413,11 @@ SessionAdapter::MessageHandlerImpl::subscribe(const string& queueName,
if(!destination.empty() && state.exists(destination))
throw NotAllowedException(QPID_MSG("Consumer tags must be unique"));
+ if (queue->getSettings().isBrowseOnly && acquireMode == 0) {
+ QPID_LOG(info, "Overriding request to consume from browse-only queue " << queue->getName());
+ acquireMode = 1;
+ }
+
// We allow browsing (acquireMode == 1) of exclusive queues, this is required by HA.
if (queue->hasExclusiveOwner() && !queue->isExclusiveOwner(&session) && acquireMode == 0)
throw ResourceLockedException(QPID_MSG("Cannot subscribe to exclusive queue "
diff --git a/cpp/src/qpid/broker/SessionHandler.cpp b/cpp/src/qpid/broker/SessionHandler.cpp
index 9888d12be2..a6b008647f 100644
--- a/cpp/src/qpid/broker/SessionHandler.cpp
+++ b/cpp/src/qpid/broker/SessionHandler.cpp
@@ -19,8 +19,9 @@
*/
#include "qpid/broker/SessionHandler.h"
-#include "qpid/broker/SessionState.h"
+#include "qpid/broker/Broker.h"
#include "qpid/broker/Connection.h"
+#include "qpid/broker/SessionState.h"
#include "qpid/log/Statement.h"
#include <boost/bind.hpp>
@@ -34,9 +35,7 @@ using namespace qpid::sys;
SessionHandler::SessionHandler(Connection& c, ChannelId ch)
: qpid::amqp_0_10::SessionHandler(&c.getOutput(), ch),
connection(c),
- proxy(out),
- clusterOrderProxy(c.getClusterOrderOutput() ?
- new SetChannelProxy(ch, c.getClusterOrderOutput()) : 0)
+ proxy(out)
{}
SessionHandler::~SessionHandler() {}
@@ -110,10 +109,7 @@ void SessionHandler::attachAs(const std::string& name)
{
SessionId id(connection.getUserId(), name);
SessionState::Configuration config = connection.broker.getSessionManager().getSessionConfig();
- // Delay creating management object till attached(). In a cluster,
- // only the active link broker calls attachAs but all brokers
- // receive the subsequent attached() call.
- session.reset(new SessionState(connection.getBroker(), *this, id, config, true));
+ session.reset(new SessionState(connection.getBroker(), *this, id, config));
sendAttach(false);
}
diff --git a/cpp/src/qpid/broker/SessionHandler.h b/cpp/src/qpid/broker/SessionHandler.h
index 21c736fa37..d42b7838bb 100644
--- a/cpp/src/qpid/broker/SessionHandler.h
+++ b/cpp/src/qpid/broker/SessionHandler.h
@@ -71,17 +71,6 @@ class SessionHandler : public qpid::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();
void attached(const std::string& name);//used by 'pushing' inter-broker bridges
void attachAs(const std::string& name);//used by 'pulling' inter-broker bridges
@@ -108,7 +97,6 @@ class SessionHandler : public qpid::amqp_0_10::SessionHandler {
Connection& connection;
framing::AMQP_ClientProxy proxy;
std::auto_ptr<SessionState> session;
- std::auto_ptr<SetChannelProxy> clusterOrderProxy;
boost::shared_ptr<ErrorListener> errorListener;
};
diff --git a/cpp/src/qpid/broker/SessionState.cpp b/cpp/src/qpid/broker/SessionState.cpp
index 944cbad0aa..d71134548a 100644
--- a/cpp/src/qpid/broker/SessionState.cpp
+++ b/cpp/src/qpid/broker/SessionState.cpp
@@ -25,7 +25,6 @@
#include "qpid/broker/DeliveryRecord.h"
#include "qpid/broker/SessionManager.h"
#include "qpid/broker/SessionHandler.h"
-#include "qpid/sys/ClusterSafe.h"
#include "qpid/framing/AMQContentBody.h"
#include "qpid/framing/AMQHeaderBody.h"
#include "qpid/framing/AMQMethodBody.h"
@@ -54,15 +53,14 @@ namespace _qmf = qmf::org::apache::qpid::broker;
SessionState::SessionState(
Broker& b, SessionHandler& h, const SessionId& id,
- const SessionState::Configuration& config, bool delayManagement)
+ const SessionState::Configuration& config)
: qpid::SessionState(id, config),
broker(b), handler(&h),
semanticState(*this),
adapter(semanticState),
- mgmtObject(0),
asyncCommandCompleter(new AsyncCommandCompleter(this))
{
- if (!delayManagement) addManagementObject();
+ addManagementObject();
attach(h);
}
@@ -72,8 +70,8 @@ void SessionState::addManagementObject() {
if (parent != 0) {
ManagementAgent* agent = getBroker().getManagementAgent();
if (agent != 0) {
- mgmtObject = new _qmf::Session
- (agent, this, parent, getId().getName());
+ mgmtObject = _qmf::Session::shared_ptr(new _qmf::Session
+ (agent, this, parent, getId().getName()));
mgmtObject->set_attached (0);
mgmtObject->set_detachedLifespan (0);
mgmtObject->clr_expireTime();
@@ -145,14 +143,9 @@ void SessionState::activateOutput() {
getConnection().outputTasks.activateOutput();
}
-void SessionState::giveReadCredit(int32_t credit) {
- if (isAttached())
- getConnection().outputTasks.giveReadCredit(credit);
-}
-
-ManagementObject* SessionState::GetManagementObject (void) const
+ManagementObject::shared_ptr SessionState::GetManagementObject(void) const
{
- return (ManagementObject*) mgmtObject;
+ return mgmtObject;
}
Manageable::status_t SessionState::ManagementMethod (uint32_t methodId,
@@ -251,11 +244,6 @@ void SessionState::completeRcvMsg(SequenceNumber id,
bool requiresAccept,
bool requiresSync)
{
- // Mark this as a cluster-unsafe scope since it can be called in
- // journal threads or connection threads as part of asynchronous
- // command completion.
- sys::ClusterUnsafeScope cus;
-
bool callSendCompletion = false;
receiverCompleted(id);
if (requiresAccept)
@@ -340,15 +328,9 @@ void SessionState::readyToSend() {
Broker& SessionState::getBroker() { return broker; }
// Session resume is not fully implemented so it is useless to set a
-// non-0 timeout. Moreover it creates problems in a cluster because
-// dead sessions are kept and interfere with failover.
+// non-0 timeout.
void SessionState::setTimeout(uint32_t) { }
-framing::AMQP_ClientProxy& SessionState::getClusterOrderProxy() {
- return handler->getClusterOrderProxy();
-}
-
-
// Current received command is an execution.sync command.
// Complete this command only when all preceding commands have completed.
// (called via the invoker() in handleCommand() above)
diff --git a/cpp/src/qpid/broker/SessionState.h b/cpp/src/qpid/broker/SessionState.h
index 5e3a77d7ed..a531ec9fc6 100644
--- a/cpp/src/qpid/broker/SessionState.h
+++ b/cpp/src/qpid/broker/SessionState.h
@@ -41,6 +41,7 @@
#include <boost/scoped_ptr.hpp>
#include <boost/intrusive_ptr.hpp>
+#include <queue>
#include <set>
#include <vector>
#include <ostream>
@@ -73,7 +74,7 @@ class SessionState : public qpid::SessionState,
{
public:
SessionState(Broker&, SessionHandler&, const SessionId&,
- const SessionState::Configuration&, bool delayManagement=false);
+ const SessionState::Configuration&);
~SessionState();
bool isAttached() const { return handler; }
@@ -98,7 +99,6 @@ class SessionState : public qpid::SessionState,
/** OutputControl **/
void abort();
void activateOutput();
- void giveReadCredit(int32_t);
void senderCompleted(const framing::SequenceSet& ranges);
@@ -110,17 +110,12 @@ class SessionState : public qpid::SessionState,
const qpid::types::Variant::Map& annotations, bool sync);
// Manageable entry points
- management::ManagementObject* GetManagementObject (void) const;
+ management::ManagementObject::shared_ptr GetManagementObject(void) const;
management::Manageable::status_t
ManagementMethod (uint32_t methodId, management::Args& args, std::string&);
void readyToSend();
- // Used by cluster to create replica sessions.
- SemanticState& getSemanticState() { return semanticState; }
- boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> getMessageInProgress() { return msgBuilder.getMessage(); }
- SessionAdapter& getSessionAdapter() { return adapter; }
-
const SessionId& getSessionId() const { return getId(); }
// Used by ExecutionHandler sync command processing. Notifies
@@ -153,22 +148,13 @@ class SessionState : public qpid::SessionState,
void sendAcceptAndCompletion();
- /**
- * If commands are sent based on the local time (e.g. in timers), they don't have
- * a well-defined ordering across cluster nodes.
- * This proxy is for sending such commands. In a clustered broker it will take steps
- * to synchronize command order across the cluster. In a stand-alone broker
- * it is just a synonym for getProxy()
- */
- framing::AMQP_ClientProxy& getClusterOrderProxy();
-
Broker& broker;
SessionHandler* handler;
sys::AbsTime expiry; // Used by SessionManager.
SemanticState semanticState;
SessionAdapter adapter;
MessageBuilder msgBuilder;
- qmf::org::apache::qpid::broker::Session* mgmtObject;
+ qmf::org::apache::qpid::broker::Session::shared_ptr mgmtObject;
qpid::framing::SequenceSet accepted;
// sequence numbers for pending received Execution.Sync commands
diff --git a/cpp/src/qpid/broker/StatefulQueueObserver.h b/cpp/src/qpid/broker/StatefulQueueObserver.h
deleted file mode 100644
index c682d460b7..0000000000
--- a/cpp/src/qpid/broker/StatefulQueueObserver.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef QPID_BROKER_STATEFULQUEUEOBSERVER_H
-#define QPID_BROKER_STATEFULQUEUEOBSERVER_H
-
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#include "qpid/broker/QueueObserver.h"
-#include "qpid/framing/FieldTable.h"
-
-namespace qpid {
-namespace broker {
-
-/**
- * Specialized type of QueueObserver that maintains internal state that has to
- * be replicated across clustered brokers.
- */
-class StatefulQueueObserver : public QueueObserver
-{
- public:
- StatefulQueueObserver(std::string _id) : id(_id) {}
- virtual ~StatefulQueueObserver() {}
-
- /** This identifier must uniquely identify this particular observer amoung
- * all observers on a queue. For cluster replication, this id will be used
- * to identify the peer queue observer for synchronization across
- * brokers.
- */
- const std::string& getId() const { return id; }
-
- /** This method should return the observer's internal state as an opaque
- * map.
- */
- virtual void getState(qpid::framing::FieldTable& state ) const = 0;
-
- /** The input map represents the internal state of the peer observer that
- * this observer should synchonize to.
- */
- virtual void setState(const qpid::framing::FieldTable&) = 0;
-
-
- private:
- std::string id;
-};
-}} // namespace qpid::broker
-
-#endif /*!QPID_BROKER_STATEFULQUEUEOBSERVER_H*/
diff --git a/cpp/src/qpid/broker/System.cpp b/cpp/src/qpid/broker/System.cpp
index fa8df6406b..8d54427fdc 100644
--- a/cpp/src/qpid/broker/System.cpp
+++ b/cpp/src/qpid/broker/System.cpp
@@ -31,7 +31,7 @@ using namespace qpid::broker;
using namespace std;
namespace _qmf = qmf::org::apache::qpid::broker;
-System::System (string _dataDir, Broker* broker) : mgmtObject(0)
+System::System (string _dataDir, Broker* broker)
{
ManagementAgent* agent = broker ? broker->getManagementAgent() : 0;
@@ -64,7 +64,7 @@ System::System (string _dataDir, Broker* broker) : mgmtObject(0)
}
}
- mgmtObject = new _qmf::System(agent, this, types::Uuid(systemId.c_array()));
+ mgmtObject = _qmf::System::shared_ptr(new _qmf::System(agent, this, types::Uuid(systemId.c_array())));
qpid::sys::SystemInfo::getSystemId (osName,
nodeName,
release,
diff --git a/cpp/src/qpid/broker/System.h b/cpp/src/qpid/broker/System.h
index 6847c662ae..52643fb2d5 100644
--- a/cpp/src/qpid/broker/System.h
+++ b/cpp/src/qpid/broker/System.h
@@ -35,7 +35,7 @@ class System : public management::Manageable
{
private:
- qmf::org::apache::qpid::broker::System* mgmtObject;
+ qmf::org::apache::qpid::broker::System::shared_ptr mgmtObject;
framing::Uuid systemId;
std::string osName, nodeName, release, version, machine;
@@ -45,7 +45,7 @@ class System : public management::Manageable
System (std::string _dataDir, Broker* broker = 0);
- management::ManagementObject* GetManagementObject (void) const
+ management::ManagementObject::shared_ptr GetManagementObject(void) const
{ return mgmtObject; }
diff --git a/cpp/src/qpid/broker/TopicExchange.cpp b/cpp/src/qpid/broker/TopicExchange.cpp
index 38d8f255ac..0badd1621c 100644
--- a/cpp/src/qpid/broker/TopicExchange.cpp
+++ b/cpp/src/qpid/broker/TopicExchange.cpp
@@ -179,7 +179,7 @@ bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, cons
}
}
- Binding::shared_ptr binding (new Binding (routingPattern, queue, this, FieldTable(), fedOrigin));
+ Binding::shared_ptr binding (new Binding (routingPattern, queue, this, args ? *args : FieldTable(), fedOrigin));
binding->startManagement();
bk->bindingVector.push_back(binding);
nBindings++;
diff --git a/cpp/src/qpid/broker/TxAccept.h b/cpp/src/qpid/broker/TxAccept.h
index a59e69a85f..daf192285a 100644
--- a/cpp/src/qpid/broker/TxAccept.h
+++ b/cpp/src/qpid/broker/TxAccept.h
@@ -71,9 +71,6 @@ namespace qpid {
virtual void commit() throw();
virtual void rollback() throw();
virtual ~TxAccept(){}
-
- // Used by cluster replication.
- const framing::SequenceSet& getAcked() const { return acked; }
};
}
}
diff --git a/cpp/src/qpid/broker/TxOpVisitor.h b/cpp/src/qpid/broker/TxOpVisitor.h
deleted file mode 100644
index e69de29bb2..0000000000
--- a/cpp/src/qpid/broker/TxOpVisitor.h
+++ /dev/null
diff --git a/cpp/src/qpid/broker/TxPublish.cpp b/cpp/src/qpid/broker/TxPublish.cpp
deleted file mode 100644
index e69de29bb2..0000000000
--- a/cpp/src/qpid/broker/TxPublish.cpp
+++ /dev/null
diff --git a/cpp/src/qpid/broker/TxPublish.h b/cpp/src/qpid/broker/TxPublish.h
deleted file mode 100644
index e69de29bb2..0000000000
--- a/cpp/src/qpid/broker/TxPublish.h
+++ /dev/null
diff --git a/cpp/src/qpid/broker/Vhost.cpp b/cpp/src/qpid/broker/Vhost.cpp
index a9ca3b42ab..e72118b570 100644
--- a/cpp/src/qpid/broker/Vhost.cpp
+++ b/cpp/src/qpid/broker/Vhost.cpp
@@ -29,7 +29,7 @@ namespace qpid { namespace management {
class Manageable;
}}
-Vhost::Vhost (qpid::management::Manageable* parentBroker, Broker* broker) : mgmtObject(0)
+Vhost::Vhost (qpid::management::Manageable* parentBroker, Broker* broker)
{
if (parentBroker != 0 && broker != 0)
{
@@ -37,7 +37,7 @@ Vhost::Vhost (qpid::management::Manageable* parentBroker, Broker* broker) : mgmt
if (agent != 0)
{
- mgmtObject = new _qmf::Vhost(agent, this, parentBroker, "/");
+ mgmtObject = _qmf::Vhost::shared_ptr(new _qmf::Vhost(agent, this, parentBroker, "/"));
agent->addObject(mgmtObject, 0, true);
}
}
diff --git a/cpp/src/qpid/broker/Vhost.h b/cpp/src/qpid/broker/Vhost.h
index 9554d641c2..599b821870 100644
--- a/cpp/src/qpid/broker/Vhost.h
+++ b/cpp/src/qpid/broker/Vhost.h
@@ -32,7 +32,7 @@ class Vhost : public management::Manageable
{
private:
- qmf::org::apache::qpid::broker::Vhost* mgmtObject;
+ qmf::org::apache::qpid::broker::Vhost::shared_ptr mgmtObject;
public:
@@ -40,7 +40,7 @@ class Vhost : public management::Manageable
Vhost (management::Manageable* parentBroker, Broker* broker = 0);
- management::ManagementObject* GetManagementObject (void) const
+ management::ManagementObject::shared_ptr GetManagementObject (void) const
{ return mgmtObject; }
void setFederationTag(const std::string& tag);
};
diff --git a/cpp/src/qpid/broker/amqp/Connection.cpp b/cpp/src/qpid/broker/amqp/Connection.cpp
new file mode 100644
index 0000000000..1f135cf931
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Connection.cpp
@@ -0,0 +1,247 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "Connection.h"
+#include "Session.h"
+#include "qpid/Exception.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/ProtocolInitiation.h"
+#include "qpid/framing/ProtocolVersion.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/OutputControl.h"
+#include <sstream>
+extern "C" {
+#include <proton/engine.h>
+#include <proton/error.h>
+}
+
+namespace qpid {
+namespace broker {
+namespace amqp {
+
+Connection::Connection(qpid::sys::OutputControl& o, const std::string& i, qpid::broker::Broker& b, bool saslInUse)
+ : ManagedConnection(b, i),
+ connection(pn_connection()),
+ transport(pn_transport()),
+ out(o), id(i), broker(b), haveOutput(true)
+{
+ if (pn_transport_bind(transport, connection)) {
+ //error
+ }
+ out.activateOutput();
+ bool enableTrace(false);
+ QPID_LOG_TEST_CAT(trace, protocol, enableTrace);
+ if (enableTrace) pn_transport_trace(transport, PN_TRACE_FRM);
+
+ if (!saslInUse) {
+ //feed in a dummy AMQP 1.0 header as engine expects one, but
+ //we already read it (if sasl is in use we read the sasl
+ //header,not the AMQP 1.0 header).
+ std::vector<char> protocolHeader(8);
+ qpid::framing::ProtocolInitiation pi(getVersion());
+ qpid::framing::Buffer buffer(&protocolHeader[0], protocolHeader.size());
+ pi.encode(buffer);
+ pn_transport_input(transport, &protocolHeader[0], protocolHeader.size());
+
+ //wont get a userid, so set a dummy one on the ManagedConnection to trigger event
+ setUserid("no authentication used");
+ }
+}
+
+
+Connection::~Connection()
+{
+
+ pn_transport_free(transport);
+ pn_connection_free(connection);
+}
+
+pn_transport_t* Connection::getTransport()
+{
+ return transport;
+}
+size_t Connection::decode(const char* buffer, size_t size)
+{
+ QPID_LOG(trace, id << " decode(" << size << ")")
+ //TODO: Fix pn_engine_input() to take const buffer
+ ssize_t n = pn_transport_input(transport, const_cast<char*>(buffer), size);
+ if (n > 0 || n == PN_EOS) {
+ //If engine returns EOS, have no way of knowing how many bytes
+ //it processed, but can assume none need to be reprocessed so
+ //consider them all read:
+ if (n == PN_EOS) n = size;
+ QPID_LOG_CAT(debug, network, id << " decoded " << n << " bytes from " << size)
+ process();
+ pn_transport_tick(transport, 0);
+ if (!haveOutput) {
+ haveOutput = true;
+ out.activateOutput();
+ }
+ return n;
+ } else if (n == PN_ERR) {
+ throw qpid::Exception(QPID_MSG("Error on input: " << getError()));
+ } else {
+ return 0;
+ }
+}
+
+size_t Connection::encode(char* buffer, size_t size)
+{
+ QPID_LOG(trace, "encode(" << size << ")")
+ ssize_t n = pn_transport_output(transport, buffer, size);
+ if (n > 0) {
+ QPID_LOG_CAT(debug, network, id << " encoded " << n << " bytes from " << size)
+ haveOutput = true;
+ return n;
+ } else if (n == PN_EOS) {
+ haveOutput = size;
+ return size;//Is this right?
+ } else if (n == PN_ERR) {
+ throw qpid::Exception(QPID_MSG("Error on output: " << getError()));
+ } else {
+ haveOutput = false;
+ return 0;
+ }
+}
+bool Connection::canEncode()
+{
+ for (Sessions::iterator i = sessions.begin();i != sessions.end(); ++i) {
+ if (i->second->dispatch()) haveOutput = true;
+ }
+ process();
+ //TODO: proper handling of time in and out of tick
+ pn_transport_tick(transport, 0);
+ QPID_LOG_CAT(trace, network, id << " canEncode(): " << haveOutput)
+ return haveOutput;
+}
+void Connection::closed()
+{
+ //TODO: tear down sessions and associated links
+ for (Sessions::iterator i = sessions.begin(); i != sessions.end(); ++i) {
+ i->second->close();
+ }
+}
+bool Connection::isClosed() const
+{
+ return pn_connection_state(connection) & PN_REMOTE_CLOSED;
+}
+framing::ProtocolVersion Connection::getVersion() const
+{
+ return qpid::framing::ProtocolVersion(1,0);
+}
+namespace {
+pn_state_t REQUIRES_OPEN = PN_LOCAL_UNINIT | PN_REMOTE_ACTIVE;
+pn_state_t REQUIRES_CLOSE = PN_LOCAL_ACTIVE | PN_REMOTE_CLOSED;
+}
+
+void Connection::process()
+{
+ QPID_LOG(trace, id << " process()");
+ if ((pn_connection_state(connection) & REQUIRES_OPEN) == REQUIRES_OPEN) {
+ QPID_LOG_CAT(debug, model, id << " connection opened");
+ pn_connection_set_container(connection, broker.getFederationTag().c_str());
+ pn_connection_open(connection);
+ }
+
+ for (pn_session_t* s = pn_session_head(connection, REQUIRES_OPEN); s; s = pn_session_next(s, REQUIRES_OPEN)) {
+ QPID_LOG_CAT(debug, model, id << " session begun");
+ pn_session_open(s);
+ boost::shared_ptr<Session> ssn(new Session(s, broker, *this, out));
+ sessions[s] = ssn;
+ }
+ for (pn_link_t* l = pn_link_head(connection, REQUIRES_OPEN); l; l = pn_link_next(l, REQUIRES_OPEN)) {
+ pn_link_open(l);
+
+ Sessions::iterator session = sessions.find(pn_link_session(l));
+ if (session == sessions.end()) {
+ QPID_LOG(error, id << " Link attached on unknown session!");
+ } else {
+ try {
+ session->second->attach(l);
+ QPID_LOG_CAT(debug, protocol, id << " link " << l << " attached on " << pn_link_session(l));
+ } catch (const std::exception& e) {
+ QPID_LOG_CAT(error, protocol, "Error on attach: " << e.what());
+ //TODO: set error details on detach when that is exposed via engine API
+ pn_link_close(l);
+ }
+ }
+ }
+
+ //handle deliveries
+ for (pn_delivery_t* delivery = pn_work_head(connection); delivery; delivery = pn_work_next(delivery)) {
+ pn_link_t* link = pn_delivery_link(delivery);
+ if (pn_link_is_receiver(link)) {
+ Sessions::iterator i = sessions.find(pn_link_session(link));
+ if (i != sessions.end()) {
+ i->second->incoming(link, delivery);
+ } else {
+ pn_delivery_update(delivery, PN_REJECTED);
+ }
+ } else { //i.e. SENDER
+ Sessions::iterator i = sessions.find(pn_link_session(link));
+ if (i != sessions.end()) {
+ QPID_LOG(trace, id << " handling outgoing delivery for " << link << " on session " << pn_link_session(link));
+ i->second->outgoing(link, delivery);
+ } else {
+ QPID_LOG(error, id << " Got delivery for non-existent session: " << pn_link_session(link) << ", link: " << link);
+ }
+ }
+ }
+
+
+ for (pn_link_t* l = pn_link_head(connection, REQUIRES_CLOSE); l; l = pn_link_next(l, REQUIRES_CLOSE)) {
+ pn_link_close(l);
+ Sessions::iterator session = sessions.find(pn_link_session(l));
+ if (session == sessions.end()) {
+ QPID_LOG(error, id << " peer attempted to detach link on unknown session!");
+ } else {
+ session->second->detach(l);
+ QPID_LOG_CAT(debug, model, id << " link detached");
+ }
+ }
+ for (pn_session_t* s = pn_session_head(connection, REQUIRES_CLOSE); s; s = pn_session_next(s, REQUIRES_CLOSE)) {
+ pn_session_close(s);
+ Sessions::iterator i = sessions.find(s);
+ if (i != sessions.end()) {
+ i->second->close();
+ sessions.erase(i);
+ QPID_LOG_CAT(debug, model, id << " session ended");
+ } else {
+ QPID_LOG(error, id << " peer attempted to close unrecognised session");
+ }
+ }
+ if ((pn_connection_state(connection) & REQUIRES_CLOSE) == REQUIRES_CLOSE) {
+ QPID_LOG_CAT(debug, model, id << " connection closed");
+ pn_connection_close(connection);
+ }
+}
+
+std::string Connection::getError()
+{
+ std::stringstream text;
+ pn_error_t* cerror = pn_connection_error(connection);
+ if (cerror) text << "connection error " << pn_error_text(cerror);
+ pn_error_t* terror = pn_transport_error(transport);
+ if (terror) text << "transport error " << pn_error_text(terror);
+ return text.str();
+}
+
+}}} // namespace qpid::broker::amqp
diff --git a/cpp/src/qpid/broker/amqp/Connection.h b/cpp/src/qpid/broker/amqp/Connection.h
new file mode 100644
index 0000000000..8af209af7a
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Connection.h
@@ -0,0 +1,73 @@
+#ifndef QPID_BROKER_AMQP1_CONNECTION_H
+#define QPID_BROKER_AMQP1_CONNECTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/sys/ConnectionCodec.h"
+#include "qpid/broker/amqp/ManagedConnection.h"
+#include <map>
+#include <boost/shared_ptr.hpp>
+
+struct pn_connection_t;
+struct pn_session_t;
+struct pn_transport_t;
+
+namespace qpid {
+namespace broker {
+
+class Broker;
+
+namespace amqp {
+
+class Session;
+/**
+ * AMQP 1.0 protocol support for broker
+ */
+class Connection : public sys::ConnectionCodec, public ManagedConnection
+{
+ public:
+ Connection(qpid::sys::OutputControl& out, const std::string& id, qpid::broker::Broker& broker, bool saslInUse);
+ ~Connection();
+ size_t decode(const char* buffer, size_t size);
+ size_t encode(char* buffer, size_t size);
+ bool canEncode();
+
+ void closed();
+ bool isClosed() const;
+
+ framing::ProtocolVersion getVersion() const;
+ pn_transport_t* getTransport();
+ private:
+ typedef std::map<pn_session_t*, boost::shared_ptr<Session> > Sessions;
+ pn_connection_t* connection;
+ pn_transport_t* transport;
+ qpid::sys::OutputControl& out;
+ const std::string id;
+ qpid::broker::Broker& broker;
+ bool haveOutput;
+ Sessions sessions;
+
+ void process();
+ std::string getError();
+};
+}}} // namespace qpid::broker::amqp
+
+#endif /*!QPID_BROKER_AMQP1_CONNECTION_H*/
diff --git a/cpp/src/qpid/broker/amqp/DataReader.cpp b/cpp/src/qpid/broker/amqp/DataReader.cpp
new file mode 100644
index 0000000000..519dd71c9c
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/DataReader.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 "DataReader.h"
+#include "qpid/amqp/CharSequence.h"
+#include "qpid/amqp/Descriptor.h"
+#include "qpid/log/Statement.h"
+#include <string>
+extern "C" {
+#include <proton/engine.h>
+}
+
+namespace qpid {
+namespace broker {
+namespace amqp {
+namespace {
+qpid::amqp::CharSequence convert(pn_bytes_t in)
+{
+ qpid::amqp::CharSequence out;
+ out.data = in.start;
+ out.size = in.size;
+ return out;
+}
+
+qpid::amqp::CharSequence convert(pn_uuid_t in)
+{
+ qpid::amqp::CharSequence out;
+ out.data = in.bytes;
+ out.size = 16;
+ return out;
+}
+}
+
+DataReader::DataReader(qpid::amqp::Reader& r) : reader(r) {}
+
+void DataReader::read(pn_data_t* data)
+{
+ /*
+ while (pn_data_next(data)) {
+ readOne(data);
+ }
+ */
+ do {
+ readOne(data);
+ } while (pn_data_next(data));
+}
+void DataReader::readOne(pn_data_t* data)
+{
+ qpid::amqp::Descriptor descriptor(0);
+ bool described = pn_data_is_described(data);
+ if (described) {
+ pn_data_enter(data);
+ pn_data_next(data);
+ if (pn_data_type(data) == PN_ULONG) {
+ descriptor = qpid::amqp::Descriptor(pn_data_get_ulong(data));
+ } else if (pn_data_type(data) == PN_SYMBOL) {
+ descriptor = qpid::amqp::Descriptor(convert(pn_data_get_symbol(data)));
+ } else {
+ QPID_LOG(notice, "Ignoring descriptor of type " << pn_data_type(data));
+ }
+ pn_data_next(data);
+ }
+ switch (pn_data_type(data)) {
+ case PN_NULL:
+ reader.onNull(described ? &descriptor : 0);
+ break;
+ case PN_BOOL:
+ reader.onBoolean(pn_data_get_bool(data), described ? &descriptor : 0);
+ break;
+ case PN_UBYTE:
+ reader.onUByte(pn_data_get_ubyte(data), described ? &descriptor : 0);
+ break;
+ case PN_BYTE:
+ reader.onByte(pn_data_get_byte(data), described ? &descriptor : 0);
+ break;
+ case PN_USHORT:
+ reader.onUShort(pn_data_get_ushort(data), described ? &descriptor : 0);
+ break;
+ case PN_SHORT:
+ reader.onShort(pn_data_get_short(data), described ? &descriptor : 0);
+ break;
+ case PN_UINT:
+ reader.onUInt(pn_data_get_uint(data), described ? &descriptor : 0);
+ break;
+ case PN_INT:
+ reader.onInt(pn_data_get_int(data), described ? &descriptor : 0);
+ break;
+ case PN_CHAR:
+ pn_data_get_char(data);
+ break;
+ case PN_ULONG:
+ reader.onULong(pn_data_get_ulong(data), described ? &descriptor : 0);
+ break;
+ case PN_LONG:
+ reader.onLong(pn_data_get_long(data), described ? &descriptor : 0);
+ break;
+ case PN_TIMESTAMP:
+ reader.onTimestamp(pn_data_get_timestamp(data), described ? &descriptor : 0);
+ break;
+ case PN_FLOAT:
+ reader.onFloat(pn_data_get_float(data), described ? &descriptor : 0);
+ break;
+ case PN_DOUBLE:
+ reader.onDouble(pn_data_get_double(data), described ? &descriptor : 0);
+ break;
+ case PN_DECIMAL32:
+ pn_data_get_decimal32(data);
+ break;
+ case PN_DECIMAL64:
+ pn_data_get_decimal64(data);
+ break;
+ case PN_DECIMAL128:
+ pn_data_get_decimal128(data);
+ break;
+ case PN_UUID:
+ reader.onUuid(convert(pn_data_get_uuid(data)), described ? &descriptor : 0);
+ break;
+ case PN_BINARY:
+ reader.onBinary(convert(pn_data_get_binary(data)), described ? &descriptor : 0);
+ break;
+ case PN_STRING:
+ reader.onString(convert(pn_data_get_string(data)), described ? &descriptor : 0);
+ break;
+ case PN_SYMBOL:
+ reader.onSymbol(convert(pn_data_get_symbol(data)), described ? &descriptor : 0);
+ break;
+ case PN_DESCRIBED:
+ break;
+ case PN_ARRAY:
+ readArray(data, described ? &descriptor : 0);
+ break;
+ case PN_LIST:
+ readList(data, described ? &descriptor : 0);
+ break;
+ case PN_MAP:
+ readMap(data, described ? &descriptor : 0);
+ break;
+ }
+ if (described) pn_data_exit(data);
+}
+
+void DataReader::readArray(pn_data_t* /*data*/, const qpid::amqp::Descriptor* /*descriptor*/)
+{
+ //not yet implemented
+}
+
+void DataReader::readList(pn_data_t* data, const qpid::amqp::Descriptor* descriptor)
+{
+ size_t count = pn_data_get_list(data);
+ reader.onStartList(count, qpid::amqp::CharSequence(), descriptor);
+ pn_data_enter(data);
+ for (size_t i = 0; i < count && pn_data_next(data); ++i) {
+ read(data);
+ }
+ pn_data_exit(data);
+ reader.onEndList(count, descriptor);
+}
+
+void DataReader::readMap(pn_data_t* data, const qpid::amqp::Descriptor* descriptor)
+{
+ size_t count = pn_data_get_map(data);
+ reader.onStartMap(count, qpid::amqp::CharSequence(), descriptor);
+ pn_data_enter(data);
+ for (size_t i = 0; i < count && pn_data_next(data); ++i) {
+ read(data);
+ }
+ pn_data_exit(data);
+ reader.onEndMap(count, descriptor);
+}
+}}} // namespace qpid::broker::amqp
diff --git a/cpp/src/qpid/broker/amqp/DataReader.h b/cpp/src/qpid/broker/amqp/DataReader.h
new file mode 100644
index 0000000000..024507e7f2
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/DataReader.h
@@ -0,0 +1,53 @@
+#ifndef QPID_BROKER_AMQP_DATAREADER_H
+#define QPID_BROKER_AMQP_DATAREADER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/Reader.h"
+
+struct pn_data_t;
+
+namespace qpid {
+namespace amqp {
+struct Descriptor;
+}
+namespace broker {
+namespace amqp {
+
+/**
+ * Allows use of Reader interface to read pn_data_t* data.
+ */
+class DataReader
+{
+ public:
+ DataReader(qpid::amqp::Reader& reader);
+ void read(pn_data_t*);
+ private:
+ qpid::amqp::Reader& reader;
+
+ void readOne(pn_data_t*);
+ void readMap(pn_data_t*, const qpid::amqp::Descriptor*);
+ void readList(pn_data_t*, const qpid::amqp::Descriptor*);
+ void readArray(pn_data_t*, const qpid::amqp::Descriptor*);
+};
+}}} // namespace qpid::broker::amqp
+
+#endif /*!QPID_BROKER_AMQP_DATAREADER_H*/
diff --git a/cpp/src/qpid/broker/amqp/Filter.cpp b/cpp/src/qpid/broker/amqp/Filter.cpp
new file mode 100644
index 0000000000..38baba0df1
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Filter.cpp
@@ -0,0 +1,150 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/amqp/Filter.h"
+#include "qpid/broker/amqp/DataReader.h"
+#include "qpid/broker/DirectExchange.h"
+#include "qpid/broker/TopicExchange.h"
+#include "qpid/amqp/descriptors.h"
+#include "qpid/log/Statement.h"
+extern "C" {
+#include <proton/engine.h>
+}
+
+namespace qpid {
+namespace broker {
+namespace amqp {
+
+void Filter::read(pn_data_t* data)
+{
+ try {
+ DataReader reader(*this);
+ reader.read(data);
+ } catch (const std::exception& e) {
+ QPID_LOG(warning, "Error parsing filter: " << e.what());
+ }
+}
+
+void Filter::write(pn_data_t* data)
+{
+ pn_data_put_map(data);
+ pn_data_enter(data);
+ subjectFilter.write(data);
+ pn_data_exit(data);
+}
+
+void Filter::onStringValue(const qpid::amqp::CharSequence& key, const qpid::amqp::CharSequence& value, const qpid::amqp::Descriptor* descriptor)
+{
+ StringFilter filter;
+ filter.key = std::string(key.data, key.size);
+ filter.value = std::string(value.data, value.size);
+ if (descriptor) {
+ filter.described = true;
+ filter.descriptor = *descriptor;
+ if (descriptor->match(qpid::amqp::filters::LEGACY_TOPIC_FILTER_SYMBOL, qpid::amqp::filters::LEGACY_TOPIC_FILTER_CODE)
+ || descriptor->match(qpid::amqp::filters::LEGACY_DIRECT_FILTER_SYMBOL, qpid::amqp::filters::LEGACY_DIRECT_FILTER_CODE)) {
+ setSubjectFilter(filter);
+ } else {
+ QPID_LOG(notice, "Skipping unrecognised string filter with key " << filter.key << " and descriptor " << filter.descriptor);
+ }
+ } else {
+ setSubjectFilter(filter);
+ }
+}
+
+bool Filter::hasSubjectFilter() const
+{
+ return !subjectFilter.value.empty();
+}
+
+std::string Filter::getSubjectFilter() const
+{
+ return subjectFilter.value;
+}
+
+
+void Filter::setSubjectFilter(const StringFilter& filter)
+{
+ if (hasSubjectFilter()) {
+ QPID_LOG(notice, "Skipping filter with key " << filter.key << "; subject filter already set");
+ } else {
+ subjectFilter = filter;
+ }
+}
+
+void Filter::bind(boost::shared_ptr<Exchange> exchange, boost::shared_ptr<Queue> queue)
+{
+ subjectFilter.bind(exchange, queue);
+}
+
+Filter::StringFilter::StringFilter() : described(false), descriptor(0) {}
+namespace {
+pn_bytes_t convert(const std::string& in)
+{
+ pn_bytes_t out;
+ out.start = const_cast<char*>(in.data());
+ out.size = in.size();
+ return out;
+}
+pn_bytes_t convert(const qpid::amqp::CharSequence& in)
+{
+ pn_bytes_t out;
+ out.start = const_cast<char*>(in.data);
+ out.size = in.size;
+ return out;
+}
+}
+void Filter::StringFilter::write(pn_data_t* data)
+{
+ pn_data_put_symbol(data, convert(key));
+ if (described) {
+ pn_data_put_described(data);
+ pn_data_enter(data);
+ switch (descriptor.type) {
+ case qpid::amqp::Descriptor::NUMERIC:
+ pn_data_put_ulong(data, descriptor.value.code);
+ break;
+ case qpid::amqp::Descriptor::SYMBOLIC:
+ pn_data_put_symbol(data, convert(descriptor.value.symbol));
+ break;
+ }
+ }
+ pn_data_put_string(data, convert(value));
+ if (described) pn_data_exit(data);
+}
+
+void Filter::StringFilter::bind(boost::shared_ptr<Exchange> exchange, boost::shared_ptr<Queue> queue)
+{
+ if (described && exchange->getType() == DirectExchange::typeName
+ && descriptor.match(qpid::amqp::filters::LEGACY_TOPIC_FILTER_SYMBOL, qpid::amqp::filters::LEGACY_TOPIC_FILTER_CODE)) {
+ QPID_LOG(info, "Using legacy topic filter as a direct matching filter for " << exchange->getName());
+ if (descriptor.type == qpid::amqp::Descriptor::NUMERIC) {
+ descriptor = qpid::amqp::Descriptor(qpid::amqp::filters::LEGACY_DIRECT_FILTER_CODE);
+ } else {
+ qpid::amqp::CharSequence symbol;
+ symbol.data = qpid::amqp::filters::LEGACY_DIRECT_FILTER_SYMBOL.data();
+ symbol.size = qpid::amqp::filters::LEGACY_DIRECT_FILTER_SYMBOL.size();
+ descriptor = qpid::amqp::Descriptor(symbol);
+ }
+ }
+ exchange->bind(queue, value, 0);
+}
+
+}}} // namespace qpid::broker::amqp
diff --git a/cpp/src/qpid/broker/amqp/Filter.h b/cpp/src/qpid/broker/amqp/Filter.h
new file mode 100644
index 0000000000..20cceb372a
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Filter.h
@@ -0,0 +1,63 @@
+#ifndef QPID_BROKER_AMQP_FILTER_H
+#define QPID_BROKER_AMQP_FILTER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/MapReader.h"
+#include "qpid/amqp/Descriptor.h"
+#include <boost/shared_ptr.hpp>
+
+struct pn_data_t;
+namespace qpid {
+namespace broker {
+class Exchange;
+class Queue;
+namespace amqp {
+
+
+class Filter : qpid::amqp::MapReader
+{
+ public:
+ void read(pn_data_t*);
+ void write(pn_data_t*);
+ bool hasSubjectFilter() const;
+ std::string getSubjectFilter() const;
+ void bind(boost::shared_ptr<Exchange> exchange, boost::shared_ptr<Queue> queue);
+ private:
+ struct StringFilter
+ {
+ bool described;
+ qpid::amqp::Descriptor descriptor;
+ std::string key;
+ std::string value;
+ StringFilter();
+ void write(pn_data_t*);
+ void bind(boost::shared_ptr<Exchange> exchange, boost::shared_ptr<Queue> queue);
+ };
+
+ void onStringValue(const qpid::amqp::CharSequence& key, const qpid::amqp::CharSequence& value, const qpid::amqp::Descriptor* descriptor);
+ void setSubjectFilter(const StringFilter&);
+
+ StringFilter subjectFilter;
+};
+}}} // namespace qpid::broker::amqp
+
+#endif /*!QPID_BROKER_AMQP_FILTER_H*/
diff --git a/cpp/src/qpid/broker/amqp/Header.cpp b/cpp/src/qpid/broker/amqp/Header.cpp
new file mode 100644
index 0000000000..493e757a56
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Header.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/broker/amqp/Header.h"
+#include "qpid/broker/Message.h"
+
+namespace qpid {
+namespace broker {
+namespace amqp {
+
+bool Header::isDurable() const
+{
+ return message.isPersistent();
+}
+
+uint8_t Header::getPriority() const
+{
+ return message.getPriority();
+}
+
+bool Header::hasTtl() const
+{
+ uint64_t dummy(0);
+ return message.getTtl(dummy);
+}
+
+uint32_t Header::getTtl() const
+{
+ uint64_t ttl(0);
+ message.getTtl(ttl);
+ if (ttl > std::numeric_limits<uint32_t>::max()) return std::numeric_limits<uint32_t>::max();
+ else return (uint32_t) ttl;
+}
+
+bool Header::isFirstAcquirer() const
+{
+ return false;//TODO
+}
+
+uint32_t Header::getDeliveryCount() const
+{
+ return message.getDeliveryCount();
+}
+
+Header::Header(const qpid::broker::Message& m) : message(m) {}
+
+
+}}} // namespace qpid::broker::amqp
diff --git a/cpp/src/qpid/broker/amqp/Header.h b/cpp/src/qpid/broker/amqp/Header.h
new file mode 100644
index 0000000000..6e4f763028
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Header.h
@@ -0,0 +1,50 @@
+#ifndef QPID_BROKER_AMQP_HEADER_H
+#define QPID_BROKER_AMQP_HEADER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/amqp/MessageEncoder.h"
+
+namespace qpid {
+namespace broker {
+class Message;
+namespace amqp {
+
+/**
+ * Adapts the broker current message abstraction to provide that
+ * required by the AMQP 1.0 message encoder.
+ */
+class Header : public qpid::amqp::MessageEncoder::Header
+{
+ public:
+ Header(const qpid::broker::Message&);
+ bool isDurable() const;
+ uint8_t getPriority() const;
+ bool hasTtl() const;
+ uint32_t getTtl() const;
+ bool isFirstAcquirer() const;
+ uint32_t getDeliveryCount() const;
+ private:
+ const qpid::broker::Message& message;
+};
+}}} // namespace qpid::broker::amqp
+
+#endif /*!QPID_BROKER_AMQP_HEADER_H*/
diff --git a/cpp/src/qpid/broker/amqp/ManagedConnection.cpp b/cpp/src/qpid/broker/amqp/ManagedConnection.cpp
new file mode 100644
index 0000000000..0253ba5552
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/ManagedConnection.cpp
@@ -0,0 +1,98 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/amqp/ManagedConnection.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/log/Statement.h"
+#include "qmf/org/apache/qpid/broker/EventClientConnect.h"
+#include "qmf/org/apache/qpid/broker/EventClientDisconnect.h"
+
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+namespace qpid {
+namespace broker {
+namespace amqp {
+
+ManagedConnection::ManagedConnection(Broker& broker, const std::string i) : id(i), agent(0)
+{
+ //management integration:
+ agent = broker.getManagementAgent();
+ if (agent != 0) {
+ qpid::management::Manageable* parent = broker.GetVhostObject();
+ // TODO set last bool true if system connection
+ connection = _qmf::Connection::shared_ptr(new _qmf::Connection(agent, this, parent, id, true, false, "AMQP 1.0"));
+ connection->set_shadow(false);
+ agent->addObject(connection);
+ }
+}
+
+ManagedConnection::~ManagedConnection()
+{
+ if (agent && connection) {
+ agent->raiseEvent(_qmf::EventClientDisconnect(id, userid, connection->get_remoteProperties()));
+ connection->resourceDestroy();
+ }
+ QPID_LOG_CAT(debug, model, "Delete connection. user:" << userid << " rhost:" << id);
+}
+
+void ManagedConnection::setUserid(const std::string& uid)
+{
+ userid = uid;
+ if (agent && connection) {
+ connection->set_authIdentity(userid);
+ agent->raiseEvent(_qmf::EventClientConnect(id, userid, connection->get_remoteProperties()));
+ }
+ QPID_LOG_CAT(debug, model, "Create connection. user:" << userid << " rhost:" << id );
+}
+
+void ManagedConnection::setSaslMechanism(const std::string& mechanism)
+{
+ connection->set_saslMechanism(mechanism);
+}
+
+void ManagedConnection::setSaslSsf(int ssf)
+{
+ connection->set_saslSsf(ssf);
+}
+
+qpid::management::ManagementObject::shared_ptr ManagedConnection::GetManagementObject() const
+{
+ return connection;
+}
+
+std::string ManagedConnection::getId() const { return id; }
+std::string ManagedConnection::getUserid() const { return userid; }
+
+bool ManagedConnection::isLocal(const ConnectionToken* t) const
+{
+ return this == t;
+}
+void ManagedConnection::outgoingMessageSent()
+{
+ if (connection) connection->inc_msgsToClient();
+}
+
+void ManagedConnection::incomingMessageReceived()
+{
+ if (connection) connection->inc_msgsFromClient();
+}
+
+}}} // namespace qpid::broker::amqp
diff --git a/cpp/src/qpid/broker/amqp/ManagedConnection.h b/cpp/src/qpid/broker/amqp/ManagedConnection.h
new file mode 100644
index 0000000000..e2d0376918
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/ManagedConnection.h
@@ -0,0 +1,59 @@
+#ifndef QPID_BROKER_AMQP_MANAGEDCONNECTION_H
+#define QPID_BROKER_AMQP_MANAGEDCONNECTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/management/Manageable.h"
+#include "qpid/broker/ConnectionToken.h"
+#include "qmf/org/apache/qpid/broker/Connection.h"
+
+namespace qpid {
+namespace management {
+class ManagementAgent;
+class ManagementObject;
+}
+namespace broker {
+class Broker;
+namespace amqp {
+
+class ManagedConnection : public qpid::management::Manageable, public ConnectionToken
+{
+ public:
+ ManagedConnection(Broker& broker, const std::string id);
+ virtual ~ManagedConnection();
+ void setUserid(const std::string&);
+ std::string getId() const;
+ std::string getUserid() const;
+ void setSaslMechanism(const std::string&);
+ void setSaslSsf(int);
+ qpid::management::ManagementObject::shared_ptr GetManagementObject() const;
+ bool isLocal(const ConnectionToken* t) const;
+ void incomingMessageReceived();
+ void outgoingMessageSent();
+ private:
+ const std::string id;
+ std::string userid;
+ qmf::org::apache::qpid::broker::Connection::shared_ptr connection;
+ qpid::management::ManagementAgent* agent;
+};
+}}} // namespace qpid::broker::amqp
+
+#endif /*!QPID_BROKER_AMQP_MANAGEDCONNECTION_H*/
diff --git a/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.cpp b/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.cpp
new file mode 100644
index 0000000000..f36a1e8da4
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.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 "ManagedOutgoingLink.h"
+#include "qpid/broker/amqp/ManagedSession.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/types/Variant.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/log/Statement.h"
+
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+namespace qpid {
+namespace broker {
+namespace amqp {
+
+ManagedOutgoingLink::ManagedOutgoingLink(Broker& broker, Queue& q, ManagedSession& p, const std::string i, bool topic)
+ : parent(p), id(i)
+{
+ qpid::management::ManagementAgent* agent = broker.getManagementAgent();
+ if (agent) {
+ subscription = _qmf::Subscription::shared_ptr(new _qmf::Subscription(agent, this, &p, q.GetManagementObject()->getObjectId(), id,
+ false/*FIXME*/, true/*FIXME*/, topic, qpid::types::Variant::Map()));
+ agent->addObject(subscription);
+ subscription->set_creditMode("n/a");
+ }
+}
+ManagedOutgoingLink::~ManagedOutgoingLink()
+{
+ if (subscription != 0) subscription->resourceDestroy();
+}
+
+qpid::management::ManagementObject::shared_ptr ManagedOutgoingLink::GetManagementObject() const
+{
+ return subscription;
+}
+
+void ManagedOutgoingLink::outgoingMessageSent()
+{
+ if (subscription) { subscription->inc_delivered(); }
+ parent.outgoingMessageSent();
+}
+void ManagedOutgoingLink::outgoingMessageAccepted()
+{
+ parent.outgoingMessageAccepted();
+}
+void ManagedOutgoingLink::outgoingMessageRejected()
+{
+ parent.outgoingMessageRejected();
+}
+
+}}} // namespace qpid::broker::amqp
diff --git a/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.h b/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.h
new file mode 100644
index 0000000000..20a1095db2
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.h
@@ -0,0 +1,53 @@
+#ifndef QPID_BROKER_AMQP_MANAGEDOUTGOINGLINK_H
+#define QPID_BROKER_AMQP_MANAGEDOUTGOINGLINK_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/management/Manageable.h"
+#include "qmf/org/apache/qpid/broker/Subscription.h"
+
+namespace qpid {
+namespace management {
+class ManagementObject;
+}
+namespace broker {
+class Broker;
+class Queue;
+namespace amqp {
+class ManagedSession;
+
+class ManagedOutgoingLink : public qpid::management::Manageable
+{
+ public:
+ ManagedOutgoingLink(Broker& broker, Queue&, ManagedSession& parent, const std::string id, bool topic);
+ virtual ~ManagedOutgoingLink();
+ qpid::management::ManagementObject::shared_ptr GetManagementObject() const;
+ void outgoingMessageSent();
+ void outgoingMessageAccepted();
+ void outgoingMessageRejected();
+ private:
+ ManagedSession& parent;
+ const std::string id;
+ qmf::org::apache::qpid::broker::Subscription::shared_ptr subscription;
+};
+}}} // namespace qpid::broker::amqp
+
+#endif /*!QPID_BROKER_AMQP_MANAGEDOUTGOINGLINK_H*/
diff --git a/cpp/src/qpid/broker/amqp/ManagedSession.cpp b/cpp/src/qpid/broker/amqp/ManagedSession.cpp
new file mode 100644
index 0000000000..9bef0e842b
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/ManagedSession.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/broker/amqp/ManagedSession.h"
+#include "qpid/broker/amqp/ManagedConnection.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/log/Statement.h"
+
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+namespace qpid {
+namespace broker {
+namespace amqp {
+
+ManagedSession::ManagedSession(Broker& broker, ManagedConnection& p, const std::string i) : parent(p), id(i), unacked(0)
+{
+ qpid::management::ManagementAgent* agent = broker.getManagementAgent();
+ if (agent != 0) {
+ session = _qmf::Session::shared_ptr(new _qmf::Session(agent, this, broker.GetVhostObject(), id));
+ session->set_attached(true);
+ session->set_detachedLifespan(0);
+ session->clr_expireTime();
+ session->set_connectionRef(parent.GetManagementObject()->getObjectId());
+ agent->addObject(session);
+ }
+}
+
+ManagedSession::~ManagedSession()
+{
+ if (session) session->resourceDestroy();
+}
+
+qpid::management::ManagementObject::shared_ptr ManagedSession::GetManagementObject() const
+{
+ return session;
+}
+
+bool ManagedSession::isLocal(const ConnectionToken* t) const
+{
+ return &parent == t;
+}
+
+void ManagedSession::outgoingMessageSent()
+{
+ if (session) session->set_unackedMessages(++unacked);
+ parent.outgoingMessageSent();
+}
+void ManagedSession::outgoingMessageAccepted()
+{
+ if (session) session->set_unackedMessages(--unacked);
+}
+void ManagedSession::outgoingMessageRejected()
+{
+ if (session) session->set_unackedMessages(--unacked);
+}
+
+void ManagedSession::incomingMessageReceived()
+{
+ parent.incomingMessageReceived();
+}
+void ManagedSession::incomingMessageAccepted()
+{
+
+}
+void ManagedSession::incomingMessageRejected()
+{
+
+}
+
+}}} // namespace qpid::broker::amqp
diff --git a/cpp/src/qpid/broker/amqp/ManagedSession.h b/cpp/src/qpid/broker/amqp/ManagedSession.h
new file mode 100644
index 0000000000..1f56964bb6
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/ManagedSession.h
@@ -0,0 +1,59 @@
+#ifndef QPID_BROKER_AMQP_MANAGEDSESSION_H
+#define QPID_BROKER_AMQP_MANAGEDSESSION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/management/Manageable.h"
+#include "qmf/org/apache/qpid/broker/Session.h"
+#include "qpid/broker/ConnectionToken.h"
+#include "qpid/broker/OwnershipToken.h"
+
+namespace qpid {
+namespace management {
+class ManagementObject;
+}
+namespace broker {
+class Broker;
+namespace amqp {
+class ManagedConnection;
+
+class ManagedSession : public qpid::management::Manageable, public OwnershipToken
+{
+ public:
+ ManagedSession(Broker& broker, ManagedConnection& parent, const std::string id);
+ virtual ~ManagedSession();
+ qpid::management::ManagementObject::shared_ptr GetManagementObject() const;
+ bool isLocal(const ConnectionToken* t) const;
+ void incomingMessageReceived();
+ void incomingMessageAccepted();
+ void incomingMessageRejected();
+ void outgoingMessageSent();
+ void outgoingMessageAccepted();
+ void outgoingMessageRejected();
+ private:
+ ManagedConnection& parent;
+ const std::string id;
+ qmf::org::apache::qpid::broker::Session::shared_ptr session;
+ size_t unacked;
+};
+}}} // namespace qpid::broker::amqp
+
+#endif /*!QPID_BROKER_AMQP_MANAGEDSESSION_H*/
diff --git a/cpp/src/qpid/broker/amqp/Message.cpp b/cpp/src/qpid/broker/amqp/Message.cpp
new file mode 100644
index 0000000000..a4c346e131
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Message.cpp
@@ -0,0 +1,264 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "Message.h"
+#include "qpid/amqp/Decoder.h"
+#include "qpid/amqp/descriptors.h"
+#include "qpid/amqp/MessageEncoder.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/Buffer.h"
+#include <string.h>
+
+namespace qpid {
+namespace broker {
+namespace amqp {
+
+namespace {
+std::string empty;
+}
+
+std::string Message::getRoutingKey() const
+{
+ std::string v;
+ v.assign(subject.data, subject.size);
+ return v;
+}
+std::string Message::getUserId() const
+{
+ std::string v;
+ v.assign(userId.data, userId.size);
+ return v;
+}
+
+bool Message::isPersistent() const
+{
+ return durable && durable.get();
+}
+bool Message::getTtl(uint64_t& t) const
+{
+ if (!ttl) {
+ return false;
+ } else {
+ t = ttl.get();
+ return true;
+ }
+}
+
+uint8_t Message::getPriority() const
+{
+ if (!priority) return 4;
+ else return priority.get();
+}
+
+std::string Message::getPropertyAsString(const std::string& /*key*/) const { return empty; }
+std::string Message::getAnnotationAsString(const std::string& /*key*/) const { return empty; }
+void Message::processProperties(MapHandler&) const {}
+
+//getContentSize() is primarily used in stats about the number of
+//bytes enqueued/dequeued etc, not sure whether this is the right name
+//and whether it should indeed only be the content that is thus
+//measured
+uint64_t Message::getContentSize() const { return data.size(); }
+//getContent() is used primarily for decoding qmf messages in management and ha
+std::string Message::getContent() const { return empty; }
+
+Message::Message(size_t size) : data(size)
+{
+ deliveryAnnotations.init();
+ messageAnnotations.init();
+ bareMessage.init();
+
+ userId.init();
+ to.init();
+ subject.init();
+ replyTo.init();
+ contentType.init();
+ contentEncoding.init();
+
+ applicationProperties.init();
+ body.init();
+ footer.init();
+}
+char* Message::getData() { return &data[0]; }
+const char* Message::getData() const { return &data[0]; }
+size_t Message::getSize() const { return data.size(); }
+
+qpid::amqp::MessageId Message::getMessageId() const
+{
+ return messageId;
+}
+qpid::amqp::CharSequence Message::getReplyTo() const
+{
+ return replyTo;
+}
+qpid::amqp::MessageId Message::getCorrelationId() const
+{
+ return correlationId;
+}
+qpid::amqp::CharSequence Message::getContentType() const
+{
+ return contentType;
+}
+qpid::amqp::CharSequence Message::getContentEncoding() const
+{
+ return contentEncoding;
+}
+
+qpid::amqp::CharSequence Message::getDeliveryAnnotations() const
+{
+ return deliveryAnnotations;
+}
+qpid::amqp::CharSequence Message::getMessageAnnotations() const
+{
+ return messageAnnotations;
+}
+qpid::amqp::CharSequence Message::getApplicationProperties() const
+{
+ return applicationProperties;
+}
+qpid::amqp::CharSequence Message::getBareMessage() const
+{
+ return bareMessage;
+}
+qpid::amqp::CharSequence Message::getBody() const
+{
+ return body;
+}
+qpid::amqp::CharSequence Message::getFooter() const
+{
+ return footer;
+}
+
+void Message::scan()
+{
+ qpid::amqp::Decoder decoder(getData(), getSize());
+ decoder.read(*this);
+ bareMessage = qpid::amqp::MessageReader::getBareMessage();
+ if (bareMessage.data && !bareMessage.size) {
+ bareMessage.size = getSize() - (bareMessage.data - getData());
+ }
+}
+
+const Message& Message::get(const qpid::broker::Message& message)
+{
+ const Message* m = dynamic_cast<const Message*>(&message.getEncoding());
+ if (!m) throw qpid::Exception("Translation not yet implemented!!");
+ return *m;
+}
+
+void Message::onDurable(bool b) { durable = b; }
+void Message::onPriority(uint8_t i) { priority = i; }
+void Message::onTtl(uint32_t i) { ttl = i; }
+void Message::onFirstAcquirer(bool b) { firstAcquirer = b; }
+void Message::onDeliveryCount(uint32_t i) { deliveryCount = i; }
+
+void Message::onMessageId(uint64_t v) { messageId.set(v); }
+void Message::onMessageId(const qpid::amqp::CharSequence& v, qpid::types::VariantType t) { messageId.set(v, t); }
+void Message::onUserId(const qpid::amqp::CharSequence& v) { userId = v; }
+void Message::onTo(const qpid::amqp::CharSequence& v) { to = v; }
+void Message::onSubject(const qpid::amqp::CharSequence& v) { subject = v; }
+void Message::onReplyTo(const qpid::amqp::CharSequence& v) { replyTo = v; }
+void Message::onCorrelationId(uint64_t v) { correlationId.set(v); }
+void Message::onCorrelationId(const qpid::amqp::CharSequence& v, qpid::types::VariantType t) { correlationId.set(v, t);}
+void Message::onContentType(const qpid::amqp::CharSequence& v) { contentType = v; }
+void Message::onContentEncoding(const qpid::amqp::CharSequence& v) { contentEncoding = v; }
+void Message::onAbsoluteExpiryTime(int64_t) {}
+void Message::onCreationTime(int64_t) {}
+void Message::onGroupId(const qpid::amqp::CharSequence&) {}
+void Message::onGroupSequence(uint32_t) {}
+void Message::onReplyToGroupId(const qpid::amqp::CharSequence&) {}
+
+void Message::onApplicationProperties(const qpid::amqp::CharSequence& v) { applicationProperties = v; }
+void Message::onDeliveryAnnotations(const qpid::amqp::CharSequence& v) { deliveryAnnotations = v; }
+void Message::onMessageAnnotations(const qpid::amqp::CharSequence& v) { messageAnnotations = v; }
+void Message::onBody(const qpid::amqp::CharSequence& v, const qpid::amqp::Descriptor&) { body = v; }
+void Message::onBody(const qpid::types::Variant&, const qpid::amqp::Descriptor&) {}
+void Message::onFooter(const qpid::amqp::CharSequence& v) { footer = v; }
+
+
+//PersistableMessage interface:
+void Message::encode(framing::Buffer& buffer) const
+{
+ buffer.putLong(0);//4-byte format indicator
+ buffer.putRawData((const uint8_t*) getData(), getSize());
+ QPID_LOG(debug, "Encoded 1.0 message of " << getSize() << " bytes, including " << bareMessage.size << " bytes of 'bare message'");
+}
+uint32_t Message::encodedSize() const
+{
+ return 4/*format indicator*/ + data.size();
+}
+//in 1.0 the binary header/content makes less sense and in any case
+//the functionality that split originally supported (i.e. lazy-loaded
+//messages) is no longer in use; for 1.0 we therefore treat the whole
+//content as 'header' and load it in the first stage.
+uint32_t Message::encodedHeaderSize() const
+{
+ return encodedSize();
+}
+void Message::decodeHeader(framing::Buffer& buffer)
+{
+ if (buffer.available() != getSize()) {
+ QPID_LOG(warning, "1.0 Message buffer was " << data.size() << " bytes, but " << buffer.available() << " bytes are available. Resizing.");
+ data.resize(buffer.available());
+ }
+ buffer.getRawData((uint8_t*) getData(), getSize());
+ scan();
+ QPID_LOG(debug, "Decoded 1.0 message of " << getSize() << " bytes, including " << bareMessage.size << " bytes of 'bare message'");
+}
+void Message::decodeContent(framing::Buffer& /*buffer*/) {}
+
+boost::intrusive_ptr<PersistableMessage> Message::merge(const std::map<std::string, qpid::types::Variant>& annotations) const
+{
+ //message- or delivery- annotations? would have to determine that from the name, for now assume always message-annotations
+ size_t extra = 0;
+ if (messageAnnotations) {
+ //TODO: actual merge required
+ } else {
+ //add whole new section
+ extra = qpid::amqp::MessageEncoder::getEncodedSize(annotations, true);
+ }
+ boost::intrusive_ptr<Message> copy(new Message(data.size()+extra));
+ size_t position(0);
+ if (deliveryAnnotations) {
+ ::memcpy(&copy->data[position], deliveryAnnotations.data, deliveryAnnotations.size);
+ position += deliveryAnnotations.size;
+ }
+ if (messageAnnotations) {
+ //TODO: actual merge required
+ ::memcpy(&copy->data[position], messageAnnotations.data, messageAnnotations.size);
+ position += messageAnnotations.size;
+ } else {
+ qpid::amqp::MessageEncoder encoder(&copy->data[position], extra);
+ encoder.writeMap(annotations, &qpid::amqp::message::MESSAGE_ANNOTATIONS, true);
+ position += extra;
+ }
+ if (bareMessage) {
+ ::memcpy(&copy->data[position], bareMessage.data, bareMessage.size);
+ position += bareMessage.size;
+ }
+ if (footer) {
+ ::memcpy(&copy->data[position], footer.data, footer.size);
+ position += footer.size;
+ }
+ copy->scan();
+ return copy;
+}
+
+}}} // namespace qpid::broker::amqp
diff --git a/cpp/src/qpid/broker/amqp/Message.h b/cpp/src/qpid/broker/amqp/Message.h
new file mode 100644
index 0000000000..cc3406f72a
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Message.h
@@ -0,0 +1,148 @@
+#ifndef QPID_BROKER_AMQP_MESSAGE_H
+#define QPID_BROKER_AMQP_MESSAGE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/Message.h"
+#include "qpid/amqp/CharSequence.h"
+#include "qpid/amqp/MessageId.h"
+#include "qpid/amqp/MessageReader.h"
+#include <boost/optional.hpp>
+
+namespace qpid {
+namespace framing {
+class Buffer;
+}
+namespace broker {
+namespace amqp {
+
+/**
+ * Represents an AMQP 1.0 format message
+ */
+class Message : public qpid::broker::Message::Encoding, private qpid::amqp::MessageReader, public qpid::broker::PersistableMessage
+{
+ public:
+ //Encoding interface:
+ std::string getRoutingKey() const;
+ bool isPersistent() const;
+ uint8_t getPriority() const;
+ uint64_t getContentSize() const;
+ std::string getPropertyAsString(const std::string& key) const;
+ std::string getAnnotationAsString(const std::string& key) const;
+ bool getTtl(uint64_t&) const;
+ std::string getContent() const;
+ void processProperties(MapHandler&) const;
+ std::string getUserId() const;
+
+ qpid::amqp::MessageId getMessageId() const;
+ qpid::amqp::CharSequence getReplyTo() const;
+ qpid::amqp::MessageId getCorrelationId() const;
+ qpid::amqp::CharSequence getContentType() const;
+ qpid::amqp::CharSequence getContentEncoding() const;
+
+ qpid::amqp::CharSequence getDeliveryAnnotations() const;
+ qpid::amqp::CharSequence getMessageAnnotations() const;
+ qpid::amqp::CharSequence getApplicationProperties() const;
+ qpid::amqp::CharSequence getBareMessage() const;
+ qpid::amqp::CharSequence getBody() const;
+ qpid::amqp::CharSequence getFooter() const;
+
+ Message(size_t size);
+ char* getData();
+ const char* getData() const;
+ size_t getSize() const;
+ void scan();
+
+ //PersistableMessage interface:
+ void encode(framing::Buffer& buffer) const;
+ uint32_t encodedSize() const;
+ void decodeHeader(framing::Buffer& buffer);
+ void decodeContent(framing::Buffer& buffer);
+ uint32_t encodedHeaderSize() const;
+ boost::intrusive_ptr<PersistableMessage> merge(const std::map<std::string, qpid::types::Variant>& annotations) const;
+
+ static const Message& get(const qpid::broker::Message&);
+ private:
+ std::vector<char> data;
+
+ //header:
+ boost::optional<bool> durable;
+ boost::optional<uint8_t> priority;
+ boost::optional<uint32_t> ttl;
+ boost::optional<bool> firstAcquirer;
+ boost::optional<uint32_t> deliveryCount;
+ //annotations:
+ qpid::amqp::CharSequence deliveryAnnotations;
+ qpid::amqp::CharSequence messageAnnotations;
+
+ qpid::amqp::CharSequence bareMessage;//properties, application-properties and content
+ //properties:
+ qpid::amqp::MessageId messageId;
+ qpid::amqp::CharSequence userId;
+ qpid::amqp::CharSequence to;
+ qpid::amqp::CharSequence subject;
+ qpid::amqp::CharSequence replyTo;
+ qpid::amqp::MessageId correlationId;
+ qpid::amqp::CharSequence contentType;
+ qpid::amqp::CharSequence contentEncoding;
+
+ //application-properties:
+ qpid::amqp::CharSequence applicationProperties;
+
+ //body:
+ qpid::amqp::CharSequence body;
+
+ //footer:
+ qpid::amqp::CharSequence footer;
+
+ //header:
+ void onDurable(bool b);
+ void onPriority(uint8_t i);
+ void onTtl(uint32_t i);
+ void onFirstAcquirer(bool b);
+ void onDeliveryCount(uint32_t i);
+ //properties:
+ void onMessageId(uint64_t);
+ void onMessageId(const qpid::amqp::CharSequence&, qpid::types::VariantType);
+ void onUserId(const qpid::amqp::CharSequence& v);
+ void onTo(const qpid::amqp::CharSequence& v);
+ void onSubject(const qpid::amqp::CharSequence& v);
+ void onReplyTo(const qpid::amqp::CharSequence& v);
+ void onCorrelationId(uint64_t);
+ void onCorrelationId(const qpid::amqp::CharSequence&, qpid::types::VariantType);
+ void onContentType(const qpid::amqp::CharSequence& v);
+ void onContentEncoding(const qpid::amqp::CharSequence& v);
+ void onAbsoluteExpiryTime(int64_t i);
+ void onCreationTime(int64_t);
+ void onGroupId(const qpid::amqp::CharSequence&);
+ void onGroupSequence(uint32_t);
+ void onReplyToGroupId(const qpid::amqp::CharSequence&);
+
+ void onApplicationProperties(const qpid::amqp::CharSequence&);
+ void onDeliveryAnnotations(const qpid::amqp::CharSequence&);
+ void onMessageAnnotations(const qpid::amqp::CharSequence&);
+ void onBody(const qpid::amqp::CharSequence&, const qpid::amqp::Descriptor&);
+ void onBody(const qpid::types::Variant&, const qpid::amqp::Descriptor&);
+ void onFooter(const qpid::amqp::CharSequence&);
+};
+}}} // namespace qpid::broker::amqp
+
+#endif /*!QPID_BROKER_AMQP_MESSAGE_H*/
diff --git a/cpp/src/qpid/broker/amqp/NodeProperties.cpp b/cpp/src/qpid/broker/amqp/NodeProperties.cpp
new file mode 100644
index 0000000000..eea7612cb9
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/NodeProperties.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/broker/amqp/NodeProperties.h"
+#include "qpid/broker/amqp/DataReader.h"
+#include "qpid/amqp/CharSequence.h"
+#include "qpid/types/Variant.h"
+#include "qpid/broker/QueueSettings.h"
+#include "qpid/log/Statement.h"
+
+using qpid::amqp::CharSequence;
+using qpid::amqp::Descriptor;
+
+namespace qpid {
+namespace broker {
+namespace amqp {
+namespace {
+//distribution modes:
+const std::string MOVE("move");
+const std::string COPY("copy");
+const std::string SUPPORTED_DIST_MODES("supported-dist-modes");
+
+//AMQP 0-10 standard parameters:
+const std::string DURABLE("durable");
+const std::string AUTO_DELETE("auto-delete");
+const std::string ALTERNATE_EXCHANGE("alternate-exchange");
+const std::string EXCHANGE_TYPE("exchange-type");
+}
+
+NodeProperties::NodeProperties() : queue(true), durable(false), autoDelete(false), exchangeType("topic") {}
+
+void NodeProperties::read(pn_data_t* data)
+{
+ DataReader reader(*this);
+ reader.read(data);
+}
+
+void NodeProperties::process(const std::string& key, const qpid::types::Variant& value)
+{
+ QPID_LOG(notice, "Processing node property " << key << " = " << value);
+ if (key == SUPPORTED_DIST_MODES) {
+ if (value == MOVE) queue = true;
+ else if (value == COPY) queue = false;
+ } else if (key == DURABLE) {
+ durable = value;
+ } else if (key == AUTO_DELETE) {
+ autoDelete = value;
+ } else if (key == ALTERNATE_EXCHANGE) {
+ alternateExchange = value.asString();
+ } else if (key == EXCHANGE_TYPE) {
+ exchangeType = value.asString();
+ } else {
+ properties[key] = value;
+ }
+}
+
+void NodeProperties::onNullValue(const CharSequence& key, const Descriptor*)
+{
+ process(key.str(), qpid::types::Variant());
+}
+
+void NodeProperties::onBooleanValue(const CharSequence& key, bool value, const Descriptor*)
+{
+ process(key.str(), value);
+}
+
+void NodeProperties::onUByteValue(const CharSequence& key, uint8_t value, const Descriptor*)
+{
+ process(key.str(), value);
+}
+
+void NodeProperties::onUShortValue(const CharSequence& key, uint16_t value, const Descriptor*)
+{
+ process(key.str(), value);
+}
+
+void NodeProperties::onUIntValue(const CharSequence& key, uint32_t value, const Descriptor*)
+{
+ process(key.str(), value);
+}
+
+void NodeProperties::onULongValue(const CharSequence& key, uint64_t value, const Descriptor*)
+{
+ process(key.str(), value);
+}
+
+void NodeProperties::onByteValue(const CharSequence& key, int8_t value, const Descriptor*)
+{
+ process(key.str(), value);
+}
+
+void NodeProperties::onShortValue(const CharSequence& key, int16_t value, const Descriptor*)
+{
+ process(key.str(), value);
+}
+
+void NodeProperties::onIntValue(const CharSequence& key, int32_t value, const Descriptor*)
+{
+ process(key.str(), value);
+}
+
+void NodeProperties::onLongValue(const CharSequence& key, int64_t value, const Descriptor*)
+{
+ process(key.str(), value);
+}
+
+void NodeProperties::onFloatValue(const CharSequence& key, float value, const Descriptor*)
+{
+ process(key.str(), value);
+}
+
+void NodeProperties::onDoubleValue(const CharSequence& key, double value, const Descriptor*)
+{
+ process(key.str(), value);
+}
+
+void NodeProperties::onUuidValue(const CharSequence& key, const CharSequence& value, const Descriptor*)
+{
+ process(key.str(), value.str());
+}
+
+void NodeProperties::onTimestampValue(const CharSequence& key, int64_t value, const Descriptor*)
+{
+ process(key.str(), value);
+}
+
+void NodeProperties::onStringValue(const CharSequence& key, const CharSequence& value, const Descriptor*)
+{
+ process(key.str(), value.str());
+}
+
+void NodeProperties::onSymbolValue(const CharSequence& key, const CharSequence& value, const Descriptor*)
+{
+ process(key.str(), value.str());
+}
+
+QueueSettings NodeProperties::getQueueSettings()
+{
+ QueueSettings settings(durable, autoDelete);
+ qpid::types::Variant::Map unused;
+ settings.populate(properties, unused);
+ return settings;
+}
+
+bool NodeProperties::isQueue() const
+{
+ return queue;
+}
+bool NodeProperties::isDurable() const
+{
+ return durable;
+}
+std::string NodeProperties::getExchangeType() const
+{
+ return exchangeType;
+}
+std::string NodeProperties::getAlternateExchange() const
+{
+ return alternateExchange;
+}
+
+}}} // namespace qpid::broker::amqp
diff --git a/cpp/src/qpid/broker/amqp/NodeProperties.h b/cpp/src/qpid/broker/amqp/NodeProperties.h
new file mode 100644
index 0000000000..b81d1d712c
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/NodeProperties.h
@@ -0,0 +1,71 @@
+#ifndef QPID_BROKER_AMQP_NODEPROPERTIES_H
+#define QPID_BROKER_AMQP_NODEPROPERTIES_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/MapReader.h"
+#include "qpid/types/Variant.h"
+
+struct pn_data_t;
+namespace qpid {
+namespace broker {
+struct QueueSettings;
+namespace amqp {
+
+class NodeProperties : public qpid::amqp::MapReader
+{
+ public:
+ NodeProperties();
+ void read(pn_data_t*);
+ void onNullValue(const qpid::amqp::CharSequence&, const qpid::amqp::Descriptor*);
+ void onBooleanValue(const qpid::amqp::CharSequence&, bool, const qpid::amqp::Descriptor*);
+ void onUByteValue(const qpid::amqp::CharSequence&, uint8_t, const qpid::amqp::Descriptor*);
+ void onUShortValue(const qpid::amqp::CharSequence&, uint16_t, const qpid::amqp::Descriptor*);
+ void onUIntValue(const qpid::amqp::CharSequence&, uint32_t, const qpid::amqp::Descriptor*);
+ void onULongValue(const qpid::amqp::CharSequence&, uint64_t, const qpid::amqp::Descriptor*);
+ void onByteValue(const qpid::amqp::CharSequence&, int8_t, const qpid::amqp::Descriptor*);
+ void onShortValue(const qpid::amqp::CharSequence&, int16_t, const qpid::amqp::Descriptor*);
+ void onIntValue(const qpid::amqp::CharSequence&, int32_t, const qpid::amqp::Descriptor*);
+ void onLongValue(const qpid::amqp::CharSequence&, int64_t, const qpid::amqp::Descriptor*);
+ void onFloatValue(const qpid::amqp::CharSequence&, float, const qpid::amqp::Descriptor*);
+ void onDoubleValue(const qpid::amqp::CharSequence&, double, const qpid::amqp::Descriptor*);
+ void onUuidValue(const qpid::amqp::CharSequence&, const qpid::amqp::CharSequence&, const qpid::amqp::Descriptor*);
+ void onTimestampValue(const qpid::amqp::CharSequence&, int64_t, const qpid::amqp::Descriptor*);
+ void onStringValue(const qpid::amqp::CharSequence&, const qpid::amqp::CharSequence&, const qpid::amqp::Descriptor*);
+ void onSymbolValue(const qpid::amqp::CharSequence&, const qpid::amqp::CharSequence&, const qpid::amqp::Descriptor*);
+ bool isQueue() const;
+ QueueSettings getQueueSettings();
+ bool isDurable() const;
+ std::string getExchangeType() const;
+ std::string getAlternateExchange() const;
+ private:
+ bool queue;
+ bool durable;
+ bool autoDelete;
+ std::string exchangeType;
+ std::string alternateExchange;
+ qpid::types::Variant::Map properties;
+
+ void process(const std::string&, const qpid::types::Variant&);
+};
+}}} // namespace qpid::broker::amqp
+
+#endif /*!QPID_BROKER_AMQP_NODEPROPERTIES_H*/
diff --git a/cpp/src/qpid/broker/amqp/Outgoing.cpp b/cpp/src/qpid/broker/amqp/Outgoing.cpp
new file mode 100644
index 0000000000..9605cacac1
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Outgoing.cpp
@@ -0,0 +1,244 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/amqp/Outgoing.h"
+#include "qpid/broker/amqp/Header.h"
+#include "qpid/broker/amqp/Translation.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/TopicKeyNode.h"
+#include "qpid/sys/OutputControl.h"
+#include "qpid/amqp/MessageEncoder.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace broker {
+namespace amqp {
+
+Outgoing::Outgoing(Broker& broker, boost::shared_ptr<Queue> q, pn_link_t* l, ManagedSession& session, qpid::sys::OutputControl& o, bool topic)
+ : Consumer(pn_link_name(l), /*FIXME*/CONSUMER),
+ ManagedOutgoingLink(broker, *q, session, pn_link_name(l), topic),
+ exclusive(topic),
+ queue(q), deliveries(5000), link(l), out(o),
+ current(0), outstanding(0),
+ buffer(1024)/*used only for header at present*/
+{
+ for (size_t i = 0 ; i < deliveries.capacity(); ++i) {
+ deliveries[i].init(i);
+ }
+}
+
+void Outgoing::init()
+{
+ queue->consume(shared_from_this(), exclusive);//may throw exception
+}
+
+bool Outgoing::dispatch()
+{
+ QPID_LOG(trace, "Dispatching to " << getName() << ": " << pn_link_credit(link));
+ if (canDeliver()) {
+ if (queue->dispatch(shared_from_this())) {
+ return true;
+ } else {
+ pn_link_drained(link);
+ QPID_LOG(debug, "No message available on " << queue->getName());
+ }
+ } else {
+ QPID_LOG(debug, "Can't deliver to " << getName() << " from " << queue->getName() << ": " << pn_link_credit(link));
+ }
+ return false;
+}
+
+void Outgoing::write(const char* data, size_t size)
+{
+ pn_link_send(link, data, size);
+}
+
+void Outgoing::handle(pn_delivery_t* delivery)
+{
+ pn_delivery_tag_t tag = pn_delivery_tag(delivery);
+ size_t i = *reinterpret_cast<const size_t*>(tag.bytes);
+ Record& r = deliveries[i];
+ if (pn_delivery_writable(delivery)) {
+ assert(r.msg);
+ assert(!r.delivery);
+ r.delivery = delivery;
+ //write header
+ qpid::amqp::MessageEncoder encoder(&buffer[0], buffer.size());
+ encoder.writeHeader(Header(r.msg));
+ write(&buffer[0], encoder.getPosition());
+ Translation t(r.msg);
+ t.write(*this);
+ if (pn_link_advance(link)) {
+ --outstanding;
+ outgoingMessageSent();
+ QPID_LOG(debug, "Sent message " << r.msg.getSequence() << " from " << queue->getName() << ", index=" << r.index);
+ } else {
+ QPID_LOG(error, "Failed to send message " << r.msg.getSequence() << " from " << queue->getName() << ", index=" << r.index);
+ }
+ }
+ if (pn_delivery_updated(delivery)) {
+ assert(r.delivery == delivery);
+ r.disposition = pn_delivery_remote_state(delivery);
+ if (r.disposition) {
+ switch (r.disposition) {
+ case PN_ACCEPTED:
+ //TODO: only if consuming
+ queue->dequeue(0, r.cursor);
+ outgoingMessageAccepted();
+ break;
+ case PN_REJECTED:
+ queue->reject(r.cursor);
+ outgoingMessageRejected();
+ break;
+ case PN_RELEASED:
+ queue->release(r.cursor, false);//TODO: for PN_RELEASED, delivery count should not be incremented
+ outgoingMessageRejected();//TODO: not quite true...
+ break;
+ case PN_MODIFIED:
+ queue->release(r.cursor, true);//TODO: proper handling of modified
+ outgoingMessageRejected();//TODO: not quite true...
+ break;
+ default:
+ QPID_LOG(warning, "Unhandled disposition: " << r.disposition);
+ }
+ //TODO: ony settle once any dequeue on store has completed
+ pn_delivery_settle(delivery);
+ r.reset();
+ }
+ }
+}
+
+bool Outgoing::canDeliver()
+{
+ return deliveries[current].delivery == 0 && pn_link_credit(link) > outstanding;
+}
+
+void Outgoing::detached()
+{
+ QPID_LOG(debug, "Detaching outgoing link from " << queue->getName());
+ queue->cancel(shared_from_this());
+ //TODO: release in a clearer order?
+ for (size_t i = 0 ; i < deliveries.capacity(); ++i) {
+ if (deliveries[i].msg) queue->release(deliveries[i].cursor, true);
+ }
+ Queue::tryAutoDelete(*queue->getBroker(), queue, "", "");
+}
+
+//Consumer interface:
+bool Outgoing::deliver(const QueueCursor& cursor, const qpid::broker::Message& msg)
+{
+ Record& r = deliveries[current++];
+ if (current >= deliveries.capacity()) current = 0;
+ r.cursor = cursor;
+ r.msg = msg;
+ pn_delivery(link, r.tag);
+ QPID_LOG(debug, "Requested delivery of " << r.msg.getSequence() << " from " << queue->getName() << ", index=" << r.index);
+ ++outstanding;
+ return true;
+}
+
+void Outgoing::notify()
+{
+ QPID_LOG(trace, "Notification received for " << queue->getName());
+ out.activateOutput();
+}
+
+bool Outgoing::accept(const qpid::broker::Message&)
+{
+ return true;
+}
+
+void Outgoing::setSubjectFilter(const std::string& f)
+{
+ subjectFilter = f;
+}
+
+namespace {
+
+bool match(TokenIterator& filter, TokenIterator& target)
+{
+ bool wild = false;
+ while (!filter.finished())
+ {
+ if (filter.match1('*')) {
+ if (target.finished()) return false;
+ //else move to next word in filter target
+ filter.next();
+ target.next();
+ } else if (filter.match1('#')) {
+ // i.e. filter word is '#' which can match a variable number of words in the target
+ filter.next();
+ if (filter.finished()) return true;
+ else if (target.finished()) return false;
+ wild = true;
+ } else {
+ //filter word needs to match target exactly
+ if (target.finished()) return false;
+ std::string word;
+ target.pop(word);
+ if (filter.match(word)) {
+ wild = false;
+ filter.next();
+ } else if (!wild) {
+ return false;
+ }
+ }
+ }
+ return target.finished();
+}
+bool match(const std::string& filter, const std::string& target)
+{
+ TokenIterator lhs(filter);
+ TokenIterator rhs(target);
+ return match(lhs, rhs);
+}
+}
+
+bool Outgoing::filter(const qpid::broker::Message& m)
+{
+ return subjectFilter.empty() || subjectFilter == m.getRoutingKey() || match(subjectFilter, m.getRoutingKey());
+}
+
+void Outgoing::cancel() {}
+
+void Outgoing::acknowledged(const qpid::broker::DeliveryRecord&) {}
+
+qpid::broker::OwnershipToken* Outgoing::getSession()
+{
+ return 0;
+}
+
+Outgoing::Record::Record() : delivery(0), disposition(0), index(0) {}
+void Outgoing::Record::init(size_t i)
+{
+ index = i;
+ tag.bytes = reinterpret_cast<const char*>(&index);
+ tag.size = sizeof(index);
+}
+void Outgoing::Record::reset()
+{
+ cursor = QueueCursor();
+ msg = qpid::broker::Message();
+ delivery = 0;
+ disposition = 0;
+}
+
+
+}}} // namespace qpid::broker::amqp
diff --git a/cpp/src/qpid/broker/amqp/Outgoing.h b/cpp/src/qpid/broker/amqp/Outgoing.h
new file mode 100644
index 0000000000..a8450a48cf
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Outgoing.h
@@ -0,0 +1,108 @@
+#ifndef QPID_BROKER_AMQP1_OUTGOING_H
+#define QPID_BROKER_AMQP1_OUTGOING_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/amqp/Message.h"
+#include "qpid/broker/amqp/ManagedOutgoingLink.h"
+#include "qpid/broker/Consumer.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+extern "C" {
+#include <proton/engine.h>
+}
+
+namespace qpid {
+namespace sys {
+class OutputControl;
+}
+namespace broker {
+class Broker;
+class Queue;
+namespace amqp {
+class ManagedSession;
+template <class T>
+class CircularArray
+{
+ public:
+ CircularArray(size_t l) : limit(l), data(new T[limit]) {}
+ T& operator[](size_t i) { return data[i]; }
+ size_t capacity() { return limit; }
+ ~CircularArray() { delete [] data; }
+ private:
+ const size_t limit;
+ T* const data;
+ size_t next;
+};
+
+/**
+ *
+ */
+class Outgoing : public qpid::broker::Consumer, public boost::enable_shared_from_this<Outgoing>, public ManagedOutgoingLink
+{
+ public:
+ Outgoing(Broker&,boost::shared_ptr<Queue> q, pn_link_t* l, ManagedSession&, qpid::sys::OutputControl& o, bool topic);
+ void setSubjectFilter(const std::string&);
+ void init();
+ bool dispatch();
+ void write(const char* data, size_t size);
+ void handle(pn_delivery_t* delivery);
+ bool canDeliver();
+ void detached();
+
+ //Consumer interface:
+ bool deliver(const QueueCursor& cursor, const qpid::broker::Message& msg);
+ void notify();
+ bool accept(const qpid::broker::Message&);
+ bool filter(const qpid::broker::Message&);
+ void cancel();
+ void acknowledged(const qpid::broker::DeliveryRecord&);
+ qpid::broker::OwnershipToken* getSession();
+
+ private:
+
+ struct Record
+ {
+ QueueCursor cursor;
+ qpid::broker::Message msg;
+ pn_delivery_t* delivery;
+ int disposition;
+ size_t index;
+ pn_delivery_tag_t tag;
+
+ Record();
+ void init(size_t i);
+ void reset();
+ };
+
+ const bool exclusive;
+ boost::shared_ptr<Queue> queue;
+ CircularArray<Record> deliveries;
+ pn_link_t* link;
+ qpid::sys::OutputControl& out;
+ size_t current;
+ int outstanding;
+ std::vector<char> buffer;
+ std::string subjectFilter;
+};
+}}} // namespace qpid::broker::amqp
+
+#endif /*!QPID_BROKER_AMQP1_OUTGOING_H*/
diff --git a/cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp b/cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp
new file mode 100644
index 0000000000..711592257c
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp
@@ -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.
+ *
+ */
+#include "qpid/Plugin.h"
+#include "qpid/SaslFactory.h"
+#include "qpid/NullSaslServer.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/Protocol.h"
+#include "qpid/broker/RecoverableMessage.h"
+#include "qpid/broker/RecoverableMessageImpl.h"
+#include "qpid/broker/amqp/Connection.h"
+#include "qpid/broker/amqp/Message.h"
+#include "qpid/broker/amqp/Sasl.h"
+#include "qpid/broker/amqp/Translation.h"
+#include "qpid/broker/amqp_0_10/MessageTransfer.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/ProtocolVersion.h"
+#include "qpid/sys/ConnectionCodec.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace broker {
+namespace amqp {
+
+class ProtocolImpl : public Protocol
+{
+ public:
+ ProtocolImpl(Broker& b) : broker(b) {}
+ qpid::sys::ConnectionCodec* create(const qpid::framing::ProtocolVersion&, qpid::sys::OutputControl&, const std::string&, const qpid::sys::SecuritySettings&);
+ boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> translate(const qpid::broker::Message&);
+ boost::shared_ptr<RecoverableMessage> recover(qpid::framing::Buffer&);
+ private:
+ Broker& broker;
+};
+
+struct ProtocolPlugin : public Plugin
+{
+ void earlyInitialize(Plugin::Target& target)
+ {
+ //need to register protocol before recovery from store
+ broker::Broker* broker = dynamic_cast<qpid::broker::Broker*>(&target);
+ if (broker) {
+ broker->getProtocolRegistry().add("AMQP 1.0", new ProtocolImpl(*broker));
+ }
+ }
+
+ void initialize(Plugin::Target&) {}
+};
+
+ProtocolPlugin instance; // Static initialization
+
+qpid::sys::ConnectionCodec* ProtocolImpl::create(const qpid::framing::ProtocolVersion& v, qpid::sys::OutputControl& out, const std::string& id, const qpid::sys::SecuritySettings& external)
+{
+ if (v == qpid::framing::ProtocolVersion(1, 0)) {
+ if (v.getProtocol() == qpid::framing::ProtocolVersion::SASL) {
+ if (broker.getOptions().auth) {
+ QPID_LOG(info, "Using AMQP 1.0 (with SASL layer)");
+ return new qpid::broker::amqp::Sasl(out, id, broker, qpid::SaslFactory::getInstance().createServer(broker.getOptions().realm, broker.getOptions().requireEncrypted, external));
+ } else {
+ std::auto_ptr<SaslServer> authenticator(new qpid::NullSaslServer(broker.getOptions().realm));
+ QPID_LOG(info, "Using AMQP 1.0 (with dummy SASL layer)");
+ return new qpid::broker::amqp::Sasl(out, id, broker, authenticator);
+ }
+ } else {
+ if (broker.getOptions().auth) {
+ throw qpid::Exception("SASL layer required!");
+ } else {
+ QPID_LOG(info, "Using AMQP 1.0 (no SASL layer)");
+ return new qpid::broker::amqp::Connection(out, id, broker, false);
+ }
+ }
+ }
+ return 0;
+}
+
+boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> ProtocolImpl::translate(const qpid::broker::Message& m)
+{
+ qpid::broker::amqp::Translation t(m);
+ return t.getTransfer();
+}
+
+boost::shared_ptr<RecoverableMessage> ProtocolImpl::recover(qpid::framing::Buffer& buffer)
+{
+ QPID_LOG(debug, "Recovering, checking for 1.0 message format indicator...");
+ uint32_t format = buffer.getLong();
+ if (format == 0) {
+ QPID_LOG(debug, "Recovered message IS in 1.0 format");
+ //this is a 1.0 format message
+ boost::intrusive_ptr<qpid::broker::amqp::Message> m(new qpid::broker::amqp::Message(buffer.available()));
+ m->decodeHeader(buffer);
+ return RecoverableMessage::shared_ptr(new RecoverableMessageImpl(qpid::broker::Message(m, m)));
+ } else {
+ QPID_LOG(debug, "Recovered message is NOT in 1.0 format");
+ return RecoverableMessage::shared_ptr();
+ }
+}
+
+
+}}} // namespace qpid::broker::amqp
diff --git a/cpp/src/qpid/broker/amqp/Sasl.cpp b/cpp/src/qpid/broker/amqp/Sasl.cpp
new file mode 100644
index 0000000000..4b89e7b15d
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Sasl.cpp
@@ -0,0 +1,167 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/amqp/Sasl.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/ProtocolInitiation.h"
+#include "qpid/sys/OutputControl.h"
+#include "qpid/sys/SecurityLayer.h"
+#include <boost/format.hpp>
+#include <vector>
+
+namespace qpid {
+namespace broker {
+namespace amqp {
+
+Sasl::Sasl(qpid::sys::OutputControl& o, const std::string& id, qpid::broker::Broker& broker, std::auto_ptr<qpid::SaslServer> auth)
+ : qpid::amqp::SaslServer(id), out(o), connection(out, id, broker, true),
+ authenticator(auth),
+ state(INCOMPLETE), writeHeader(true), haveOutput(true)
+{
+ out.activateOutput();
+ mechanisms(authenticator->getMechanisms());
+}
+
+Sasl::~Sasl() {}
+
+size_t Sasl::decode(const char* buffer, size_t size)
+{
+ if (state == AUTHENTICATED) {
+ if (securityLayer.get()) return securityLayer->decode(buffer, size);
+ else return connection.decode(buffer, size);
+ } else if (state == INCOMPLETE && size) {
+ size_t decoded = read(buffer, size);
+ QPID_LOG(trace, id << " Sasl::decode(" << size << "): " << decoded);
+ return decoded;
+ } else {
+ return 0;
+ }
+}
+
+size_t Sasl::encode(char* buffer, size_t size)
+{
+ if (state == AUTHENTICATED) {
+ if (securityLayer.get()) return securityLayer->encode(buffer, size);
+ else return connection.encode(buffer, size);
+ } else {
+ size_t encoded = 0;
+ if (writeHeader) {
+ encoded += writeProtocolHeader(buffer, size);
+ if (!encoded) return 0;
+ writeHeader = false;
+ }
+ if (encoded < size) {
+ encoded += write(buffer + encoded, size - encoded);
+ }
+ if (state == SUCCESS_PENDING) {
+ state = AUTHENTICATED;
+ } else if (state == FAILURE_PENDING) {
+ state = FAILED;
+ } else {
+ haveOutput = (encoded == size);
+ }
+ QPID_LOG(trace, id << " Sasl::encode(" << size << "): " << encoded);
+ return encoded;
+ }
+}
+
+bool Sasl::canEncode()
+{
+ if (state == AUTHENTICATED) {
+ if (securityLayer.get()) return securityLayer->canEncode();
+ else return connection.canEncode();
+ } else {
+ return haveOutput;
+ }
+}
+
+void Sasl::closed()
+{
+ if (state == AUTHENTICATED) {
+ connection.closed();
+ } else {
+ QPID_LOG(info, id << " Connection closed prior to authentication completing");
+ state = FAILED;
+ }
+}
+bool Sasl::isClosed() const
+{
+ if (state == AUTHENTICATED) {
+ return connection.isClosed();
+ } else {
+ return state == FAILED;
+ }
+}
+
+framing::ProtocolVersion Sasl::getVersion() const
+{
+ return connection.getVersion();
+}
+namespace {
+const std::string EMPTY;
+}
+
+void Sasl::init(const std::string& mechanism, const std::string* response, const std::string* /*hostname*/)
+{
+ QPID_LOG_CAT(debug, protocol, id << " Received SASL-INIT(" << mechanism << ", " << (response ? *response : EMPTY) << ")");
+ //TODO: what should we do with hostname here?
+ std::string c;
+ respond(authenticator->start(mechanism, response, c), c);
+ connection.setSaslMechanism(mechanism);
+}
+
+void Sasl::response(const std::string* r)
+{
+ QPID_LOG_CAT(debug, protocol, id << " Received SASL-RESPONSE(" << (r ? *r : EMPTY) << ")");
+ std::string c;
+ respond(authenticator->step(r, c), c);
+}
+
+void Sasl::respond(qpid::SaslServer::Status status, const std::string& chllnge)
+{
+ switch (status) {
+ case qpid::SaslServer::OK:
+ connection.setUserid(authenticator->getUserid());
+ completed(true);
+ //can't set authenticated & failed until we have actually sent the outcome
+ state = SUCCESS_PENDING;
+ securityLayer = authenticator->getSecurityLayer(65535);
+ if (securityLayer.get()) {
+ QPID_LOG_CAT(info, security, id << " Security layer installed");
+ securityLayer->init(&connection);
+ connection.setSaslSsf(securityLayer->getSsf());
+ }
+ QPID_LOG_CAT(info, security, id << " Authenticated as " << authenticator->getUserid());
+ break;
+ case qpid::SaslServer::FAIL:
+ completed(false);
+ state = FAILURE_PENDING;
+ QPID_LOG_CAT(info, security, id << " Failed to authenticate");
+ break;
+ case qpid::SaslServer::CHALLENGE:
+ challenge(&chllnge);
+ QPID_LOG_CAT(info, security, id << " Challenge issued");
+ break;
+ }
+ haveOutput = true;
+ out.activateOutput();
+}
+}}} // namespace qpid::broker::amqp
diff --git a/cpp/src/qpid/broker/amqp/Sasl.h b/cpp/src/qpid/broker/amqp/Sasl.h
new file mode 100644
index 0000000000..079128be02
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Sasl.h
@@ -0,0 +1,72 @@
+#ifndef QPID_BROKER_AMQP_SASL_H
+#define QPID_BROKER_AMQP_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 "qpid/broker/amqp/Connection.h"
+#include "qpid/SaslServer.h"
+#include "qpid/amqp/SaslServer.h"
+#include "qpid/sys/ConnectionCodec.h"
+#include <memory>
+namespace qpid {
+namespace sys {
+class SecurityLayer;
+}
+namespace broker {
+namespace amqp {
+
+/**
+ * An AMQP 1.0 SASL Security Layer for authentication and optionally
+ * encryption.
+ */
+class Sasl : public sys::ConnectionCodec, qpid::amqp::SaslServer
+{
+ public:
+ Sasl(qpid::sys::OutputControl& out, const std::string& id, qpid::broker::Broker& broker, std::auto_ptr<qpid::SaslServer> authenticator);
+ ~Sasl();
+
+ size_t decode(const char* buffer, size_t size);
+ size_t encode(char* buffer, size_t size);
+ bool canEncode();
+
+ void closed();
+ bool isClosed() const;
+
+ framing::ProtocolVersion getVersion() const;
+ private:
+ qpid::sys::OutputControl& out;
+ Connection connection;
+ std::auto_ptr<qpid::sys::SecurityLayer> securityLayer;
+ std::auto_ptr<qpid::SaslServer> authenticator;
+ enum {
+ INCOMPLETE, SUCCESS_PENDING, FAILURE_PENDING, AUTHENTICATED, FAILED
+ } state;
+
+ bool writeHeader;
+ bool haveOutput;
+
+ void init(const std::string& mechanism, const std::string* response, const std::string* hostname);
+ void response(const std::string*);
+ void respond(qpid::SaslServer::Status status, const std::string& challenge);
+};
+}}} // namespace qpid::broker::amqp
+
+#endif /*!QPID_BROKER_AMQP_SASL_H*/
diff --git a/cpp/src/qpid/broker/amqp/Session.cpp b/cpp/src/qpid/broker/amqp/Session.cpp
new file mode 100644
index 0000000000..fabe609473
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Session.cpp
@@ -0,0 +1,332 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "Session.h"
+#include "Outgoing.h"
+#include "Message.h"
+#include "ManagedConnection.h"
+#include "qpid/broker/AsyncCompletion.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/broker/DirectExchange.h"
+#include "qpid/broker/TopicExchange.h"
+#include "qpid/broker/FanOutExchange.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/TopicExchange.h"
+#include "qpid/broker/amqp/Filter.h"
+#include "qpid/broker/amqp/NodeProperties.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/log/Statement.h"
+#include <boost/intrusive_ptr.hpp>
+#include <boost/format.hpp>
+#include <map>
+#include <sstream>
+extern "C" {
+#include <proton/engine.h>
+}
+
+namespace qpid {
+namespace broker {
+namespace amqp {
+
+class Target
+{
+ public:
+ Target(pn_link_t* l) : credit(100), window(0), link(l) {}
+ virtual ~Target() {}
+ bool flow();
+ bool needFlow();
+ virtual void handle(qpid::broker::Message& m) = 0;//TODO: revise this for proper message
+ protected:
+ const uint32_t credit;
+ uint32_t window;
+ pn_link_t* link;
+};
+
+class Queue : public Target
+{
+ public:
+ Queue(boost::shared_ptr<qpid::broker::Queue> q, pn_link_t* l) : Target(l), queue(q) {}
+ void handle(qpid::broker::Message& m);
+ private:
+ boost::shared_ptr<qpid::broker::Queue> queue;
+};
+
+class Exchange : public Target
+{
+ public:
+ Exchange(boost::shared_ptr<qpid::broker::Exchange> e, pn_link_t* l) : Target(l), exchange(e) {}
+ void handle(qpid::broker::Message& m);
+ private:
+ boost::shared_ptr<qpid::broker::Exchange> exchange;
+};
+
+Session::Session(pn_session_t* s, qpid::broker::Broker& b, ManagedConnection& c, qpid::sys::OutputControl& o)
+ : ManagedSession(b, c, (boost::format("%1%") % s).str()), session(s), broker(b), connection(c), out(o), deleted(false) {}
+
+
+Session::ResolvedNode Session::resolve(const std::string name, pn_terminus_t* terminus)
+{
+ ResolvedNode node;
+ node.exchange = broker.getExchanges().find(name);
+ node.queue = broker.getQueues().find(name);
+ if (!node.queue && !node.exchange && pn_terminus_is_dynamic(terminus)) {
+ //TODO: handle dynamic creation
+ //is it a queue or an exchange?
+ NodeProperties properties;
+ properties.read(pn_terminus_properties(terminus));
+ if (properties.isQueue()) {
+ node.queue = broker.createQueue(name, properties.getQueueSettings(), this, properties.getAlternateExchange(), connection.getUserid(), connection.getId()).first;
+ } else {
+ qpid::framing::FieldTable args;
+ node.exchange = broker.createExchange(name, properties.getExchangeType(), properties.isDurable(), properties.getAlternateExchange(),
+ args, connection.getUserid(), connection.getId()).first;
+ }
+ } else if (node.queue && node.exchange) {
+ QPID_LOG_CAT(warning, protocol, "Ambiguous node name; " << name << " could be queue or exchange, assuming queue");
+ node.exchange.reset();
+ }
+ return node;
+}
+
+void Session::attach(pn_link_t* link)
+{
+ if (pn_link_is_sender(link)) {
+ pn_terminus_t* source = pn_link_remote_source(link);
+ //i.e a subscription
+ if (pn_terminus_get_type(source) == PN_UNSPECIFIED) {
+ throw qpid::Exception("No source specified!");/*invalid-field?*/
+ }
+ std::string name = pn_terminus_get_address(source);
+ QPID_LOG(debug, "Received attach request for outgoing link from " << name);
+ pn_terminus_set_address(pn_link_source(link), name.c_str());
+
+ ResolvedNode node = resolve(name, source);
+ Filter filter;
+ filter.read(pn_terminus_filter(source));
+
+ if (node.queue) {
+ boost::shared_ptr<Outgoing> q(new Outgoing(broker, node.queue, link, *this, out, false));
+ q->init();
+ if (filter.hasSubjectFilter()) {
+ q->setSubjectFilter(filter.getSubjectFilter());
+ }
+ senders[link] = q;
+ } else if (node.exchange) {
+ QueueSettings settings(false, true);
+ //TODO: populate settings from source details when available from engine
+ boost::shared_ptr<qpid::broker::Queue> queue
+ = broker.createQueue(name + qpid::types::Uuid(true).str(), settings, this, "", connection.getUserid(), connection.getId()).first;
+ if (filter.hasSubjectFilter()) {
+ filter.bind(node.exchange, queue);
+ filter.write(pn_terminus_filter(pn_link_source(link)));
+ } else if (node.exchange->getType() == FanOutExchange::typeName) {
+ node.exchange->bind(queue, std::string(), 0);
+ } else if (node.exchange->getType() == TopicExchange::typeName) {
+ node.exchange->bind(queue, "#", 0);
+ } else {
+ throw qpid::Exception("Exchange type requires a filter: " + node.exchange->getType());/*not-supported?*/
+ }
+ boost::shared_ptr<Outgoing> q(new Outgoing(broker, queue, link, *this, out, true));
+ senders[link] = q;
+ q->init();
+ } else {
+ pn_terminus_set_type(pn_link_source(link), PN_UNSPECIFIED);
+ throw qpid::Exception("Node not found: " + name);/*not-found*/
+ }
+ QPID_LOG(debug, "Outgoing link attached");
+ } else {
+ pn_terminus_t* target = pn_link_remote_target(link);
+ if (pn_terminus_get_type(target) == PN_UNSPECIFIED) {
+ throw qpid::Exception("No target specified!");/*invalid field?*/
+ }
+ std::string name = pn_terminus_get_address(target);
+ QPID_LOG(debug, "Received attach request for incoming link to " << name);
+ pn_terminus_set_address(pn_link_target(link), name.c_str());
+
+ ResolvedNode node = resolve(name, target);
+
+ if (node.queue) {
+ boost::shared_ptr<Target> q(new Queue(node.queue, link));
+ targets[link] = q;
+ } else if (node.exchange) {
+ boost::shared_ptr<Target> e(new Exchange(node.exchange, link));
+ targets[link] = e;
+ } else {
+ pn_terminus_set_type(pn_link_target(link), PN_UNSPECIFIED);
+ throw qpid::Exception("Node not found: " + name);/*not-found*/
+ }
+ QPID_LOG(debug, "Incoming link attached");
+ }
+}
+
+void Session::detach(pn_link_t* link)
+{
+ if (pn_link_is_sender(link)) {
+ Senders::iterator i = senders.find(link);
+ if (i != senders.end()) {
+ i->second->detached();
+ senders.erase(i);
+ QPID_LOG(debug, "Outgoing link detached");
+ }
+ } else {
+ targets.erase(link);
+ QPID_LOG(debug, "Incoming link detached");
+ }
+}
+namespace {
+ class Transfer : public qpid::broker::AsyncCompletion::Callback
+ {
+ public:
+ Transfer(pn_delivery_t* d, boost::shared_ptr<Session> s) : delivery(d), session(s) {}
+ void completed(bool sync) { session->accepted(delivery, sync); }
+ boost::intrusive_ptr<qpid::broker::AsyncCompletion::Callback> clone()
+ {
+ boost::intrusive_ptr<qpid::broker::AsyncCompletion::Callback> copy(new Transfer(delivery, session));
+ return copy;
+ }
+ private:
+ pn_delivery_t* delivery;
+ boost::shared_ptr<Session> session;
+ };
+}
+
+void Session::accepted(pn_delivery_t* delivery, bool sync)
+{
+ if (sync) {
+ //this is on IO thread
+ pn_delivery_update(delivery, PN_ACCEPTED);
+ pn_delivery_settle(delivery);//do we need to check settlement modes/orders?
+ incomingMessageAccepted();
+ } else {
+ //this is not on IO thread, need to delay processing until on IO thread
+ qpid::sys::Mutex::ScopedLock l(lock);
+ if (!deleted) {
+ completed.push_back(delivery);
+ out.activateOutput();
+ }
+ }
+}
+
+void Session::incoming(pn_link_t* link, pn_delivery_t* delivery)
+{
+ pn_delivery_tag_t tag = pn_delivery_tag(delivery);
+ QPID_LOG(debug, "received delivery: " << std::string(tag.bytes, tag.size));
+ boost::intrusive_ptr<Message> received(new Message(pn_delivery_pending(delivery)));
+ /*ssize_t read = */pn_link_recv(link, received->getData(), received->getSize());
+ received->scan();
+ pn_link_advance(link);
+
+ qpid::broker::Message message(received, received);
+
+ incomingMessageReceived();
+ Targets::iterator target = targets.find(link);
+ if (target == targets.end()) {
+ QPID_LOG(error, "Received message on unknown link");
+ pn_delivery_update(delivery, PN_REJECTED);
+ pn_delivery_settle(delivery);//do we need to check settlement modes/orders?
+ incomingMessageRejected();
+ } else {
+ target->second->handle(message);
+ received->begin();
+ Transfer t(delivery, shared_from_this());
+ received->end(t);
+ if (target->second->needFlow()) out.activateOutput();
+ }
+}
+void Session::outgoing(pn_link_t* link, pn_delivery_t* delivery)
+{
+ Senders::iterator sender = senders.find(link);
+ if (sender == senders.end()) {
+ QPID_LOG(error, "Delivery returned for unknown link");
+ } else {
+ sender->second->handle(delivery);
+ }
+}
+
+bool Session::dispatch()
+{
+ bool output(false);
+ for (Senders::iterator s = senders.begin(); s != senders.end(); ++s) {
+ if (s->second->dispatch()) output = true;
+ }
+ if (completed.size()) {
+ output = true;
+ std::deque<pn_delivery_t*> copy;
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ completed.swap(copy);
+ }
+ for (std::deque<pn_delivery_t*>::iterator i = copy.begin(); i != copy.end(); ++i) {
+ accepted(*i, true);
+ }
+ }
+ for (Targets::iterator t = targets.begin(); t != targets.end(); ++t) {
+ if (t->second->flow()) output = true;
+ }
+
+ return output;
+}
+
+void Session::close()
+{
+ for (Senders::iterator i = senders.begin(); i != senders.end(); ++i) {
+ i->second->detached();
+ }
+ senders.clear();
+ targets.clear();//at present no explicit cleanup required for targets
+ QPID_LOG(debug, "Session closed, all senders cancelled.");
+ qpid::sys::Mutex::ScopedLock l(lock);
+ deleted = true;
+}
+
+void Queue::handle(qpid::broker::Message& message)
+{
+ queue->deliver(message);
+ --window;
+}
+
+void Exchange::handle(qpid::broker::Message& message)
+{
+ DeliverableMessage deliverable(message, 0);
+ exchange->route(deliverable);
+ --window;
+}
+
+bool Target::flow()
+{
+ bool issue = window < credit;
+ if (issue) {
+ pn_link_flow(link, credit - window);//TODO: proper flow control
+ window = credit;
+ }
+ return issue;
+}
+
+bool Target::needFlow()
+{
+ return window <= (credit/2);
+}
+
+}}} // namespace qpid::broker::amqp
diff --git a/cpp/src/qpid/broker/amqp/Session.h b/cpp/src/qpid/broker/amqp/Session.h
new file mode 100644
index 0000000000..7dbdaf05fc
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Session.h
@@ -0,0 +1,87 @@
+#ifndef QPID_BROKER_AMQP1_SESSION_H
+#define QPID_BROKER_AMQP1_SESSION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/OutputControl.h"
+#include "qpid/broker/amqp/ManagedSession.h"
+#include <deque>
+#include <map>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+struct pn_delivery_t;
+struct pn_link_t;
+struct pn_session_t;
+struct pn_terminus_t;
+
+namespace qpid {
+namespace broker {
+
+class Broker;
+class Exchange;
+class Queue;
+
+namespace amqp {
+
+class ManagedConnection;
+class Outgoing;
+class Target;
+/**
+ *
+ */
+class Session : public ManagedSession, public boost::enable_shared_from_this<Session>
+{
+ public:
+ Session(pn_session_t*, qpid::broker::Broker&, ManagedConnection&, qpid::sys::OutputControl&);
+ void attach(pn_link_t*);
+ void detach(pn_link_t*);
+ void incoming(pn_link_t*, pn_delivery_t*);
+ void outgoing(pn_link_t*, pn_delivery_t*);
+ bool dispatch();
+ void close();
+
+ //called when a transfer is completly processed (e.g.including stored on disk)
+ void accepted(pn_delivery_t*, bool sync);
+ private:
+ typedef std::map<pn_link_t*, boost::shared_ptr<Outgoing> > Senders;
+ typedef std::map<pn_link_t*, boost::shared_ptr<Target> > Targets;
+ pn_session_t* session;
+ qpid::broker::Broker& broker;
+ ManagedConnection& connection;
+ qpid::sys::OutputControl& out;
+ Targets targets;
+ Senders senders;
+ std::deque<pn_delivery_t*> completed;
+ bool deleted;
+ qpid::sys::Mutex lock;
+ struct ResolvedNode
+ {
+ boost::shared_ptr<qpid::broker::Exchange> exchange;
+ boost::shared_ptr<qpid::broker::Queue> queue;
+ };
+
+ ResolvedNode resolve(const std::string name, pn_terminus_t* terminus);
+};
+}}} // namespace qpid::broker::amqp
+
+#endif /*!QPID_BROKER_AMQP1_SESSION_H*/
diff --git a/cpp/src/qpid/broker/amqp/Translation.cpp b/cpp/src/qpid/broker/amqp/Translation.cpp
new file mode 100644
index 0000000000..ca2094b965
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Translation.cpp
@@ -0,0 +1,241 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/amqp/Translation.h"
+#include "qpid/broker/amqp/Outgoing.h"
+#include "qpid/broker/amqp_0_10/MessageTransfer.h"
+#include "qpid/amqp/Decoder.h"
+#include "qpid/amqp/descriptors.h"
+#include "qpid/amqp/MessageEncoder.h"
+#include "qpid/amqp_0_10/Codecs.h"
+#include "qpid/types/Variant.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/log/Statement.h"
+#include <boost/lexical_cast.hpp>
+
+namespace qpid {
+namespace broker {
+namespace amqp {
+namespace {
+
+const std::string EMPTY;
+const std::string FORWARD_SLASH("/");
+
+std::string translate(const qpid::framing::ReplyTo r)
+{
+ if (r.hasExchange()) {
+ if (r.hasRoutingKey()) return r.getExchange() + FORWARD_SLASH + r.getRoutingKey();
+ else return r.getExchange();
+ } else return r.getRoutingKey();
+}
+std::string translate(const qpid::amqp::CharSequence& chars)
+{
+ if (chars.data && chars.size) return std::string(chars.data, chars.size);
+ else return EMPTY;
+}
+bool setMessageId(qpid::framing::MessageProperties& m, const qpid::amqp::CharSequence& chars)
+{
+ if (chars.data && chars.size) {
+ if (chars.size == 16) {
+ m.setMessageId(qpid::framing::Uuid(chars.data));
+ return true;
+ } else {
+ std::istringstream in(translate(chars));
+ qpid::framing::Uuid uuid;
+ in >> uuid;
+ if (!in.fail()) {
+ m.setMessageId(uuid);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+class Properties_0_10 : public qpid::amqp::MessageEncoder::Properties
+{
+ public:
+ bool hasMessageId() const { return messageProperties && messageProperties->hasMessageId(); }
+ std::string getMessageId() const { return messageProperties ? messageProperties->getMessageId().str() : EMPTY; }
+ bool hasUserId() const { return messageProperties && messageProperties->hasUserId(); }
+ std::string getUserId() const { return messageProperties ? messageProperties->getUserId() : EMPTY; }
+ bool hasTo() const { return getDestination().size() || hasSubject(); }
+ std::string getTo() const { return getDestination().size() ? getDestination() : getSubject(); }
+ bool hasSubject() const { return deliveryProperties && getDestination().size() && deliveryProperties->hasRoutingKey(); }
+ std::string getSubject() const { return deliveryProperties && getDestination().size() ? deliveryProperties->getRoutingKey() : EMPTY; }
+ bool hasReplyTo() const { return messageProperties && messageProperties->hasReplyTo(); }
+ std::string getReplyTo() const { return messageProperties ? translate(messageProperties->getReplyTo()) : EMPTY; }
+ bool hasCorrelationId() const { return messageProperties && messageProperties->hasCorrelationId(); }
+ std::string getCorrelationId() const { return messageProperties ? messageProperties->getCorrelationId() : EMPTY; }
+ bool hasContentType() const { return messageProperties && messageProperties->hasContentType(); }
+ std::string getContentType() const { return messageProperties ? messageProperties->getContentType() : EMPTY; }
+ bool hasContentEncoding() const { return messageProperties && messageProperties->hasContentEncoding(); }
+ std::string getContentEncoding() const { return messageProperties ? messageProperties->getContentEncoding() : EMPTY; }
+ bool hasAbsoluteExpiryTime() const { return deliveryProperties && deliveryProperties->hasExpiration(); }
+ int64_t getAbsoluteExpiryTime() const { return deliveryProperties ? deliveryProperties->getExpiration() : 0; }
+ bool hasCreationTime() const { return false; }
+ int64_t getCreationTime() const { return 0; }
+ bool hasGroupId() const {return false; }
+ std::string getGroupId() const { return EMPTY; }
+ bool hasGroupSequence() const { return false; }
+ uint32_t getGroupSequence() const { return 0; }
+ bool hasReplyToGroupId() const { return false; }
+ std::string getReplyToGroupId() const { return EMPTY; }
+
+ const qpid::framing::FieldTable& getApplicationProperties() { return messageProperties->getApplicationHeaders(); }
+ Properties_0_10(const qpid::broker::amqp_0_10::MessageTransfer& t) : transfer(t),
+ messageProperties(transfer.getProperties<qpid::framing::MessageProperties>()),
+ deliveryProperties(transfer.getProperties<qpid::framing::DeliveryProperties>())
+ {}
+ private:
+ const qpid::broker::amqp_0_10::MessageTransfer& transfer;
+ const qpid::framing::MessageProperties* messageProperties;
+ const qpid::framing::DeliveryProperties* deliveryProperties;
+
+ std::string getDestination() const
+ {
+ return transfer.getMethod<qpid::framing::MessageTransferBody>()->getDestination();
+ }
+};
+}
+
+Translation::Translation(const qpid::broker::Message& m) : original(m) {}
+
+
+boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> Translation::getTransfer()
+{
+ boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> t =
+ boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer>(dynamic_cast<const qpid::broker::amqp_0_10::MessageTransfer*>(&original.getEncoding()));
+ if (t) {
+ return t;//no translation required
+ } else {
+ const Message* message = dynamic_cast<const Message*>(&original.getEncoding());
+ if (message) {
+ //translate 1.0 message into 0-10
+ boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> transfer(new qpid::broker::amqp_0_10::MessageTransfer());
+ qpid::framing::AMQFrame method((qpid::framing::MessageTransferBody(qpid::framing::ProtocolVersion(), EMPTY, 0, 0)));
+ qpid::framing::AMQFrame header((qpid::framing::AMQHeaderBody()));
+ qpid::framing::AMQFrame content((qpid::framing::AMQContentBody()));
+ method.setEof(false);
+ header.setBof(false);
+ header.setEof(false);
+ content.setBof(false);
+
+ transfer->getFrames().append(method);
+ transfer->getFrames().append(header);
+
+ qpid::amqp::CharSequence body = message->getBody();
+ content.castBody<qpid::framing::AMQContentBody>()->getData().assign(body.data, body.size);
+ transfer->getFrames().append(content);
+
+ qpid::framing::MessageProperties* props =
+ transfer->getFrames().getHeaders()->get<qpid::framing::MessageProperties>(true);
+ props->setContentLength(body.size);
+
+ qpid::amqp::MessageId mid = message->getMessageId();
+ qpid::framing::Uuid uuid;
+ switch (mid.type) {
+ case qpid::amqp::MessageId::UUID:
+ case qpid::amqp::MessageId::BYTES:
+ if (mid.value.bytes.size == 0) break;
+ if (setMessageId(*props, mid.value.bytes)) break;
+ case qpid::amqp::MessageId::ULONG:
+ QPID_LOG(info, "Skipping message id in translation from 1.0 to 0-10 as it is not a UUID");
+ break;
+ }
+
+ qpid::amqp::MessageId cid = message->getCorrelationId();
+ switch (cid.type) {
+ case qpid::amqp::MessageId::UUID:
+ assert(cid.value.bytes.size = 16);
+ props->setCorrelationId(qpid::framing::Uuid(cid.value.bytes.data).str());
+ break;
+ case qpid::amqp::MessageId::BYTES:
+ if (cid.value.bytes.size) {
+ props->setCorrelationId(translate(cid.value.bytes));
+ }
+ break;
+ case qpid::amqp::MessageId::ULONG:
+ props->setCorrelationId(boost::lexical_cast<std::string>(cid.value.ulong));
+ break;
+ }
+ // TODO: ReplyTo - there is no way to reliably determine
+ // the type of the node from just its name, unless we
+ // query the brokers registries
+
+ if (message->getContentType()) props->setContentType(translate(message->getContentType()));
+ if (message->getContentEncoding()) props->setContentEncoding(translate(message->getContentEncoding()));
+ props->setUserId(message->getUserId());
+ // TODO: FieldTable applicationHeaders;
+ qpid::amqp::CharSequence ap = message->getApplicationProperties();
+ if (ap) {
+ qpid::amqp::Decoder d(ap.data, ap.size);
+ qpid::amqp_0_10::translate(d.readMap(), props->getApplicationHeaders());
+ }
+
+ qpid::framing::DeliveryProperties* dp =
+ transfer->getFrames().getHeaders()->get<qpid::framing::DeliveryProperties>(true);
+ dp->setPriority(message->getPriority());
+ if (message->isPersistent()) dp->setDeliveryMode(2);
+ if (message->getRoutingKey().size()) dp->setRoutingKey(message->getRoutingKey());
+
+ return transfer.get();
+ } else {
+ throw qpid::Exception("Could not write message data in AMQP 0-10 format");
+ }
+ }
+}
+
+void Translation::write(Outgoing& out)
+{
+ const Message* message = dynamic_cast<const Message*>(&original.getEncoding());
+ if (message) {
+ //write annotations
+ //TODO: merge in any newly added annotations
+ qpid::amqp::CharSequence deliveryAnnotations = message->getDeliveryAnnotations();
+ qpid::amqp::CharSequence messageAnnotations = message->getMessageAnnotations();
+ if (deliveryAnnotations.size) out.write(deliveryAnnotations.data, deliveryAnnotations.size);
+ if (messageAnnotations.size) out.write(messageAnnotations.data, messageAnnotations.size);
+ //write bare message
+ qpid::amqp::CharSequence bareMessage = message->getBareMessage();
+ if (bareMessage.size) out.write(bareMessage.data, bareMessage.size);
+ //write footer:
+ qpid::amqp::CharSequence footer = message->getFooter();
+ if (footer.size) out.write(footer.data, footer.size);
+ } else {
+ const qpid::broker::amqp_0_10::MessageTransfer* transfer = dynamic_cast<const qpid::broker::amqp_0_10::MessageTransfer*>(&original.getEncoding());
+ if (transfer) {
+ Properties_0_10 properties(*transfer);
+ qpid::types::Variant::Map applicationProperties;
+ qpid::amqp_0_10::translate(properties.getApplicationProperties(), applicationProperties);
+ std::string content = transfer->getContent();
+ size_t size = qpid::amqp::MessageEncoder::getEncodedSize(properties, applicationProperties, content);
+ std::vector<char> buffer(size);
+ qpid::amqp::MessageEncoder encoder(&buffer[0], buffer.size());
+ encoder.writeProperties(properties);
+ encoder.writeApplicationProperties(applicationProperties);
+ encoder.writeBinary(content, &qpid::amqp::message::DATA);
+ out.write(&buffer[0], encoder.getPosition());
+ } else {
+ QPID_LOG(error, "Could not write message data in AMQP 1.0 format");
+ }
+ }
+}
+
+}}} // namespace qpid::broker::amqp
diff --git a/cpp/src/qpid/broker/amqp/Translation.h b/cpp/src/qpid/broker/amqp/Translation.h
new file mode 100644
index 0000000000..64d96560e3
--- /dev/null
+++ b/cpp/src/qpid/broker/amqp/Translation.h
@@ -0,0 +1,58 @@
+#ifndef QPID_BROKER_AMQP_TRANSLATION_H
+#define QPID_BROKER_AMQP_TRANSLATION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+namespace broker {
+class Message;
+namespace amqp_0_10 {
+class MessageTransfer;
+}
+namespace amqp {
+
+class Outgoing;
+/**
+ *
+ */
+class Translation
+{
+ public:
+ Translation(const qpid::broker::Message& message);
+
+ /**
+ * @returns a pointer to an AMQP 0-10 message transfer suitable
+ * for sending on an 0-10 session, translating from 1.0 as
+ * necessary
+ */
+ boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> getTransfer();
+ /**
+ * Writes the AMQP 1.0 bare message and any annotations, translating from 0-10 if necessary
+ */
+ void write(Outgoing&);
+ private:
+ const qpid::broker::Message& original;
+};
+}}} // namespace qpid::broker::amqp
+
+#endif /*!QPID_BROKER_AMQP_TRANSLATION_H*/
diff --git a/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp b/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp
index cac4434c48..b78b69b2d6 100644
--- a/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp
+++ b/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp
@@ -29,6 +29,7 @@
#include "qpid/framing/TypeFilter.h"
#include "qpid/framing/SendContent.h"
#include "qpid/log/Statement.h"
+#include "boost/lexical_cast.hpp"
using namespace qpid::framing;
@@ -51,7 +52,12 @@ std::string MessageTransfer::getAnnotationAsString(const std::string& key) const
{
const qpid::framing::MessageProperties* mp = getProperties<qpid::framing::MessageProperties>();
if (mp && mp->hasApplicationHeaders()) {
- return mp->getApplicationHeaders().getAsString(key);
+ FieldTable::ValuePtr value = mp->getApplicationHeaders().get(key);
+ if (value) {
+ if (value->convertsTo<std::string>()) return value->get<std::string>();
+ else if (value->convertsTo<int>()) return boost::lexical_cast<std::string>(value->get<int>());
+ }
+ return std::string();
} else {
return std::string();
}
@@ -116,11 +122,6 @@ void MessageTransfer::computeRequiredCredit()
requiredCredit = sum.getSize();
cachedRequiredCredit = true;
}
-uint32_t MessageTransfer::getRequiredCredit(const qpid::broker::Message& msg)
-{
- //TODO: may need to reflect annotations and other modifications in this also
- return get(msg).getRequiredCredit();
-}
qpid::framing::FrameSet& MessageTransfer::getFrames()
{
diff --git a/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h b/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h
index 590e389518..9e432235e6 100644
--- a/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h
+++ b/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h
@@ -109,7 +109,6 @@ class MessageTransfer : public qpid::broker::Message::Encoding, public qpid::bro
QPID_BROKER_EXTERN bool isLastQMFResponse(const std::string correlation) const;
static bool isImmediateDeliveryRequired(const qpid::broker::Message& message);
- static uint32_t getRequiredCredit(const qpid::broker::Message&);
static MessageTransfer& get(qpid::broker::Message& message) {
return *dynamic_cast<MessageTransfer*>(&message.getEncoding());
}
diff --git a/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp b/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp
index 40e74be018..c04d037a6e 100644
--- a/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp
+++ b/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp
@@ -23,6 +23,7 @@
// accessing authentication mechanisms, analogous to Cyrus SASL.
#include "qpid/broker/Connection.h"
+#include "qpid/broker/Broker.h"
#include "qpid/log/Statement.h"
#include "qpid/framing/reply_exceptions.h"
#include "qpid/framing/FieldValue.h"
diff --git a/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp b/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp
index 420e04e832..a07afe45ae 100644
--- a/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp
+++ b/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp
@@ -1,352 +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/sys/ProtocolFactory.h"
-
-#include "qpid/Plugin.h"
-#include "qpid/broker/Broker.h"
-#include "qpid/log/Statement.h"
-#include "qpid/sys/AsynchIOHandler.h"
-#include "qpid/sys/ConnectionCodec.h"
-#include "qpid/sys/Socket.h"
-#include "qpid/sys/SocketAddress.h"
-#include "qpid/sys/SystemInfo.h"
-#include "qpid/sys/windows/SslAsynchIO.h"
-
-#include <boost/bind.hpp>
-#include <boost/ptr_container/ptr_vector.hpp>
-#include <memory>
-
-// security.h needs to see this to distinguish from kernel use.
-#define SECURITY_WIN32
-#include <security.h>
-#include <Schnlsp.h>
-#undef SECURITY_WIN32
-
-
-namespace qpid {
-namespace sys {
-
-class Timer;
-
-namespace windows {
-
-struct SslServerOptions : qpid::Options
-{
- std::string certStore;
- std::string certStoreLocation;
- std::string certName;
- uint16_t port;
- bool clientAuth;
-
- SslServerOptions() : qpid::Options("SSL Options"),
- certStore("My"),
- certStoreLocation("CurrentUser"),
- certName("localhost"),
- port(5671),
- clientAuth(false)
- {
- qpid::Address me;
- if (qpid::sys::SystemInfo::getLocalHostname(me))
- certName = me.host;
-
- addOptions()
- ("ssl-cert-store", optValue(certStore, "NAME"), "Local store name from which to obtain certificate")
- ("ssl-cert-store-location", optValue(certStoreLocation, "NAME"),
- "Local store name location for certificates ( CurrentUser | LocalMachine | CurrentService )")
- ("ssl-cert-name", optValue(certName, "NAME"), "Name of the certificate to use")
- ("ssl-port", optValue(port, "PORT"), "Port on which to listen for SSL connections")
- ("ssl-require-client-authentication", optValue(clientAuth),
- "Forces clients to authenticate in order to establish an SSL connection");
- }
-};
-
-class SslProtocolFactory : public qpid::sys::ProtocolFactory {
- boost::ptr_vector<Socket> listeners;
- boost::ptr_vector<AsynchAcceptor> acceptors;
- Timer& brokerTimer;
- uint32_t maxNegotiateTime;
- uint16_t listeningPort;
- const bool tcpNoDelay;
- std::string brokerHost;
- const bool clientAuthSelected;
- std::auto_ptr<qpid::sys::AsynchAcceptor> acceptor;
- ConnectFailedCallback connectFailedCallback;
- CredHandle credHandle;
-
- public:
- SslProtocolFactory(const SslServerOptions&, const std::string& host, const std::string& port,
- int backlog, bool nodelay,
- Timer& timer, uint32_t maxTime);
- ~SslProtocolFactory();
- void accept(sys::Poller::shared_ptr, sys::ConnectionCodec::Factory*);
- void connect(sys::Poller::shared_ptr, const std::string& host, const std::string& port,
- sys::ConnectionCodec::Factory*,
- ConnectFailedCallback failed);
-
- uint16_t getPort() const;
- bool supports(const std::string& capability);
-
- private:
- void connectFailed(const qpid::sys::Socket&,
- int err,
- const std::string& msg);
- void established(sys::Poller::shared_ptr,
- const qpid::sys::Socket&,
- sys::ConnectionCodec::Factory*,
- bool isClient);
-};
-
-// Static instance to initialise plugin
-static struct SslPlugin : public Plugin {
- SslServerOptions options;
-
- Options* getOptions() { return &options; }
-
- void earlyInitialize(Target&) {
- }
-
- void initialize(Target& target) {
- broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
- // Only provide to a Broker
- if (broker) {
- try {
- const broker::Broker::Options& opts = broker->getOptions();
- ProtocolFactory::shared_ptr protocol(new SslProtocolFactory(options,
- "", boost::lexical_cast<std::string>(options.port),
- opts.connectionBacklog, opts.tcpNoDelay,
- broker->getTimer(), opts.maxNegotiateTime));
- QPID_LOG(notice, "Listening for SSL connections on TCP port " << protocol->getPort());
- broker->registerProtocolFactory("ssl", protocol);
- } catch (const std::exception& e) {
- QPID_LOG(error, "Failed to initialise SSL listener: " << e.what());
- }
- }
- }
-} sslPlugin;
-
-SslProtocolFactory::SslProtocolFactory(const SslServerOptions& options,
- const std::string& host, const std::string& port,
- int backlog, bool nodelay,
- Timer& timer, uint32_t maxTime)
- : brokerTimer(timer),
- maxNegotiateTime(maxTime),
- tcpNoDelay(nodelay),
- clientAuthSelected(options.clientAuth) {
-
- // Make sure that certificate store is good before listening to sockets
- // to avoid having open and listening sockets when there is no cert store
- SecInvalidateHandle(&credHandle);
-
- // Get the certificate for this server.
- DWORD flags = 0;
- std::string certStoreLocation = options.certStoreLocation;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/ProtocolFactory.h"
+
+#include "qpid/Plugin.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/AsynchIOHandler.h"
+#include "qpid/sys/ConnectionCodec.h"
+#include "qpid/sys/Socket.h"
+#include "qpid/sys/SocketAddress.h"
+#include "qpid/sys/SystemInfo.h"
+#include "qpid/sys/windows/SslAsynchIO.h"
+
+#include <boost/bind.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <memory>
+
+// security.h needs to see this to distinguish from kernel use.
+#define SECURITY_WIN32
+#include <security.h>
+#include <Schnlsp.h>
+#undef SECURITY_WIN32
+
+
+namespace qpid {
+namespace sys {
+
+class Timer;
+
+namespace windows {
+
+struct SslServerOptions : qpid::Options
+{
+ std::string certStore;
+ std::string certStoreLocation;
+ std::string certName;
+ uint16_t port;
+ bool clientAuth;
+
+ SslServerOptions() : qpid::Options("SSL Options"),
+ certStore("My"),
+ certStoreLocation("CurrentUser"),
+ certName("localhost"),
+ port(5671),
+ clientAuth(false)
+ {
+ qpid::Address me;
+ if (qpid::sys::SystemInfo::getLocalHostname(me))
+ certName = me.host;
+
+ addOptions()
+ ("ssl-cert-store", optValue(certStore, "NAME"), "Local store name from which to obtain certificate")
+ ("ssl-cert-store-location", optValue(certStoreLocation, "NAME"),
+ "Local store name location for certificates ( CurrentUser | LocalMachine | CurrentService )")
+ ("ssl-cert-name", optValue(certName, "NAME"), "Name of the certificate to use")
+ ("ssl-port", optValue(port, "PORT"), "Port on which to listen for SSL connections")
+ ("ssl-require-client-authentication", optValue(clientAuth),
+ "Forces clients to authenticate in order to establish an SSL connection");
+ }
+};
+
+class SslProtocolFactory : public qpid::sys::ProtocolFactory {
+ boost::ptr_vector<Socket> listeners;
+ boost::ptr_vector<AsynchAcceptor> acceptors;
+ Timer& brokerTimer;
+ uint32_t maxNegotiateTime;
+ uint16_t listeningPort;
+ const bool tcpNoDelay;
+ std::string brokerHost;
+ const bool clientAuthSelected;
+ std::auto_ptr<qpid::sys::AsynchAcceptor> acceptor;
+ ConnectFailedCallback connectFailedCallback;
+ CredHandle credHandle;
+
+ public:
+ SslProtocolFactory(const qpid::broker::Broker::Options& opts, const SslServerOptions&, Timer& timer);
+ ~SslProtocolFactory();
+ void accept(sys::Poller::shared_ptr, sys::ConnectionCodec::Factory*);
+ void connect(sys::Poller::shared_ptr, const std::string& name, const std::string& host, const std::string& port,
+ sys::ConnectionCodec::Factory*,
+ ConnectFailedCallback failed);
+
+ uint16_t getPort() const;
+
+ private:
+ void connectFailed(const qpid::sys::Socket&,
+ int err,
+ const std::string& msg);
+ void establishedIncoming(sys::Poller::shared_ptr, const qpid::sys::Socket&, sys::ConnectionCodec::Factory*);
+ void establishedOutgoing(sys::Poller::shared_ptr, const qpid::sys::Socket&, sys::ConnectionCodec::Factory*, std::string& );
+ void establishedCommon(sys::Poller::shared_ptr, sys::AsynchIOHandler*, sys::AsynchIO*, const qpid::sys::Socket&);
+};
+
+// Static instance to initialise plugin
+static struct SslPlugin : public Plugin {
+ SslServerOptions options;
+
+ Options* getOptions() { return &options; }
+
+ void earlyInitialize(Target&) {
+ }
+
+ void initialize(Target& target) {
+ broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
+ // Only provide to a Broker
+ if (broker) {
+ try {
+ const broker::Broker::Options& opts = broker->getOptions();
+ ProtocolFactory::shared_ptr protocol(new SslProtocolFactory(opts, options, broker->getTimer()));
+ QPID_LOG(notice, "Listening for SSL connections on TCP port " << protocol->getPort());
+ broker->registerProtocolFactory("ssl", protocol);
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Failed to initialise SSL listener: " << e.what());
+ }
+ }
+ }
+} sslPlugin;
+
+namespace {
+ // Expand list of Interfaces and addresses to a list of addresses
+ std::vector<std::string> expandInterfaces(const std::vector<std::string>& interfaces) {
+ std::vector<std::string> addresses;
+ // If there are no specific interfaces listed use a single "" to listen on every interface
+ if (interfaces.empty()) {
+ addresses.push_back("");
+ return addresses;
+ }
+ for (unsigned i = 0; i < interfaces.size(); ++i) {
+ const std::string& interface = interfaces[i];
+ if (!(SystemInfo::getInterfaceAddresses(interface, addresses))) {
+ // We don't have an interface of that name -
+ // Check for IPv6 ('[' ']') brackets and remove them
+ // then pass to be looked up directly
+ if (interface[0]=='[' && interface[interface.size()-1]==']') {
+ addresses.push_back(interface.substr(1, interface.size()-2));
+ } else {
+ addresses.push_back(interface);
+ }
+ }
+ }
+ return addresses;
+ }
+}
+
+SslProtocolFactory::SslProtocolFactory(const qpid::broker::Broker::Options& opts, const SslServerOptions& options, Timer& timer)
+ : brokerTimer(timer),
+ maxNegotiateTime(opts.maxNegotiateTime),
+ tcpNoDelay(opts.tcpNoDelay),
+ clientAuthSelected(options.clientAuth) {
+
+ // Make sure that certificate store is good before listening to sockets
+ // to avoid having open and listening sockets when there is no cert store
+ SecInvalidateHandle(&credHandle);
+
+ // Get the certificate for this server.
+ DWORD flags = 0;
+ std::string certStoreLocation = options.certStoreLocation;
std::transform(certStoreLocation.begin(), certStoreLocation.end(), certStoreLocation.begin(), ::tolower);
- if (certStoreLocation == "currentuser") {
- flags = CERT_SYSTEM_STORE_CURRENT_USER;
- } else if (certStoreLocation == "localmachine") {
- flags = CERT_SYSTEM_STORE_LOCAL_MACHINE;
- } else if (certStoreLocation == "currentservice") {
- flags = CERT_SYSTEM_STORE_CURRENT_SERVICE;
- } else {
- QPID_LOG(error, "Unrecognised SSL certificate store location: " << options.certStoreLocation
- << " - Using default location");
- }
- HCERTSTORE certStoreHandle;
- certStoreHandle = ::CertOpenStore(CERT_STORE_PROV_SYSTEM_A,
- X509_ASN_ENCODING,
- 0,
- flags |
- CERT_STORE_READONLY_FLAG,
- options.certStore.c_str());
- if (!certStoreHandle)
- throw qpid::Exception(QPID_MSG("Opening store " << options.certStore << " " << qpid::sys::strError(GetLastError())));
-
- PCCERT_CONTEXT certContext;
- certContext = ::CertFindCertificateInStore(certStoreHandle,
- X509_ASN_ENCODING,
- 0,
- CERT_FIND_SUBJECT_STR_A,
- options.certName.c_str(),
- NULL);
- if (certContext == NULL) {
- int err = ::GetLastError();
- ::CertCloseStore(certStoreHandle, 0);
- throw qpid::Exception(QPID_MSG("Locating certificate " << options.certName << " in store " << options.certStore << " " << qpid::sys::strError(GetLastError())));
- throw QPID_WINDOWS_ERROR(err);
- }
-
- SCHANNEL_CRED cred;
- memset(&cred, 0, sizeof(cred));
- cred.dwVersion = SCHANNEL_CRED_VERSION;
- cred.cCreds = 1;
- cred.paCred = &certContext;
- SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL,
- UNISP_NAME,
- SECPKG_CRED_INBOUND,
- NULL,
- &cred,
- NULL,
- NULL,
- &credHandle,
- NULL);
- if (status != SEC_E_OK)
- throw QPID_WINDOWS_ERROR(status);
- ::CertFreeCertificateContext(certContext);
- ::CertCloseStore(certStoreHandle, 0);
-
- // Listen to socket(s)
- SocketAddress sa(host, port);
-
- // We must have at least one resolved address
- QPID_LOG(info, "SSL Listening to: " << sa.asString())
- Socket* s = new Socket;
- listeningPort = s->listen(sa, backlog);
- listeners.push_back(s);
-
- // Try any other resolved addresses
- while (sa.nextAddress()) {
- QPID_LOG(info, "SSL Listening to: " << sa.asString())
- Socket* s = new Socket;
- s->listen(sa, backlog);
- listeners.push_back(s);
- }
-}
-
-SslProtocolFactory::~SslProtocolFactory() {
- ::FreeCredentialsHandle(&credHandle);
-}
-
-void SslProtocolFactory::connectFailed(const qpid::sys::Socket&,
- int err,
- const std::string& msg) {
- if (connectFailedCallback)
- connectFailedCallback(err, msg);
-}
-
-void SslProtocolFactory::established(sys::Poller::shared_ptr poller,
- const qpid::sys::Socket& s,
- sys::ConnectionCodec::Factory* f,
- bool isClient) {
- sys::AsynchIOHandler* async = new sys::AsynchIOHandler(s.getFullAddress(), f);
-
- if (tcpNoDelay) {
- s.setTcpNoDelay();
- QPID_LOG(info,
- "Set TCP_NODELAY on connection to " << s.getPeerAddress());
- }
-
- SslAsynchIO *aio;
- if (isClient) {
- async->setClient();
- aio =
- new qpid::sys::windows::ClientSslAsynchIO(brokerHost,
- s,
- credHandle,
- boost::bind(&AsynchIOHandler::readbuff, async, _1, _2),
- boost::bind(&AsynchIOHandler::eof, async, _1),
- boost::bind(&AsynchIOHandler::disconnect, async, _1),
- boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2),
- boost::bind(&AsynchIOHandler::nobuffs, async, _1),
- boost::bind(&AsynchIOHandler::idle, async, _1));
- }
- else {
- aio =
- new qpid::sys::windows::ServerSslAsynchIO(clientAuthSelected,
- s,
- credHandle,
- boost::bind(&AsynchIOHandler::readbuff, async, _1, _2),
- boost::bind(&AsynchIOHandler::eof, async, _1),
- boost::bind(&AsynchIOHandler::disconnect, async, _1),
- boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2),
- boost::bind(&AsynchIOHandler::nobuffs, async, _1),
- boost::bind(&AsynchIOHandler::idle, async, _1));
- }
-
- async->init(aio, brokerTimer, maxNegotiateTime);
- aio->start(poller);
-}
-
-uint16_t SslProtocolFactory::getPort() const {
- return listeningPort; // Immutable no need for lock.
-}
-
-void SslProtocolFactory::accept(sys::Poller::shared_ptr poller,
- sys::ConnectionCodec::Factory* fact) {
- for (unsigned i = 0; i<listeners.size(); ++i) {
- acceptors.push_back(
- AsynchAcceptor::create(listeners[i],
- boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, false)));
- acceptors[i].start(poller);
- }
-}
-
-void SslProtocolFactory::connect(sys::Poller::shared_ptr poller,
- const std::string& host,
- const std::string& port,
- sys::ConnectionCodec::Factory* fact,
- ConnectFailedCallback failed)
-{
- SCHANNEL_CRED cred;
- memset(&cred, 0, sizeof(cred));
- cred.dwVersion = SCHANNEL_CRED_VERSION;
- SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL,
- UNISP_NAME,
- SECPKG_CRED_OUTBOUND,
- NULL,
- &cred,
- NULL,
- NULL,
- &credHandle,
- NULL);
- if (status != SEC_E_OK)
- throw QPID_WINDOWS_ERROR(status);
-
- brokerHost = host;
- // Note that the following logic does not cause a memory leak.
- // The allocated Socket is freed either by the AsynchConnector
- // upon connection failure or by the AsynchIO upon connection
- // shutdown. The allocated AsynchConnector frees itself when it
- // is no longer needed.
- qpid::sys::Socket* socket = new qpid::sys::Socket();
- connectFailedCallback = failed;
- AsynchConnector::create(*socket,
- host,
- port,
- boost::bind(&SslProtocolFactory::established,
- this, poller, _1, fact, true),
- boost::bind(&SslProtocolFactory::connectFailed,
- this, _1, _2, _3));
-}
-
-namespace
-{
-const std::string SSL = "ssl";
-}
-
-bool SslProtocolFactory::supports(const std::string& capability)
-{
- std::string s = capability;
- transform(s.begin(), s.end(), s.begin(), tolower);
- return s == SSL;
-}
-
-}}} // namespace qpid::sys::windows
+ if (certStoreLocation == "currentuser") {
+ flags = CERT_SYSTEM_STORE_CURRENT_USER;
+ } else if (certStoreLocation == "localmachine") {
+ flags = CERT_SYSTEM_STORE_LOCAL_MACHINE;
+ } else if (certStoreLocation == "currentservice") {
+ flags = CERT_SYSTEM_STORE_CURRENT_SERVICE;
+ } else {
+ QPID_LOG(error, "Unrecognised SSL certificate store location: " << options.certStoreLocation
+ << " - Using default location");
+ }
+ HCERTSTORE certStoreHandle;
+ certStoreHandle = ::CertOpenStore(CERT_STORE_PROV_SYSTEM_A,
+ X509_ASN_ENCODING,
+ 0,
+ flags |
+ CERT_STORE_READONLY_FLAG,
+ options.certStore.c_str());
+ if (!certStoreHandle)
+ throw qpid::Exception(QPID_MSG("Opening store " << options.certStore << " " << qpid::sys::strError(GetLastError())));
+
+ PCCERT_CONTEXT certContext;
+ certContext = ::CertFindCertificateInStore(certStoreHandle,
+ X509_ASN_ENCODING,
+ 0,
+ CERT_FIND_SUBJECT_STR_A,
+ options.certName.c_str(),
+ NULL);
+ if (certContext == NULL) {
+ int err = ::GetLastError();
+ ::CertCloseStore(certStoreHandle, 0);
+ throw qpid::Exception(QPID_MSG("Locating certificate " << options.certName << " in store " << options.certStore << " " << qpid::sys::strError(GetLastError())));
+ throw QPID_WINDOWS_ERROR(err);
+ }
+
+ SCHANNEL_CRED cred;
+ memset(&cred, 0, sizeof(cred));
+ cred.dwVersion = SCHANNEL_CRED_VERSION;
+ cred.cCreds = 1;
+ cred.paCred = &certContext;
+ SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL,
+ UNISP_NAME,
+ SECPKG_CRED_INBOUND,
+ NULL,
+ &cred,
+ NULL,
+ NULL,
+ &credHandle,
+ NULL);
+ if (status != SEC_E_OK)
+ throw QPID_WINDOWS_ERROR(status);
+ ::CertFreeCertificateContext(certContext);
+ ::CertCloseStore(certStoreHandle, 0);
+
+ std::vector<std::string> addresses = expandInterfaces(opts.listenInterfaces);
+ if (addresses.empty()) {
+ // We specified some interfaces, but couldn't find addresses for them
+ QPID_LOG(warning, "TCP/TCP6: No specified network interfaces found: Not Listening");
+ listeningPort = 0;
+ }
+
+ for (unsigned i = 0; i<addresses.size(); ++i) {
+ QPID_LOG(debug, "Using interface: " << addresses[i]);
+ SocketAddress sa(addresses[i], boost::lexical_cast<std::string>(options.port));
+
+
+ // We must have at least one resolved address
+ QPID_LOG(info, "SSL Listening to: " << sa.asString())
+ Socket* s = createSocket();
+ listeningPort = s->listen(sa, opts.connectionBacklog);
+ listeners.push_back(s);
+
+ // Try any other resolved addresses
+ while (sa.nextAddress()) {
+ QPID_LOG(info, "SSL Listening to: " << sa.asString())
+ Socket* s = createSocket();
+ s->listen(sa, opts.connectionBacklog);
+ listeners.push_back(s);
+ }
+ }
+}
+
+SslProtocolFactory::~SslProtocolFactory() {
+ ::FreeCredentialsHandle(&credHandle);
+}
+
+void SslProtocolFactory::connectFailed(const qpid::sys::Socket&,
+ int err,
+ const std::string& msg) {
+ if (connectFailedCallback)
+ connectFailedCallback(err, msg);
+}
+
+void SslProtocolFactory::establishedIncoming(sys::Poller::shared_ptr poller,
+ const qpid::sys::Socket& s,
+ sys::ConnectionCodec::Factory* f) {
+ sys::AsynchIOHandler* async = new sys::AsynchIOHandler(s.getFullAddress(), f, false, false);
+
+ sys::AsynchIO *aio =
+ new qpid::sys::windows::ServerSslAsynchIO(
+ clientAuthSelected,
+ s,
+ credHandle,
+ boost::bind(&AsynchIOHandler::readbuff, async, _1, _2),
+ boost::bind(&AsynchIOHandler::eof, async, _1),
+ boost::bind(&AsynchIOHandler::disconnect, async, _1),
+ boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2),
+ boost::bind(&AsynchIOHandler::nobuffs, async, _1),
+ boost::bind(&AsynchIOHandler::idle, async, _1));
+
+ establishedCommon(poller, async, aio, s);
+}
+
+void SslProtocolFactory::establishedOutgoing(sys::Poller::shared_ptr poller,
+ const qpid::sys::Socket& s,
+ sys::ConnectionCodec::Factory* f,
+ std::string& name) {
+ sys::AsynchIOHandler* async = new sys::AsynchIOHandler(name, f, true, false);
+
+ sys::AsynchIO *aio =
+ new qpid::sys::windows::ClientSslAsynchIO(
+ brokerHost,
+ s,
+ credHandle,
+ boost::bind(&AsynchIOHandler::readbuff, async, _1, _2),
+ boost::bind(&AsynchIOHandler::eof, async, _1),
+ boost::bind(&AsynchIOHandler::disconnect, async, _1),
+ boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2),
+ boost::bind(&AsynchIOHandler::nobuffs, async, _1),
+ boost::bind(&AsynchIOHandler::idle, async, _1));
+
+ establishedCommon(poller, async, aio, s);
+}
+
+void SslProtocolFactory::establishedCommon(sys::Poller::shared_ptr poller,
+ sys::AsynchIOHandler* async,
+ sys::AsynchIO* aio,
+ const qpid::sys::Socket& s) {
+ if (tcpNoDelay) {
+ s.setTcpNoDelay();
+ QPID_LOG(info,
+ "Set TCP_NODELAY on connection to " << s.getPeerAddress());
+ }
+
+ async->init(aio, brokerTimer, maxNegotiateTime);
+ aio->start(poller);
+}
+
+uint16_t SslProtocolFactory::getPort() const {
+ return listeningPort; // Immutable no need for lock.
+}
+
+void SslProtocolFactory::accept(sys::Poller::shared_ptr poller,
+ sys::ConnectionCodec::Factory* fact) {
+ for (unsigned i = 0; i<listeners.size(); ++i) {
+ acceptors.push_back(
+ AsynchAcceptor::create(listeners[i],
+ boost::bind(&SslProtocolFactory::establishedIncoming, this, poller, _1, fact)));
+ acceptors[i].start(poller);
+ }
+}
+
+void SslProtocolFactory::connect(sys::Poller::shared_ptr poller,
+ const std::string& name,
+ const std::string& host,
+ const std::string& port,
+ sys::ConnectionCodec::Factory* fact,
+ ConnectFailedCallback failed)
+{
+ SCHANNEL_CRED cred;
+ memset(&cred, 0, sizeof(cred));
+ cred.dwVersion = SCHANNEL_CRED_VERSION;
+ SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL,
+ UNISP_NAME,
+ SECPKG_CRED_OUTBOUND,
+ NULL,
+ &cred,
+ NULL,
+ NULL,
+ &credHandle,
+ NULL);
+ if (status != SEC_E_OK)
+ throw QPID_WINDOWS_ERROR(status);
+
+ brokerHost = host;
+ // Note that the following logic does not cause a memory leak.
+ // The allocated Socket is freed either by the AsynchConnector
+ // upon connection failure or by the AsynchIO upon connection
+ // shutdown. The allocated AsynchConnector frees itself when it
+ // is no longer needed.
+ qpid::sys::Socket* socket = createSocket();
+ connectFailedCallback = failed;
+ AsynchConnector::create(*socket,
+ host,
+ port,
+ boost::bind(&SslProtocolFactory::establishedOutgoing,
+ this, poller, _1, fact, name),
+ boost::bind(&SslProtocolFactory::connectFailed,
+ this, _1, _2, _3));
+}
+
+}}} // namespace qpid::sys::windows
diff --git a/cpp/src/qpid/client/ConnectionHandler.cpp b/cpp/src/qpid/client/ConnectionHandler.cpp
index 91838d8e8b..4f88cb97ee 100644
--- a/cpp/src/qpid/client/ConnectionHandler.cpp
+++ b/cpp/src/qpid/client/ConnectionHandler.cpp
@@ -82,9 +82,11 @@ void ConnectionHandler::Adapter::handle(qpid::framing::AMQFrame& f)
handler.out(f);
}
-ConnectionHandler::ConnectionHandler(const ConnectionSettings& s, ProtocolVersion& v, Bounds& b)
- : StateManager(NOT_STARTED), ConnectionSettings(s), outHandler(*this, b), proxy(outHandler),
- errorCode(CLOSE_CODE_NORMAL), version(v)
+ConnectionHandler::ConnectionHandler(
+ const ConnectionSettings& s, ProtocolVersion& v, Bounds& b)
+ : StateManager(NOT_STARTED), ConnectionSettings(s),
+ outHandler(*this, b), proxy(outHandler), errorCode(CLOSE_CODE_NORMAL), version(v),
+ properties(s.clientProperties)
{
insist = true;
diff --git a/cpp/src/qpid/client/ConnectionImpl.cpp b/cpp/src/qpid/client/ConnectionImpl.cpp
index 85b0e8303e..0abfbe09ec 100644
--- a/cpp/src/qpid/client/ConnectionImpl.cpp
+++ b/cpp/src/qpid/client/ConnectionImpl.cpp
@@ -128,15 +128,17 @@ public:
// and we can't do that before we're unloaded as we can't
// restart the Poller after shutting it down
~IOThread() {
- std::vector<Thread> threads;
- {
- ScopedLock<Mutex> l(threadLock);
- if (poller_)
- poller_->shutdown();
- t.swap(threads);
- }
- for (std::vector<Thread>::iterator i = threads.begin(); i != threads.end(); ++i) {
- i->join();
+ if (SystemInfo::threadSafeShutdown()) {
+ std::vector<Thread> threads;
+ {
+ ScopedLock<Mutex> l(threadLock);
+ if (poller_)
+ poller_->shutdown();
+ t.swap(threads);
+ }
+ for (std::vector<Thread>::iterator i = threads.begin(); i != threads.end(); ++i) {
+ i->join();
+ }
}
}
};
diff --git a/cpp/src/qpid/client/FailoverManager.cpp b/cpp/src/qpid/client/FailoverManager.cpp
index 9405765b47..f27aeb5b52 100644
--- a/cpp/src/qpid/client/FailoverManager.cpp
+++ b/cpp/src/qpid/client/FailoverManager.cpp
@@ -34,6 +34,9 @@ using qpid::sys::Duration;
FailoverManager::FailoverManager(const ConnectionSettings& s,
ReconnectionStrategy* rs) : settings(s), strategy(rs), state(IDLE) {}
+FailoverManager::~FailoverManager()
+{}
+
void FailoverManager::execute(Command& c)
{
bool retry = false;
diff --git a/cpp/src/qpid/client/LoadPlugins.cpp b/cpp/src/qpid/client/LoadPlugins.cpp
index d76e1d458e..c5d8924014 100644
--- a/cpp/src/qpid/client/LoadPlugins.cpp
+++ b/cpp/src/qpid/client/LoadPlugins.cpp
@@ -48,7 +48,7 @@ struct LoadtimeInitialise {
for (vector<string>::iterator iter = moduleOptions.load.begin();
iter != moduleOptions.load.end();
iter++)
- qpid::tryShlib (iter->data(), false);
+ qpid::tryShlib (*iter);
if (!moduleOptions.noLoad) {
bool isDefault = defaultPath == moduleOptions.loadDir;
diff --git a/cpp/src/qpid/client/LoadPlugins.h b/cpp/src/qpid/client/LoadPlugins.h
index 0be4ae9f0c..0b398f6831 100644
--- a/cpp/src/qpid/client/LoadPlugins.h
+++ b/cpp/src/qpid/client/LoadPlugins.h
@@ -22,10 +22,12 @@
#ifndef _LoadPlugins_
#define _LoadPlugins_
+#include "qpid/client/ClientImportExport.h"
+
namespace qpid {
namespace client {
-void theModuleLoader();
+QPID_CLIENT_EXTERN void theModuleLoader();
}}
diff --git a/cpp/src/qpid/client/QueueOptions.cpp b/cpp/src/qpid/client/QueueOptions.cpp
index f4c1483859..460f3f5490 100644
--- a/cpp/src/qpid/client/QueueOptions.cpp
+++ b/cpp/src/qpid/client/QueueOptions.cpp
@@ -49,8 +49,8 @@ QueueOptions::~QueueOptions()
void QueueOptions::setSizePolicy(QueueSizePolicy sp, uint64_t maxSize, uint32_t maxCount)
{
- if (maxCount) setInt(strMaxCountKey, maxCount);
- if (maxSize) setInt(strMaxSizeKey, maxSize);
+ if (maxCount) setUInt64(strMaxCountKey, maxCount);
+ if (maxSize) setUInt64(strMaxSizeKey, maxSize);
if (maxSize || maxCount){
switch (sp)
{
diff --git a/cpp/src/qpid/client/RdmaConnector.cpp b/cpp/src/qpid/client/RdmaConnector.cpp
index 21143a1a75..9b6dcd645d 100644
--- a/cpp/src/qpid/client/RdmaConnector.cpp
+++ b/cpp/src/qpid/client/RdmaConnector.cpp
@@ -109,7 +109,7 @@ class RdmaConnector : public Connector, public sys::Codec
const qpid::sys::SecuritySettings* getSecuritySettings() { return 0; }
size_t decode(const char* buffer, size_t size);
- size_t encode(const char* buffer, size_t size);
+ size_t encode(char* buffer, size_t size);
bool canEncode();
public:
@@ -371,9 +371,9 @@ bool RdmaConnector::canEncode()
return aio->writable() && (lastEof || currentSize >= maxFrameSize);
}
-size_t RdmaConnector::encode(const char* buffer, size_t size)
+size_t RdmaConnector::encode(char* buffer, size_t size)
{
- framing::Buffer out(const_cast<char*>(buffer), size);
+ framing::Buffer out(buffer, size);
size_t bytesWritten(0);
{
Mutex::ScopedLock l(lock);
diff --git a/cpp/src/qpid/client/SessionImpl.cpp b/cpp/src/qpid/client/SessionImpl.cpp
index 91e728d5ae..01e614e041 100644
--- a/cpp/src/qpid/client/SessionImpl.cpp
+++ b/cpp/src/qpid/client/SessionImpl.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
@@ -61,9 +61,7 @@ SessionImpl::SessionImpl(const std::string& name, boost::shared_ptr<ConnectionIm
ioHandler(*this),
proxy(ioHandler),
nextIn(0),
- nextOut(0),
- doClearDeliveryPropertiesExchange(true),
- autoDetach(true)
+ nextOut(0)
{
channel.next = connection.get();
}
@@ -72,12 +70,10 @@ SessionImpl::~SessionImpl() {
{
Lock l(state);
if (state != DETACHED && state != DETACHING) {
- if (autoDetach) {
- QPID_LOG(warning, "Session was not closed cleanly: " << id);
- // Inform broker but don't wait for detached as that deadlocks.
- // The detached will be ignored as the channel will be invalid.
- try { detach(); } catch (...) {} // ignore errors.
- }
+ QPID_LOG(warning, "Session was not closed cleanly: " << id);
+ // Inform broker but don't wait for detached as that deadlocks.
+ // The detached will be ignored as the channel will be invalid.
+ try { detach(); } catch (...) {} // ignore errors.
setState(DETACHED);
handleClosed();
state.waitWaiters();
@@ -136,10 +132,10 @@ void SessionImpl::resume(boost::shared_ptr<ConnectionImpl>) // user thread
void SessionImpl::suspend() //user thread
{
Lock l(state);
- detach();
+ detach();
}
-void SessionImpl::detach() //call with lock held
+void SessionImpl::detach() //call with lock held
{
if (state == ATTACHED) {
setState(DETACHING);
@@ -149,8 +145,8 @@ void SessionImpl::detach() //call with lock held
uint16_t SessionImpl::getChannel() const // user thread
-{
- return channel;
+{
+ return channel;
}
void SessionImpl::setChannel(uint16_t c) // user thread
@@ -182,7 +178,7 @@ void SessionImpl::waitForCompletionImpl(const SequenceNumber& id) //call with lo
bool SessionImpl::isComplete(const SequenceNumber& id)
{
- Lock l(state);
+ Lock l(state);
return !incompleteOut.contains(id);
}
@@ -219,7 +215,7 @@ framing::SequenceNumber SessionImpl::getCompleteUpTo()
return --firstIncomplete;
}
-struct MarkCompleted
+struct MarkCompleted
{
const SequenceNumber& id;
SequenceSet& completedIn;
@@ -230,7 +226,7 @@ struct MarkCompleted
{
if (id >= end) {
completedIn.add(start, end);
- } else if (id >= start) {
+ } else if (id >= start) {
completedIn.add(start, id);
}
}
@@ -244,13 +240,13 @@ void SessionImpl::markCompleted(const SequenceSet& ids, bool notifyPeer)
completedIn.add(ids);
if (notifyPeer) {
sendCompletion();
- }
+ }
}
void SessionImpl::markCompleted(const SequenceNumber& id, bool cumulative, bool notifyPeer)
{
Lock l(state);
- if (cumulative) {
+ if (cumulative) {
//everything in incompleteIn less than or equal to id is now complete
MarkCompleted f(id, completedIn);
incompleteIn.for_each(f);
@@ -260,11 +256,11 @@ void SessionImpl::markCompleted(const SequenceNumber& id, bool cumulative, bool
incompleteIn.remove(completedIn);
} else if (incompleteIn.contains(id)) {
incompleteIn.remove(id);
- completedIn.add(id);
+ completedIn.add(id);
}
if (notifyPeer) {
sendCompletion();
- }
+ }
}
void SessionImpl::setException(const sys::ExceptionHolder& ex) {
@@ -310,42 +306,24 @@ namespace {
struct SendContentFn {
FrameHandler& handler;
void operator()(const AMQFrame& f) {
- if (!f.getMethod())
+ 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()) {}
-
- const AMQHeaderBody& getHeader() const
- {
- return header;
- }
- const std::string& getData() const
- {
- return content;
- }
-};
-
}
-
-Future SessionImpl::send(const AMQBody& command, const FrameSet& content, bool reframe) {
+
+Future SessionImpl::send(const AMQBody& command, const FrameSet& content) {
Acquire a(sendLock);
SequenceNumber id = nextOut++;
{
Lock l(state);
- checkOpen();
+ checkOpen();
incompleteOut.add(id);
}
Future f(id);
- if (command.getMethod()->resultExpected()) {
+ if (command.getMethod()->resultExpected()) {
Lock l(state);
//result listener must be set before the command is sent
f.setFutureResult(results.listenForResult(id));
@@ -353,14 +331,8 @@ Future SessionImpl::send(const AMQBody& command, const FrameSet& content, bool r
AMQFrame frame(command);
frame.setEof(false);
handleOut(frame);
-
- if (reframe) {
- MethodContentAdaptor c(content);
- sendContent(c);
- } else {
- SendContentFn send(out);
- content.map(send);
- }
+ SendContentFn send(out);
+ content.map(send);
return f;
}
@@ -375,11 +347,11 @@ Future SessionImpl::sendCommand(const AMQBody& command, const MethodContent* con
SequenceNumber id = nextOut++;
{
Lock l(state);
- checkOpen();
+ checkOpen();
incompleteOut.add(id);
}
Future f(id);
- if (command.getMethod()->resultExpected()) {
+ if (command.getMethod()->resultExpected()) {
Lock l(state);
//result listener must be set before the command is sent
f.setFutureResult(results.listenForResult(id));
@@ -399,23 +371,13 @@ void SessionImpl::sendContent(const MethodContent& content)
{
AMQFrame header(content.getHeader());
- // doClearDeliveryPropertiesExchange is set by cluster update client so
- // it can send messages with delivery-properties.exchange set.
- //
- if (doClearDeliveryPropertiesExchange) {
- // Normal client is not allowed to set the delivery-properties.exchange
- // so clear it here.
- AMQHeaderBody* headerp = static_cast<AMQHeaderBody*>(header.getBody());
- if (headerp && headerp->get<DeliveryProperties>())
- headerp->get<DeliveryProperties>(true)->clearExchangeFlag();
- }
header.setFirstSegment(false);
uint64_t data_length = content.getData().length();
if(data_length > 0){
header.setLastSegment(false);
- handleOut(header);
+ handleOut(header);
/*Note: end of frame marker included in overhead but not in size*/
- const uint32_t frag_size = maxFrameSize - AMQFrame::frameOverhead();
+ const uint32_t frag_size = maxFrameSize - AMQFrame::frameOverhead();
if(data_length < frag_size){
AMQFrame frame((AMQContentBody(content.getData())));
@@ -442,7 +404,7 @@ void SessionImpl::sendContent(const MethodContent& content)
}
}
} else {
- handleOut(header);
+ handleOut(header);
}
}
@@ -462,7 +424,7 @@ bool isContentFrame(AMQFrame& frame)
{
AMQBody* body = frame.getBody();
uint8_t type = body->type();
- return type == HEADER_BODY || type == CONTENT_BODY || isMessageMethod(body);
+ return type == HEADER_BODY || type == CONTENT_BODY || isMessageMethod(body);
}
void SessionImpl::handleIn(AMQFrame& frame) // network thread
@@ -585,7 +547,7 @@ void SessionImpl::timeout(uint32_t t)
void SessionImpl::commandPoint(const framing::SequenceNumber& id, uint64_t offset)
{
if (offset) throw NotImplementedException("Non-zero byte offset not yet supported for command-point");
-
+
Lock l(state);
nextIn = id;
}
@@ -677,10 +639,10 @@ void SessionImpl::exception(uint16_t errorCode,
{
Lock l(state);
setExceptionLH(createSessionException(errorCode, description));
- QPID_LOG(warning, "Exception received from broker: " << exceptionHolder.what()
+ QPID_LOG(warning, "Exception received from broker: " << exceptionHolder.what()
<< " [caused by " << commandId << " " << classCode << ":" << commandCode << "]");
- if (detachedLifetime)
+ if (detachedLifetime)
setTimeout(0);
}
@@ -748,6 +710,4 @@ 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 4f9213a00a..e6ea8e6b90 100644
--- a/cpp/src/qpid/client/SessionImpl.h
+++ b/cpp/src/qpid/client/SessionImpl.h
@@ -87,15 +87,7 @@ public:
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);
+ QPID_CLIENT_EXTERN Future send(const framing::AMQBody& command, const framing::FrameSet& content);
void sendRawFrame(framing::AMQFrame& frame);
Demux& getDemux();
@@ -125,11 +117,6 @@ public:
*/
boost::shared_ptr<ConnectionImpl> getConnection();
- void setDoClearDeliveryPropertiesExchange(bool b=true) { doClearDeliveryPropertiesExchange = b; }
-
- /** Suppress sending detach in destructor. Used by cluster to build session state */
- void disableAutoDetach();
-
private:
enum State {
INACTIVE,
@@ -225,10 +212,6 @@ private:
SessionState sessionState;
- bool doClearDeliveryPropertiesExchange;
-
- bool autoDetach;
-
friend class client::SessionHandler;
};
diff --git a/cpp/src/qpid/client/SslConnector.cpp b/cpp/src/qpid/client/SslConnector.cpp
index c2081a88f2..11707eb3f7 100644
--- a/cpp/src/qpid/client/SslConnector.cpp
+++ b/cpp/src/qpid/client/SslConnector.cpp
@@ -30,8 +30,9 @@
#include "qpid/framing/AMQFrame.h"
#include "qpid/framing/InitiationHandler.h"
#include "qpid/sys/ssl/util.h"
-#include "qpid/sys/ssl/SslIo.h"
+#include "qpid/sys/AsynchIO.h"
#include "qpid/sys/ssl/SslSocket.h"
+#include "qpid/sys/SocketAddress.h"
#include "qpid/sys/Dispatcher.h"
#include "qpid/sys/Poller.h"
#include "qpid/sys/SecuritySettings.h"
@@ -72,23 +73,28 @@ class SslConnector : public Connector
sys::ssl::SslSocket socket;
- sys::ssl::SslIO* aio;
+ sys::AsynchConnector* connector;
+ sys::AsynchIO* aio;
std::string identifier;
Poller::shared_ptr poller;
SecuritySettings securitySettings;
~SslConnector();
- void readbuff(qpid::sys::ssl::SslIO&, qpid::sys::ssl::SslIOBufferBase*);
- void writebuff(qpid::sys::ssl::SslIO&);
+ void readbuff(AsynchIO&, AsynchIOBufferBase*);
+ void writebuff(AsynchIO&);
void writeDataBlock(const framing::AMQDataBlock& data);
- void eof(qpid::sys::ssl::SslIO&);
- void disconnected(qpid::sys::ssl::SslIO&);
+ void eof(AsynchIO&);
+ void disconnected(AsynchIO&);
void connect(const std::string& host, const std::string& port);
+ void connected(const sys::Socket&);
+ void connectFailed(const std::string& msg);
+
void close();
void send(framing::AMQFrame& frame);
- void abort() {} // TODO: Need to fix for heartbeat timeouts to work
+ void abort();
+ void connectAborted();
void setInputHandler(framing::InputHandler* handler);
void setShutdownHandler(sys::ShutdownHandler* handler);
@@ -96,10 +102,10 @@ class SslConnector : public Connector
framing::OutputHandler* getOutputHandler();
const std::string& getIdentifier() const;
const SecuritySettings* getSecuritySettings();
- void socketClosed(qpid::sys::ssl::SslIO&, const qpid::sys::ssl::SslSocket&);
+ void socketClosed(AsynchIO&, const Socket&);
size_t decode(const char* buffer, size_t size);
- size_t encode(const char* buffer, size_t size);
+ size_t encode(char* buffer, size_t size);
bool canEncode();
public:
@@ -164,32 +170,46 @@ SslConnector::~SslConnector() {
close();
}
-void SslConnector::connect(const std::string& host, const std::string& port){
+void SslConnector::connect(const std::string& host, const std::string& port) {
Mutex::ScopedLock l(lock);
assert(closed);
- try {
- socket.connect(host, port);
- } catch (const std::exception& e) {
- socket.close();
- throw TransportFailure(e.what());
- }
-
+ connector = AsynchConnector::create(
+ socket,
+ host, port,
+ boost::bind(&SslConnector::connected, this, _1),
+ boost::bind(&SslConnector::connectFailed, this, _3));
closed = false;
- aio = new SslIO(socket,
- boost::bind(&SslConnector::readbuff, this, _1, _2),
- boost::bind(&SslConnector::eof, this, _1),
- boost::bind(&SslConnector::disconnected, this, _1),
- boost::bind(&SslConnector::socketClosed, this, _1, _2),
- 0, // nobuffs
- boost::bind(&SslConnector::writebuff, this, _1));
+
+ connector->start(poller);
+}
+
+void SslConnector::connected(const Socket&) {
+ connector = 0;
+ aio = AsynchIO::create(socket,
+ boost::bind(&SslConnector::readbuff, this, _1, _2),
+ boost::bind(&SslConnector::eof, this, _1),
+ boost::bind(&SslConnector::disconnected, this, _1),
+ boost::bind(&SslConnector::socketClosed, this, _1, _2),
+ 0, // nobuffs
+ boost::bind(&SslConnector::writebuff, this, _1));
aio->createBuffers(maxFrameSize);
- identifier = str(format("[%1% %2%]") % socket.getLocalPort() % socket.getPeerAddress());
+ identifier = str(format("[%1%]") % socket.getFullAddress());
ProtocolInitiation init(version);
writeDataBlock(init);
aio->start(poller);
}
+void SslConnector::connectFailed(const std::string& msg) {
+ connector = 0;
+ QPID_LOG(warning, "Connect failed: " << msg);
+ socket.close();
+ if (!closed)
+ closed = true;
+ if (shutdownHandler)
+ shutdownHandler->shutdown();
+}
+
void SslConnector::close() {
Mutex::ScopedLock l(lock);
if (!closed) {
@@ -199,13 +219,31 @@ void SslConnector::close() {
}
}
-void SslConnector::socketClosed(SslIO&, const SslSocket&) {
+void SslConnector::socketClosed(AsynchIO&, const Socket&) {
if (aio)
aio->queueForDeletion();
if (shutdownHandler)
shutdownHandler->shutdown();
}
+void SslConnector::connectAborted() {
+ connector->stop();
+ connectFailed("Connection timedout");
+}
+
+void SslConnector::abort() {
+ // Can't abort a closed connection
+ if (!closed) {
+ if (aio) {
+ // Established connection
+ aio->requestCallback(boost::bind(&SslConnector::eof, this, _1));
+ } else if (connector) {
+ // We're still connecting
+ connector->requestCallback(boost::bind(&SslConnector::connectAborted, this));
+ }
+ }
+}
+
void SslConnector::setInputHandler(InputHandler* handler){
input = handler;
}
@@ -255,7 +293,7 @@ void SslConnector::send(AMQFrame& frame) {
}
}
-void SslConnector::writebuff(SslIO& /*aio*/)
+void SslConnector::writebuff(AsynchIO& /*aio*/)
{
// It's possible to be disconnected and be writable
if (closed)
@@ -265,7 +303,7 @@ void SslConnector::writebuff(SslIO& /*aio*/)
return;
}
- SslIO::BufferBase* buffer = aio->getQueuedBuffer();
+ AsynchIOBufferBase* buffer = aio->getQueuedBuffer();
if (buffer) {
size_t encoded = encode(buffer->bytes, buffer->byteCount);
@@ -285,9 +323,9 @@ bool SslConnector::canEncode()
}
// Called in IO thread.
-size_t SslConnector::encode(const char* buffer, size_t size)
+size_t SslConnector::encode(char* buffer, size_t size)
{
- framing::Buffer out(const_cast<char*>(buffer), size);
+ framing::Buffer out(buffer, size);
size_t bytesWritten(0);
{
Mutex::ScopedLock l(lock);
@@ -304,7 +342,7 @@ size_t SslConnector::encode(const char* buffer, size_t size)
return bytesWritten;
}
-void SslConnector::readbuff(SslIO& aio, SslIO::BufferBase* buff)
+void SslConnector::readbuff(AsynchIO& aio, AsynchIOBufferBase* buff)
{
int32_t decoded = decode(buff->bytes+buff->dataStart, buff->dataCount);
// TODO: unreading needs to go away, and when we can cope
@@ -343,7 +381,7 @@ size_t SslConnector::decode(const char* buffer, size_t size)
}
void SslConnector::writeDataBlock(const AMQDataBlock& data) {
- SslIO::BufferBase* buff = aio->getQueuedBuffer();
+ AsynchIOBufferBase* buff = aio->getQueuedBuffer();
assert(buff);
framing::Buffer out(buff->bytes, buff->byteCount);
data.encode(out);
@@ -351,11 +389,11 @@ void SslConnector::writeDataBlock(const AMQDataBlock& data) {
aio->queueWrite(buff);
}
-void SslConnector::eof(SslIO&) {
+void SslConnector::eof(AsynchIO&) {
close();
}
-void SslConnector::disconnected(SslIO&) {
+void SslConnector::disconnected(AsynchIO&) {
close();
socketClosed(*aio, socket);
}
diff --git a/cpp/src/qpid/client/TCPConnector.cpp b/cpp/src/qpid/client/TCPConnector.cpp
index a5c6465bad..783742764b 100644
--- a/cpp/src/qpid/client/TCPConnector.cpp
+++ b/cpp/src/qpid/client/TCPConnector.cpp
@@ -72,12 +72,13 @@ TCPConnector::TCPConnector(Poller::shared_ptr p,
closed(true),
shutdownHandler(0),
input(0),
+ socket(createSocket()),
connector(0),
aio(0),
poller(p)
{
QPID_LOG(debug, "TCPConnector created for " << version);
- settings.configureSocket(socket);
+ settings.configureSocket(*socket);
}
TCPConnector::~TCPConnector() {
@@ -88,7 +89,7 @@ void TCPConnector::connect(const std::string& host, const std::string& port) {
Mutex::ScopedLock l(lock);
assert(closed);
connector = AsynchConnector::create(
- socket,
+ *socket,
host, port,
boost::bind(&TCPConnector::connected, this, _1),
boost::bind(&TCPConnector::connectFailed, this, _3));
@@ -99,7 +100,7 @@ void TCPConnector::connect(const std::string& host, const std::string& port) {
void TCPConnector::connected(const Socket&) {
connector = 0;
- aio = AsynchIO::create(socket,
+ aio = AsynchIO::create(*socket,
boost::bind(&TCPConnector::readbuff, this, _1, _2),
boost::bind(&TCPConnector::eof, this, _1),
boost::bind(&TCPConnector::disconnected, this, _1),
@@ -116,7 +117,7 @@ void TCPConnector::start(sys::AsynchIO* aio_) {
aio->createBuffers(maxFrameSize);
- identifier = str(format("[%1%]") % socket.getFullAddress());
+ identifier = str(format("[%1%]") % socket->getFullAddress());
}
void TCPConnector::initAmqp() {
@@ -127,7 +128,7 @@ void TCPConnector::initAmqp() {
void TCPConnector::connectFailed(const std::string& msg) {
connector = 0;
QPID_LOG(warning, "Connect failed: " << msg);
- socket.close();
+ socket->close();
if (!closed)
closed = true;
if (shutdownHandler)
@@ -150,6 +151,11 @@ void TCPConnector::socketClosed(AsynchIO&, const Socket&) {
shutdownHandler->shutdown();
}
+void TCPConnector::connectAborted() {
+ connector->stop();
+ connectFailed("Connection timedout");
+}
+
void TCPConnector::abort() {
// Can't abort a closed connection
if (!closed) {
@@ -158,8 +164,7 @@ void TCPConnector::abort() {
aio->requestCallback(boost::bind(&TCPConnector::eof, this, _1));
} else if (connector) {
// We're still connecting
- connector->stop();
- connectFailed("Connection timedout");
+ connector->requestCallback(boost::bind(&TCPConnector::connectAborted, this));
}
}
}
@@ -245,9 +250,9 @@ bool TCPConnector::canEncode()
}
// Called in IO thread.
-size_t TCPConnector::encode(const char* buffer, size_t size)
+size_t TCPConnector::encode(char* buffer, size_t size)
{
- framing::Buffer out(const_cast<char*>(buffer), size);
+ framing::Buffer out(buffer, size);
size_t bytesWritten(0);
{
Mutex::ScopedLock l(lock);
@@ -318,7 +323,7 @@ void TCPConnector::eof(AsynchIO&) {
void TCPConnector::disconnected(AsynchIO&) {
close();
- socketClosed(*aio, socket);
+ socketClosed(*aio, *socket);
}
void TCPConnector::activateSecurityLayer(std::auto_ptr<qpid::sys::SecurityLayer> sl)
diff --git a/cpp/src/qpid/client/TCPConnector.h b/cpp/src/qpid/client/TCPConnector.h
index c0bc26028d..63af3b878a 100644
--- a/cpp/src/qpid/client/TCPConnector.h
+++ b/cpp/src/qpid/client/TCPConnector.h
@@ -35,7 +35,7 @@
#include "qpid/sys/Thread.h"
#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
#include <deque>
#include <string>
@@ -66,7 +66,7 @@ class TCPConnector : public Connector, public sys::Codec
sys::ShutdownHandler* shutdownHandler;
framing::InputHandler* input;
- sys::Socket socket;
+ boost::scoped_ptr<sys::Socket> socket;
sys::AsynchConnector* connector;
sys::AsynchIO* aio;
@@ -80,6 +80,7 @@ class TCPConnector : public Connector, public sys::Codec
void close();
void send(framing::AMQFrame& frame);
void abort();
+ void connectAborted();
void setInputHandler(framing::InputHandler* handler);
void setShutdownHandler(sys::ShutdownHandler* handler);
@@ -90,7 +91,7 @@ class TCPConnector : public Connector, public sys::Codec
const qpid::sys::SecuritySettings* getSecuritySettings() { return 0; }
size_t decode(const char* buffer, size_t size);
- size_t encode(const char* buffer, size_t size);
+ size_t encode(char* buffer, size_t size);
bool canEncode();
protected:
diff --git a/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp b/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
index aaebec0720..f43119ea4c 100644
--- a/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
+++ b/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
@@ -26,6 +26,7 @@
#include "qpid/framing/Uuid.h"
#include "qpid/log/Statement.h"
#include "qpid/Url.h"
+#include "qpid/amqp_0_10/Codecs.h"
#include <boost/intrusive_ptr.hpp>
#include <vector>
#include <sstream>
@@ -156,6 +157,8 @@ void ConnectionImpl::setOption(const std::string& name, const Variant& value)
settings.sslCertName = value.asString();
} else if (name == "x-reconnect-on-limit-exceeded" || name == "x_reconnect_on_limit_exceeded") {
reconnectOnLimitExceeded = value;
+ } else if (name == "client-properties") {
+ amqp_0_10::translate(value.asMap(), settings.clientProperties);
} else {
throw qpid::messaging::MessagingException(QPID_MSG("Invalid option: " << name << " not recognised"));
}
diff --git a/cpp/src/qpid/client/windows/ClientDllMain.cpp b/cpp/src/qpid/client/windows/ClientDllMain.cpp
new file mode 100644
index 0000000000..d636489908
--- /dev/null
+++ b/cpp/src/qpid/client/windows/ClientDllMain.cpp
@@ -0,0 +1,22 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/windows/QpidDllMain.h"
diff --git a/cpp/src/qpid/client/windows/SslConnector.cpp b/cpp/src/qpid/client/windows/SslConnector.cpp
index 2aa31e8202..e1f34e7aea 100644
--- a/cpp/src/qpid/client/windows/SslConnector.cpp
+++ b/cpp/src/qpid/client/windows/SslConnector.cpp
@@ -71,6 +71,8 @@ class SslConnector : public qpid::client::TCPConnector
void redirectReadbuff(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*);
void redirectWritebuff(qpid::sys::AsynchIO&);
void redirectEof(qpid::sys::AsynchIO&);
+ void redirectDisconnect(qpid::sys::AsynchIO&);
+ void redirectSocketClosed(qpid::sys::AsynchIO&, const qpid::sys::Socket&);
public:
SslConnector(boost::shared_ptr<qpid::sys::Poller>,
@@ -79,7 +81,6 @@ public:
ConnectionImpl*);
virtual void connect(const std::string& host, const std::string& port);
virtual void connected(const Socket&);
- unsigned int getSSF();
};
// Static constructor which registers connector here
@@ -124,6 +125,14 @@ void SslConnector::redirectEof(qpid::sys::AsynchIO& a) {
eof(a);
}
+void SslConnector::redirectDisconnect(qpid::sys::AsynchIO& a) {
+ disconnected(a);
+}
+
+void SslConnector::redirectSocketClosed(qpid::sys::AsynchIO& a, const qpid::sys::Socket& s) {
+ socketClosed(a, s);
+}
+
SslConnector::SslConnector(boost::shared_ptr<qpid::sys::Poller> p,
framing::ProtocolVersion ver,
const ConnectionSettings& settings,
@@ -164,8 +173,8 @@ void SslConnector::connected(const Socket& s) {
credHandle,
boost::bind(&SslConnector::redirectReadbuff, this, _1, _2),
boost::bind(&SslConnector::redirectEof, this, _1),
- boost::bind(&SslConnector::redirectEof, this, _1),
- 0, // closed
+ boost::bind(&SslConnector::redirectDisconnect, this, _1),
+ boost::bind(&SslConnector::redirectSocketClosed, this, _1, _2),
0, // nobuffs
boost::bind(&SslConnector::redirectWritebuff, this, _1),
boost::bind(&SslConnector::negotiationDone, this, _1));
@@ -173,9 +182,4 @@ void SslConnector::connected(const Socket& s) {
shim->start(poller);
}
-unsigned int SslConnector::getSSF()
-{
- return shim->getSslKeySize();
-}
-
}}} // namespace qpid::client::windows
diff --git a/cpp/src/qpid/framing/FieldValue.cpp b/cpp/src/qpid/framing/FieldValue.cpp
index ce5a50117c..4abed0f77f 100644
--- a/cpp/src/qpid/framing/FieldValue.cpp
+++ b/cpp/src/qpid/framing/FieldValue.cpp
@@ -23,6 +23,7 @@
#include "qpid/framing/Buffer.h"
#include "qpid/framing/Endian.h"
#include "qpid/framing/List.h"
+#include "qpid/framing/Uuid.h"
#include "qpid/framing/reply_exceptions.h"
#include "qpid/Msg.h"
@@ -43,7 +44,9 @@ void FieldValue::setType(uint8_t type)
data.reset(new EncodedValue<List>());
} else if (typeOctet == 0xAA) {
data.reset(new EncodedValue<Array>());
- } else {
+ } else if (typeOctet == 0x48) {
+ data.reset(new UuidData());
+ } else {
uint8_t lenType = typeOctet >> 4;
switch(lenType){
case 0:
@@ -213,9 +216,12 @@ Integer8Value::Integer8Value(int8_t v) :
Integer16Value::Integer16Value(int16_t v) :
FieldValue(0x11, new FixedWidthValue<2>(v))
{}
-UuidValue::UuidValue(const unsigned char* v) :
- FieldValue(0x48, new FixedWidthValue<16>(v))
-{}
+
+UuidData::UuidData() {}
+UuidData::UuidData(const unsigned char* bytes) : FixedWidthValue<16>(bytes) {}
+bool UuidData::convertsToString() const { return true; }
+std::string UuidData::getString() const { return Uuid(rawOctets()).str(); }
+UuidValue::UuidValue(const unsigned char* v) : FieldValue(0x48, new UuidData(v)) {}
void FieldValue::print(std::ostream& out) const {
data->print(out);
diff --git a/cpp/src/qpid/framing/FrameSet.h b/cpp/src/qpid/framing/FrameSet.h
index 3b9f60950b..9640abb7ac 100644
--- a/cpp/src/qpid/framing/FrameSet.h
+++ b/cpp/src/qpid/framing/FrameSet.h
@@ -1,3 +1,5 @@
+#ifndef QPID_FRAMING_FRAMESET_H
+#define QPID_FRAMING_FRAMESET_H
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -18,6 +20,7 @@
* under the License.
*
*/
+
#include <string>
#include "qpid/InlineVector.h"
#include "qpid/framing/amqp_framing.h"
@@ -25,9 +28,6 @@
#include "qpid/framing/SequenceNumber.h"
#include "qpid/CommonImportExport.h"
-#ifndef _FrameSet_
-#define _FrameSet_
-
namespace qpid {
namespace framing {
@@ -117,4 +117,4 @@ public:
}
-#endif
+#endif /*!QPID_FRAMING_FRAMESET_H*/
diff --git a/cpp/src/qpid/framing/Handler.h b/cpp/src/qpid/framing/Handler.h
index fa8db36f49..e0c0e59d09 100644
--- a/cpp/src/qpid/framing/Handler.h
+++ b/cpp/src/qpid/framing/Handler.h
@@ -49,29 +49,12 @@ struct Handler {
* Functor<F>(f) will copy f.
* Functor<F&>(f) will only take a reference to x.
*/
- template <class F> class Functor : public Handler<T> {
- public:
- Functor(F f, Handler<T>* next=0) : Handler<T>(next), functor(f) {}
- void handle(T t) { functor(t); }
- private:
- F functor;
- };
+ template <class F> class Functor;
/** Adapt a member function of X as a Handler.
* Only holds a reference to its target, not a copy.
*/
- template <class X, void (X::*F)(T)>
- class MemFunRef : public Handler<T> {
- public:
- MemFunRef(X& x, Handler<T>* next=0) : Handler(next), target(&x) {}
- void handle(T t) { (target->*F)(t); }
-
- /** Allow calling with -> syntax */
- MemFunRef* operator->() { return this; }
-
- private:
- X* target;
- };
+ template <class X, void (X::*F)(T)> class MemFunRef;
/** Interface for a handler that implements a
* pair of in/out handle operations.
@@ -94,7 +77,29 @@ struct Handler {
};
};
+template <class T>
+template <class F>
+class Handler<T>::Functor : public Handler<T> {
+ public:
+ Functor(F f, Handler<T>* next=0) : Handler<T>(next), functor(f) {}
+ void handle(T t) { functor(t); }
+ private:
+ F functor;
+};
+
+template <class T>
+template <class X, void (X::*F)(T)>
+class Handler<T>::MemFunRef : public Handler<T> {
+ public:
+ MemFunRef(X& x, Handler<T>* next=0) : Handler(next), target(&x) {}
+ void handle(T t) { (target->*F)(t); }
+
+ /** Allow calling with -> syntax */
+ MemFunRef* operator->() { return this; }
+ private:
+ X* target;
+};
}}
#endif /*!QPID_FRAMING_HANDLER_H*/
diff --git a/cpp/src/qpid/framing/ProtocolInitiation.cpp b/cpp/src/qpid/framing/ProtocolInitiation.cpp
index 00ddb55a3b..19cb3f0e3d 100644
--- a/cpp/src/qpid/framing/ProtocolInitiation.cpp
+++ b/cpp/src/qpid/framing/ProtocolInitiation.cpp
@@ -38,10 +38,17 @@ void ProtocolInitiation::encode(Buffer& buffer) const {
buffer.putOctet('M');
buffer.putOctet('Q');
buffer.putOctet('P');
- buffer.putOctet(1);//class
- buffer.putOctet(1);//instance
- buffer.putOctet(version.getMajor());
- buffer.putOctet(version.getMinor());
+ if (version.getMajor() == 1) {
+ buffer.putOctet(version.getProtocol());
+ buffer.putOctet(version.getMajor());
+ buffer.putOctet(version.getMinor());
+ buffer.putOctet(0);//revision
+ } else {
+ buffer.putOctet(1);//class
+ buffer.putOctet(1);//instance
+ buffer.putOctet(version.getMajor());
+ buffer.putOctet(version.getMinor());
+ }
}
bool ProtocolInitiation::decode(Buffer& buffer){
@@ -50,10 +57,18 @@ bool ProtocolInitiation::decode(Buffer& buffer){
buffer.getOctet();//M
buffer.getOctet();//Q
buffer.getOctet();//P
- buffer.getOctet();//class
- buffer.getOctet();//instance
- version.setMajor(buffer.getOctet());
- version.setMinor(buffer.getOctet());
+ uint8_t protocolClass = buffer.getOctet();//class
+ version.setProtocol(protocolClass);
+ if (protocolClass == 1) {
+ //old (pre-1.0) style
+ buffer.getOctet();//instance
+ version.setMajor(buffer.getOctet());
+ version.setMinor(buffer.getOctet());
+ } else {
+ version.setMajor(buffer.getOctet());
+ version.setMinor(buffer.getOctet());
+ buffer.getOctet();//revision
+ }
return true;
}else{
return false;
diff --git a/cpp/src/qpid/framing/ProtocolVersion.cpp b/cpp/src/qpid/framing/ProtocolVersion.cpp
index c63cddb4cc..174fc3e65b 100644
--- a/cpp/src/qpid/framing/ProtocolVersion.cpp
+++ b/cpp/src/qpid/framing/ProtocolVersion.cpp
@@ -27,6 +27,10 @@ const std::string ProtocolVersion::toString() const
{
std::stringstream ss;
ss << major_ << "-" << minor_;
+ if (major_ == 1) {
+ if (protocol_ == SASL) ss << " (SASL)";
+ else if (protocol_ == TLS) ss << " (TLS)";
+ }
return ss.str();
}
@@ -42,3 +46,7 @@ bool ProtocolVersion::operator==(ProtocolVersion p) const
return major_ == p.major_ && minor_ == p.minor_;
}
+uint8_t ProtocolVersion::AMQP(0);
+uint8_t ProtocolVersion::LEGACY_AMQP(1);
+uint8_t ProtocolVersion::TLS(2);
+uint8_t ProtocolVersion::SASL(3);
diff --git a/cpp/src/qpid/ha/AlternateExchangeSetter.h b/cpp/src/qpid/ha/AlternateExchangeSetter.h
index 08690e68bc..2386a01084 100644
--- a/cpp/src/qpid/ha/AlternateExchangeSetter.h
+++ b/cpp/src/qpid/ha/AlternateExchangeSetter.h
@@ -43,12 +43,14 @@ class AlternateExchangeSetter
AlternateExchangeSetter(broker::ExchangeRegistry& er) : exchanges(er) {}
+ /** If altEx is already known, call setter(altEx) now else save for later */
void setAlternate(const std::string& altEx, const SetFunction& setter) {
- broker::Exchange::shared_ptr ex = exchanges.find(altEx);
+ boost::shared_ptr<broker::Exchange> ex = exchanges.find(altEx);
if (ex) setter(ex); // Set immediately.
else setters.insert(Setters::value_type(altEx, setter)); // Save for later.
}
+ /** Add an exchange and call any setters that are waiting for it. */
void addExchange(boost::shared_ptr<broker::Exchange> exchange) {
// Update the setters for this exchange
std::pair<Setters::iterator, Setters::iterator> range = setters.equal_range(exchange->getName());
diff --git a/cpp/src/qpid/ha/Backup.cpp b/cpp/src/qpid/ha/Backup.cpp
index 6852a58b0c..2affc12bf6 100644
--- a/cpp/src/qpid/ha/Backup.cpp
+++ b/cpp/src/qpid/ha/Backup.cpp
@@ -20,9 +20,12 @@
*/
#include "Backup.h"
#include "BrokerReplicator.h"
+#include "ConnectionObserver.h"
#include "HaBroker.h"
+#include "Primary.h"
#include "ReplicatingSubscription.h"
#include "Settings.h"
+#include "StatusCheck.h"
#include "qpid/Url.h"
#include "qpid/amqp_0_10/Codecs.h"
#include "qpid/broker/Bridge.h"
@@ -44,28 +47,38 @@ using namespace framing;
using namespace broker;
using types::Variant;
using std::string;
+using sys::Mutex;
Backup::Backup(HaBroker& hb, const Settings& s) :
- logPrefix("Backup: "), haBroker(hb), broker(hb.getBroker()), settings(s)
+ logPrefix("Backup: "), membership(hb.getMembership()), stopped(false),
+ haBroker(hb), broker(hb.getBroker()), settings(s),
+ statusCheck(
+ new StatusCheck(
+ logPrefix, broker.getLinkHearbeatInterval(), hb.getBrokerInfo()))
{
- // Empty brokerUrl means delay initialization until seBrokertUrl() is called.
- if (!s.brokerUrl.empty()) initialize(Url(s.brokerUrl));
+ // Set link properties to tag outgoing links.
+ framing::FieldTable linkProperties = broker.getLinkClientProperties();
+ linkProperties.setTable(
+ ConnectionObserver::BACKUP_TAG, hb.getBrokerInfo().asFieldTable());
+ broker.setLinkClientProperties(linkProperties);
}
-void Backup::initialize(const Url& brokers) {
- if (brokers.empty()) throw Url::Invalid("HA broker URL is empty");
- QPID_LOG(info, logPrefix << "Connecting to cluster, broker URL: " << brokers);
- string protocol = brokers[0].protocol.empty() ? "tcp" : brokers[0].protocol;
- types::Uuid uuid(true);
- // Declare the link
- std::pair<Link::shared_ptr, bool> result = broker.getLinks().declare(
- broker::QPID_NAME_PREFIX + string("ha.link.") + uuid.str(),
- brokers[0].host, brokers[0].port, protocol,
- false, // durable
- settings.mechanism, settings.username, settings.password,
- false); // no amq.failover - don't want to use client URL.
- {
- sys::Mutex::ScopedLock l(lock);
+void Backup::setBrokerUrl(const Url& brokers) {
+ if (brokers.empty()) return;
+ Mutex::ScopedLock l(lock);
+ if (stopped) return;
+ if (haBroker.getStatus() == JOINING) statusCheck->setUrl(brokers);
+ if (!link) { // Not yet initialized
+ QPID_LOG(info, logPrefix << "Connecting to cluster, broker URL: " << brokers);
+ string protocol = brokers[0].protocol.empty() ? "tcp" : brokers[0].protocol;
+ types::Uuid uuid(true);
+ std::pair<Link::shared_ptr, bool> result;
+ result = broker.getLinks().declare(
+ broker::QPID_NAME_PREFIX + string("ha.link.") + uuid.str(),
+ brokers[0].host, brokers[0].port, protocol,
+ false, // durable
+ settings.mechanism, settings.username, settings.password,
+ false); // no amq.failover - don't want to use client URL.
link = result.first;
replicator.reset(new BrokerReplicator(haBroker, link));
replicator->initialize();
@@ -74,36 +87,57 @@ void Backup::initialize(const Url& brokers) {
link->setUrl(brokers); // Outside the lock, once set link doesn't change.
}
-Backup::~Backup() {
+void Backup::stop(Mutex::ScopedLock&) {
+ if (stopped) return;
+ stopped = true;
+ QPID_LOG(debug, logPrefix << "Leaving backup role.");
if (link) link->close();
- if (replicator.get()) broker.getExchanges().destroy(replicator->getName());
+ if (replicator.get()) {
+ replicator->shutdown();
+ replicator.reset();
+ }
}
-// Called via management.
-void Backup::setBrokerUrl(const Url& url) {
- // Ignore empty URLs seen during start-up for some tests.
- if (url.empty()) return;
- bool linkSet = false;
+Role* Backup::recover(Mutex::ScopedLock&) {
+ BrokerInfo::Set backups;
{
- sys::Mutex::ScopedLock l(lock);
- linkSet = link;
+ Mutex::ScopedLock l(lock);
+ if (stopped) return 0;
+ stop(l); // Stop backup activity before starting primary.
+ QPID_LOG(notice, "Promoting to primary: " << haBroker.getBrokerInfo());
+ // Reset membership before allowing backups to connect.
+ backups = membership.otherBackups();
+ membership.clear();
+ return new Primary(haBroker, backups);
}
- if (linkSet)
- link->setUrl(url); // Outside lock, once set link doesn't change
- else
- initialize(url); // Deferred initialization
}
-void Backup::setStatus(BrokerStatus status) {
- switch (status) {
- case READY:
- QPID_LOG(notice, logPrefix << "Ready to become primary.");
+Role* Backup::promote() {
+ Mutex::ScopedLock l(lock);
+ if (stopped) return 0;
+ switch (haBroker.getStatus()) {
+ case JOINING:
+ if (statusCheck->canPromote()) return recover(l);
+ else {
+ QPID_LOG(error,
+ logPrefix << "Joining active cluster, cannot be promoted.");
+ throw Exception("Joining active cluster, cannot be promoted.");
+ }
break;
case CATCHUP:
- QPID_LOG(notice, logPrefix << "Catching up on primary, cannot be promoted.");
+ QPID_LOG(error, logPrefix << "Still catching up, cannot be promoted.");
+ throw Exception("Still catching up, cannot be promoted.");
+ break;
+ case READY: return recover(l); break;
default:
- assert(0);
+ assert(0); // Not a valid state for the Backup role..
}
+ return 0; // Keep compiler happy
+}
+
+Backup::~Backup() {
+ Mutex::ScopedLock l(lock);
+ stop(l);
}
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/Backup.h b/cpp/src/qpid/ha/Backup.h
index 4f2d5babde..4943ca5e2e 100644
--- a/cpp/src/qpid/ha/Backup.h
+++ b/cpp/src/qpid/ha/Backup.h
@@ -22,6 +22,7 @@
*
*/
+#include "Role.h"
#include "Settings.h"
#include "qpid/Url.h"
#include "qpid/sys/Mutex.h"
@@ -38,30 +39,41 @@ namespace ha {
class Settings;
class BrokerReplicator;
class HaBroker;
+class StatusCheck;
+class Membership;
/**
- * State associated with a backup broker. Manages connections to primary.
+ * Backup role: Manages connections to primary, replicates management events and queue contents.
*
* THREAD SAFE
*/
-class Backup
+class Backup : public Role
{
public:
Backup(HaBroker&, const Settings&);
~Backup();
+
+ std::string getLogPrefix() const { return logPrefix; }
+
void setBrokerUrl(const Url&);
- void setStatus(BrokerStatus);
+
+ Role* promote();
private:
- void initialize(const Url&);
+ void stop(sys::Mutex::ScopedLock&);
+ Role* recover(sys::Mutex::ScopedLock&);
+
std::string logPrefix;
+ Membership& membership;
sys::Mutex lock;
+ bool stopped;
HaBroker& haBroker;
broker::Broker& broker;
Settings settings;
boost::shared_ptr<broker::Link> link;
boost::shared_ptr<BrokerReplicator> replicator;
+ std::auto_ptr<StatusCheck> statusCheck;
};
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/BrokerInfo.cpp b/cpp/src/qpid/ha/BrokerInfo.cpp
index c8bd1a14be..8efed91b17 100644
--- a/cpp/src/qpid/ha/BrokerInfo.cpp
+++ b/cpp/src/qpid/ha/BrokerInfo.cpp
@@ -33,27 +33,22 @@ namespace qpid {
namespace ha {
namespace {
-std::string SYSTEM_ID="system-id";
-std::string HOST_NAME="host-name";
-std::string PORT="port";
-std::string STATUS="status";
+const std::string SYSTEM_ID="system-id";
+const std::string HOST_NAME="host-name";
+const std::string PORT="port";
+const std::string STATUS="status";
}
using types::Uuid;
using types::Variant;
using framing::FieldTable;
-BrokerInfo::BrokerInfo(const std::string& host, uint16_t port_, const types::Uuid& id) :
- hostName(host), port(port_), systemId(id)
-{
- updateLogId();
-}
+BrokerInfo::BrokerInfo() : port(0), status(JOINING) {}
-void BrokerInfo::updateLogId() {
- std::ostringstream o;
- o << hostName << ":" << port;
- logId = o.str();
-}
+BrokerInfo::BrokerInfo(const types::Uuid& id, BrokerStatus s,
+ const std::string& host, uint16_t port_) :
+ hostName(host), port(port_), systemId(id), status(s)
+{}
FieldTable BrokerInfo::asFieldTable() const {
Variant::Map m = asMap();
@@ -91,7 +86,6 @@ void BrokerInfo::assign(const Variant::Map& m) {
hostName = get(m, HOST_NAME).asString();
port = get(m, PORT).asUint16();
status = BrokerStatus(get(m, STATUS).asUint8());
- updateLogId();
}
std::ostream& operator<<(std::ostream& o, const BrokerInfo& b) {
diff --git a/cpp/src/qpid/ha/BrokerInfo.h b/cpp/src/qpid/ha/BrokerInfo.h
index 642f7c1361..40358336b0 100644
--- a/cpp/src/qpid/ha/BrokerInfo.h
+++ b/cpp/src/qpid/ha/BrokerInfo.h
@@ -43,8 +43,9 @@ class BrokerInfo
typedef std::set<BrokerInfo> Set;
typedef std::map<types::Uuid, BrokerInfo> Map;
- BrokerInfo() {}
- BrokerInfo(const std::string& host, uint16_t port_, const types::Uuid& id);
+ BrokerInfo();
+ BrokerInfo(const types::Uuid& id, BrokerStatus,
+ const std::string& host=std::string(), uint16_t port=0);
BrokerInfo(const framing::FieldTable& ft) { assign(ft); }
BrokerInfo(const types::Variant::Map& m) { assign(m); }
@@ -52,7 +53,6 @@ class BrokerInfo
std::string getHostName() const { return hostName; }
BrokerStatus getStatus() const { return status; }
uint16_t getPort() const { return port; }
- std::string getLogId() const { return logId; }
void setStatus(BrokerStatus s) { status = s; }
@@ -66,8 +66,6 @@ class BrokerInfo
bool operator<(const BrokerInfo x) const { return systemId < x.systemId; }
private:
- void updateLogId();
- std::string logId;
std::string hostName;
uint16_t port;
types::Uuid systemId;
diff --git a/cpp/src/qpid/ha/BrokerReplicator.cpp b/cpp/src/qpid/ha/BrokerReplicator.cpp
index 3a3c9c2954..983b976d76 100644
--- a/cpp/src/qpid/ha/BrokerReplicator.cpp
+++ b/cpp/src/qpid/ha/BrokerReplicator.cpp
@@ -23,16 +23,19 @@
#include "QueueReplicator.h"
#include "qpid/broker/Broker.h"
#include "qpid/broker/Connection.h"
+#include "qpid/broker/ConnectionObserver.h"
#include "qpid/broker/Queue.h"
#include "qpid/broker/QueueSettings.h"
#include "qpid/broker/Link.h"
#include "qpid/broker/amqp_0_10/MessageTransfer.h"
#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FieldValue.h"
#include "qpid/log/Statement.h"
#include "qpid/amqp_0_10/Codecs.h"
#include "qpid/broker/SessionHandler.h"
#include "qpid/framing/reply_exceptions.h"
#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/reply_exceptions.h"
#include "qmf/org/apache/qpid/broker/EventBind.h"
#include "qmf/org/apache/qpid/broker/EventUnbind.h"
#include "qmf/org/apache/qpid/broker/EventExchangeDeclare.h"
@@ -41,6 +44,7 @@
#include "qmf/org/apache/qpid/broker/EventQueueDelete.h"
#include "qmf/org/apache/qpid/broker/EventSubscribe.h"
#include "qmf/org/apache/qpid/ha/EventMembersUpdate.h"
+#include <boost/bind.hpp>
#include <algorithm>
#include <sstream>
#include <iostream>
@@ -57,23 +61,25 @@ using qmf::org::apache::qpid::broker::EventQueueDeclare;
using qmf::org::apache::qpid::broker::EventQueueDelete;
using qmf::org::apache::qpid::broker::EventSubscribe;
using qmf::org::apache::qpid::ha::EventMembersUpdate;
+using qpid::broker::amqp_0_10::MessageTransfer;
using namespace framing;
-using std::string;
+using namespace std;
using std::ostream;
using types::Variant;
using namespace broker;
namespace {
-const string QPID_CONFIGURATION_REPLICATOR("qpid.configuration-replicator");
+const string QPID_CONFIGURATION_REPLICATOR("qpid.broker-replicator");
const string CLASS_NAME("_class_name");
const string EVENT("_event");
const string OBJECT_NAME("_object_name");
const string PACKAGE_NAME("_package_name");
const string QUERY_RESPONSE("_query_response");
-const string SCHEMA_ID("_schema_id");
const string VALUES("_values");
+const string SCHEMA_ID("_schema_id");
+const string WHAT("_what");
const string ALTEX("altEx");
const string ALTEXCHANGE("altExchange");
@@ -81,24 +87,27 @@ const string ARGS("args");
const string ARGUMENTS("arguments");
const string AUTODEL("autoDel");
const string AUTODELETE("autoDelete");
-const string EXCL("excl");
-const string EXCLUSIVE("exclusive");
const string BIND("bind");
-const string UNBIND("unbind");
const string BINDING("binding");
+const string BINDING_KEY("bindingKey");
const string CREATED("created");
const string DISP("disp");
+const string DEST("dest");
const string DURABLE("durable");
const string EXCHANGE("exchange");
+const string EXCL("excl");
+const string EXCLUSIVE("exclusive");
const string EXNAME("exName");
const string EXTYPE("exType");
+const string HA_BROKER("habroker");
const string KEY("key");
const string NAME("name");
+const string PARTIAL("partial");
const string QNAME("qName");
const string QUEUE("queue");
const string TYPE("type");
-const string HA_BROKER("habroker");
-const string PARTIAL("partial");
+const string UNBIND("unbind");
+const string CONSUMER_COUNT("consumerCount");
const string AGENT_EVENT_BROKER("agent.ind.event.org_apache_qpid_broker.#");
const string AGENT_EVENT_HA("agent.ind.event.org_apache_qpid_ha.#");
@@ -107,10 +116,6 @@ const string QMF_CONTENT("qmf.content");
const string QMF_DEFAULT_TOPIC("qmf.default.topic");
const string QMF_OPCODE("qmf.opcode");
-const string _WHAT("_what");
-const string _CLASS_NAME("_class_name");
-const string _PACKAGE_NAME("_package_name");
-const string _SCHEMA_ID("_schema_id");
const string OBJECT("OBJECT");
const string ORG_APACHE_QPID_BROKER("org.apache.qpid.broker");
const string ORG_APACHE_QPID_HA("org.apache.qpid.ha");
@@ -118,21 +123,18 @@ const string QMF_DEFAULT_DIRECT("qmf.default.direct");
const string _QUERY_REQUEST("_query_request");
const string BROKER("broker");
const string MEMBERS("members");
-
-template <class T> bool match(Variant::Map& schema) {
- return T::match(schema[CLASS_NAME], schema[PACKAGE_NAME]);
-}
+const string AUTO_DELETE_TIMEOUT("qpid.auto_delete_timeout");
void sendQuery(const string& packageName, const string& className, const string& queueName,
SessionHandler& sessionHandler)
{
framing::AMQP_ServerProxy peer(sessionHandler.out);
Variant::Map request;
- request[_WHAT] = OBJECT;
+ request[WHAT] = OBJECT;
Variant::Map schema;
- schema[_CLASS_NAME] = className;
- schema[_PACKAGE_NAME] = packageName;
- request[_SCHEMA_ID] = schema;
+ schema[CLASS_NAME] = className;
+ schema[PACKAGE_NAME] = packageName;
+ request[SCHEMA_ID] = schema;
AMQFrame method((MessageTransferBody(ProtocolVersion(), QMF_DEFAULT_DIRECT, 0, 0)));
method.setBof(true);
@@ -170,19 +172,144 @@ Variant::Map asMapVoid(const Variant& value) {
}
} // namespace
+// Listens for errors on the bridge session.
+class BrokerReplicator::ErrorListener : public SessionHandler::ErrorListener {
+ public:
+ ErrorListener(const std::string& lp, BrokerReplicator& br) :
+ logPrefix(lp), brokerReplicator(br) {}
+
+ void connectionException(framing::connection::CloseCode, const std::string& msg) {
+ QPID_LOG(error, logPrefix << "Connection error: " << msg);
+ }
+ void channelException(framing::session::DetachCode, const std::string& msg) {
+ QPID_LOG(error, logPrefix << "Channel error: " << msg);
+ }
+ void executionException(framing::execution::ErrorCode, const std::string& msg) {
+ QPID_LOG(error, logPrefix << "Execution error: " << msg);
+ }
+ void detach() {
+ QPID_LOG(debug, logPrefix << "Session detached.");
+ }
+
+ private:
+ std::string logPrefix;
+ BrokerReplicator& brokerReplicator;
+};
+
+class BrokerReplicator::ConnectionObserver : public broker::ConnectionObserver
+{
+ public:
+ ConnectionObserver(BrokerReplicator& br) : brokerReplicator(br) {}
+ virtual void connection(Connection&) {}
+ virtual void opened(Connection&) {}
+
+ virtual void closed(Connection& c) {
+ if (brokerReplicator.link && &c == brokerReplicator.connection)
+ brokerReplicator.disconnected();
+ }
+ virtual void forced(Connection& c, const std::string& /*message*/) { closed(c); }
+ private:
+ BrokerReplicator& brokerReplicator;
+};
+
+/** Keep track of queues or exchanges during the update process to solve 2
+ * problems.
+ *
+ * 1. Once all responses are processed, remove any queues/exchanges
+ * that were not mentioned as they no longer exist on the primary.
+ *
+ * 2. During the update if we see an event for an object we should
+ * ignore any subsequent responses for that object as they are out
+ * of date.
+ */
+class BrokerReplicator::UpdateTracker {
+ public:
+ typedef std::set<std::string> Names;
+ typedef boost::function<void (const std::string&)> CleanFn;
+
+ UpdateTracker(const std::string& type_, // "queue" or "exchange"
+ CleanFn f, const ReplicationTest& rt)
+ : type(type_), cleanFn(f), repTest(rt) {}
+
+ /** Destructor cleans up remaining initial queues. */
+ ~UpdateTracker() {
+ // Don't throw in a destructor.
+ try { for_each(initial.begin(), initial.end(), cleanFn); }
+ catch (const std::exception& e) {
+ QPID_LOG(error, "Error in cleanup of lost objects: " << e.what());
+ }
+ }
+
+ /** Add an exchange name */
+ void addExchange(Exchange::shared_ptr ex) {
+ if (repTest.getLevel(*ex))
+ initial.insert(ex->getName());
+ }
+
+ /** Add a queue name. */
+ void addQueue(Queue::shared_ptr q) {
+ if (repTest.getLevel(*q))
+ initial.insert(q->getName());
+ }
+
+ /** Received an event for name */
+ void event(const std::string& name) {
+ initial.erase(name); // no longer a candidate for deleting
+ events.insert(name); // we have seen an event for this name
+ }
+
+ /** Received a response for name.
+ *@return true if this response should be processed, false if we have
+ *already seen an event for this object.
+ */
+ bool response(const std::string& name) {
+ initial.erase(name); // no longer a candidate for deleting
+ return events.find(name) == events.end(); // true if no event seen yet.
+ }
+
+ private:
+ void clean(const std::string& name) {
+ QPID_LOG(info, "Backup updated, deleting " << type << " " << name);
+ cleanFn(name);
+ }
+
+ std::string type;
+ Names initial, events;
+ CleanFn cleanFn;
+ ReplicationTest repTest;
+};
+
BrokerReplicator::BrokerReplicator(HaBroker& hb, const boost::shared_ptr<Link>& l)
: Exchange(QPID_CONFIGURATION_REPLICATOR),
- logPrefix("Backup: "), replicationTest(hb.getReplicationTest()),
- haBroker(hb), broker(hb.getBroker()), link(l),
+ logPrefix("Backup: "), replicationTest(NONE),
+ haBroker(hb), broker(hb.getBroker()),
+ exchanges(broker.getExchanges()), queues(broker.getQueues()),
+ link(l),
initialized(false),
- alternates(hb.getBroker().getExchanges())
-{}
+ alternates(hb.getBroker().getExchanges()),
+ connection(0)
+{
+ connectionObserver.reset(new ConnectionObserver(*this));
+ broker.getConnectionObservers().add(connectionObserver);
+ framing::FieldTable args = getArgs();
+ args.setString(QPID_REPLICATE, printable(NONE).str());
+ setArgs(args);
+
+ dispatch[EventQueueDeclare::getFullName()] = &BrokerReplicator::doEventQueueDeclare;
+ dispatch[EventQueueDelete::getFullName()] = &BrokerReplicator::doEventQueueDelete;
+ dispatch[EventExchangeDeclare::getFullName()] = &BrokerReplicator::doEventExchangeDeclare;
+ dispatch[EventExchangeDelete::getFullName()] = &BrokerReplicator::doEventExchangeDelete;
+ dispatch[EventBind::getFullName()] = &BrokerReplicator::doEventBind;
+ dispatch[EventUnbind::getFullName()] = &BrokerReplicator::doEventUnbind;
+ dispatch[EventMembersUpdate::getFullName()] = &BrokerReplicator::doEventMembersUpdate;
+ dispatch[EventSubscribe::getFullName()] = &BrokerReplicator::doEventSubscribe;
+}
void BrokerReplicator::initialize() {
// Can't do this in the constructor because we need a shared_ptr to this.
types::Uuid uuid(true);
const std::string name(QPID_CONFIGURATION_REPLICATOR + ".bridge." + uuid.str());
- broker.getLinks().declare(
+ std::pair<Bridge::shared_ptr, bool> result = broker.getLinks().declare(
name, // name for bridge
*link, // parent
false, // durable
@@ -195,21 +322,47 @@ void BrokerReplicator::initialize() {
"", // excludes
false, // dynamic
0, // sync?
- // shared_ptr keeps this in memory until outstanding initializeBridge
+ // shared_ptr keeps this in memory until outstanding connected
// calls are run.
- boost::bind(&BrokerReplicator::initializeBridge, shared_from_this(), _1, _2)
+ boost::bind(&BrokerReplicator::connected, shared_from_this(), _1, _2)
);
+ assert(result.second);
+ result.first->setErrorListener(
+ boost::shared_ptr<ErrorListener>(new ErrorListener(logPrefix, *this)));
}
-BrokerReplicator::~BrokerReplicator() { }
+BrokerReplicator::~BrokerReplicator() { shutdown(); }
+
+namespace {
+void collectQueueReplicators(
+ const boost::shared_ptr<Exchange> ex, set<boost::shared_ptr<QueueReplicator> >& collect)
+{
+ boost::shared_ptr<QueueReplicator> qr(boost::dynamic_pointer_cast<QueueReplicator>(ex));
+ if (qr) collect.insert(qr);
+}
+} // namespace
+
+void BrokerReplicator::shutdown() {
+ // NOTE: this is called in a QMF dispatch thread, not the Link's connection
+ // thread. It's OK to be unlocked because it doesn't use any mutable state,
+ // it only calls thread safe functions objects belonging to the Broker.
+
+ // Unregister with broker objects:
+ if (connectionObserver) {
+ broker.getConnectionObservers().remove(connectionObserver);
+ connectionObserver.reset();
+ }
+ broker.getExchanges().destroy(getName());
+}
// This is called in the connection IO thread when the bridge is started.
-void BrokerReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionHandler) {
+void BrokerReplicator::connected(Bridge& bridge, SessionHandler& sessionHandler) {
// Use the credentials of the outgoing Link connection for creating queues,
// exchanges etc. We know link->getConnection() is non-zero because we are
// being called in the connections thread context.
//
- assert(link->getConnection());
+ connection = link->getConnection();
+ assert(connection);
userId = link->getConnection()->getUserId();
remoteHost = link->getConnection()->getUrl();
@@ -221,6 +374,19 @@ void BrokerReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionH
<< " status:" << printable(haBroker.getStatus()));
initialized = true;
+ exchangeTracker.reset(
+ new UpdateTracker("exchange",
+ boost::bind(&BrokerReplicator::deleteExchange, this, _1),
+ replicationTest));
+ exchanges.eachExchange(
+ boost::bind(&UpdateTracker::addExchange, exchangeTracker.get(), _1));
+
+ queueTracker.reset(
+ new UpdateTracker("queue",
+ boost::bind(&BrokerReplicator::deleteQueue, this, _1, true),
+ replicationTest));
+ queues.eachQueue(boost::bind(&UpdateTracker::addQueue, queueTracker.get(), _1));
+
framing::AMQP_ServerProxy peer(sessionHandler.out);
const qmf::org::apache::qpid::broker::ArgsLinkBridge& args(bridge.getArgs());
@@ -231,9 +397,14 @@ void BrokerReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionH
peer.getExchange().bind(queueName, QMF_DEFAULT_TOPIC, AGENT_EVENT_BROKER, FieldTable());
peer.getExchange().bind(queueName, QMF_DEFAULT_TOPIC, AGENT_EVENT_HA, FieldTable());
//subscribe to the queue
- 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);
+ FieldTable arguments;
+ arguments.setInt(QueueReplicator::QPID_SYNC_FREQUENCY, 1); // FIXME aconway 2012-05-22: optimize?
+ peer.getMessage().subscribe(
+ queueName, args.i_dest, 1/*accept-none*/, 0/*pre-acquired*/,
+ false/*exclusive*/, "", 0, arguments);
+ peer.getMessage().setFlowMode(args.i_dest, 1); // Window
+ peer.getMessage().flow(args.i_dest, 0, haBroker.getSettings().getFlowMessages());
+ peer.getMessage().flow(args.i_dest, 1, haBroker.getSettings().getFlowBytes());
// Issue a query request for queues, exchanges, bindings and the habroker
// using event queue as the reply-to address
@@ -247,12 +418,12 @@ void BrokerReplicator::route(Deliverable& msg) {
// We transition from JOINING->CATCHUP on the first message received from the primary.
// Until now we couldn't be sure if we had a good connection to the primary.
if (haBroker.getStatus() == JOINING) {
- haBroker.setStatus(CATCHUP);
+ haBroker.getMembership().setStatus(CATCHUP);
QPID_LOG(notice, logPrefix << "Connected to primary " << primary);
}
Variant::List list;
try {
- if (!qpid::broker::amqp_0_10::MessageTransfer::isQMFv2(msg.getMessage()))
+ if (!MessageTransfer::isQMFv2(msg.getMessage()))
throw Exception("Unexpected message, not QMF2 event or query response.");
// decode as list
string content = msg.getMessage().getContent();
@@ -264,13 +435,9 @@ void BrokerReplicator::route(Deliverable& msg) {
QPID_LOG(trace, "Broker replicator event: " << map);
Variant::Map& schema = map[SCHEMA_ID].asMap();
Variant::Map& values = map[VALUES].asMap();
- if (match<EventQueueDeclare>(schema)) doEventQueueDeclare(values);
- else if (match<EventQueueDelete>(schema)) doEventQueueDelete(values);
- else if (match<EventExchangeDeclare>(schema)) doEventExchangeDeclare(values);
- else if (match<EventExchangeDelete>(schema)) doEventExchangeDelete(values);
- else if (match<EventBind>(schema)) doEventBind(values);
- else if (match<EventUnbind>(schema)) doEventUnbind(values);
- else if (match<EventMembersUpdate>(schema)) doEventMembersUpdate(values);
+ EventKey key(schema[PACKAGE_NAME], schema[CLASS_NAME]);
+ EventDispatchMap::iterator j = dispatch.find(key);
+ if (j != dispatch.end()) (this->*(j->second))(values);
}
} else if (msg.getMessage().getPropertyAsString(QMF_OPCODE) == QUERY_RESPONSE) {
for (Variant::List::iterator i = list.begin(); i != list.end(); ++i) {
@@ -285,15 +452,21 @@ void BrokerReplicator::route(Deliverable& msg) {
else if (type == BINDING) doResponseBind(values);
else if (type == HA_BROKER) doResponseHaBroker(values);
}
- if (qpid::broker::amqp_0_10::MessageTransfer::isLastQMFResponse(msg.getMessage(), EXCHANGE)) {
- // We have received all of the exchange response.
+ if (MessageTransfer::isLastQMFResponse(msg.getMessage(), EXCHANGE)) {
+ QPID_LOG(debug, logPrefix << "All exchange responses received.")
+ exchangeTracker.reset(); // Clean up exchanges that no longer exist in the primary
alternates.clear();
}
+ if (MessageTransfer::isLastQMFResponse(msg.getMessage(), QUEUE)) {
+ QPID_LOG(debug, logPrefix << "All queue responses received.");
+ queueTracker.reset(); // Clean up queues that no longer exist in the primary
+ }
}
} catch (const std::exception& e) {
- QPID_LOG(critical, logPrefix << "Configuration failed: " << e.what()
- << ": while handling: " << list);
- haBroker.shutdown();
+;
+ haBroker.shutdown(
+ QPID_MSG(logPrefix << "Configuration replication failed: "
+ << e.what() << ": while handling: " << list));
throw;
}
}
@@ -301,31 +474,22 @@ void BrokerReplicator::route(Deliverable& msg) {
void BrokerReplicator::doEventQueueDeclare(Variant::Map& values) {
Variant::Map argsMap = asMapVoid(values[ARGS]);
- bool autoDel = values[AUTODEL].asBool();
- bool excl = values[EXCL].asBool();
- if (values[DISP] == CREATED && replicationTest.isReplicated(CONFIGURATION, argsMap, autoDel, excl)) {
+ if (values[DISP] == CREATED && replicationTest.getLevel(argsMap)) {
string name = values[QNAME].asString();
QueueSettings settings(values[DURABLE].asBool(), values[AUTODEL].asBool());
+ QPID_LOG(debug, logPrefix << "Queue declare event: " << name);
+ if (queueTracker.get()) queueTracker->event(name);
framing::FieldTable args;
qpid::amqp_0_10::translate(argsMap, args);
// If we already have a queue with this name, replace it.
// The queue was definitely created on the primary.
- if (broker.getQueues().find(name)) {
- QPID_LOG(warning, logPrefix << "Replacing exsiting queue: " << name);
- broker.getQueues().destroy(name);
- stopQueueReplicator(name);
+ if (queues.find(name)) {
+ QPID_LOG(warning, logPrefix << "Declare event, replacing exsiting queue: "
+ << name);
+ deleteQueue(name);
}
- settings.populate(args, settings.storeSettings);
- std::pair<boost::shared_ptr<Queue>, bool> result =
- broker.createQueue(
- name,
- settings,
- 0 /*i.e. no owner regardless of exclusivity on master*/,
- values[ALTEX].asString(),
- userId,
- remoteHost);
- assert(result.second); // Should be true since we destroyed existing queue above
- startQueueReplicator(result.first);
+ replicateQueue(name, values[DURABLE].asBool(), values[AUTODEL].asBool(), args,
+ values[ALTEX].asString());
}
}
@@ -333,7 +497,7 @@ boost::shared_ptr<QueueReplicator> BrokerReplicator::findQueueReplicator(
const std::string& qname)
{
string rname = QueueReplicator::replicatorName(qname);
- boost::shared_ptr<broker::Exchange> ex = broker.getExchanges().find(rname);
+ boost::shared_ptr<broker::Exchange> ex = exchanges.find(rname);
return boost::dynamic_pointer_cast<QueueReplicator>(ex);
}
@@ -341,79 +505,85 @@ void BrokerReplicator::doEventQueueDelete(Variant::Map& values) {
// The remote queue has already been deleted so replicator
// sessions may be closed by a "queue deleted" exception.
string name = values[QNAME].asString();
- boost::shared_ptr<Queue> queue = broker.getQueues().find(name);
- if (queue && replicationTest.replicateLevel(queue->getSettings().storeSettings)) {
+ boost::shared_ptr<Queue> queue = queues.find(name);
+ if (queue && replicationTest.getLevel(*queue)) {
QPID_LOG(debug, logPrefix << "Queue delete event: " << name);
- stopQueueReplicator(name);
- broker.deleteQueue(name, userId, remoteHost);
+ if (queueTracker.get()) queueTracker->event(name);
+ deleteQueue(name);
}
}
void BrokerReplicator::doEventExchangeDeclare(Variant::Map& values) {
Variant::Map argsMap(asMapVoid(values[ARGS]));
- if (!replicationTest.replicateLevel(argsMap)) return; // Not a replicated exchange.
- if (values[DISP] == CREATED && replicationTest.replicateLevel(argsMap)) {
+ if (values[DISP] == CREATED && replicationTest.getLevel(argsMap)) {
string name = values[EXNAME].asString();
QPID_LOG(debug, logPrefix << "Exchange declare event: " << name);
+ if (exchangeTracker.get()) exchangeTracker->event(name);
framing::FieldTable args;
qpid::amqp_0_10::translate(argsMap, args);
// If we already have a exchange with this name, replace it.
// The exchange was definitely created on the primary.
- if (broker.getExchanges().find(name)) {
- broker.getExchanges().destroy(name);
- QPID_LOG(warning, logPrefix << "Replaced exsiting exchange: " << name);
+ if (exchanges.find(name)) {
+ deleteExchange(name);
+ QPID_LOG(warning, logPrefix << "Declare event, replacing existing exchange: "
+ << name);
}
- boost::shared_ptr<Exchange> exchange =
- createExchange(name, values[EXTYPE].asString(), values[DURABLE].asBool(), args, values[ALTEX].asString());
- assert(exchange);
+ CreateExchangeResult result = createExchange(
+ name, values[EXTYPE].asString(), values[DURABLE].asBool(), args,
+ values[ALTEX].asString());
+ replicatedExchanges.insert(name);
+ assert(result.second);
}
}
void BrokerReplicator::doEventExchangeDelete(Variant::Map& values) {
string name = values[EXNAME].asString();
- boost::shared_ptr<Exchange> exchange = broker.getExchanges().find(name);
+ boost::shared_ptr<Exchange> exchange = exchanges.find(name);
if (!exchange) {
- QPID_LOG(warning, logPrefix << "Exchange delete event, does not exist: " << name);
- } else if (!replicationTest.replicateLevel(exchange->getArgs())) {
+ QPID_LOG(warning, logPrefix << "Exchange delete event, not found: " << name);
+ } else if (!replicationTest.getLevel(*exchange)) {
QPID_LOG(warning, logPrefix << "Exchange delete event, not replicated: " << name);
} else {
QPID_LOG(debug, logPrefix << "Exchange delete event:" << name);
- broker.deleteExchange(name, userId, remoteHost);
+ if (exchangeTracker.get()) exchangeTracker->event(name);
+ deleteExchange(name);
+ replicatedExchanges.erase(name);
}
}
void BrokerReplicator::doEventBind(Variant::Map& values) {
boost::shared_ptr<Exchange> exchange =
- broker.getExchanges().find(values[EXNAME].asString());
+ exchanges.find(values[EXNAME].asString());
boost::shared_ptr<Queue> queue =
- broker.getQueues().find(values[QNAME].asString());
- // We only replicate binds for a replicated queue to replicated
- // exchange that both exist locally.
- if (exchange && replicationTest.replicateLevel(exchange->getArgs()) &&
- queue && replicationTest.replicateLevel(queue->getSettings().storeSettings))
+ queues.find(values[QNAME].asString());
+ framing::FieldTable args;
+ qpid::amqp_0_10::translate(asMapVoid(values[ARGS]), args);
+ // We only replicate binds for a replicated queue to replicated exchange
+ // that both exist locally. Respect the replication level set in the
+ // bind arguments, but replicate by default.
+ if (exchange && replicationTest.getLevel(*exchange) &&
+ queue && replicationTest.getLevel(*queue) &&
+ ReplicationTest(ALL).getLevel(args))
{
- framing::FieldTable args;
- qpid::amqp_0_10::translate(asMapVoid(values[ARGS]), args);
string key = values[KEY].asString();
QPID_LOG(debug, logPrefix << "Bind event: exchange=" << exchange->getName()
<< " queue=" << queue->getName()
- << " key=" << key);
+ << " key=" << key
+ << " args=" << args);
exchange->bind(queue, key, &args, 0);
}
}
void BrokerReplicator::doEventUnbind(Variant::Map& values) {
boost::shared_ptr<Exchange> exchange =
- broker.getExchanges().find(values[EXNAME].asString());
+ exchanges.find(values[EXNAME].asString());
boost::shared_ptr<Queue> queue =
- broker.getQueues().find(values[QNAME].asString());
+ queues.find(values[QNAME].asString());
// We only replicate unbinds for a replicated queue to replicated
// exchange that both exist locally.
- if (exchange && replicationTest.replicateLevel(exchange->getArgs()) &&
- queue && replicationTest.replicateLevel(queue->getSettings().storeSettings))
+ if (exchange && replicationTest.getLevel(*exchange) &&
+ queue && replicationTest.getLevel(*queue))
{
- framing::FieldTable args;
- qpid::amqp_0_10::translate(asMapVoid(values[ARGS]), args);
string key = values[KEY].asString();
QPID_LOG(debug, logPrefix << "Unbind event: exchange=" << exchange->getName()
<< " queue=" << queue->getName()
@@ -424,7 +594,17 @@ void BrokerReplicator::doEventUnbind(Variant::Map& values) {
void BrokerReplicator::doEventMembersUpdate(Variant::Map& values) {
Variant::List members = values[MEMBERS].asList();
- haBroker.setMembership(members);
+ setMembership(members);
+}
+
+void BrokerReplicator::doEventSubscribe(Variant::Map& values) {
+ // Ignore queue replicator subscriptions.
+ if (QueueReplicator::isReplicatorName(values[DEST].asString())) return;
+ boost::shared_ptr<QueueReplicator> qr = findQueueReplicator(values[QNAME]);
+ if (qr) {
+ qr->setSubscribed();
+ QPID_LOG(debug, logPrefix << "Subscribe event: " << values[QNAME]);
+ }
}
namespace {
@@ -441,40 +621,68 @@ string getAltExchange(const types::Variant& var) {
}
else return string();
}
+
+Variant getHaUuid(const Variant::Map& map) {
+ Variant::Map::const_iterator i = map.find(QPID_HA_UUID);
+ return i == map.end() ? Variant() : i->second;
}
+} // namespace
+
+
void BrokerReplicator::doResponseQueue(Variant::Map& values) {
Variant::Map argsMap(asMapVoid(values[ARGUMENTS]));
- if (!replicationTest.isReplicated(
- CONFIGURATION,
- values[ARGUMENTS].asMap(),
- values[AUTODELETE].asBool(),
- values[EXCLUSIVE].asBool()))
- return;
+ if (!replicationTest.getLevel(argsMap)) return;
string name(values[NAME].asString());
+ if (!queueTracker.get())
+ throw Exception(QPID_MSG("Unexpected queue response: " << values));
+ if (!queueTracker->response(name)) return; // Response is out-of-date
QPID_LOG(debug, logPrefix << "Queue response: " << name);
+ // If we see a queue with the same name as one we have, but not the same UUID,
+ // then replace the one we have.
+ boost::shared_ptr<Queue> queue = queues.find(name);
+ if (queue && getHaUuid(queue->getSettings().original) != getHaUuid(argsMap)) {
+ QPID_LOG(warning, logPrefix << "UUID mismatch, replacing queue: "
+ << name);
+ deleteQueue(name);
+ }
framing::FieldTable args;
qpid::amqp_0_10::translate(argsMap, args);
- boost::shared_ptr<Queue> queue =
- createQueue(name, values[DURABLE].asBool(), values[AUTODELETE].asBool(), args,
- getAltExchange(values[ALTEXCHANGE]));
- // It is normal for the queue to already exist if we are failing over.
- if (queue) startQueueReplicator(queue);
- else QPID_LOG(debug, logPrefix << "Queue already replicated: " << name);
+ boost::shared_ptr<QueueReplicator> qr = replicateQueue(
+ name, values[DURABLE].asBool(), values[AUTODELETE].asBool(), args,
+ getAltExchange(values[ALTEXCHANGE]));
+ if (qr) {
+ Variant::Map::const_iterator i = values.find(CONSUMER_COUNT);
+ if (i != values.end() && isIntegerType(i->second.getType())) {
+ if (i->second.asInt64()) qr->setSubscribed();
+ }
+ }
}
void BrokerReplicator::doResponseExchange(Variant::Map& values) {
Variant::Map argsMap(asMapVoid(values[ARGUMENTS]));
- if (!replicationTest.replicateLevel(argsMap)) return;
+ if (!replicationTest.getLevel(argsMap)) return;
string name = values[NAME].asString();
+ if (!exchangeTracker.get())
+ throw Exception(QPID_MSG("Unexpected exchange response: " << values));
+ if (!exchangeTracker->response(name)) return; // Response is out of date.
QPID_LOG(debug, logPrefix << "Exchange response: " << name);
framing::FieldTable args;
qpid::amqp_0_10::translate(argsMap, args);
- boost::shared_ptr<Exchange> exchange = createExchange(
+ // If we see an exchange with the same name as one we have, but not the same UUID,
+ // then replace the one we have.
+ boost::shared_ptr<Exchange> exchange = exchanges.find(name);
+ if (exchange &&
+ exchange->getArgs().getAsString(QPID_HA_UUID) != args.getAsString(QPID_HA_UUID))
+ {
+ QPID_LOG(warning, logPrefix << "UUID mismatch, replacing exchange: "
+ << name);
+ deleteExchange(name);
+ }
+ CreateExchangeResult result = createExchange(
name, values[TYPE].asString(), values[DURABLE].asBool(), args,
getAltExchange(values[ALTEXCHANGE]));
- // It is normal for the exchange to already exist if we are failing over.
- QPID_LOG_IF(debug, !exchange, logPrefix << "Exchange already replicated: " << name);
+ replicatedExchanges.insert(name);
}
namespace {
@@ -501,19 +709,25 @@ const std::string QUEUE_REF("queueRef");
void BrokerReplicator::doResponseBind(Variant::Map& values) {
std::string exName = getRefName(EXCHANGE_REF_PREFIX, values[EXCHANGE_REF]);
std::string qName = getRefName(QUEUE_REF_PREFIX, values[QUEUE_REF]);
- boost::shared_ptr<Exchange> exchange = broker.getExchanges().find(exName);
- boost::shared_ptr<Queue> queue = broker.getQueues().find(qName);
+ boost::shared_ptr<Exchange> exchange = exchanges.find(exName);
+ boost::shared_ptr<Queue> queue = queues.find(qName);
- // Automatically replicate binding if queue and exchange exist and are replicated
- if (exchange && replicationTest.replicateLevel(exchange->getArgs()) &&
- queue && replicationTest.replicateLevel(queue->getSettings().storeSettings))
+ framing::FieldTable args;
+ qpid::amqp_0_10::translate(asMapVoid(values[ARGUMENTS]), args);
+
+ // Automatically replicate binding if queue and exchange exist and are replicated.
+ // Respect replicate setting in binding args but default to replicated.
+ if (exchange && replicationTest.getLevel(*exchange) &&
+ queue && replicationTest.getLevel(*queue) &&
+ ReplicationTest(ALL).getLevel(args))
{
- string key = values[KEY].asString();
+ string key = values[BINDING_KEY].asString();
QPID_LOG(debug, logPrefix << "Bind response: exchange:" << exName
<< " queue:" << qName
- << " key:" << key);
- framing::FieldTable args;
- qpid::amqp_0_10::translate(asMapVoid(values[ARGUMENTS]), args);
+ << " key:" << key
+ << " args:" << args);
+// framing::FieldTable args;
+// qpid::amqp_0_10::translate(asMapVoid(values[ARGUMENTS]), args);
exchange->bind(queue, key, &args, 0);
}
}
@@ -527,42 +741,65 @@ void BrokerReplicator::doResponseHaBroker(Variant::Map& values) {
try {
QPID_LOG(trace, logPrefix << "HA Broker response: " << values);
ReplicateLevel mine = haBroker.getSettings().replicateDefault.get();
- ReplicateLevel primary = replicationTest.replicateLevel(
- values[REPLICATE_DEFAULT].asString());
+ ReplicateLevel primary = replicationTest.getLevel(values[REPLICATE_DEFAULT].asString());
if (mine != primary)
throw Exception(QPID_MSG("Replicate default on backup (" << mine
<< ") does not match primary (" << primary << ")"));
- haBroker.setMembership(values[MEMBERS].asList());
+ setMembership(values[MEMBERS].asList());
} catch (const std::exception& e) {
- QPID_LOG(critical, logPrefix << "Invalid HA Broker response: " << e.what()
- << ": " << values);
- haBroker.shutdown();
+ haBroker.shutdown(
+ QPID_MSG(logPrefix << "Invalid HA Broker response: " << e.what()
+ << ": " << values));
+
throw;
}
}
-void BrokerReplicator::startQueueReplicator(const boost::shared_ptr<Queue>& queue)
+boost::shared_ptr<QueueReplicator> BrokerReplicator::startQueueReplicator(
+ const boost::shared_ptr<Queue>& queue)
{
- if (replicationTest.replicateLevel(queue->getSettings().storeSettings) == ALL) {
+ if (replicationTest.getLevel(*queue) == ALL) {
boost::shared_ptr<QueueReplicator> qr(
new QueueReplicator(haBroker, queue, link));
- if (!broker.getExchanges().registerExchange(qr))
+ if (!exchanges.registerExchange(qr))
throw Exception(QPID_MSG("Duplicate queue replicator " << qr->getName()));
qr->activate();
+ return qr;
}
+ return boost::shared_ptr<QueueReplicator>();
}
-void BrokerReplicator::stopQueueReplicator(const std::string& name) {
- boost::shared_ptr<QueueReplicator> qr = findQueueReplicator(name);
- if (qr) {
- qr->deactivate();
- // QueueReplicator's bridge is now queued for destruction but may not
- // actually be destroyed.
- broker.getExchanges().destroy(qr->getName());
+void BrokerReplicator::deleteQueue(const std::string& name, bool purge) {
+ Queue::shared_ptr queue = queues.find(name);
+ if (queue) {
+ // Purge before deleting to ensure that we don't reroute any
+ // messages. Any reroutes will be done at the primary and
+ // replicated as normal.
+ if (purge) queue->purge(0, boost::shared_ptr<Exchange>());
+ broker.deleteQueue(name, userId, remoteHost);
+ QPID_LOG(debug, logPrefix << "Queue deleted: " << name);
+ }
+}
+
+void BrokerReplicator::deleteExchange(const std::string& name) {
+ try {
+ boost::shared_ptr<broker::Exchange> exchange = exchanges.find(name);
+ if (!exchange) {
+ QPID_LOG(warning, logPrefix << "Cannot delete exchange, not found: " << name);
+ return;
+ }
+ if (exchange->inUseAsAlternate()) {
+ QPID_LOG(warning, "Cannot delete exchange, in use as alternate: " << name);
+ return;
+ }
+ broker.deleteExchange(name, userId, remoteHost);
+ QPID_LOG(debug, logPrefix << "Exchange deleted: " << name);
+ } catch (const framing::NotFoundException&) {
+ QPID_LOG(debug, logPrefix << "Exchange not found for deletion: " << name);
}
}
-boost::shared_ptr<Queue> BrokerReplicator::createQueue(
+boost::shared_ptr<QueueReplicator> BrokerReplicator::replicateQueue(
const std::string& name,
bool durable,
bool autodelete,
@@ -571,7 +808,7 @@ boost::shared_ptr<Queue> BrokerReplicator::createQueue(
{
QueueSettings settings(durable, autodelete);
settings.populate(arguments, settings.storeSettings);
- std::pair<boost::shared_ptr<Queue>, bool> result =
+ CreateQueueResult result =
broker.createQueue(
name,
settings,
@@ -579,24 +816,23 @@ boost::shared_ptr<Queue> BrokerReplicator::createQueue(
string(), // Set alternate exchange below
userId,
remoteHost);
- if (result.second) {
- if (!alternateExchange.empty()) {
- alternates.setAlternate(
- alternateExchange, boost::bind(&Queue::setAlternateExchange, result.first, _1));
- }
- return result.first;
+ boost::shared_ptr<QueueReplicator> qr;
+ if (!findQueueReplicator(name)) qr = startQueueReplicator(result.first);
+ if (result.second && !alternateExchange.empty()) {
+ alternates.setAlternate(
+ alternateExchange, boost::bind(&Queue::setAlternateExchange, result.first, _1));
}
- else return boost::shared_ptr<Queue>();
+ return qr;
}
-boost::shared_ptr<Exchange> BrokerReplicator::createExchange(
+BrokerReplicator::CreateExchangeResult BrokerReplicator::createExchange(
const std::string& name,
const std::string& type,
bool durable,
const qpid::framing::FieldTable& args,
const std::string& alternateExchange)
{
- std::pair<boost::shared_ptr<Exchange>, bool> result =
+ CreateExchangeResult result =
broker.createExchange(
name,
type,
@@ -605,15 +841,12 @@ boost::shared_ptr<Exchange> BrokerReplicator::createExchange(
args,
userId,
remoteHost);
- if (result.second) {
- alternates.addExchange(result.first);
- if (!alternateExchange.empty()) {
- alternates.setAlternate(
- alternateExchange, boost::bind(&Exchange::setAlternate, result.first, _1));
- }
- return result.first;
+ alternates.addExchange(result.first);
+ if (!alternateExchange.empty()) {
+ alternates.setAlternate(
+ alternateExchange, boost::bind(&Exchange::setAlternate, result.first, _1));
}
- else return boost::shared_ptr<Exchange>();
+ return result;
}
bool BrokerReplicator::bind(boost::shared_ptr<Queue>, const string&, const framing::FieldTable*, qpid::broker::AsyncStore* const) { return false; }
@@ -626,4 +859,61 @@ void BrokerReplicator::write(char* /*target*/) {}
string BrokerReplicator::getType() const { return QPID_CONFIGURATION_REPLICATOR; }
+void BrokerReplicator::autoDeleteCheck(boost::shared_ptr<Exchange> ex) {
+ boost::shared_ptr<QueueReplicator> qr(boost::dynamic_pointer_cast<QueueReplicator>(ex));
+ if (!qr) return;
+ assert(qr);
+ if (qr->getQueue()->isAutoDelete() && qr->isSubscribed()) {
+ if (qr->getQueue()->getSettings().autoDeleteDelay) {
+ // Start the auto-delete timer
+ Queue::tryAutoDelete(broker, qr->getQueue(), remoteHost, userId);
+ }
+ else {
+ // Delete immediately. Don't purge, the primary is gone so we need
+ // to reroute the deleted messages.
+ deleteQueue(qr->getQueue()->getName(), false);
+ }
+ }
+}
+
+// Callback function for accumulating exchange candidates
+namespace {
+ void exchangeAccumulatorCallback(vector<boost::shared_ptr<Exchange> >& c, const Exchange::shared_ptr& i) {
+ c.push_back(i);
+ }
+}
+
+void BrokerReplicator::disconnected() {
+ QPID_LOG(info, logPrefix << "Disconnected from " << primary);
+ connection = 0;
+ // Clean up auto-delete queues
+ vector<boost::shared_ptr<Exchange> > collect;
+ // Make a copy so we can work outside the ExchangeRegistry lock
+ exchanges.eachExchange(
+ boost::bind(&exchangeAccumulatorCallback, boost::ref(collect), _1));
+ for_each(collect.begin(), collect.end(),
+ boost::bind(&BrokerReplicator::autoDeleteCheck, this, _1));
+}
+
+void BrokerReplicator::setMembership(const Variant::List& brokers) {
+ Membership& membership(haBroker.getMembership());
+ membership.assign(brokers);
+ // Check if the primary has signalled a change in my status:
+ // from CATCHUP to READY when we are caught up.
+ // from READY TO CATCHUP if we are timed out during fail-over.
+ BrokerInfo info;
+ if (membership.get(membership.getSelf(), info)) {
+ BrokerStatus oldStatus = haBroker.getStatus();
+ BrokerStatus newStatus = info.getStatus();
+ if (oldStatus == CATCHUP && newStatus == READY) {
+ QPID_LOG(info, logPrefix << logPrefix << "Caught-up and ready");
+ haBroker.getMembership().setStatus(READY);
+ }
+ else if (oldStatus == READY && newStatus == CATCHUP) {
+ QPID_LOG(info, logPrefix << logPrefix << "No longer ready, catching up");
+ haBroker.getMembership().setStatus(CATCHUP);
+ }
+ }
+}
+
}} // namespace broker
diff --git a/cpp/src/qpid/ha/BrokerReplicator.h b/cpp/src/qpid/ha/BrokerReplicator.h
index f6983e8719..a42d607263 100644
--- a/cpp/src/qpid/ha/BrokerReplicator.h
+++ b/cpp/src/qpid/ha/BrokerReplicator.h
@@ -31,6 +31,7 @@
#include "qpid/management/ManagementObject.h"
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
+#include <set>
namespace qpid {
@@ -40,6 +41,9 @@ class Broker;
class Link;
class Bridge;
class SessionHandler;
+class Connection;
+class QueueRegistry;
+class ExchangeRegistry;
}
namespace framing {
@@ -58,7 +62,9 @@ class QueueReplicator;
* exchanges and bindings to replicate the primary.
* It also creates QueueReplicators for newly replicated queues.
*
- * THREAD UNSAFE: Only called in Link connection thread, no need for locking.
+ * THREAD UNSAFE:
+ * All members except shutdown are only called in the Link's connection thread context.
+ * shutdown() does not use any mutable state.
*
*/
class BrokerReplicator : public broker::Exchange,
@@ -76,6 +82,7 @@ class BrokerReplicator : public broker::Exchange,
bool unbind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*, qpid::broker::AsyncStore* const store);
void route(broker::Deliverable&);
bool isBound(boost::shared_ptr<broker::Queue>, const std::string* const, const framing::FieldTable* const);
+ void shutdown();
// DataSource interface - used to write persistence data to async store
uint64_t getSize();
@@ -83,8 +90,20 @@ class BrokerReplicator : public broker::Exchange,
private:
typedef boost::shared_ptr<QueueReplicator> QueueReplicatorPtr;
+ typedef std::pair<boost::shared_ptr<broker::Queue>, bool> CreateQueueResult;
+ typedef std::pair<boost::shared_ptr<broker::Exchange>, bool> CreateExchangeResult;
- void initializeBridge(broker::Bridge&, broker::SessionHandler&);
+ typedef std::pair<std::string,std::string> EventKey;
+ typedef void (BrokerReplicator::*DispatchFunction)(types::Variant::Map&);
+ typedef std::map<EventKey, DispatchFunction> EventDispatchMap;
+
+ typedef std::map<std::string, QueueReplicatorPtr> QueueReplicatorMap;
+
+ class UpdateTracker;
+ class ErrorListener;
+ class ConnectionObserver;
+
+ void connected(broker::Bridge&, broker::SessionHandler&);
void doEventQueueDeclare(types::Variant::Map& values);
void doEventQueueDelete(types::Variant::Map& values);
@@ -93,6 +112,7 @@ class BrokerReplicator : public broker::Exchange,
void doEventBind(types::Variant::Map&);
void doEventUnbind(types::Variant::Map&);
void doEventMembersUpdate(types::Variant::Map&);
+ void doEventSubscribe(types::Variant::Map&);
void doResponseQueue(types::Variant::Map& values);
void doResponseExchange(types::Variant::Map& values);
@@ -100,32 +120,50 @@ class BrokerReplicator : public broker::Exchange,
void doResponseHaBroker(types::Variant::Map& values);
QueueReplicatorPtr findQueueReplicator(const std::string& qname);
- void startQueueReplicator(const boost::shared_ptr<broker::Queue>&);
- void stopQueueReplicator(const std::string& name);
+ QueueReplicatorPtr startQueueReplicator(const boost::shared_ptr<broker::Queue>&);
- boost::shared_ptr<broker::Queue> createQueue(
+ QueueReplicatorPtr replicateQueue(
const std::string& name,
bool durable,
bool autodelete,
const qpid::framing::FieldTable& arguments,
const std::string& alternateExchange);
- boost::shared_ptr<broker::Exchange> createExchange(
+ CreateExchangeResult createExchange(
const std::string& name,
const std::string& type,
bool durable,
const qpid::framing::FieldTable& args,
const std::string& alternateExchange);
+ bool deactivate(boost::shared_ptr<broker::Exchange> ex, bool destroy);
+ void deleteQueue(const std::string& name, bool purge=true);
+ void deleteExchange(const std::string& name);
+
+ void autoDeleteCheck(boost::shared_ptr<broker::Exchange>);
+
+ void disconnected();
+
+ void setMembership(const types::Variant::List&); // Set membership from list.
+
std::string logPrefix;
- std::string userId, remoteHost;
ReplicationTest replicationTest;
+ std::string userId, remoteHost;
HaBroker& haBroker;
broker::Broker& broker;
+ broker::ExchangeRegistry& exchanges;
+ broker::QueueRegistry& queues;
boost::shared_ptr<broker::Link> link;
bool initialized;
AlternateExchangeSetter alternates;
qpid::Address primary;
+ typedef std::set<std::string> StringSet;
+ StringSet replicatedExchanges; // exchanges that have been replicated.
+ broker::Connection* connection;
+ EventDispatchMap dispatch;
+ std::auto_ptr<UpdateTracker> queueTracker;
+ std::auto_ptr<UpdateTracker> exchangeTracker;
+ boost::shared_ptr<ConnectionObserver> connectionObserver;
};
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/ha/ConnectionObserver.cpp b/cpp/src/qpid/ha/ConnectionObserver.cpp
index 81ba3e4301..775222efd3 100644
--- a/cpp/src/qpid/ha/ConnectionObserver.cpp
+++ b/cpp/src/qpid/ha/ConnectionObserver.cpp
@@ -30,7 +30,7 @@ namespace qpid {
namespace ha {
ConnectionObserver::ConnectionObserver(HaBroker& hb, const types::Uuid& uuid)
- : haBroker(hb), logPrefix("Connections: "), self(uuid) {}
+ : haBroker(hb), logPrefix("Backup: "), self(uuid) {}
bool ConnectionObserver::getBrokerInfo(const broker::Connection& connection, BrokerInfo& info) {
framing::FieldTable ft;
@@ -41,9 +41,11 @@ bool ConnectionObserver::getBrokerInfo(const broker::Connection& connection, Bro
return false;
}
-void ConnectionObserver::setObserver(const ObserverPtr& o){
+void ConnectionObserver::setObserver(const ObserverPtr& o, const std::string& newlogPrefix)
+{
sys::Mutex::ScopedLock l(lock);
observer = o;
+ logPrefix = newlogPrefix;
}
ConnectionObserver::ObserverPtr ConnectionObserver::getObserver() {
diff --git a/cpp/src/qpid/ha/ConnectionObserver.h b/cpp/src/qpid/ha/ConnectionObserver.h
index e3a6d1154a..5374660dbe 100644
--- a/cpp/src/qpid/ha/ConnectionObserver.h
+++ b/cpp/src/qpid/ha/ConnectionObserver.h
@@ -55,7 +55,7 @@ class ConnectionObserver : public broker::ConnectionObserver
ConnectionObserver(HaBroker& haBroker, const types::Uuid& self);
- void setObserver(const ObserverPtr&);
+ void setObserver(const ObserverPtr&, const std::string& logPrefix);
ObserverPtr getObserver();
void opened(broker::Connection& connection);
diff --git a/cpp/src/qpid/ha/HaBroker.cpp b/cpp/src/qpid/ha/HaBroker.cpp
index ffbcb684bc..590db7efa5 100644
--- a/cpp/src/qpid/ha/HaBroker.cpp
+++ b/cpp/src/qpid/ha/HaBroker.cpp
@@ -26,6 +26,7 @@
#include "QueueReplicator.h"
#include "ReplicatingSubscription.h"
#include "Settings.h"
+#include "StandAlone.h"
#include "qpid/amqp_0_10/Codecs.h"
#include "qpid/Exception.h"
#include "qpid/broker/Broker.h"
@@ -41,7 +42,6 @@
#include "qmf/org/apache/qpid/ha/ArgsHaBrokerReplicate.h"
#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetBrokersUrl.h"
#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetPublicUrl.h"
-#include "qmf/org/apache/qpid/ha/EventMembersUpdate.h"
#include "qpid/log/Statement.h"
#include <boost/shared_ptr.hpp>
@@ -54,134 +54,100 @@ using namespace std;
using types::Variant;
using types::Uuid;
using sys::Mutex;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
// Called in Plugin::earlyInitialize
HaBroker::HaBroker(broker::Broker& b, const Settings& s)
- : logPrefix("Broker: "),
- broker(b),
- systemId(broker.getSystem()->getSystemId().data()),
+ : systemId(b.getSystem()->getSystemId().data()),
settings(s),
+ broker(b),
observer(new ConnectionObserver(*this, systemId)),
- mgmtObject(0),
- status(STANDALONE),
- membership(systemId),
- replicationTest(s.replicateDefault.get())
+ role(new StandAlone),
+ membership(BrokerInfo(systemId, STANDALONE), *this)
{
// If we are joining a cluster we must start excluding clients now,
// otherwise there's a window for a client to connect before we get to
// initialize()
if (settings.cluster) {
- QPID_LOG(debug, logPrefix << "Rejecting client connections.");
- observer->setObserver(boost::shared_ptr<broker::ConnectionObserver>(
- new BackupConnectionExcluder));
+ QPID_LOG(debug, "Broker startup, rejecting client connections.");
+ shared_ptr<broker::ConnectionObserver> excluder(new BackupConnectionExcluder);
+ observer->setObserver(excluder, "Backup: ");
broker.getConnectionObservers().add(observer);
}
}
+namespace {
+const std::string NONE("none");
+bool isNone(const std::string& x) { return x.empty() || x == NONE; }
+}
+
// Called in Plugin::initialize
void HaBroker::initialize() {
-
// FIXME aconway 2012-07-19: assumes there's a TCP transport with a meaningful port.
- brokerInfo = BrokerInfo(
- broker.getSystem()->getNodeName(),
- broker.getPort(broker::Broker::TCP_TRANSPORT),
- systemId);
-
- QPID_LOG(notice, logPrefix << "Initializing: " << brokerInfo);
+ membership.add(
+ BrokerInfo(
+ membership.getSelf(),
+ settings.cluster ? JOINING : membership.getStatus(),
+ broker.getSystem()->getNodeName(),
+ broker.getPort(broker::Broker::TCP_TRANSPORT)
+ )
+ );
+ QPID_LOG(notice, "Initializing: " << membership.getInfo());
// Set up the management object.
ManagementAgent* ma = broker.getManagementAgent();
if (settings.cluster && !ma)
throw Exception("Cannot start HA: management is disabled");
_qmf::Package packageInit(ma);
- mgmtObject = new _qmf::HaBroker(ma, this, "ha-broker");
+ mgmtObject = _qmf::HaBroker::shared_ptr(new _qmf::HaBroker(ma, this, "ha-broker"));
mgmtObject->set_replicateDefault(settings.replicateDefault.str());
mgmtObject->set_systemId(systemId);
ma->addObject(mgmtObject);
+ membership.setMgmtObject(mgmtObject);
// Register a factory for replicating subscriptions.
broker.getConsumerFactories().add(
- boost::shared_ptr<ReplicatingSubscription::Factory>(
+ shared_ptr<ReplicatingSubscription::Factory>(
new ReplicatingSubscription::Factory()));
// If we are in a cluster, start as backup in joining state.
if (settings.cluster) {
- status = JOINING;
- backup.reset(new Backup(*this, settings));
+ assert(membership.getStatus() == JOINING);
+ role.reset(new Backup(*this, settings));
broker.getKnownBrokers = boost::bind(&HaBroker::getKnownBrokers, this);
+ if (!isNone(settings.publicUrl)) setPublicUrl(Url(settings.publicUrl));
+ if (!isNone(settings.brokerUrl)) setBrokerUrl(Url(settings.brokerUrl));
}
-
- if (!settings.clientUrl.empty()) setClientUrl(Url(settings.clientUrl));
- if (!settings.brokerUrl.empty()) setBrokerUrl(Url(settings.brokerUrl));
-
-
- // NOTE: lock is not needed in a constructor, but create one
- // to pass to functions that have a ScopedLock parameter.
- Mutex::ScopedLock l(lock);
- statusChanged(l);
}
HaBroker::~HaBroker() {
- QPID_LOG(notice, logPrefix << "Shut down: " << brokerInfo);
+ QPID_LOG(notice, role->getLogPrefix() << "Shut down");
broker.getConnectionObservers().remove(observer);
}
-void HaBroker::recover() {
- auto_ptr<Backup> b;
- {
- Mutex::ScopedLock l(lock);
- // No longer replicating, close link. Note: link must be closed before we
- // setStatus(RECOVERING) as that will remove our broker info from the
- // outgoing link properties so we won't recognize self-connects.
- b = backup;
- }
- b.reset(); // Call destructor outside of lock.
- BrokerInfo::Set backups;
- {
- Mutex::ScopedLock l(lock);
- setStatus(RECOVERING, l);
- backups = membership.otherBackups();
- membership.reset(brokerInfo);
- // Drop the lock, new Primary may call back on activate.
- }
- // Outside of lock, may call back on activate()
- primary.reset(new Primary(*this, backups)); // Starts primary-ready check.
-}
-
-// Called back from Primary active check.
-void HaBroker::activate() { setStatus(ACTIVE); }
-
Manageable::status_t HaBroker::ManagementMethod (uint32_t methodId, Args& args, string&) {
switch (methodId) {
case _qmf::HaBroker::METHOD_PROMOTE: {
- switch (getStatus()) {
- case JOINING: recover(); break;
- case CATCHUP:
- QPID_LOG(error, logPrefix << "Still catching up, cannot be promoted.");
- throw Exception("Still catching up, cannot be promoted.");
- break;
- case READY: recover(); break;
- case RECOVERING: break;
- case ACTIVE: break;
- case STANDALONE: break;
- }
- break;
+ Role* r = role->promote();
+ if (r) role.reset(r);
+ break;
}
case _qmf::HaBroker::METHOD_SETBROKERSURL: {
setBrokerUrl(Url(dynamic_cast<_qmf::ArgsHaBrokerSetBrokersUrl&>(args).i_url));
break;
}
case _qmf::HaBroker::METHOD_SETPUBLICURL: {
- setClientUrl(Url(dynamic_cast<_qmf::ArgsHaBrokerSetPublicUrl&>(args).i_url));
+ setPublicUrl(Url(dynamic_cast<_qmf::ArgsHaBrokerSetPublicUrl&>(args).i_url));
break;
}
case _qmf::HaBroker::METHOD_REPLICATE: {
_qmf::ArgsHaBrokerReplicate& bq_args =
dynamic_cast<_qmf::ArgsHaBrokerReplicate&>(args);
- QPID_LOG(debug, logPrefix << "Replicate individual queue "
+ QPID_LOG(debug, role->getLogPrefix() << "Replicate individual queue "
<< bq_args.i_queue << " from " << bq_args.i_broker);
- boost::shared_ptr<broker::Queue> queue = broker.getQueues().get(bq_args.i_queue);
+ shared_ptr<broker::Queue> queue = broker.getQueues().get(bq_args.i_queue);
Url url(bq_args.i_broker);
string protocol = url[0].protocol.empty() ? "tcp" : url[0].protocol;
Uuid uuid(true);
@@ -191,10 +157,10 @@ Manageable::status_t HaBroker::ManagementMethod (uint32_t methodId, Args& args,
false, // durable
settings.mechanism, settings.username, settings.password,
false); // no amq.failover - don't want to use client URL.
- boost::shared_ptr<broker::Link> link = result.first;
+ shared_ptr<broker::Link> link = result.first;
link->setUrl(url);
// Create a queue replicator
- boost::shared_ptr<QueueReplicator> qr(
+ shared_ptr<QueueReplicator> qr(
new QueueReplicator(*this, queue, link));
qr->activate();
broker.getExchanges().registerExchange(qr);
@@ -207,31 +173,23 @@ Manageable::status_t HaBroker::ManagementMethod (uint32_t methodId, Args& args,
return Manageable::STATUS_OK;
}
-void HaBroker::setClientUrl(const Url& url) {
+void HaBroker::setPublicUrl(const Url& url) {
Mutex::ScopedLock l(lock);
- if (url.empty()) throw Exception("Invalid empty URL for HA client failover");
- clientUrl = url;
- updateClientUrl(l);
-}
-
-void HaBroker::updateClientUrl(Mutex::ScopedLock&) {
- Url url = clientUrl.empty() ? brokerUrl : clientUrl;
- if (url.empty()) throw Url::Invalid("HA client URL is empty");
+ publicUrl = url;
mgmtObject->set_publicUrl(url.str());
knownBrokers.clear();
knownBrokers.push_back(url);
- QPID_LOG(debug, logPrefix << "Setting client URL to: " << url);
+ QPID_LOG(debug, role->getLogPrefix() << "Setting public URL to: " << url);
}
void HaBroker::setBrokerUrl(const Url& url) {
- Mutex::ScopedLock l(lock);
- if (url.empty()) throw Url::Invalid("HA broker URL is empty");
- brokerUrl = url;
- mgmtObject->set_brokersUrl(brokerUrl.str());
- QPID_LOG(info, logPrefix << "Broker URL set to: " << url);
- if (backup.get()) backup->setBrokerUrl(brokerUrl);
- // Updating broker URL also updates defaulted client URL:
- if (clientUrl.empty()) updateClientUrl(l);
+ {
+ Mutex::ScopedLock l(lock);
+ brokerUrl = url;
+ mgmtObject->set_brokersUrl(brokerUrl.str());
+ QPID_LOG(info, role->getLogPrefix() << "Brokers URL set to: " << url);
+ }
+ role->setBrokerUrl(url); // Oustside lock
}
std::vector<Url> HaBroker::getKnownBrokers() const {
@@ -239,116 +197,14 @@ std::vector<Url> HaBroker::getKnownBrokers() const {
return knownBrokers;
}
-void HaBroker::shutdown() {
- QPID_LOG(critical, logPrefix << "Critical error, shutting down.");
+void HaBroker::shutdown(const std::string& message) {
+ QPID_LOG(critical, message);
broker.shutdown();
+ throw Exception(message);
}
BrokerStatus HaBroker::getStatus() const {
- Mutex::ScopedLock l(lock);
- return status;
-}
-
-void HaBroker::setStatus(BrokerStatus newStatus) {
- Mutex::ScopedLock l(lock);
- setStatus(newStatus, l);
-}
-
-namespace {
-bool checkTransition(BrokerStatus from, BrokerStatus to) {
- // Legal state transitions. Initial state is JOINING, ACTIVE is terminal.
- static const BrokerStatus TRANSITIONS[][2] = {
- { JOINING, CATCHUP }, // Connected to primary
- { JOINING, RECOVERING }, // Chosen as initial primary.
- { CATCHUP, READY }, // Caught up all queues, ready to take over.
- { READY, RECOVERING }, // Chosen as new primary
- { READY, CATCHUP }, // Timed out failing over, demoted to catch-up.
- { RECOVERING, ACTIVE } // All expected backups are ready
- };
- static const size_t N = sizeof(TRANSITIONS)/sizeof(TRANSITIONS[0]);
- for (size_t i = 0; i < N; ++i) {
- if (TRANSITIONS[i][0] == from && TRANSITIONS[i][1] == to)
- return true;
- }
- return false;
-}
-} // namespace
-
-void HaBroker::setStatus(BrokerStatus newStatus, Mutex::ScopedLock& l) {
- QPID_LOG(info, logPrefix << "Status change: "
- << printable(status) << " -> " << printable(newStatus));
- bool legal = checkTransition(status, newStatus);
- assert(legal);
- if (!legal) {
- QPID_LOG(critical, logPrefix << "Illegal state transition: "
- << printable(status) << " -> " << printable(newStatus));
- shutdown();
- }
- status = newStatus;
- statusChanged(l);
-}
-
-void HaBroker::statusChanged(Mutex::ScopedLock& l) {
- mgmtObject->set_status(printable(status).str());
- brokerInfo.setStatus(status);
- setLinkProperties(l);
-}
-
-void HaBroker::membershipUpdated(Mutex::ScopedLock&) {
- QPID_LOG(info, logPrefix << "Membership changed: " << membership);
- Variant::List brokers = membership.asList();
- mgmtObject->set_members(brokers);
- broker.getManagementAgent()->raiseEvent(_qmf::EventMembersUpdate(brokers));
-}
-
-void HaBroker::setMembership(const Variant::List& brokers) {
- Mutex::ScopedLock l(lock);
- membership.assign(brokers);
- QPID_LOG(info, logPrefix << "Membership update: " << membership);
- BrokerInfo info;
- // Update my status to what the primary says it is. The primary can toggle
- // status between READY and CATCHUP based on the state of our subscriptions.
- if (membership.get(systemId, info) && status != info.getStatus()) {
- setStatus(info.getStatus(), l);
- if (backup.get()) backup->setStatus(status);
- }
- membershipUpdated(l);
-}
-
-void HaBroker::resetMembership(const BrokerInfo& b) {
- Mutex::ScopedLock l(lock);
- membership.reset(b);
- QPID_LOG(debug, logPrefix << "Membership reset to: " << membership);
- membershipUpdated(l);
-}
-
-void HaBroker::addBroker(const BrokerInfo& b) {
- Mutex::ScopedLock l(lock);
- membership.add(b);
- QPID_LOG(debug, logPrefix << "Membership add: " << b);
- membershipUpdated(l);
-}
-
-void HaBroker::removeBroker(const Uuid& id) {
- Mutex::ScopedLock l(lock);
- membership.remove(id);
- QPID_LOG(debug, logPrefix << "Membership remove: " << id);
- membershipUpdated(l);
-}
-
-void HaBroker::setLinkProperties(Mutex::ScopedLock&) {
- framing::FieldTable linkProperties = broker.getLinkClientProperties();
- if (isBackup(status)) {
- // If this is a backup then any outgoing links are backup
- // links and need to be tagged.
- linkProperties.setTable(ConnectionObserver::BACKUP_TAG, brokerInfo.asFieldTable());
- }
- else {
- // If this is a primary then any outgoing links are federation links
- // and should not be tagged.
- linkProperties.erase(ConnectionObserver::BACKUP_TAG);
- }
- broker.setLinkClientProperties(linkProperties);
+ return membership.getStatus();
}
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/HaBroker.h b/cpp/src/qpid/ha/HaBroker.h
index 7dabe6e35b..6b15c88e0a 100644
--- a/cpp/src/qpid/ha/HaBroker.h
+++ b/cpp/src/qpid/ha/HaBroker.h
@@ -25,14 +25,12 @@
#include "BrokerInfo.h"
#include "Membership.h"
#include "types.h"
-#include "ReplicationTest.h"
#include "Settings.h"
#include "qpid/Url.h"
#include "qpid/sys/Mutex.h"
#include "qmf/org/apache/qpid/ha/HaBroker.h"
#include "qpid/management/Manageable.h"
#include "qpid/types/Variant.h"
-#include <memory>
#include <set>
#include <boost/shared_ptr.hpp>
@@ -54,11 +52,15 @@ namespace ha {
class Backup;
class ConnectionObserver;
class Primary;
-
+class Role;
/**
* HA state and actions associated with a HA broker. Holds all the management info.
*
* THREAD SAFE: may be called in arbitrary broker IO or timer threads.
+
+ * NOTE: HaBroker and Role subclasses follow this lock hierarchy:
+ * - HaBroker MUST NOT hold its own lock across calls Role subclasses.
+ * - Role subclasses MAY hold their locks accross calls to HaBroker.
*/
class HaBroker : public management::Manageable
{
@@ -71,66 +73,46 @@ class HaBroker : public management::Manageable
void initialize();
// Implement Manageable.
- qpid::management::ManagementObject* GetManagementObject() const { return mgmtObject; }
+ qpid::management::ManagementObject::shared_ptr GetManagementObject() const { return mgmtObject; }
management::Manageable::status_t ManagementMethod (
uint32_t methodId, management::Args& args, std::string& text);
broker::Broker& getBroker() { return broker; }
const Settings& getSettings() const { return settings; }
- /** Shut down the broker. Caller should log a critical error message. */
- void shutdown();
+ /** Shut down the broker because of a critical error. */
+ void shutdown(const std::string& message);
BrokerStatus getStatus() const;
- void setStatus(BrokerStatus);
- void activate();
-
- Backup* getBackup() { return backup.get(); }
- ReplicationTest getReplicationTest() const { return replicationTest; }
-
boost::shared_ptr<ConnectionObserver> getObserver() { return observer; }
- const BrokerInfo& getBrokerInfo() const { return brokerInfo; }
-
- void setMembership(const types::Variant::List&); // Set membership from list.
- void resetMembership(const BrokerInfo& b); // Reset to contain just one member.
- void addBroker(const BrokerInfo& b); // Add a broker to the membership.
- void removeBroker(const types::Uuid& id); // Remove a broker from membership.
-
+ BrokerInfo getBrokerInfo() const { return membership.getInfo(); }
+ Membership& getMembership() { return membership; }
types::Uuid getSystemId() const { return systemId; }
private:
- void setClientUrl(const Url&);
+
+ void setPublicUrl(const Url&);
void setBrokerUrl(const Url&);
void updateClientUrl(sys::Mutex::ScopedLock&);
- bool isPrimary(sys::Mutex::ScopedLock&) { return !backup.get(); }
-
- void setStatus(BrokerStatus, sys::Mutex::ScopedLock&);
- void recover();
- void statusChanged(sys::Mutex::ScopedLock&);
- void setLinkProperties(sys::Mutex::ScopedLock&);
-
std::vector<Url> getKnownBrokers() const;
- void membershipUpdated(sys::Mutex::ScopedLock&);
-
- std::string logPrefix;
- broker::Broker& broker;
- types::Uuid systemId;
+ // Immutable members
+ const types::Uuid systemId;
const Settings settings;
+ // Member variables protected by lock
mutable sys::Mutex lock;
- boost::shared_ptr<ConnectionObserver> observer; // Used by Backup and Primary
- std::auto_ptr<Backup> backup;
- std::auto_ptr<Primary> primary;
- qmf::org::apache::qpid::ha::HaBroker* mgmtObject;
- Url clientUrl, brokerUrl;
+ Url publicUrl, brokerUrl;
std::vector<Url> knownBrokers;
- BrokerStatus status;
- BrokerInfo brokerInfo;
+
+ // Independently thread-safe member variables
+ broker::Broker& broker;
+ qmf::org::apache::qpid::ha::HaBroker::shared_ptr mgmtObject;
+ boost::shared_ptr<ConnectionObserver> observer; // Used by Backup and Primary
+ boost::shared_ptr<Role> role;
Membership membership;
- ReplicationTest replicationTest;
};
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/HaPlugin.cpp b/cpp/src/qpid/ha/HaPlugin.cpp
index f7fe553d9b..d26b466847 100644
--- a/cpp/src/qpid/ha/HaPlugin.cpp
+++ b/cpp/src/qpid/ha/HaPlugin.cpp
@@ -33,9 +33,11 @@ struct Options : public qpid::Options {
addOptions()
("ha-cluster", optValue(settings.cluster, "yes|no"),
"Join a HA active/passive cluster.")
+ ("ha-queue-replication", optValue(settings.queueReplication, "yes|no"),
+ "Enable replication of specific queues without joining a cluster")
("ha-brokers-url", optValue(settings.brokerUrl,"URL"),
"URL with address of each broker in the cluster.")
- ("ha-public-url", optValue(settings.clientUrl,"URL"),
+ ("ha-public-url", optValue(settings.publicUrl,"URL"),
"URL advertized to clients to connect to the cluster.")
("ha-replicate",
optValue(settings.replicateDefault, "LEVEL"),
@@ -48,6 +50,10 @@ struct Options : public qpid::Options {
"Authentication mechanism for connections between HA brokers")
("ha-backup-timeout", optValue(settings.backupTimeout, "SECONDS"),
"Maximum time to wait for an expected backup to connect and become ready.")
+ ("ha-flow-messages", optValue(settings.flowMessages, "N"),
+ "Flow control message count limit for replication, 0 means no limit")
+ ("ha-flow-bytes", optValue(settings.flowBytes, "N"),
+ "Flow control byte limit for replication, 0 means no limit")
;
}
};
@@ -64,17 +70,23 @@ struct HaPlugin : public Plugin {
void earlyInitialize(Plugin::Target& target) {
broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
- if (broker) {
- // Must create the HaBroker in earlyInitialize so it can set up its
- // connection observer before clients start connecting.
- haBroker.reset(new ha::HaBroker(*broker, settings));
- broker->addFinalizer(boost::bind(&HaPlugin::finalize, this));
+ if (broker && (settings.cluster || settings.queueReplication)) {
+ if (!broker->getManagementAgent()) {
+ QPID_LOG(info, "HA plugin disabled because management is disabled");
+ if (settings.cluster)
+ throw Exception("Cannot start HA: management is disabled");
+ } else {
+ // Must create the HaBroker in earlyInitialize so it can set up its
+ // connection observer before clients start connecting.
+ haBroker.reset(new ha::HaBroker(*broker, settings));
+ broker->addFinalizer(boost::bind(&HaPlugin::finalize, this));
+ }
}
}
void initialize(Plugin::Target& target) {
broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
- if (broker) haBroker->initialize();
+ if (broker && haBroker.get()) haBroker->initialize();
}
void finalize() {
diff --git a/cpp/src/qpid/ha/Membership.cpp b/cpp/src/qpid/ha/Membership.cpp
index 74580f9b1e..6c64d86fd7 100644
--- a/cpp/src/qpid/ha/Membership.cpp
+++ b/cpp/src/qpid/ha/Membership.cpp
@@ -19,6 +19,12 @@
*
*/
#include "Membership.h"
+#include "HaBroker.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/types/Variant.h"
+#include "qmf/org/apache/qpid/ha/EventMembersUpdate.h"
+#include "qmf/org/apache/qpid/ha/HaBroker.h"
#include <boost/bind.hpp>
#include <iostream>
#include <iterator>
@@ -26,37 +32,58 @@
namespace qpid {
namespace ha {
+namespace _qmf = ::qmf::org::apache::qpid::ha;
-void Membership::reset(const BrokerInfo& b) {
+using sys::Mutex;
+using types::Variant;
+
+Membership::Membership(const BrokerInfo& info, HaBroker& b)
+ : haBroker(b), self(info.getSystemId())
+{
+ brokers[self] = info;
+}
+
+void Membership::clear() {
+ Mutex::ScopedLock l(lock);
+ BrokerInfo me = brokers[self];
brokers.clear();
- brokers[b.getSystemId()] = b;
+ brokers[self] = me;
}
void Membership::add(const BrokerInfo& b) {
+ Mutex::ScopedLock l(lock);
brokers[b.getSystemId()] = b;
+ update(l);
}
void Membership::remove(const types::Uuid& id) {
+ Mutex::ScopedLock l(lock);
+ if (id == self) return; // Never remove myself
BrokerInfo::Map::iterator i = brokers.find(id);
if (i != brokers.end()) {
brokers.erase(i);
- }
+ update(l);
+ }
}
bool Membership::contains(const types::Uuid& id) {
+ Mutex::ScopedLock l(lock);
return brokers.find(id) != brokers.end();
}
void Membership::assign(const types::Variant::List& list) {
- brokers.clear();
+ Mutex::ScopedLock l(lock);
+ clear();
for (types::Variant::List::const_iterator i = list.begin(); i != list.end(); ++i) {
BrokerInfo b(i->asMap());
brokers[b.getSystemId()] = b;
}
+ update(l);
}
types::Variant::List Membership::asList() const {
+ Mutex::ScopedLock l(lock);
types::Variant::List list;
for (BrokerInfo::Map::const_iterator i = brokers.begin(); i != brokers.end(); ++i)
list.push_back(i->second.asMap());
@@ -64,6 +91,7 @@ types::Variant::List Membership::asList() const {
}
BrokerInfo::Set Membership::otherBackups() const {
+ Mutex::ScopedLock l(lock);
BrokerInfo::Set result;
for (BrokerInfo::Map::const_iterator i = brokers.begin(); i != brokers.end(); ++i)
if (i->second.getStatus() == READY && i->second.getSystemId() != self)
@@ -71,15 +99,84 @@ BrokerInfo::Set Membership::otherBackups() const {
return result;
}
-bool Membership::get(const types::Uuid& id, BrokerInfo& result) {
- BrokerInfo::Map::iterator i = brokers.find(id);
+bool Membership::get(const types::Uuid& id, BrokerInfo& result) const {
+ Mutex::ScopedLock l(lock);
+ BrokerInfo::Map::const_iterator i = brokers.find(id);
if (i == brokers.end()) return false;
result = i->second;
return true;
}
-std::ostream& operator<<(std::ostream& o, const Membership& members) {
- return o << members.brokers;
+void Membership::update(Mutex::ScopedLock& l) {
+ QPID_LOG(info, "Membership: " << brokers);
+ Variant::List brokers = asList();
+ if (mgmtObject) mgmtObject->set_status(printable(getStatus(l)).str());
+ if (mgmtObject) mgmtObject->set_members(brokers);
+ haBroker.getBroker().getManagementAgent()->raiseEvent(
+ _qmf::EventMembersUpdate(brokers));
+}
+
+void Membership::setMgmtObject(boost::shared_ptr<_qmf::HaBroker> mo) {
+ Mutex::ScopedLock l(lock);
+ mgmtObject = mo;
+ update(l);
+}
+
+
+namespace {
+bool checkTransition(BrokerStatus from, BrokerStatus to) {
+ // Legal state transitions. Initial state is JOINING, ACTIVE is terminal.
+ static const BrokerStatus TRANSITIONS[][2] = {
+ { STANDALONE, JOINING }, // Initialization of backup broker
+ { JOINING, CATCHUP }, // Connected to primary
+ { JOINING, RECOVERING }, // Chosen as initial primary.
+ { CATCHUP, READY }, // Caught up all queues, ready to take over.
+ { READY, RECOVERING }, // Chosen as new primary
+ { READY, CATCHUP }, // Timed out failing over, demoted to catch-up.
+ { RECOVERING, ACTIVE } // All expected backups are ready
+ };
+ static const size_t N = sizeof(TRANSITIONS)/sizeof(TRANSITIONS[0]);
+ for (size_t i = 0; i < N; ++i) {
+ if (TRANSITIONS[i][0] == from && TRANSITIONS[i][1] == to)
+ return true;
+ }
+ return false;
+}
+} // namespace
+
+void Membership::setStatus(BrokerStatus newStatus) {
+ BrokerStatus status = getStatus();
+ QPID_LOG(info, "Status change: "
+ << printable(status) << " -> " << printable(newStatus));
+ bool legal = checkTransition(status, newStatus);
+ if (!legal) {
+ haBroker.shutdown(QPID_MSG("Illegal state transition: " << printable(status)
+ << " -> " << printable(newStatus)));
+ }
+
+ Mutex::ScopedLock l(lock);
+ brokers[self].setStatus(newStatus);
+ if (mgmtObject) mgmtObject->set_status(printable(newStatus).str());
+ update(l);
+}
+
+BrokerStatus Membership::getStatus() const {
+ Mutex::ScopedLock l(lock);
+ return getStatus(l);
+}
+
+BrokerStatus Membership::getStatus(sys::Mutex::ScopedLock&) const {
+ BrokerInfo::Map::const_iterator i = brokers.find(self);
+ assert(i != brokers.end());
+ return i->second.getStatus();
+}
+
+BrokerInfo Membership::getInfo() const {
+ Mutex::ScopedLock l(lock);
+ BrokerInfo::Map::const_iterator i = brokers.find(self);
+ assert(i != brokers.end());
+ return i->second;
}
+// FIXME aconway 2013-01-23: move to .h?
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/Membership.h b/cpp/src/qpid/ha/Membership.h
index 8406dccd5d..956569fbd8 100644
--- a/cpp/src/qpid/ha/Membership.h
+++ b/cpp/src/qpid/ha/Membership.h
@@ -24,45 +24,72 @@
#include "BrokerInfo.h"
#include "types.h"
-#include "qpid/framing/Uuid.h"
#include "qpid/log/Statement.h"
+#include "qpid/sys/Mutex.h"
#include "qpid/types/Variant.h"
#include <boost/function.hpp>
#include <set>
#include <vector>
#include <iosfwd>
+
+namespace qmf { namespace org { namespace apache { namespace qpid { namespace ha {
+class HaBroker;
+}}}}}
+
namespace qpid {
+
+namespace broker {
+class Broker;
+}
+
+namespace types {
+class Uuid;
+}
+
namespace ha {
+class HaBroker;
/**
* Keep track of the brokers in the membership.
- * THREAD UNSAFE: caller must serialize
+ * Send management when events on membership changes.
+ * THREAD SAFE
*/
class Membership
{
public:
- Membership(const types::Uuid& self_) : self(self_) {}
+ Membership(const BrokerInfo& info, HaBroker&);
- void reset(const BrokerInfo& b); ///< Reset to contain just one member.
+ void setMgmtObject(boost::shared_ptr<qmf::org::apache::qpid::ha::HaBroker>);
+
+ void clear(); ///< Clear all but self.
void add(const BrokerInfo& b);
void remove(const types::Uuid& id);
bool contains(const types::Uuid& id);
+
/** Return IDs of all READY backups other than self */
BrokerInfo::Set otherBackups() const;
void assign(const types::Variant::List&);
types::Variant::List asList() const;
- bool get(const types::Uuid& id, BrokerInfo& result);
+ bool get(const types::Uuid& id, BrokerInfo& result) const;
+
+ types::Uuid getSelf() const { return self; }
+ BrokerInfo getInfo() const;
+ BrokerStatus getStatus() const;
+ void setStatus(BrokerStatus s);
private:
- types::Uuid self;
+ void update(sys::Mutex::ScopedLock&);
+ BrokerStatus getStatus(sys::Mutex::ScopedLock&) const;
+
+ mutable sys::Mutex lock;
+ HaBroker& haBroker;
+ boost::shared_ptr<qmf::org::apache::qpid::ha::HaBroker> mgmtObject;
+ const types::Uuid self;
BrokerInfo::Map brokers;
- friend std::ostream& operator<<(std::ostream&, const Membership&);
};
-std::ostream& operator<<(std::ostream&, const Membership&);
-
}} // namespace qpid::ha
#endif /*!QPID_HA_MEMBERSHIP_H*/
diff --git a/cpp/src/qpid/ha/Primary.cpp b/cpp/src/qpid/ha/Primary.cpp
index e4bf9671b8..93dbbbea85 100644
--- a/cpp/src/qpid/ha/Primary.cpp
+++ b/cpp/src/qpid/ha/Primary.cpp
@@ -31,6 +31,8 @@
#include "qpid/broker/Connection.h"
#include "qpid/broker/Queue.h"
#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/framing/Uuid.h"
#include "qpid/log/Statement.h"
#include "qpid/sys/Timer.h"
#include <boost/bind.hpp>
@@ -39,6 +41,8 @@ namespace qpid {
namespace ha {
using sys::Mutex;
+using namespace std;
+using namespace framing;
namespace {
@@ -58,6 +62,8 @@ class PrimaryConfigurationObserver : public broker::ConfigurationObserver
PrimaryConfigurationObserver(Primary& p) : primary(p) {}
void queueCreate(const Primary::QueuePtr& q) { primary.queueCreate(q); }
void queueDestroy(const Primary::QueuePtr& q) { primary.queueDestroy(q); }
+ void exchangeCreate(const Primary::ExchangePtr& q) { primary.exchangeCreate(q); }
+ void exchangeDestroy(const Primary::ExchangePtr& q) { primary.exchangeDestroy(q); }
private:
Primary& primary;
};
@@ -76,8 +82,11 @@ class ExpectedBackupTimerTask : public sys::TimerTask {
Primary* Primary::instance = 0;
Primary::Primary(HaBroker& hb, const BrokerInfo::Set& expect) :
- haBroker(hb), logPrefix("Primary: "), active(false)
+ haBroker(hb), membership(hb.getMembership()),
+ logPrefix("Primary: "), active(false),
+ replicationTest(hb.getSettings().replicateDefault.get())
{
+ hb.getMembership().setStatus(RECOVERING);
assert(instance == 0);
instance = this; // Let queue replicators find us.
if (expect.empty()) {
@@ -89,11 +98,10 @@ Primary::Primary(HaBroker& hb, const BrokerInfo::Set& expect) :
// the QueueGuards are created.
QPID_LOG(notice, logPrefix << "Promoted to primary. Expected backups: " << expect);
for (BrokerInfo::Set::const_iterator i = expect.begin(); i != expect.end(); ++i) {
- boost::shared_ptr<RemoteBackup> backup(
- new RemoteBackup(*i, haBroker.getReplicationTest(), false));
+ boost::shared_ptr<RemoteBackup> backup(new RemoteBackup(*i, 0));
backups[i->getSystemId()] = backup;
if (!backup->isReady()) expectedBackups.insert(backup);
- backup->setInitialQueues(hb.getBroker().getQueues(), true); // Create guards
+ backup->setCatchupQueues(hb.getBroker().getQueues(), true); // Create guards
}
// Set timeout for expected brokers to connect and become ready.
sys::Duration timeout(int64_t(hb.getSettings().backupTimeout*sys::TIME_SEC));
@@ -102,14 +110,21 @@ Primary::Primary(HaBroker& hb, const BrokerInfo::Set& expect) :
hb.getBroker().getTimer().add(timerTask);
}
+
+ // Remove backup tag property from outgoing link properties.
+ framing::FieldTable linkProperties = hb.getBroker().getLinkClientProperties();
+ linkProperties.erase(ConnectionObserver::BACKUP_TAG);
+ hb.getBroker().setLinkClientProperties(linkProperties);
+
configurationObserver.reset(new PrimaryConfigurationObserver(*this));
haBroker.getBroker().getConfigurationObservers().add(configurationObserver);
Mutex::ScopedLock l(lock); // We are now active as a configurationObserver
checkReady(l);
+
// Allow client connections
connectionObserver.reset(new PrimaryConnectionObserver(*this));
- haBroker.getObserver()->setObserver(connectionObserver);
+ haBroker.getObserver()->setObserver(connectionObserver, logPrefix);
}
Primary::~Primary() {
@@ -122,7 +137,7 @@ void Primary::checkReady(Mutex::ScopedLock&) {
active = true;
Mutex::ScopedUnlock u(lock); // Don't hold lock across callback
QPID_LOG(notice, logPrefix << "Finished waiting for backups, primary is active.");
- haBroker.activate();
+ membership.setStatus(ACTIVE);
}
}
@@ -130,7 +145,7 @@ void Primary::checkReady(BackupMap::iterator i, Mutex::ScopedLock& l) {
if (i != backups.end() && i->second->reportReady()) {
BrokerInfo info = i->second->getBrokerInfo();
info.setStatus(READY);
- haBroker.addBroker(info);
+ membership.add(info);
if (expectedBackups.erase(i->second)) {
QPID_LOG(info, logPrefix << "Expected backup is ready: " << info);
checkReady(l);
@@ -155,9 +170,10 @@ void Primary::timeoutExpectedBackups() {
expectedBackups.erase(i++);
backups.erase(info.getSystemId());
rb->cancel();
- // Downgrade the broker to CATCHUP
+ // Downgrade the broker's status to CATCHUP
+ // The broker will get this status change when it eventually connects.
info.setStatus(CATCHUP);
- haBroker.addBroker(info);
+ membership.add(info);
}
else ++i;
}
@@ -178,46 +194,78 @@ void Primary::readyReplica(const ReplicatingSubscription& rs) {
}
}
+// NOTE: Called with queue registry lock held.
void Primary::queueCreate(const QueuePtr& q) {
- // Throw if there is an invalid replication level in the queue settings.
- haBroker.getReplicationTest().replicateLevel(q->getSettings().storeSettings);
- Mutex::ScopedLock l(lock);
- for (BackupMap::iterator i = backups.begin(); i != backups.end(); ++i) {
- i->second->queueCreate(q);
- checkReady(i, l);
+ // Set replication argument.
+ ReplicateLevel level = replicationTest.useLevel(*q);
+ QPID_LOG(debug, logPrefix << "Created queue " << q->getName()
+ << " replication: " << printable(level));
+ q->addArgument(QPID_REPLICATE, printable(level).str());
+ if (level) {
+ // Give each queue a unique id to avoid confusion of same-named queues.
+ q->addArgument(QPID_HA_UUID, types::Variant(Uuid(true)));
+ Mutex::ScopedLock l(lock);
+ for (BackupMap::iterator i = backups.begin(); i != backups.end(); ++i) {
+ i->second->queueCreate(q);
+ checkReady(i, l);
+ }
}
}
+// NOTE: Called with queue registry lock held.
void Primary::queueDestroy(const QueuePtr& q) {
+ QPID_LOG(debug, logPrefix << "Destroyed queue " << q->getName());
Mutex::ScopedLock l(lock);
for (BackupMap::iterator i = backups.begin(); i != backups.end(); ++i)
i->second->queueDestroy(q);
checkReady(l);
}
+// NOTE: Called with exchange registry lock held.
+void Primary::exchangeCreate(const ExchangePtr& ex) {
+ ReplicateLevel level = replicationTest.useLevel(*ex);
+ QPID_LOG(debug, logPrefix << "Created exchange " << ex->getName()
+ << " replication: " << printable(level));
+ FieldTable args = ex->getArgs();
+ args.setString(QPID_REPLICATE, printable(level).str()); // Set replication arg.
+ if (level) {
+ // Give each exchange a unique id to avoid confusion of same-named exchanges.
+ args.set(QPID_HA_UUID, FieldTable::ValuePtr(new UuidValue(&Uuid(true)[0])));
+ }
+ ex->setArgs(args);
+}
+
+// NOTE: Called with exchange registry lock held.
+void Primary::exchangeDestroy(const ExchangePtr& ex) {
+ QPID_LOG(debug, logPrefix << "Destroyed exchange " << ex->getName());
+ // Do nothing
+ }
+
void Primary::opened(broker::Connection& connection) {
BrokerInfo info;
if (ha::ConnectionObserver::getBrokerInfo(connection, info)) {
Mutex::ScopedLock l(lock);
BackupMap::iterator i = backups.find(info.getSystemId());
if (i == backups.end()) {
- QPID_LOG(debug, logPrefix << "New backup connected: " << info);
- boost::shared_ptr<RemoteBackup> backup(
- new RemoteBackup(info, haBroker.getReplicationTest(), true));
+ QPID_LOG(info, logPrefix << "New backup connected: " << info);
+ boost::shared_ptr<RemoteBackup> backup(new RemoteBackup(info, &connection));
{
// Avoid deadlock with queue registry lock.
Mutex::ScopedUnlock u(lock);
- backup->setInitialQueues(haBroker.getBroker().getQueues(), false);
+ backup->setCatchupQueues(haBroker.getBroker().getQueues(), false);
}
backups[info.getSystemId()] = backup;
+ i = backups.find(info.getSystemId());
}
else {
- QPID_LOG(debug, logPrefix << "Known backup connected: " << info);
- i->second->setConnected(true);
- checkReady(i, l);
+ QPID_LOG(info, logPrefix << "Known backup connected: " << info);
+ i->second->setConnection(&connection);
}
- if (info.getStatus() == JOINING) info.setStatus(CATCHUP);
- haBroker.addBroker(info);
+ if (info.getStatus() == JOINING) {
+ info.setStatus(CATCHUP);
+ membership.add(info);
+ }
+ if (i != backups.end()) checkReady(i, l);
}
else
QPID_LOG(debug, logPrefix << "Accepted client connection "
@@ -225,19 +273,20 @@ void Primary::opened(broker::Connection& connection) {
}
void Primary::closed(broker::Connection& connection) {
- // NOTE: It is possible for a backup connection to be rejected while we are
- // a backup, but closed() is called after we have become primary.
- //
- // For this reason we do not remove from the backups map here, the backups
- // map holds all the backups we know about whether connected or not.
- //
- Mutex::ScopedLock l(lock);
BrokerInfo info;
if (ha::ConnectionObserver::getBrokerInfo(connection, info)) {
- QPID_LOG(debug, logPrefix << "Backup disconnected: " << info);
- haBroker.removeBroker(info.getSystemId());
+ Mutex::ScopedLock l(lock);
BackupMap::iterator i = backups.find(info.getSystemId());
- if (i != backups.end()) i->second->setConnected(false);
+ // NOTE: It is possible for a backup connection to be rejected while we
+ // are a backup, but closed() is called after we have become primary.
+ // Checking isConnected() lets us ignore such spurious closes.
+ if (i != backups.end() && i->second->isConnected()) {
+ QPID_LOG(info, logPrefix << "Backup disconnected: " << info);
+ membership.remove(info.getSystemId());
+ expectedBackups.erase(i->second);
+ backups.erase(i);
+ checkReady(l);
+ }
}
}
@@ -249,4 +298,9 @@ boost::shared_ptr<QueueGuard> Primary::getGuard(const QueuePtr& q, const BrokerI
return i == backups.end() ? boost::shared_ptr<QueueGuard>() : i->second->guard(q);
}
+Role* Primary::promote() {
+ QPID_LOG(info, "Ignoring promotion, already primary: " << haBroker.getBrokerInfo());
+ return 0;
+}
+
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/Primary.h b/cpp/src/qpid/ha/Primary.h
index 26883f4416..ff85837882 100644
--- a/cpp/src/qpid/ha/Primary.h
+++ b/cpp/src/qpid/ha/Primary.h
@@ -24,6 +24,8 @@
#include "types.h"
#include "BrokerInfo.h"
+#include "ReplicationTest.h"
+#include "Role.h"
#include "qpid/sys/Mutex.h"
#include <boost/shared_ptr.hpp>
#include <boost/intrusive_ptr.hpp>
@@ -48,6 +50,7 @@ class HaBroker;
class ReplicatingSubscription;
class RemoteBackup;
class QueueGuard;
+class Membership;
/**
* State associated with a primary broker:
@@ -56,22 +59,30 @@ class QueueGuard;
*
* THREAD SAFE: called concurrently in arbitrary connection threads.
*/
-class Primary
+class Primary : public Role
{
public:
typedef boost::shared_ptr<broker::Queue> QueuePtr;
+ typedef boost::shared_ptr<broker::Exchange> ExchangePtr;
static Primary* get() { return instance; }
Primary(HaBroker& hb, const BrokerInfo::Set& expectedBackups);
~Primary();
+ // Role implementation
+ std::string getLogPrefix() const { return logPrefix; }
+ Role* promote();
+ void setBrokerUrl(const Url&) {}
+
void readyReplica(const ReplicatingSubscription&);
void removeReplica(const std::string& q);
// Called via ConfigurationObserver
void queueCreate(const QueuePtr&);
void queueDestroy(const QueuePtr&);
+ void exchangeCreate(const ExchangePtr&);
+ void exchangeDestroy(const ExchangePtr&);
// Called via ConnectionObserver
void opened(broker::Connection& connection);
@@ -91,17 +102,19 @@ class Primary
sys::Mutex lock;
HaBroker& haBroker;
+ Membership& membership;
std::string logPrefix;
bool active;
+ ReplicationTest replicationTest;
+
/**
* Set of expected backups that must be ready before we declare ourselves
- * active
+ * active. These are backups that were known and ready before the primary
+ * crashed. As new primary we expect them to re-connect.
*/
BackupSet expectedBackups;
/**
- * Map of all the remote backups we know about: any expected backups plus
- * all actual backups that have connected. We do not remove entries when a
- * backup disconnects. @see Primary::closed()
+ * Map of all the expected backups plus all connected backups.
*/
BackupMap backups;
boost::shared_ptr<broker::ConnectionObserver> connectionObserver;
diff --git a/cpp/src/qpid/ha/QueueGuard.cpp b/cpp/src/qpid/ha/QueueGuard.cpp
index 77e1f81a38..d06d88ca29 100644
--- a/cpp/src/qpid/ha/QueueGuard.cpp
+++ b/cpp/src/qpid/ha/QueueGuard.cpp
@@ -50,10 +50,10 @@ class QueueGuard::QueueObserver : public broker::QueueObserver
QueueGuard::QueueGuard(broker::Queue& q, const BrokerInfo& info)
- : queue(q), subscription(0)
+ : cancelled(false), queue(q), subscription(0)
{
std::ostringstream os;
- os << "Primary guard " << queue.getName() << "@" << info.getLogId() << ": ";
+ os << "Primary guard " << queue.getName() << "@" << info << ": ";
logPrefix = os.str();
observer.reset(new QueueObserver(*this));
queue.addObserver(observer);
@@ -66,45 +66,31 @@ QueueGuard::~QueueGuard() { cancel(); }
// NOTE: Called with message lock held.
void QueueGuard::enqueued(const Message& m) {
// Delay completion
- QPID_LOG(trace, logPrefix << "Delayed completion of " << m);
+ QPID_LOG(trace, logPrefix << "Delayed completion of " << m.getSequence());
+ Mutex::ScopedLock l(lock);
+ if (cancelled) return; // Don't record enqueues after we are cancelled.
+ assert(delayed.find(m.getSequence()) == delayed.end());
+ delayed[m.getSequence()] = m.getIngressCompletion();
m.getIngressCompletion()->startCompleter();
- {
- Mutex::ScopedLock l(lock);
- if (!delayed.insert(Delayed::value_type(m.getSequence(), m.getIngressCompletion())).second) {
- QPID_LOG(critical, logPrefix << "Second enqueue for message with sequence " << m.getSequence());
- assert(false);
- }
- }
}
// NOTE: Called with message lock held.
void QueueGuard::dequeued(const Message& m) {
QPID_LOG(trace, logPrefix << "Dequeued " << m);
- ReplicatingSubscription* rs=0;
- {
- Mutex::ScopedLock l(lock);
- rs = subscription;
- }
- if (rs) rs->dequeued(m);
- complete(m.getSequence());
-}
-
-void QueueGuard::completeRange(Delayed::iterator begin, Delayed::iterator end) {
- for (Delayed::iterator i = begin; i != end; ++i) {
- QPID_LOG(trace, logPrefix << "Completed " << i->first);
- i->second->finishCompleter();
- }
+ Mutex::ScopedLock l(lock);
+ if (subscription) subscription->dequeued(m);
+ complete(m.getSequence(), l);
}
void QueueGuard::cancel() {
queue.removeObserver(observer);
- Delayed removed;
- {
- Mutex::ScopedLock l(lock);
- if (delayed.empty()) return; // No need if no delayed messages.
- delayed.swap(removed);
+ Mutex::ScopedLock l(lock);
+ if (cancelled) return;
+ cancelled = true;
+ for (Delayed::iterator i = delayed.begin(); i != delayed.end();) {
+ complete(i, l);
+ delayed.erase(i++);
}
- completeRange(removed.begin(), removed.end());
}
void QueueGuard::attach(ReplicatingSubscription& rs) {
@@ -113,38 +99,36 @@ void QueueGuard::attach(ReplicatingSubscription& rs) {
}
bool QueueGuard::subscriptionStart(SequenceNumber position) {
- Delayed removed;
- {
- Mutex::ScopedLock l(lock);
- // Complete any messages before or at the ReplicatingSubscription start position.
- // Those messages are already on the backup.
- for (Delayed::iterator i = delayed.begin(); i != delayed.end() && i->first <= position;) {
- removed.insert(*i);
- delayed.erase(i++);
- }
+ // Complete any messages before or at the ReplicatingSubscription start position.
+ // Those messages are already on the backup.
+ Mutex::ScopedLock l(lock);
+ Delayed::iterator i = delayed.begin();
+ while(i != delayed.end() && i->first <= position) {
+ complete(i, l);
+ delayed.erase(i++);
}
- completeRange(removed.begin(), removed.end());
return position >= range.back;
}
void QueueGuard::complete(SequenceNumber sequence) {
- boost::intrusive_ptr<broker::AsyncCompletion> m;
- {
- Mutex::ScopedLock l(lock);
- // The same message can be completed twice, by
- // ReplicatingSubscription::acknowledged and dequeued. Remove it
- // from the map so we only call finishCompleter() once
- Delayed::iterator i = delayed.find(sequence);
- if (i != delayed.end()) {
- m = i->second;
- delayed.erase(i);
- }
+ Mutex::ScopedLock l(lock);
+ complete(sequence, l);
+}
+void QueueGuard::complete(SequenceNumber sequence, Mutex::ScopedLock& l) {
+ // The same message can be completed twice, by
+ // ReplicatingSubscription::acknowledged and dequeued. Remove it
+ // from the map so we only call finishCompleter() once
+ Delayed::iterator i = delayed.find(sequence);
+ if (i != delayed.end()) {
+ complete(i, l);
+ delayed.erase(i);
}
- if (m) {
- QPID_LOG(trace, logPrefix << "Completed " << sequence);
- m->finishCompleter();
- }
+}
+
+void QueueGuard::complete(Delayed::iterator i, Mutex::ScopedLock&) {
+ QPID_LOG(trace, logPrefix << "Completed " << i->first);
+ i->second->finishCompleter();
}
}} // namespaces qpid::ha
diff --git a/cpp/src/qpid/ha/QueueGuard.h b/cpp/src/qpid/ha/QueueGuard.h
index 3904b3bd3f..e7ceb351e8 100644
--- a/cpp/src/qpid/ha/QueueGuard.h
+++ b/cpp/src/qpid/ha/QueueGuard.h
@@ -54,6 +54,9 @@ class ReplicatingSubscription;
* THREAD SAFE: Concurrent calls:
* - enqueued() via QueueObserver in arbitrary connection threads.
* - attach(), cancel(), complete() from ReplicatingSubscription in subscription thread.
+ *
+ * Lock Hierarchy: ReplicatingSubscription MUS NOT call QueueGuard with it's lock held
+ * QueueGuard MAY call ReplicatingSubscription with it's lock held.
*/
class QueueGuard {
public:
@@ -104,17 +107,20 @@ class QueueGuard {
private:
class QueueObserver;
+ typedef std::map<framing::SequenceNumber,
+ boost::intrusive_ptr<broker::AsyncCompletion> > Delayed;
+
+ void complete(framing::SequenceNumber, sys::Mutex::ScopedLock &);
+ void complete(Delayed::iterator, sys::Mutex::ScopedLock &);
sys::Mutex lock;
+ bool cancelled;
std::string logPrefix;
broker::Queue& queue;
- typedef std::map<framing::SequenceNumber, boost::intrusive_ptr<broker::AsyncCompletion> > Delayed;
Delayed delayed;
ReplicatingSubscription* subscription;
boost::shared_ptr<QueueObserver> observer;
QueueRange range;
-
- void completeRange(Delayed::iterator begin, Delayed::iterator end);
};
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/QueueRange.h b/cpp/src/qpid/ha/QueueRange.h
index d734326910..f67ac146e6 100644
--- a/cpp/src/qpid/ha/QueueRange.h
+++ b/cpp/src/qpid/ha/QueueRange.h
@@ -24,6 +24,7 @@
#include "ReplicatingSubscription.h"
#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueueCursor.h"
#include "qpid/framing/FieldTable.h"
#include "qpid/framing/SequenceNumber.h"
#include <iostream>
@@ -51,15 +52,7 @@ struct QueueRange {
QueueRange() : front(1), back(0) { } // Empty range.
- QueueRange(broker::Queue& q) {
- if (ReplicatingSubscription::getFront(q, front))
- back = q.getPosition();
- else {
- back = q.getPosition();
- front = back+1; // empty
- }
- assert(front <= back + 1);
- }
+ QueueRange(broker::Queue& q) { q.getRange(front, back, broker::REPLICATOR); }
QueueRange(const framing::FieldTable& args) {
back = args.getAsInt(ReplicatingSubscription::QPID_BACK);
diff --git a/cpp/src/qpid/ha/QueueReplicator.cpp b/cpp/src/qpid/ha/QueueReplicator.cpp
index cac1fdac29..98220b2098 100644
--- a/cpp/src/qpid/ha/QueueReplicator.cpp
+++ b/cpp/src/qpid/ha/QueueReplicator.cpp
@@ -22,12 +22,15 @@
#include "HaBroker.h"
#include "QueueReplicator.h"
#include "ReplicatingSubscription.h"
+#include "Settings.h"
#include "qpid/broker/Bridge.h"
#include "qpid/broker/Broker.h"
#include "qpid/broker/Link.h"
#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueueObserver.h"
#include "qpid/broker/QueueRegistry.h"
#include "qpid/broker/SessionHandler.h"
+#include "qpid/broker/SessionHandler.h"
#include "qpid/framing/SequenceSet.h"
#include "qpid/framing/FieldTable.h"
#include "qpid/log/Statement.h"
@@ -37,43 +40,89 @@
namespace {
const std::string QPID_REPLICATOR_("qpid.replicator-");
const std::string TYPE_NAME("qpid.queue-replicator");
-const std::string QPID_SYNC_FREQUENCY("qpid.sync_frequency");
+const std::string QPID_HA("qpid.ha-");
}
namespace qpid {
namespace ha {
using namespace broker;
using namespace framing;
+using namespace std;
+using sys::Mutex;
-const std::string QPID_HA_EVENT_PREFIX("qpid.ha-event:");
-const std::string QueueReplicator::DEQUEUE_EVENT_KEY(QPID_HA_EVENT_PREFIX+"dequeue");
-const std::string QueueReplicator::POSITION_EVENT_KEY(QPID_HA_EVENT_PREFIX+"position");
+const std::string QueueReplicator::DEQUEUE_EVENT_KEY(QPID_HA+"dequeue");
+const std::string QueueReplicator::POSITION_EVENT_KEY(QPID_HA+"position");
+const std::string QueueReplicator::QPID_SYNC_FREQUENCY("qpid.sync_frequency");
std::string QueueReplicator::replicatorName(const std::string& queueName) {
return QPID_REPLICATOR_ + queueName;
}
+bool QueueReplicator::isReplicatorName(const std::string& name) {
+ return name.compare(0, QPID_REPLICATOR_.size(), QPID_REPLICATOR_) == 0;
+}
+
bool QueueReplicator::isEventKey(const std::string key) {
- const std::string& prefix = QPID_HA_EVENT_PREFIX;
+ const std::string& prefix = QPID_HA;
bool ret = key.size() > prefix.size() && key.compare(0, prefix.size(), prefix) == 0;
return ret;
}
+class QueueReplicator::ErrorListener : public SessionHandler::ErrorListener {
+ public:
+ ErrorListener(const std::string& prefix) : logPrefix(prefix) {}
+ void connectionException(framing::connection::CloseCode, const std::string& msg) {
+ QPID_LOG(error, logPrefix << "Connection error: " << msg);
+ }
+ void channelException(framing::session::DetachCode, const std::string& msg) {
+ QPID_LOG(error, logPrefix << "Channel error: " << msg);
+ }
+ void executionException(framing::execution::ErrorCode, const std::string& msg) {
+ QPID_LOG(error, logPrefix << "Execution error: " << msg);
+ }
+ void detach() {
+ QPID_LOG(debug, logPrefix << "Session detached");
+ }
+ private:
+ std::string logPrefix;
+};
+
+class QueueReplicator::QueueObserver : public broker::QueueObserver {
+ public:
+ QueueObserver(boost::shared_ptr<QueueReplicator> qr) : queueReplicator(qr) {}
+ void enqueued(const Message&) {}
+ void dequeued(const Message&) {}
+ void acquired(const Message&) {}
+ void requeued(const Message&) {}
+ void consumerAdded( const Consumer& ) {}
+ void consumerRemoved( const Consumer& ) {}
+ // Queue observer is destroyed when the queue is.
+ void destroy() { queueReplicator->destroy(); }
+ private:
+ boost::shared_ptr<QueueReplicator> queueReplicator;
+};
+
QueueReplicator::QueueReplicator(HaBroker& hb,
boost::shared_ptr<Queue> q,
boost::shared_ptr<Link> l)
: Exchange(replicatorName(q->getName()), 0, q->getBroker()),
haBroker(hb),
logPrefix("Backup queue "+q->getName()+": "),
- queue(q), link(l), brokerInfo(hb.getBrokerInfo())
+ queue(q), link(l), brokerInfo(hb.getBrokerInfo()), subscribed(false),
+ settings(hb.getSettings())
{
+ args.setString(QPID_REPLICATE, printable(NONE).str());
Uuid uuid(true);
bridgeName = replicatorName(q->getName()) + std::string(".") + uuid.str();
+ framing::FieldTable args = getArgs();
+ args.setString(QPID_REPLICATE, printable(NONE).str());
+ setArgs(args);
}
// This must be separate from the constructor so we can call shared_from_this.
void QueueReplicator::activate() {
- sys::Mutex::ScopedLock l(lock);
+ Mutex::ScopedLock l(lock);
+ if (!queue) return; // Already destroyed
std::pair<Bridge::shared_ptr, bool> result =
queue->getBroker()->getLinks().declare(
bridgeName,
@@ -93,48 +142,57 @@ void QueueReplicator::activate() {
boost::bind(&QueueReplicator::initializeBridge, shared_from_this(), _1, _2)
);
bridge = result.first;
+ bridge->setErrorListener(
+ boost::shared_ptr<ErrorListener>(new ErrorListener(logPrefix)));
+ boost::shared_ptr<QueueObserver> observer(new QueueObserver(shared_from_this()));
+ queue->addObserver(observer);
}
-QueueReplicator::~QueueReplicator() { deactivate(); }
+QueueReplicator::~QueueReplicator() {}
-void QueueReplicator::deactivate() {
- // destroy the route
- sys::Mutex::ScopedLock l(lock);
- if (bridge) {
- bridge->close();
- bridge.reset();
- QPID_LOG(debug, logPrefix << "Deactivated bridge " << bridgeName);
- }
+void QueueReplicator::destroy() {
+ // Called from Queue::destroyed()
+ Mutex::ScopedLock l(lock);
+ if (!bridge) return;
+ QPID_LOG(debug, logPrefix << "Destroyed.");
+ bridge->close();
+ // Need to drop shared pointers to avoid pointer cycles keeping this in memory.
+ queue.reset();
+ link.reset();
+ bridge.reset();
+ getBroker()->getExchanges().destroy(getName());
}
// Called in a broker connection thread when the bridge is created.
void QueueReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionHandler) {
- sys::Mutex::ScopedLock l(lock);
+ Mutex::ScopedLock l(lock);
+ if (!queue) return; // Already destroyed
AMQP_ServerProxy peer(sessionHandler.out);
const qmf::org::apache::qpid::broker::ArgsLinkBridge& args(bridge.getArgs());
- FieldTable settings;
- settings.setInt(ReplicatingSubscription::QPID_REPLICATING_SUBSCRIPTION, 1);
- settings.setInt(QPID_SYNC_FREQUENCY, 1); // FIXME aconway 2012-05-22: optimize?
- settings.setInt(ReplicatingSubscription::QPID_BACK,
- queue->getPosition());
- settings.setTable(ReplicatingSubscription::QPID_BROKER_INFO,
- brokerInfo.asFieldTable());
- SequenceNumber front;
- if (ReplicatingSubscription::getFront(*queue, front)) {
- settings.setInt(ReplicatingSubscription::QPID_FRONT, front);
- QPID_LOG(debug, "QPID_FRONT for " << queue->getName() << " is " << front);
+ FieldTable arguments;
+ arguments.setInt(ReplicatingSubscription::QPID_REPLICATING_SUBSCRIPTION, 1);
+ arguments.setInt(QPID_SYNC_FREQUENCY, 1); // FIXME aconway 2012-05-22: optimize?
+ arguments.setInt(ReplicatingSubscription::QPID_BACK, queue->getPosition());
+ arguments.setTable(ReplicatingSubscription::QPID_BROKER_INFO,brokerInfo.asFieldTable());
+ SequenceNumber front, back;
+ queue->getRange(front, back, broker::REPLICATOR);
+ if (front <= back) arguments.setInt(ReplicatingSubscription::QPID_FRONT, front);
+ try {
+ peer.getMessage().subscribe(
+ args.i_src, args.i_dest, 0/*accept-explicit*/, 1/*not-acquired*/,
+ false/*exclusive*/, "", 0, arguments);
+ peer.getMessage().setFlowMode(getName(), 1); // Window
+ peer.getMessage().flow(getName(), 0, settings.getFlowMessages());
+ peer.getMessage().flow(getName(), 1, settings.getFlowBytes());
+ }
+ catch(const exception& e) {
+ QPID_LOG(error, QPID_MSG(logPrefix + "Cannot connect to primary: " << e.what()));
+ throw;
}
- peer.getMessage().subscribe(
- args.i_src, args.i_dest, 0/*accept-explicit*/, 1/*not-acquired*/,
- false/*exclusive*/, "", 0, settings);
- // FIXME aconway 2012-05-22: use a finite credit window?
- peer.getMessage().flow(getName(), 0, 0xFFFFFFFF);
- peer.getMessage().flow(getName(), 1, 0xFFFFFFFF);
-
qpid::Address primary;
link->getRemoteAddress(primary);
QPID_LOG(info, logPrefix << "Connected to " << primary << "(" << bridgeName << ")");
- QPID_LOG(trace, logPrefix << "Subscription settings: " << settings);
+ QPID_LOG(trace, logPrefix << "Subscription arguments: " << arguments);
}
namespace {
@@ -147,17 +205,35 @@ template <class T> T decodeContent(Message& m) {
}
}
-void QueueReplicator::dequeue(SequenceNumber n, sys::Mutex::ScopedLock&) {
+void QueueReplicator::dequeue(SequenceNumber n, Mutex::ScopedLock&) {
+ boost::shared_ptr<Queue> q;
+ {
+ Mutex::ScopedLock l(lock);
+ if (!queue) return; // Already destroyed
+ q = queue;
+ }
// Thread safe: only calls thread safe Queue functions.
queue->dequeueMessageAt(n);
}
+namespace {
+bool getSequence(const Message& message, SequenceNumber& result) {
+ result = message.getSequence();
+ return true;
+}
+bool getNext(broker::Queue& q, SequenceNumber position, SequenceNumber& result) {
+ QueueCursor cursor(REPLICATOR);
+ return q.seek(cursor, boost::bind(&getSequence, _1, boost::ref(result)), position+1);
+}
+} // namespace
+
// Called in connection thread of the queues bridge to primary.
void QueueReplicator::route(Deliverable& msg)
{
try {
const std::string& key = msg.getMessage().getRoutingKey();
- sys::Mutex::ScopedLock l(lock);
+ Mutex::ScopedLock l(lock);
+ if (!queue) return; // Already destroyed
if (!isEventKey(key)) {
msg.deliverTo(queue);
// We are on a backup so the queue is not modified except via this.
@@ -176,16 +252,15 @@ void QueueReplicator::route(Deliverable& msg)
<< " to " << position);
// Verify that there are no messages after the new position in the queue.
SequenceNumber next;
- if (ReplicatingSubscription::getNext(*queue, position, next))
- throw Exception("Invalid position move, preceeds messages");
+ if (getNext(*queue, position, next))
+ throw Exception(QPID_MSG(logPrefix << "Invalid position " << position
+ << " preceeds message at " << next));
queue->setPosition(position);
}
// Ignore unknown event keys, may be introduced in later versions.
}
catch (const std::exception& e) {
- QPID_LOG(critical, logPrefix << "Replication failed: " << e.what());
- haBroker.shutdown();
- throw;
+ haBroker.shutdown(QPID_MSG(logPrefix << "Replication failed: " << e.what()));
}
}
diff --git a/cpp/src/qpid/ha/QueueReplicator.h b/cpp/src/qpid/ha/QueueReplicator.h
index 8d8a41a5ba..e8a793f611 100644
--- a/cpp/src/qpid/ha/QueueReplicator.h
+++ b/cpp/src/qpid/ha/QueueReplicator.h
@@ -41,6 +41,7 @@ class Deliverable;
namespace ha {
class HaBroker;
+class Settings;
/**
* Exchange created on a backup broker to replicate a queue on the primary.
@@ -57,7 +58,11 @@ class QueueReplicator : public broker::Exchange,
public:
static const std::string DEQUEUE_EVENT_KEY;
static const std::string POSITION_EVENT_KEY;
+ static const std::string QPID_SYNC_FREQUENCY;
+
static std::string replicatorName(const std::string& queueName);
+ static bool isReplicatorName(const std::string&);
+
/** Test if a string is an event key */
static bool isEventKey(const std::string key);
@@ -68,7 +73,6 @@ class QueueReplicator : public broker::Exchange,
~QueueReplicator();
void activate(); // Call after ctor
- void deactivate(); // Call before dtor
std::string getType() const;
bool bind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*, qpid::broker::AsyncStore* const);
@@ -80,8 +84,18 @@ class QueueReplicator : public broker::Exchange,
uint64_t getSize();
void write(char* target);
+ // Set if the queue has ever been subscribed to, used for auto-delete cleanup.
+ void setSubscribed() { subscribed = true; }
+ bool isSubscribed() { return subscribed; }
+
+ boost::shared_ptr<broker::Queue> getQueue() const { return queue; }
+
private:
+ class ErrorListener;
+ class QueueObserver;
+
void initializeBridge(broker::Bridge& bridge, broker::SessionHandler& sessionHandler);
+ void destroy(); // Called when the queue is destroyed.
void dequeue(framing::SequenceNumber, sys::Mutex::ScopedLock&);
HaBroker& haBroker;
@@ -92,6 +106,8 @@ class QueueReplicator : public broker::Exchange,
boost::shared_ptr<broker::Link> link;
boost::shared_ptr<broker::Bridge> bridge;
BrokerInfo brokerInfo;
+ bool subscribed;
+ const Settings& settings;
};
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/RemoteBackup.cpp b/cpp/src/qpid/ha/RemoteBackup.cpp
index 3421380940..798ade3f73 100644
--- a/cpp/src/qpid/ha/RemoteBackup.cpp
+++ b/cpp/src/qpid/ha/RemoteBackup.cpp
@@ -21,6 +21,7 @@
#include "RemoteBackup.h"
#include "QueueGuard.h"
#include "qpid/broker/Broker.h"
+#include "qpid/broker/Connection.h"
#include "qpid/broker/Queue.h"
#include "qpid/broker/QueueRegistry.h"
#include "qpid/log/Statement.h"
@@ -32,32 +33,45 @@ namespace ha {
using sys::Mutex;
using boost::bind;
-RemoteBackup::RemoteBackup(const BrokerInfo& info, ReplicationTest rt, bool con) :
- logPrefix("Primary: Remote backup "+info.getLogId()+": "),
- brokerInfo(info), replicationTest(rt), connected(con), reportedReady(false)
-{}
+RemoteBackup::RemoteBackup(
+ const BrokerInfo& info, broker::Connection* c
+) : brokerInfo(info), replicationTest(NONE), connection(c), reportedReady(false)
+{
+ std::ostringstream oss;
+ oss << "Primary: Remote backup " << info << ": ";
+ logPrefix = oss.str();
+}
-void RemoteBackup::setInitialQueues(broker::QueueRegistry& queues, bool createGuards)
+void RemoteBackup::setCatchupQueues(broker::QueueRegistry& queues, bool createGuards)
{
- QPID_LOG(debug, logPrefix << "Setting initial queues" << (createGuards ? " and guards" : ""));
- queues.eachQueue(boost::bind(&RemoteBackup::initialQueue, this, _1, createGuards));
+ queues.eachQueue(boost::bind(&RemoteBackup::catchupQueue, this, _1, createGuards));
+ QPID_LOG(debug, logPrefix << "Set " << catchupQueues.size() << " catch-up queues"
+ << (createGuards ? " and guards" : ""));
}
RemoteBackup::~RemoteBackup() { cancel(); }
void RemoteBackup::cancel() {
+ QPID_LOG(debug, logPrefix << "Cancelled " << (connection? "connected":"disconnected")
+ << " backup: " << brokerInfo);
for (GuardMap::iterator i = guards.begin(); i != guards.end(); ++i)
i->second->cancel();
guards.clear();
+ if (connection) {
+ connection->abort();
+ connection = 0;
+ }
}
bool RemoteBackup::isReady() {
- return connected && initialQueues.empty();
+ return connection && catchupQueues.empty();
}
-void RemoteBackup::initialQueue(const QueuePtr& q, bool createGuard) {
- if (replicationTest.isReplicated(ALL, *q)) {
- initialQueues.insert(q);
+void RemoteBackup::catchupQueue(const QueuePtr& q, bool createGuard) {
+ if (replicationTest.getLevel(*q) == ALL) {
+ QPID_LOG(debug, logPrefix << "Catch-up queue"
+ << (createGuard ? " and guard" : "") << ": " << q->getName());
+ catchupQueues.insert(q);
if (createGuard) guards[q].reset(new QueueGuard(*q, brokerInfo));
}
}
@@ -88,21 +102,24 @@ std::ostream& operator<<(std::ostream& o, const QueueSetPrinter& qp) {
}
void RemoteBackup::ready(const QueuePtr& q) {
- initialQueues.erase(q);
- QPID_LOG(debug, logPrefix << "Queue ready: " << q->getName()
- << QueueSetPrinter(", waiting for: ", initialQueues));
- if (isReady()) QPID_LOG(debug, logPrefix << "All queues ready");
+ catchupQueues.erase(q);
+ if (catchupQueues.size()) {
+ QPID_LOG(debug, logPrefix << "Caught up on queue: " << q->getName() << ", "
+ << catchupQueues.size() << " remain to catch up");
+ }
+ else
+ QPID_LOG(debug, logPrefix << "Caught up on queue: " << q->getName() );
}
-// Called via ConfigurationObserver::queueCreate and from initialQueue
+// Called via ConfigurationObserver::queueCreate and from catchupQueue
void RemoteBackup::queueCreate(const QueuePtr& q) {
- if (replicationTest.isReplicated(ALL, *q))
+ if (replicationTest.getLevel(*q) == ALL)
guards[q].reset(new QueueGuard(*q, brokerInfo));
}
// Called via ConfigurationObserver
void RemoteBackup::queueDestroy(const QueuePtr& q) {
- initialQueues.erase(q);
+ catchupQueues.erase(q);
GuardMap::iterator i = guards.find(q);
if (i != guards.end()) {
i->second->cancel();
diff --git a/cpp/src/qpid/ha/RemoteBackup.h b/cpp/src/qpid/ha/RemoteBackup.h
index 8ee776e90b..769c50457e 100644
--- a/cpp/src/qpid/ha/RemoteBackup.h
+++ b/cpp/src/qpid/ha/RemoteBackup.h
@@ -33,6 +33,7 @@ namespace qpid {
namespace broker {
class Queue;
class QueueRegistry;
+class Connection;
}
namespace ha {
@@ -54,20 +55,20 @@ class RemoteBackup
/** Note: isReady() can be true after construction
*@param connected true if the backup is already connected.
*/
- RemoteBackup(const BrokerInfo& info, ReplicationTest, bool connected);
+ RemoteBackup(const BrokerInfo&, broker::Connection*);
~RemoteBackup();
- /** Set the initial queues for all queues in the registry.
- *@createGuards if true create guards also, if false guards will be created on demand.
+ /** Set all queues in the registry as catch-up queues.
+ *@createGuards if true create guards also, if false guards are created on demand.
*/
- void setInitialQueues(broker::QueueRegistry&, bool createGuards);
+ void setCatchupQueues(broker::QueueRegistry&, bool createGuards);
/** Return guard associated with a queue. Used to create ReplicatingSubscription. */
GuardPtr guard(const QueuePtr&);
/** Is the remote backup connected? */
- void setConnected(bool b) { connected=b; }
- bool isConnected() const { return connected; }
+ void setConnection(broker::Connection* c) { connection = c; }
+ bool isConnected() const { return connection; }
/** ReplicatingSubscription associated with queue is ready.
* Note: may set isReady()
@@ -80,7 +81,7 @@ class RemoteBackup
/** Called via ConfigurationObserver. Note: may set isReady() */
void queueDestroy(const QueuePtr&);
- /**@return true when all initial queues for this backup are ready. */
+ /**@return true when all catch-up queues for this backup are ready. */
bool isReady();
/**@return true if isReady() and this is the first call to reportReady */
@@ -94,15 +95,14 @@ class RemoteBackup
typedef std::map<QueuePtr, GuardPtr> GuardMap;
typedef std::set<QueuePtr> QueueSet;
- /** Add queue to guard as an initial queue */
- void initialQueue(const QueuePtr&, bool createGuard);
+ void catchupQueue(const QueuePtr&, bool createGuard);
std::string logPrefix;
BrokerInfo brokerInfo;
ReplicationTest replicationTest;
GuardMap guards;
- QueueSet initialQueues;
- bool connected;
+ QueueSet catchupQueues;
+ broker::Connection* connection;
bool reportedReady;
};
diff --git a/cpp/src/qpid/ha/ReplicatingSubscription.cpp b/cpp/src/qpid/ha/ReplicatingSubscription.cpp
index 6f7519cd1f..933716e8fa 100644
--- a/cpp/src/qpid/ha/ReplicatingSubscription.cpp
+++ b/cpp/src/qpid/ha/ReplicatingSubscription.cpp
@@ -91,25 +91,6 @@ string mask(const string& in)
return DOLLAR + in + INTERNAL;
}
-namespace {
-bool getSequence(const Message& message, SequenceNumber& result)
-{
- result = message.getSequence();
- return true;
-}
-}
-bool ReplicatingSubscription::getNext(
- broker::Queue& q, SequenceNumber from, SequenceNumber& result)
-{
- QueueCursor cursor(REPLICATOR);
- return q.seek(cursor, boost::bind(&getSequence, _1, boost::ref(result)), from);
-}
-
-bool ReplicatingSubscription::getFront(broker::Queue& q, SequenceNumber& front) {
- QueueCursor cursor(REPLICATOR);
- return q.seek(cursor, boost::bind(&getSequence, _1, boost::ref(front)));
-}
-
/* Called by SemanticState::consume to create a consumer */
boost::shared_ptr<broker::SemanticState::ConsumerImpl>
ReplicatingSubscription::Factory::create(
@@ -157,7 +138,7 @@ ReplicatingSubscription::ReplicatingSubscription(
// Set a log prefix message that identifies the remote broker.
ostringstream os;
- os << "Primary " << queue->getName() << "@" << info.getLogId() << ": ";
+ os << "Primary " << queue->getName() << "@" << info << ": ";
logPrefix = os.str();
// NOTE: Once the guard is attached we can have concurrent
@@ -171,6 +152,7 @@ ReplicatingSubscription::ReplicatingSubscription(
guard->attach(*this);
QueueRange backup(arguments); // Remote backup range.
+ QueueRange backupOriginal(backup);
QueueRange primary(guard->getRange()); // Unguarded range when the guard was set.
backupPosition = backup.back;
@@ -207,7 +189,7 @@ ReplicatingSubscription::ReplicatingSubscription(
// queue and hasn't been tampered with then that will be the case.
QPID_LOG(debug, logPrefix << "Subscribed:"
- << " backup:" << backup
+ << " backup:" << backupOriginal << " adjusted backup:" << backup
<< " primary:" << primary
<< " catch-up: " << position << "-" << primary.back
<< "(" << primary.back-position << ")");
@@ -222,9 +204,7 @@ ReplicatingSubscription::ReplicatingSubscription(
}
}
-ReplicatingSubscription::~ReplicatingSubscription() {
- QPID_LOG(debug, logPrefix << "Detroyed replicating subscription");
-}
+ReplicatingSubscription::~ReplicatingSubscription() {}
// Called in subscription's connection thread when the subscription is created.
// Called separate from ctor because sending events requires
@@ -248,19 +228,20 @@ void ReplicatingSubscription::initialize() {
}
// Message is delivered in the subscription's connection thread.
-bool ReplicatingSubscription::deliver(const qpid::broker::QueueCursor& c, const qpid::broker::Message& m) {
- position = m.getSequence();
+bool ReplicatingSubscription::deliver(
+ const qpid::broker::QueueCursor& c, const qpid::broker::Message& m)
+{
try {
- QPID_LOG(trace, logPrefix << "Replicating " << getQueue()->getName() << "[" << m.getSequence() << "]");
+ QPID_LOG(trace, logPrefix << "Replicating " << m.getSequence());
{
Mutex::ScopedLock l(lock);
- //FIXME GRS: position is no longer set//assert(position == m.getSequence());
+ position = m.getSequence();
- // m.getSequence() is the position of the newly enqueued message on local queue.
+ // m.getSequence() is the position of the new message on local queue.
// backupPosition is latest position on backup queue before enqueueing
if (m.getSequence() <= backupPosition)
throw Exception(
- QPID_MSG("Expected position > " << backupPosition
+ QPID_MSG(logPrefix << "Expected position > " << backupPosition
<< " but got " << m.getSequence()));
if (m.getSequence() - backupPosition > 1) {
// Position has advanced because of messages dequeued ahead of us.
@@ -272,7 +253,7 @@ bool ReplicatingSubscription::deliver(const qpid::broker::QueueCursor& c, const
}
return ConsumerImpl::deliver(c, m);
} catch (const std::exception& e) {
- QPID_LOG(critical, logPrefix << "Error replicating " << getQueue()->getName() << "[" << m.getSequence() << "]"
+ QPID_LOG(critical, logPrefix << "Error replicating " << m.getSequence()
<< ": " << e.what());
throw;
}
@@ -292,6 +273,7 @@ void ReplicatingSubscription::setReady() {
// Called in the subscription's connection thread.
void ReplicatingSubscription::cancel()
{
+ QPID_LOG(debug, logPrefix << "Cancelled");
guard->cancel();
ConsumerImpl::cancel();
}
@@ -299,7 +281,7 @@ void ReplicatingSubscription::cancel()
// Consumer override, called on primary in the backup's IO thread.
void ReplicatingSubscription::acknowledged(const broker::DeliveryRecord& r) {
// Finish completion of message, it has been acknowledged by the backup.
- QPID_LOG(trace, logPrefix << "Acknowledged " << getQueue()->getName() << "[" << r.getMessageId() << "]");
+ QPID_LOG(trace, logPrefix << "Acknowledged " << r.getMessageId());
guard->complete(r.getMessageId());
// If next message is protected by the guard then we are ready
if (r.getMessageId() >= guard->getRange().back) setReady();
@@ -328,7 +310,7 @@ void ReplicatingSubscription::sendDequeueEvent(Mutex::ScopedLock&)
// arbitrary connection threads.
void ReplicatingSubscription::dequeued(const Message& m)
{
- QPID_LOG(trace, logPrefix << "Dequeued " << getQueue()->getName() << "[" << m.getSequence() << "]");
+ QPID_LOG(trace, logPrefix << "Dequeued " << m.getSequence());
{
Mutex::ScopedLock l(lock);
dequeues.add(m.getSequence());
@@ -396,7 +378,14 @@ bool ReplicatingSubscription::doDispatch()
Mutex::ScopedLock l(lock);
if (!dequeues.empty()) sendDequeueEvent(l);
}
- return ConsumerImpl::doDispatch();
+ try {
+ return ConsumerImpl::doDispatch();
+ }
+ catch (const std::exception& e) {
+ // FIXME aconway 2012-10-05: detect queue deletion, no warning.
+ QPID_LOG(warning, logPrefix << " exception in dispatch: " << e.what());
+ return false;
+ }
}
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/ReplicatingSubscription.h b/cpp/src/qpid/ha/ReplicatingSubscription.h
index 8a2984846e..7fcb4ccf13 100644
--- a/cpp/src/qpid/ha/ReplicatingSubscription.h
+++ b/cpp/src/qpid/ha/ReplicatingSubscription.h
@@ -61,10 +61,14 @@ class QueueGuard;
*
* Lifecycle: broker::Queue holds shared_ptrs to this as a consumer.
*
+ * Lock Hierarchy: ReplicatingSubscription MUS NOT call QueueGuard with it's lock held
+ * QueueGuard MAY call ReplicatingSubscription with it's lock held.
*/
class ReplicatingSubscription : public broker::SemanticState::ConsumerImpl
{
public:
+ typedef broker::SemanticState::ConsumerImpl ConsumerImpl;
+
struct Factory : public broker::ConsumerFactory {
boost::shared_ptr<broker::SemanticState::ConsumerImpl> create(
broker::SemanticState* parent,
@@ -80,17 +84,6 @@ class ReplicatingSubscription : public broker::SemanticState::ConsumerImpl
static const std::string QPID_FRONT;
static const std::string QPID_BROKER_INFO;
- // TODO aconway 2012-05-23: these don't belong on ReplicatingSubscription
- /** Get position of front message on queue.
- *@return false if queue is empty.
- */
- static bool getFront(broker::Queue&, framing::SequenceNumber& result);
- /** Get next message after from in queue.
- *@return false if none found.
- */
- static bool getNext(broker::Queue&, framing::SequenceNumber from,
- framing::SequenceNumber& result);
-
ReplicatingSubscription(broker::SemanticState* parent,
const std::string& name, boost::shared_ptr<broker::Queue> ,
bool ack, bool acquire, bool exclusive, const std::string& tag,
@@ -114,6 +107,8 @@ class ReplicatingSubscription : public broker::SemanticState::ConsumerImpl
// Hide the "queue deleted" error for a ReplicatingSubscription when a
// queue is deleted, this is normal and not an error.
bool hideDeletedError() { return true; }
+ // Not counted for auto deletion and immediate message purposes.
+ bool isCounted() { return false; }
/** Initialization that must be done separately from construction
* because it requires a shared_ptr to this to exist.
diff --git a/cpp/src/qpid/ha/ReplicationTest.cpp b/cpp/src/qpid/ha/ReplicationTest.cpp
index 88a969dbfd..647523ef2c 100644
--- a/cpp/src/qpid/ha/ReplicationTest.cpp
+++ b/cpp/src/qpid/ha/ReplicationTest.cpp
@@ -19,7 +19,9 @@
*
*/
#include "ReplicationTest.h"
+#include "qpid/log/Statement.h"
#include "qpid/broker/Queue.h"
+#include "qpid/broker/Exchange.h"
#include "qpid/framing/FieldTable.h"
namespace qpid {
@@ -27,48 +29,47 @@ namespace ha {
using types::Variant;
-ReplicateLevel ReplicationTest::replicateLevel(const std::string& str) {
+ReplicateLevel ReplicationTest::getLevel(const std::string& str) {
Enum<ReplicateLevel> rl(replicateDefault);
if (!str.empty()) rl.parse(str);
return rl.get();
}
-ReplicateLevel ReplicationTest::replicateLevel(const framing::FieldTable& f) {
+ReplicateLevel ReplicationTest::getLevel(const framing::FieldTable& f) {
if (f.isSet(QPID_REPLICATE))
- return replicateLevel(f.getAsString(QPID_REPLICATE));
+ return getLevel(f.getAsString(QPID_REPLICATE));
else
return replicateDefault;
}
-ReplicateLevel ReplicationTest::replicateLevel(const Variant::Map& m) {
+ReplicateLevel ReplicationTest::getLevel(const Variant::Map& m) {
Variant::Map::const_iterator i = m.find(QPID_REPLICATE);
if (i != m.end())
- return replicateLevel(i->second.asString());
+ return getLevel(i->second.asString());
else
return replicateDefault;
}
-namespace {
-const std::string AUTO_DELETE_TIMEOUT("qpid.auto_delete_timeout");
+ReplicateLevel ReplicationTest::getLevel(const broker::Queue& q) {
+ const Variant::Map& qmap(q.getSettings().original);
+ Variant::Map::const_iterator i = qmap.find(QPID_REPLICATE);
+ if (i != qmap.end())
+ return getLevel(i->second.asString());
+ else
+ return getLevel(q.getSettings().storeSettings);
}
-bool ReplicationTest::isReplicated(
- ReplicateLevel level, const Variant::Map& args, bool autodelete, bool exclusive)
-{
- bool ignore = autodelete && exclusive && args.find(AUTO_DELETE_TIMEOUT) == args.end();
- return !ignore && replicateLevel(args) >= level;
+ReplicateLevel ReplicationTest::getLevel(const broker::Exchange& ex) {
+ return getLevel(ex.getArgs());
}
-bool ReplicationTest::isReplicated(
- ReplicateLevel level, const framing::FieldTable& args, bool autodelete, bool exclusive)
+ReplicateLevel ReplicationTest::useLevel(const broker::Queue& q)
{
- bool ignore = autodelete && exclusive && !args.isSet(AUTO_DELETE_TIMEOUT);
- return !ignore && replicateLevel(args) >= level;
+ return q.getSettings().isTemporary ? ReplicationTest(NONE).getLevel(q) : getLevel(q);
}
-bool ReplicationTest::isReplicated(ReplicateLevel level, const broker::Queue& q)
-{
- return isReplicated(level, q.getSettings().storeSettings, q.isAutoDelete(), q.hasExclusiveOwner());
+ReplicateLevel ReplicationTest::useLevel(const broker::Exchange& ex) {
+ return ReplicationTest::getLevel(ex);
}
diff --git a/cpp/src/qpid/ha/ReplicationTest.h b/cpp/src/qpid/ha/ReplicationTest.h
index 9f6976a8e4..7d44d82a21 100644
--- a/cpp/src/qpid/ha/ReplicationTest.h
+++ b/cpp/src/qpid/ha/ReplicationTest.h
@@ -30,6 +30,7 @@ namespace qpid {
namespace broker {
class Queue;
+class Exchange;
}
namespace framing {
@@ -47,21 +48,24 @@ class ReplicationTest
ReplicationTest(ReplicateLevel replicateDefault_) :
replicateDefault(replicateDefault_) {}
- // Return the simple replication level, accounting for defaults.
- ReplicateLevel replicateLevel(const std::string& str);
- ReplicateLevel replicateLevel(const framing::FieldTable& f);
- ReplicateLevel replicateLevel(const types::Variant::Map& m);
+ // Get the replication level set on an object, or default if not set.
+ ReplicateLevel getLevel(const std::string& str);
+ ReplicateLevel getLevel(const framing::FieldTable& f);
+ ReplicateLevel getLevel(const types::Variant::Map& m);
+ ReplicateLevel getLevel(const broker::Queue&);
+ ReplicateLevel getLevel(const broker::Exchange&);
+
+ // Calculate level for objects that may not have replication set,
+ // including auto-delete/exclusive settings.
+ ReplicateLevel useLevel(const types::Variant::Map& args, bool autodelete, bool exclusive);
+ ReplicateLevel useLevel(const framing::FieldTable& args, bool autodelete, bool exclusive);
+ ReplicateLevel useLevel(const broker::Queue&);
+ ReplicateLevel useLevel(const broker::Exchange&);
- // Return true if replication for a queue is enabled at level or higher,
- // taking account of default level and queue settings.
- bool isReplicated(ReplicateLevel level,
- const types::Variant::Map& args, bool autodelete, bool exclusive);
- bool isReplicated(ReplicateLevel level,
- const framing::FieldTable& args, bool autodelete, bool exclusive);
- bool isReplicated(ReplicateLevel level, const broker::Queue&);
private:
ReplicateLevel replicateDefault;
};
+
}} // namespace qpid::ha
#endif /*!QPID_HA_REPLICATIONTEST_H*/
diff --git a/cpp/src/qpid/ha/Role.h b/cpp/src/qpid/ha/Role.h
new file mode 100644
index 0000000000..9d6f7cd123
--- /dev/null
+++ b/cpp/src/qpid/ha/Role.h
@@ -0,0 +1,55 @@
+#ifndef QPID_HA_ROLE_H
+#define QPID_HA_ROLE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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 {
+struct Url;
+
+namespace ha {
+
+/**
+ * A HaBroker has a role, e.g. Primary, Backup, StandAlone.
+ * Role subclasses define the actions of the broker in each role.
+ * The Role interface allows the HaBroker to pass management actions
+ * to be implemented by the role.
+ */
+class Role
+{
+ public:
+ /** Log prefix appropriate to the role */
+ virtual std::string getLogPrefix() const = 0;
+
+ /** QMF promote method handler.
+ * @return The new role if promoted, 0 if not. Caller takes ownership.
+ */
+ virtual Role* promote() = 0;
+
+ virtual void setBrokerUrl(const Url& url) = 0;
+
+ private:
+};
+}} // namespace qpid::ha
+
+#endif /*!QPID_HA_ROLE_H*/
diff --git a/cpp/src/qpid/ha/Settings.h b/cpp/src/qpid/ha/Settings.h
index 37235b5c79..53b61415cf 100644
--- a/cpp/src/qpid/ha/Settings.h
+++ b/cpp/src/qpid/ha/Settings.h
@@ -23,6 +23,7 @@
*/
#include "types.h"
+#include "qpid/sys/IntegerTypes.h"
#include <string>
namespace qpid {
@@ -34,16 +35,25 @@ namespace ha {
class Settings
{
public:
- Settings() : cluster(false), replicateDefault(NONE), backupTimeout(5)
+ Settings() : cluster(false), queueReplication(false),
+ replicateDefault(NONE), backupTimeout(5),
+ flowMessages(100), flowBytes(0)
{}
bool cluster; // True if we are a cluster member.
- std::string clientUrl;
+ bool queueReplication; // True if enabled.
+ std::string publicUrl;
std::string brokerUrl;
Enum<ReplicateLevel> replicateDefault;
std::string username, password, mechanism;
double backupTimeout;
- private:
+
+ uint32_t flowMessages, flowBytes;
+
+ static const uint32_t NO_LIMIT=0xFFFFFFFF;
+ static uint32_t flowValue(uint32_t n) { return n ? n : NO_LIMIT; }
+ uint32_t getFlowMessages() const { return flowValue(flowMessages); }
+ uint32_t getFlowBytes() const { return flowValue(flowBytes); }
};
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/broker/ConnectionFactory.h b/cpp/src/qpid/ha/StandAlone.h
index 7c1a9a08e1..d052996d40 100644
--- a/cpp/src/qpid/broker/ConnectionFactory.h
+++ b/cpp/src/qpid/ha/StandAlone.h
@@ -1,3 +1,6 @@
+#ifndef QPID_HA_STANDALONE_H
+#define QPID_HA_STANDALONE_H
+
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -7,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
@@ -18,34 +21,25 @@
* under the License.
*
*/
-#ifndef _ConnectionFactory_
-#define _ConnectionFactory_
-
-#include "qpid/sys/ConnectionCodec.h"
-
namespace qpid {
-namespace broker {
-class Broker;
+struct Url;
+
+namespace ha {
-class ConnectionFactory : public sys::ConnectionCodec::Factory
+/**
+ * Stand-alone role: acts as a stand-alone broker, no clustering.
+ * HA module needed to setting up replication via QMF methods.
+ */
+class StandAlone : public Role
{
public:
- ConnectionFactory(Broker& b);
-
- virtual ~ConnectionFactory();
-
- sys::ConnectionCodec*
- create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id,
- const qpid::sys::SecuritySettings&);
-
- sys::ConnectionCodec*
- create(sys::OutputControl&, const std::string& id, const qpid::sys::SecuritySettings&);
+ std::string getLogPrefix() const { return logPrefix; }
+ Role* promote() { return 0; }
+ void setBrokerUrl(const Url&) {}
private:
- Broker& broker;
+ std::string logPrefix;
};
+}} // namespace qpid::ha
-}}
-
-
-#endif
+#endif /*!QPID_HA_STANDALONE_H*/
diff --git a/cpp/src/qpid/ha/StatusCheck.cpp b/cpp/src/qpid/ha/StatusCheck.cpp
new file mode 100644
index 0000000000..17613ce3dd
--- /dev/null
+++ b/cpp/src/qpid/ha/StatusCheck.cpp
@@ -0,0 +1,132 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "StatusCheck.h"
+#include "qpid/log/Statement.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/types/Variant.h"
+
+namespace qpid {
+namespace ha {
+
+using namespace qpid::messaging;
+using namespace qpid::types;
+using namespace std;
+using namespace sys;
+
+const string HA_BROKER = "org.apache.qpid.ha:habroker:ha-broker";
+
+class StatusCheckThread : public sys::Runnable {
+ public:
+ StatusCheckThread(StatusCheck& sc, const qpid::Address& addr, const BrokerInfo& self)
+ : url(addr), statusCheck(sc), brokerInfo(self) {}
+ void run();
+ private:
+ Url url;
+ StatusCheck& statusCheck;
+ uint16_t linkHeartbeatInterval;
+ BrokerInfo brokerInfo;
+};
+
+void StatusCheckThread::run() {
+ QPID_LOG(debug, statusCheck.logPrefix << "Checking status of " << url);
+ Variant::Map options, clientProperties;
+ clientProperties = brokerInfo.asMap(); // Detect self connections.
+ clientProperties["qpid.ha-admin"] = 1; // Allow connection to backups.
+
+ options["client-properties"] = clientProperties;
+ options["heartbeat"] = statusCheck.linkHeartbeatInterval;
+ Connection c(url.str(), options);
+
+ try {
+ c.open();
+ Session session = c.createSession();
+ messaging::Address responses("#;{create:always,node:{x-declare:{exclusive:True,auto-delete:True,arguments:{'qpid.replicate':none}}}}");
+ Receiver r = session.createReceiver(responses);
+ Sender s = session.createSender("qmf.default.direct/broker");
+ Message request;
+ request.setReplyTo(responses);
+ request.setContentType("amqp/map");
+ request.setProperty("x-amqp-0-10.app-id", "qmf2");
+ request.setProperty("qmf.opcode", "_query_request");
+ Variant::Map oid;
+ oid["_object_name"] = HA_BROKER;
+ Variant::Map content;
+ content["_what"] = "OBJECT";
+ content["_object_id"] = oid;
+ encode(content, request);
+ s.send(request);
+ Message response = r.fetch(statusCheck.linkHeartbeatInterval*Duration::SECOND);
+ session.acknowledge();
+ Variant::List contentIn;
+ decode(response, contentIn);
+ if (contentIn.size() == 1) {
+ Variant::Map details = contentIn.front().asMap()["_values"].asMap();
+ string status = details["status"].getString();
+ if (status != "joining") {
+ statusCheck.setPromote(false);
+ QPID_LOG(info, statusCheck.logPrefix << "Status of " << url << " is "
+ << status << ", this broker will refuse promotion.");
+ }
+ QPID_LOG(debug, statusCheck.logPrefix << "Status of " << url << ": " << status);
+ }
+ } catch(const exception& error) {
+ QPID_LOG(info, "Checking status of " << url << ": " << error.what());
+ }
+ delete this;
+}
+
+StatusCheck::StatusCheck(const string& lp, uint16_t lh, const BrokerInfo& self)
+ : logPrefix(lp), promote(true), linkHeartbeatInterval(lh), brokerInfo(self)
+{}
+
+StatusCheck::~StatusCheck() {
+ // Join any leftovers
+ for (size_t i = 0; i < threads.size(); ++i) threads[i].join();
+}
+
+void StatusCheck::setUrl(const Url& url) {
+ Mutex::ScopedLock l(lock);
+ for (size_t i = 0; i < url.size(); ++i)
+ threads.push_back(Thread(new StatusCheckThread(*this, url[i], brokerInfo)));
+}
+
+bool StatusCheck::canPromote() {
+ Mutex::ScopedLock l(lock);
+ while (!threads.empty()) {
+ Thread t = threads.back();
+ threads.pop_back();
+ Mutex::ScopedUnlock u(lock);
+ t.join();
+ }
+ return promote;
+}
+
+void StatusCheck::setPromote(bool p) {
+ Mutex::ScopedLock l(lock);
+ promote = p;
+}
+
+}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/StatusCheck.h b/cpp/src/qpid/ha/StatusCheck.h
new file mode 100644
index 0000000000..997ced4159
--- /dev/null
+++ b/cpp/src/qpid/ha/StatusCheck.h
@@ -0,0 +1,71 @@
+#ifndef QPID_HA_STATUSCHECK_H
+#define QPID_HA_STATUSCHECK_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "BrokerInfo.h"
+#include "qpid/Url.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Runnable.h"
+#include <vector>
+
+namespace qpid {
+namespace ha {
+
+// FIXME aconway 2012-12-21: This solution is incomplete. It will only protect
+// against bad promotion if there are READY brokers when this broker starts.
+// It will not help the situation where brokers became READY after this one starts.
+//
+
+/**
+ * Check whether a JOINING broker can be promoted .
+ *
+ * A JOINING broker can be promoted as long as all the other brokers are also
+ * JOINING. If there are READY brokers in the cluster the JOINING broker should
+ * refuse to promote so that one of the READY brokers can. This situation
+ * only comes about if the primary is dead and no new primary has been promoted.
+ *
+ * THREAD SAFE: setUrl and canPromote are called in arbitrary management threads.
+ */
+class StatusCheck
+{
+ public:
+ StatusCheck(const std::string& logPrefix, uint16_t linkHeartbeatInteval, const BrokerInfo& self);
+ ~StatusCheck();
+ void setUrl(const Url&);
+ bool canPromote();
+
+ private:
+ void setPromote(bool p);
+
+ std::string logPrefix;
+ sys::Mutex lock;
+ std::vector<sys::Thread> threads;
+ bool promote;
+ uint16_t linkHeartbeatInterval;
+ BrokerInfo brokerInfo;
+ friend class StatusCheckThread;
+};
+}} // namespace qpid::ha
+
+#endif /*!QPID_HA_STATUSCHECK_H*/
diff --git a/cpp/src/qpid/ha/types.cpp b/cpp/src/qpid/ha/types.cpp
index 53e2056213..bb4bf83574 100644
--- a/cpp/src/qpid/ha/types.cpp
+++ b/cpp/src/qpid/ha/types.cpp
@@ -33,6 +33,7 @@ namespace ha {
using namespace std;
const string QPID_REPLICATE("qpid.replicate");
+const string QPID_HA_UUID("qpid.ha-uuid");
string EnumBase::str() const {
assert(value < count);
@@ -55,6 +56,11 @@ template <> const char* Enum<ReplicateLevel>::NAMES[] = { "none", "configuration
template <> const size_t Enum<ReplicateLevel>::N = 3;
template <> const char* Enum<BrokerStatus>::NAME = "HA broker status";
+
+// NOTE: Changing status names will have an impact on qpid-ha and
+// the qpidd-primary init script.
+// Don't change them unless you are going to update all dependent code.
+//
template <> const char* Enum<BrokerStatus>::NAMES[] = {
"joining", "catchup", "ready", "recovering", "active", "standalone"
};
diff --git a/cpp/src/qpid/ha/types.h b/cpp/src/qpid/ha/types.h
index 35faf9f624..f8c48afc5a 100644
--- a/cpp/src/qpid/ha/types.h
+++ b/cpp/src/qpid/ha/types.h
@@ -99,6 +99,7 @@ inline bool isBackup(BrokerStatus s) { return !isPrimary(s); }
// String constants.
extern const std::string QPID_REPLICATE;
+extern const std::string QPID_HA_UUID;
/** Define IdSet type, not a typedef so we can overload operator << */
class IdSet : public std::set<types::Uuid> {};
diff --git a/cpp/src/qpid/legacystore/BindingDbt.cpp b/cpp/src/qpid/legacystore/BindingDbt.cpp
new file mode 100644
index 0000000000..a48c156e71
--- /dev/null
+++ b/cpp/src/qpid/legacystore/BindingDbt.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/legacystore/BindingDbt.h"
+
+namespace mrg {
+namespace msgstore {
+
+BindingDbt::BindingDbt(const qpid::broker::PersistableExchange& e, const qpid::broker::PersistableQueue& q, const std::string& k, const qpid::framing::FieldTable& a)
+ : data(new char[encodedSize(e, q, k, a)]),
+ buffer(data, encodedSize(e, q, k, a))
+{
+ buffer.putLongLong(q.getPersistenceId());
+ buffer.putShortString(q.getName());
+ buffer.putShortString(k);
+ buffer.put(a);
+
+ set_data(data);
+ set_size(encodedSize(e, q, k, a));
+}
+
+BindingDbt::~BindingDbt()
+{
+ delete [] data;
+}
+
+uint32_t BindingDbt::encodedSize(const qpid::broker::PersistableExchange& /*not used*/, const qpid::broker::PersistableQueue& q, const std::string& k, const qpid::framing::FieldTable& a)
+{
+ return 8 /*queue id*/ + q.getName().size() + 1 + k.size() + 1 + a.encodedSize();
+}
+
+}}
diff --git a/cpp/src/qpid/legacystore/BindingDbt.h b/cpp/src/qpid/legacystore/BindingDbt.h
new file mode 100644
index 0000000000..63c7cd144e
--- /dev/null
+++ b/cpp/src/qpid/legacystore/BindingDbt.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifndef QPID_LEGACYSTORE_BINDINGDBT_H
+#define QPID_LEGACYSTORE_BINDINGDBT_H
+
+#include "db-inc.h"
+#include "qpid/broker/PersistableExchange.h"
+#include "qpid/broker/PersistableQueue.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/FieldTable.h"
+
+namespace mrg{
+namespace msgstore{
+
+class BindingDbt : public Dbt
+{
+ char* data;
+ qpid::framing::Buffer buffer;
+
+ static uint32_t encodedSize(const qpid::broker::PersistableExchange& e,
+ const qpid::broker::PersistableQueue& q,
+ const std::string& k,
+ const qpid::framing::FieldTable& a);
+
+public:
+ BindingDbt(const qpid::broker::PersistableExchange& e,
+ const qpid::broker::PersistableQueue& q,
+ const std::string& k,
+ const qpid::framing::FieldTable& a);
+
+ virtual ~BindingDbt();
+
+};
+
+}}
+
+#endif // ifndef QPID_LEGACYSTORE_BINDINGDBT_H
diff --git a/cpp/src/qpid/legacystore/BufferValue.cpp b/cpp/src/qpid/legacystore/BufferValue.cpp
new file mode 100644
index 0000000000..fb2c471cd7
--- /dev/null
+++ b/cpp/src/qpid/legacystore/BufferValue.cpp
@@ -0,0 +1,56 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/legacystore/BufferValue.h"
+
+namespace mrg {
+namespace msgstore {
+
+
+
+BufferValue::BufferValue(u_int32_t size, u_int64_t offset)
+ : data(new char[size]),
+ buffer(data, size)
+{
+ set_data(data);
+ set_size(size);
+ set_flags(DB_DBT_USERMEM | DB_DBT_PARTIAL);
+ set_doff(offset);
+ set_dlen(size);
+ set_ulen(size);
+}
+
+BufferValue::BufferValue(const qpid::broker::Persistable& p)
+ : data(new char[p.encodedSize()]),
+ buffer(data, p.encodedSize())
+{
+ p.encode(buffer);
+
+ set_data(data);
+ set_size(p.encodedSize());
+}
+
+BufferValue::~BufferValue()
+{
+ delete [] data;
+}
+
+}}
diff --git a/cpp/src/qpid/legacystore/BufferValue.h b/cpp/src/qpid/legacystore/BufferValue.h
new file mode 100644
index 0000000000..527fbcf577
--- /dev/null
+++ b/cpp/src/qpid/legacystore/BufferValue.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifndef QPID_LEGACYSTORE_BUFFERVALUE_H
+#define QPID_LEGACYSTORE_BUFFERVALUE_H
+
+#include "db-inc.h"
+#include "qpid/broker/Persistable.h"
+#include "qpid/framing/Buffer.h"
+
+namespace mrg{
+namespace msgstore{
+
+class BufferValue : public Dbt
+{
+ char* data;
+
+public:
+ qpid::framing::Buffer buffer;
+
+ BufferValue(u_int32_t size, u_int64_t offset);
+ BufferValue(const qpid::broker::Persistable& p);
+ virtual ~BufferValue();
+};
+
+}}
+
+#endif // ifndef QPID_LEGACYSTORE_BUFFERVALUE_H
diff --git a/cpp/src/qpid/legacystore/Cursor.h b/cpp/src/qpid/legacystore/Cursor.h
new file mode 100644
index 0000000000..0c869c29a0
--- /dev/null
+++ b/cpp/src/qpid/legacystore/Cursor.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 QPID_LEGACYSTORE_CURSOR_H
+#define QPID_LEGACYSTORE_CURSOR_H
+
+#include <boost/shared_ptr.hpp>
+#include "db-inc.h"
+
+namespace mrg{
+namespace msgstore{
+
+class Cursor
+{
+ Dbc* cursor;
+public:
+ typedef boost::shared_ptr<Db> db_ptr;
+
+ Cursor() : cursor(0) {}
+ virtual ~Cursor() { if(cursor) cursor->close(); }
+
+ void open(db_ptr db, DbTxn* txn, u_int32_t flags = 0) { db->cursor(txn, &cursor, flags); }
+ void close() { if(cursor) cursor->close(); cursor = 0; }
+ Dbc* get() { return cursor; }
+ Dbc* operator->() { return cursor; }
+ bool next(Dbt& key, Dbt& value) { return cursor->get(&key, &value, DB_NEXT) == 0; }
+ bool current(Dbt& key, Dbt& value) { return cursor->get(&key, &value, DB_CURRENT) == 0; }
+};
+
+}}
+
+#endif // ifndef QPID_LEGACYSTORE_CURSOR_H
diff --git a/cpp/src/qpid/legacystore/DataTokenImpl.cpp b/cpp/src/qpid/legacystore/DataTokenImpl.cpp
new file mode 100644
index 0000000000..796d4c02f0
--- /dev/null
+++ b/cpp/src/qpid/legacystore/DataTokenImpl.cpp
@@ -0,0 +1,28 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/legacystore/DataTokenImpl.h"
+
+using namespace mrg::msgstore;
+
+DataTokenImpl::DataTokenImpl():data_tok() {}
+
+DataTokenImpl::~DataTokenImpl() {}
diff --git a/cpp/src/qpid/legacystore/DataTokenImpl.h b/cpp/src/qpid/legacystore/DataTokenImpl.h
new file mode 100644
index 0000000000..e01d471e1b
--- /dev/null
+++ b/cpp/src/qpid/legacystore/DataTokenImpl.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifndef QPID_LEGACYSTORE_DATATOKENIMPL_H
+#define QPID_LEGACYSTORE_DATATOKENIMPL_H
+
+#include "qpid/legacystore/jrnl/data_tok.h"
+#include "qpid/broker/PersistableMessage.h"
+#include <boost/intrusive_ptr.hpp>
+
+namespace mrg {
+namespace msgstore {
+
+class DataTokenImpl : public journal::data_tok, public qpid::RefCounted
+{
+ private:
+ boost::intrusive_ptr<qpid::broker::PersistableMessage> sourceMsg;
+ public:
+ DataTokenImpl();
+ virtual ~DataTokenImpl();
+
+ inline boost::intrusive_ptr<qpid::broker::PersistableMessage>& getSourceMessage() { return sourceMsg; }
+ inline void setSourceMessage(const boost::intrusive_ptr<qpid::broker::PersistableMessage>& msg) { sourceMsg = msg; }
+};
+
+} // namespace msgstore
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_DATATOKENIMPL_H
diff --git a/cpp/src/qpid/legacystore/IdDbt.cpp b/cpp/src/qpid/legacystore/IdDbt.cpp
new file mode 100644
index 0000000000..d9edaf80e6
--- /dev/null
+++ b/cpp/src/qpid/legacystore/IdDbt.cpp
@@ -0,0 +1,42 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/legacystore/IdDbt.h"
+
+using namespace mrg::msgstore;
+
+IdDbt::IdDbt() : id(0)
+{
+ init();
+}
+
+IdDbt::IdDbt(u_int64_t _id) : id(_id)
+{
+ init();
+}
+
+void IdDbt::init()
+{
+ set_data(&id);
+ set_size(sizeof(u_int64_t));
+ set_ulen(sizeof(u_int64_t));
+ set_flags(DB_DBT_USERMEM);
+}
diff --git a/cpp/src/qpid/legacystore/IdDbt.h b/cpp/src/qpid/legacystore/IdDbt.h
new file mode 100644
index 0000000000..ecf5922963
--- /dev/null
+++ b/cpp/src/qpid/legacystore/IdDbt.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifndef QPID_LEGACYSTORE_IDDBT_H
+#define QPID_LEGACYSTORE_IDDBT_H
+
+#include "db-inc.h"
+
+namespace mrg{
+namespace msgstore{
+
+class IdDbt : public Dbt
+{
+ void init();
+public:
+ u_int64_t id;
+
+ IdDbt(u_int64_t id);
+ IdDbt();
+};
+
+}}
+
+#endif // ifndef QPID_LEGACYSTORE_IDDBT_H
diff --git a/cpp/src/qpid/legacystore/IdSequence.cpp b/cpp/src/qpid/legacystore/IdSequence.cpp
new file mode 100644
index 0000000000..975b1107e7
--- /dev/null
+++ b/cpp/src/qpid/legacystore/IdSequence.cpp
@@ -0,0 +1,40 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/legacystore/IdSequence.h"
+
+using namespace mrg::msgstore;
+using qpid::sys::Mutex;
+
+IdSequence::IdSequence() : id(1) {}
+
+u_int64_t IdSequence::next()
+{
+ Mutex::ScopedLock guard(lock);
+ if (!id) id++; // avoid 0 when folding around
+ return id++;
+}
+
+void IdSequence::reset(uint64_t value)
+{
+ //deliberately not threadsafe, used only on recovery
+ id = value;
+}
diff --git a/cpp/src/qpid/legacystore/IdSequence.h b/cpp/src/qpid/legacystore/IdSequence.h
new file mode 100644
index 0000000000..11d7ff61ca
--- /dev/null
+++ b/cpp/src/qpid/legacystore/IdSequence.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifndef QPID_LEGACYSTORE_IDSEQUENCE_H
+#define QPID_LEGACYSTORE_IDSEQUENCE_H
+
+#include "qpid/framing/amqp_types.h"
+#include "qpid/sys/Mutex.h"
+#include <sys/types.h>
+
+namespace mrg{
+namespace msgstore{
+
+class IdSequence
+{
+ qpid::sys::Mutex lock;
+ uint64_t id;
+public:
+ IdSequence();
+ uint64_t next();
+ void reset(uint64_t value);
+};
+
+}}
+
+#endif // ifndef QPID_LEGACYSTORE_IDSEQUENCE_H
diff --git a/cpp/src/qpid/legacystore/JournalImpl.cpp b/cpp/src/qpid/legacystore/JournalImpl.cpp
new file mode 100644
index 0000000000..ba3f2aecae
--- /dev/null
+++ b/cpp/src/qpid/legacystore/JournalImpl.cpp
@@ -0,0 +1,633 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/legacystore/JournalImpl.h"
+
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+#include "qpid/log/Statement.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qmf/org/apache/qpid/legacystore/ArgsJournalExpand.h"
+#include "qmf/org/apache/qpid/legacystore/EventCreated.h"
+#include "qmf/org/apache/qpid/legacystore/EventEnqThresholdExceeded.h"
+#include "qmf/org/apache/qpid/legacystore/EventFull.h"
+#include "qmf/org/apache/qpid/legacystore/EventRecovered.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/Timer.h"
+#include "qpid/legacystore/StoreException.h"
+
+using namespace mrg::msgstore;
+using namespace mrg::journal;
+using qpid::management::ManagementAgent;
+namespace _qmf = qmf::org::apache::qpid::legacystore;
+
+InactivityFireEvent::InactivityFireEvent(JournalImpl* p, const qpid::sys::Duration timeout):
+ qpid::sys::TimerTask(timeout, "JournalInactive:"+p->id()), _parent(p) {}
+
+void InactivityFireEvent::fire() { qpid::sys::Mutex::ScopedLock sl(_ife_lock); if (_parent) _parent->flushFire(); }
+
+GetEventsFireEvent::GetEventsFireEvent(JournalImpl* p, const qpid::sys::Duration timeout):
+ qpid::sys::TimerTask(timeout, "JournalGetEvents:"+p->id()), _parent(p) {}
+
+void GetEventsFireEvent::fire() { qpid::sys::Mutex::ScopedLock sl(_gefe_lock); if (_parent) _parent->getEventsFire(); }
+
+JournalImpl::JournalImpl(qpid::sys::Timer& timer_,
+ const std::string& journalId,
+ const std::string& journalDirectory,
+ const std::string& journalBaseFilename,
+ const qpid::sys::Duration getEventsTimeout,
+ const qpid::sys::Duration flushTimeout,
+ qpid::management::ManagementAgent* a,
+ DeleteCallback onDelete):
+ jcntl(journalId, journalDirectory, journalBaseFilename),
+ timer(timer_),
+ getEventsTimerSetFlag(false),
+ lastReadRid(0),
+ writeActivityFlag(false),
+ flushTriggeredFlag(true),
+ _xidp(0),
+ _datap(0),
+ _dlen(0),
+ _dtok(),
+ _external(false),
+ deleteCallback(onDelete)
+{
+ getEventsFireEventsPtr = new GetEventsFireEvent(this, getEventsTimeout);
+ inactivityFireEventPtr = new InactivityFireEvent(this, flushTimeout);
+ {
+ timer.start();
+ timer.add(inactivityFireEventPtr);
+ }
+
+ initManagement(a);
+
+ log(LOG_NOTICE, "Created");
+ std::ostringstream oss;
+ oss << "Journal directory = \"" << journalDirectory << "\"; Base file name = \"" << journalBaseFilename << "\"";
+ log(LOG_DEBUG, oss.str());
+}
+
+JournalImpl::~JournalImpl()
+{
+ if (deleteCallback) deleteCallback(*this);
+ if (_init_flag && !_stop_flag){
+ try { stop(true); } // NOTE: This will *block* until all outstanding disk aio calls are complete!
+ catch (const jexception& e) { log(LOG_ERROR, e.what()); }
+ }
+ getEventsFireEventsPtr->cancel();
+ inactivityFireEventPtr->cancel();
+ free_read_buffers();
+
+ if (_mgmtObject.get() != 0) {
+ _mgmtObject->resourceDestroy();
+ _mgmtObject.reset();
+ }
+
+ log(LOG_NOTICE, "Destroyed");
+}
+
+void
+JournalImpl::initManagement(qpid::management::ManagementAgent* a)
+{
+ _agent = a;
+ if (_agent != 0)
+ {
+ _mgmtObject = _qmf::Journal::shared_ptr (
+ new _qmf::Journal(_agent, this));
+
+ _mgmtObject->set_name(_jid);
+ _mgmtObject->set_directory(_jdir.dirname());
+ _mgmtObject->set_baseFileName(_base_filename);
+ _mgmtObject->set_readPageSize(JRNL_RMGR_PAGE_SIZE * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE);
+ _mgmtObject->set_readPages(JRNL_RMGR_PAGES);
+
+ // The following will be set on initialize(), but being properties, these must be set to 0 in the meantime
+ _mgmtObject->set_initialFileCount(0);
+ _mgmtObject->set_dataFileSize(0);
+ _mgmtObject->set_currentFileCount(0);
+ _mgmtObject->set_writePageSize(0);
+ _mgmtObject->set_writePages(0);
+
+ _agent->addObject(_mgmtObject, 0, true);
+ }
+}
+
+
+void
+JournalImpl::initialize(const u_int16_t num_jfiles,
+ const bool auto_expand,
+ const u_int16_t ae_max_jfiles,
+ const u_int32_t jfsize_sblks,
+ const u_int16_t wcache_num_pages,
+ const u_int32_t wcache_pgsize_sblks,
+ mrg::journal::aio_callback* const cbp)
+{
+ std::ostringstream oss;
+ oss << "Initialize; num_jfiles=" << num_jfiles << " jfsize_sblks=" << jfsize_sblks;
+ oss << " wcache_pgsize_sblks=" << wcache_pgsize_sblks;
+ oss << " wcache_num_pages=" << wcache_num_pages;
+ log(LOG_DEBUG, oss.str());
+ jcntl::initialize(num_jfiles, auto_expand, ae_max_jfiles, jfsize_sblks, wcache_num_pages, wcache_pgsize_sblks, cbp);
+ log(LOG_DEBUG, "Initialization complete");
+
+ if (_mgmtObject.get() != 0)
+ {
+ _mgmtObject->set_initialFileCount(_lpmgr.num_jfiles());
+ _mgmtObject->set_autoExpand(_lpmgr.is_ae());
+ _mgmtObject->set_currentFileCount(_lpmgr.num_jfiles());
+ _mgmtObject->set_maxFileCount(_lpmgr.ae_max_jfiles());
+ _mgmtObject->set_dataFileSize(_jfsize_sblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE);
+ _mgmtObject->set_writePageSize(wcache_pgsize_sblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE);
+ _mgmtObject->set_writePages(wcache_num_pages);
+ }
+ if (_agent != 0)
+ _agent->raiseEvent(qmf::org::apache::qpid::legacystore::EventCreated(_jid, _jfsize_sblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE, _lpmgr.num_jfiles()),
+ qpid::management::ManagementAgent::SEV_NOTE);
+}
+
+void
+JournalImpl::recover(const u_int16_t num_jfiles,
+ const bool auto_expand,
+ const u_int16_t ae_max_jfiles,
+ const u_int32_t jfsize_sblks,
+ const u_int16_t wcache_num_pages,
+ const u_int32_t wcache_pgsize_sblks,
+ mrg::journal::aio_callback* const cbp,
+ boost::ptr_list<msgstore::PreparedTransaction>* prep_tx_list_ptr,
+ u_int64_t& highest_rid,
+ u_int64_t queue_id)
+{
+ std::ostringstream oss1;
+ oss1 << "Recover; num_jfiles=" << num_jfiles << " jfsize_sblks=" << jfsize_sblks;
+ oss1 << " queue_id = 0x" << std::hex << queue_id << std::dec;
+ oss1 << " wcache_pgsize_sblks=" << wcache_pgsize_sblks;
+ oss1 << " wcache_num_pages=" << wcache_num_pages;
+ log(LOG_DEBUG, oss1.str());
+
+ if (_mgmtObject.get() != 0)
+ {
+ _mgmtObject->set_initialFileCount(_lpmgr.num_jfiles());
+ _mgmtObject->set_autoExpand(_lpmgr.is_ae());
+ _mgmtObject->set_currentFileCount(_lpmgr.num_jfiles());
+ _mgmtObject->set_maxFileCount(_lpmgr.ae_max_jfiles());
+ _mgmtObject->set_dataFileSize(_jfsize_sblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE);
+ _mgmtObject->set_writePageSize(wcache_pgsize_sblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE);
+ _mgmtObject->set_writePages(wcache_num_pages);
+ }
+
+ if (prep_tx_list_ptr) {
+ // Create list of prepared xids
+ std::vector<std::string> prep_xid_list;
+ for (msgstore::PreparedTransaction::list::iterator i = prep_tx_list_ptr->begin(); i != prep_tx_list_ptr->end(); i++) {
+ prep_xid_list.push_back(i->xid);
+ }
+
+ jcntl::recover(num_jfiles, auto_expand, ae_max_jfiles, jfsize_sblks, wcache_num_pages, wcache_pgsize_sblks,
+ cbp, &prep_xid_list, highest_rid);
+ } else {
+ jcntl::recover(num_jfiles, auto_expand, ae_max_jfiles, jfsize_sblks, wcache_num_pages, wcache_pgsize_sblks,
+ cbp, 0, highest_rid);
+ }
+
+ // Populate PreparedTransaction lists from _tmap
+ if (prep_tx_list_ptr)
+ {
+ for (msgstore::PreparedTransaction::list::iterator i = prep_tx_list_ptr->begin(); i != prep_tx_list_ptr->end(); i++) {
+ txn_data_list tdl = _tmap.get_tdata_list(i->xid); // tdl will be empty if xid not found
+ for (tdl_itr tdl_itr = tdl.begin(); tdl_itr < tdl.end(); tdl_itr++) {
+ if (tdl_itr->_enq_flag) { // enqueue op
+ i->enqueues->add(queue_id, tdl_itr->_rid);
+ } else { // dequeue op
+ i->dequeues->add(queue_id, tdl_itr->_drid);
+ }
+ }
+ }
+ }
+ std::ostringstream oss2;
+ oss2 << "Recover phase 1 complete; highest rid found = 0x" << std::hex << highest_rid;
+ oss2 << std::dec << "; emap.size=" << _emap.size() << "; tmap.size=" << _tmap.size();
+ oss2 << "; journal now read-only.";
+ log(LOG_DEBUG, oss2.str());
+
+ if (_mgmtObject.get() != 0)
+ {
+ _mgmtObject->inc_recordDepth(_emap.size());
+ _mgmtObject->inc_enqueues(_emap.size());
+ _mgmtObject->inc_txn(_tmap.size());
+ _mgmtObject->inc_txnEnqueues(_tmap.enq_cnt());
+ _mgmtObject->inc_txnDequeues(_tmap.deq_cnt());
+ }
+}
+
+void
+JournalImpl::recover_complete()
+{
+ jcntl::recover_complete();
+ log(LOG_DEBUG, "Recover phase 2 complete; journal now writable.");
+ if (_agent != 0)
+ _agent->raiseEvent(qmf::org::apache::qpid::legacystore::EventRecovered(_jid, _jfsize_sblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE, _lpmgr.num_jfiles(),
+ _emap.size(), _tmap.size(), _tmap.enq_cnt(), _tmap.deq_cnt()), qpid::management::ManagementAgent::SEV_NOTE);
+}
+
+//#define MAX_AIO_SLEEPS 1000000 // tot: ~10 sec
+//#define AIO_SLEEP_TIME_US 10 // 0.01 ms
+// Return true if content is recovered from store; false if content is external and must be recovered from an external store.
+// Throw exception for all errors.
+bool
+JournalImpl::loadMsgContent(u_int64_t rid, std::string& data, size_t length, size_t offset)
+{
+ qpid::sys::Mutex::ScopedLock sl(_read_lock);
+ if (_dtok.rid() != rid)
+ {
+ // Free any previous msg
+ free_read_buffers();
+
+ // Last read encountered out-of-order rids, check if this rid is in that list
+ bool oooFlag = false;
+ for (std::vector<u_int64_t>::const_iterator i=oooRidList.begin(); i!=oooRidList.end() && !oooFlag; i++) {
+ if (*i == rid) {
+ oooFlag = true;
+ }
+ }
+
+ // TODO: This is a brutal approach - very inefficient and slow. Rather introduce a system of remembering
+ // jumpover points and allow the read to jump back to the first known jumpover point - but this needs
+ // a mechanism in rrfc to accomplish it. Also helpful is a struct containing a journal address - a
+ // combination of lid/offset.
+ // NOTE: The second part of the if stmt (rid < lastReadRid) is required to handle browsing.
+ if (oooFlag || rid < lastReadRid) {
+ _rmgr.invalidate();
+ oooRidList.clear();
+ }
+ _dlen = 0;
+ _dtok.reset();
+ _dtok.set_wstate(DataTokenImpl::ENQ);
+ _dtok.set_rid(0);
+ _external = false;
+ size_t xlen = 0;
+ bool transient = false;
+ bool done = false;
+ bool rid_found = false;
+ while (!done) {
+ iores res = read_data_record(&_datap, _dlen, &_xidp, xlen, transient, _external, &_dtok);
+ switch (res) {
+ case mrg::journal::RHM_IORES_SUCCESS:
+ if (_dtok.rid() != rid) {
+ // Check if this is an out-of-order rid that may impact next read
+ if (_dtok.rid() > rid)
+ oooRidList.push_back(_dtok.rid());
+ free_read_buffers();
+ // Reset data token for next read
+ _dlen = 0;
+ _dtok.reset();
+ _dtok.set_wstate(DataTokenImpl::ENQ);
+ _dtok.set_rid(0);
+ } else {
+ rid_found = _dtok.rid() == rid;
+ lastReadRid = rid;
+ done = true;
+ }
+ break;
+ case mrg::journal::RHM_IORES_PAGE_AIOWAIT:
+ if (get_wr_events(&_aio_cmpl_timeout) == journal::jerrno::AIO_TIMEOUT) {
+ std::stringstream ss;
+ ss << "read_data_record() returned " << mrg::journal::iores_str(res);
+ ss << "; timed out waiting for page to be processed.";
+ throw jexception(mrg::journal::jerrno::JERR__TIMEOUT, ss.str().c_str(), "JournalImpl",
+ "loadMsgContent");
+ }
+ break;
+ default:
+ std::stringstream ss;
+ ss << "read_data_record() returned " << mrg::journal::iores_str(res);
+ throw jexception(mrg::journal::jerrno::JERR__UNEXPRESPONSE, ss.str().c_str(), "JournalImpl",
+ "loadMsgContent");
+ }
+ }
+ if (!rid_found) {
+ std::stringstream ss;
+ ss << "read_data_record() was unable to find rid 0x" << std::hex << rid << std::dec;
+ ss << " (" << rid << "); last rid found was 0x" << std::hex << _dtok.rid() << std::dec;
+ ss << " (" << _dtok.rid() << ")";
+ throw jexception(mrg::journal::jerrno::JERR__RECNFOUND, ss.str().c_str(), "JournalImpl", "loadMsgContent");
+ }
+ }
+
+ if (_external) return false;
+
+ u_int32_t hdr_offs = qpid::framing::Buffer(static_cast<char*>(_datap), sizeof(u_int32_t)).getLong() + sizeof(u_int32_t);
+ if (hdr_offs + offset + length > _dlen) {
+ data.append((const char*)_datap + hdr_offs + offset, _dlen - hdr_offs - offset);
+ } else {
+ data.append((const char*)_datap + hdr_offs + offset, length);
+ }
+ return true;
+}
+
+void
+JournalImpl::enqueue_data_record(const void* const data_buff, const size_t tot_data_len,
+ const size_t this_data_len, data_tok* dtokp, const bool transient)
+{
+ handleIoResult(jcntl::enqueue_data_record(data_buff, tot_data_len, this_data_len, dtokp, transient));
+
+ if (_mgmtObject.get() != 0)
+ {
+ _mgmtObject->inc_enqueues();
+ _mgmtObject->inc_recordDepth();
+ }
+}
+
+void
+JournalImpl::enqueue_extern_data_record(const size_t tot_data_len, data_tok* dtokp,
+ const bool transient)
+{
+ handleIoResult(jcntl::enqueue_extern_data_record(tot_data_len, dtokp, transient));
+
+ if (_mgmtObject.get() != 0)
+ {
+ _mgmtObject->inc_enqueues();
+ _mgmtObject->inc_recordDepth();
+ }
+}
+
+void
+JournalImpl::enqueue_txn_data_record(const void* const data_buff, const size_t tot_data_len,
+ const size_t this_data_len, data_tok* dtokp, const std::string& xid, const bool transient)
+{
+ bool txn_incr = _mgmtObject.get() != 0 ? _tmap.in_map(xid) : false;
+
+ handleIoResult(jcntl::enqueue_txn_data_record(data_buff, tot_data_len, this_data_len, dtokp, xid, transient));
+
+ if (_mgmtObject.get() != 0)
+ {
+ if (!txn_incr) // If this xid was not in _tmap, it will be now...
+ _mgmtObject->inc_txn();
+ _mgmtObject->inc_enqueues();
+ _mgmtObject->inc_txnEnqueues();
+ _mgmtObject->inc_recordDepth();
+ }
+}
+
+void
+JournalImpl::enqueue_extern_txn_data_record(const size_t tot_data_len, data_tok* dtokp,
+ const std::string& xid, const bool transient)
+{
+ bool txn_incr = _mgmtObject.get() != 0 ? _tmap.in_map(xid) : false;
+
+ handleIoResult(jcntl::enqueue_extern_txn_data_record(tot_data_len, dtokp, xid, transient));
+
+ if (_mgmtObject.get() != 0)
+ {
+ if (!txn_incr) // If this xid was not in _tmap, it will be now...
+ _mgmtObject->inc_txn();
+ _mgmtObject->inc_enqueues();
+ _mgmtObject->inc_txnEnqueues();
+ _mgmtObject->inc_recordDepth();
+ }
+}
+
+void
+JournalImpl::dequeue_data_record(data_tok* const dtokp, const bool txn_coml_commit)
+{
+ handleIoResult(jcntl::dequeue_data_record(dtokp, txn_coml_commit));
+
+ if (_mgmtObject.get() != 0)
+ {
+ _mgmtObject->inc_dequeues();
+ _mgmtObject->inc_txnDequeues();
+ _mgmtObject->dec_recordDepth();
+ }
+}
+
+void
+JournalImpl::dequeue_txn_data_record(data_tok* const dtokp, const std::string& xid, const bool txn_coml_commit)
+{
+ bool txn_incr = _mgmtObject.get() != 0 ? _tmap.in_map(xid) : false;
+
+ handleIoResult(jcntl::dequeue_txn_data_record(dtokp, xid, txn_coml_commit));
+
+ if (_mgmtObject.get() != 0)
+ {
+ if (!txn_incr) // If this xid was not in _tmap, it will be now...
+ _mgmtObject->inc_txn();
+ _mgmtObject->inc_dequeues();
+ _mgmtObject->inc_txnDequeues();
+ _mgmtObject->dec_recordDepth();
+ }
+}
+
+void
+JournalImpl::txn_abort(data_tok* const dtokp, const std::string& xid)
+{
+ handleIoResult(jcntl::txn_abort(dtokp, xid));
+
+ if (_mgmtObject.get() != 0)
+ {
+ _mgmtObject->dec_txn();
+ _mgmtObject->inc_txnAborts();
+ }
+}
+
+void
+JournalImpl::txn_commit(data_tok* const dtokp, const std::string& xid)
+{
+ handleIoResult(jcntl::txn_commit(dtokp, xid));
+
+ if (_mgmtObject.get() != 0)
+ {
+ _mgmtObject->dec_txn();
+ _mgmtObject->inc_txnCommits();
+ }
+}
+
+void
+JournalImpl::stop(bool block_till_aio_cmpl)
+{
+ InactivityFireEvent* ifep = dynamic_cast<InactivityFireEvent*>(inactivityFireEventPtr.get());
+ assert(ifep); // dynamic_cast can return null if the cast fails
+ ifep->cancel();
+ jcntl::stop(block_till_aio_cmpl);
+
+ if (_mgmtObject.get() != 0) {
+ _mgmtObject->resourceDestroy();
+ _mgmtObject.reset();
+ }
+}
+
+iores
+JournalImpl::flush(const bool block_till_aio_cmpl)
+{
+ const iores res = jcntl::flush(block_till_aio_cmpl);
+ {
+ qpid::sys::Mutex::ScopedLock sl(_getf_lock);
+ if (_wmgr.get_aio_evt_rem() && !getEventsTimerSetFlag) { setGetEventTimer(); }
+ }
+ return res;
+}
+
+void
+JournalImpl::log(mrg::journal::log_level ll, const std::string& log_stmt) const
+{
+ log(ll, log_stmt.c_str());
+}
+
+void
+JournalImpl::log(mrg::journal::log_level ll, const char* const log_stmt) const
+{
+ switch (ll)
+ {
+ case LOG_TRACE: QPID_LOG(trace, "Journal \"" << _jid << "\": " << log_stmt); break;
+ case LOG_DEBUG: QPID_LOG(debug, "Journal \"" << _jid << "\": " << log_stmt); break;
+ case LOG_INFO: QPID_LOG(info, "Journal \"" << _jid << "\": " << log_stmt); break;
+ case LOG_NOTICE: QPID_LOG(notice, "Journal \"" << _jid << "\": " << log_stmt); break;
+ case LOG_WARN: QPID_LOG(warning, "Journal \"" << _jid << "\": " << log_stmt); break;
+ case LOG_ERROR: QPID_LOG(error, "Journal \"" << _jid << "\": " << log_stmt); break;
+ case LOG_CRITICAL: QPID_LOG(critical, "Journal \"" << _jid << "\": " << log_stmt); break;
+ }
+}
+
+void
+JournalImpl::getEventsFire()
+{
+ qpid::sys::Mutex::ScopedLock sl(_getf_lock);
+ getEventsTimerSetFlag = false;
+ if (_wmgr.get_aio_evt_rem()) { jcntl::get_wr_events(0); }
+ if (_wmgr.get_aio_evt_rem()) { setGetEventTimer(); }
+}
+
+void
+JournalImpl::flushFire()
+{
+ if (writeActivityFlag) {
+ writeActivityFlag = false;
+ flushTriggeredFlag = false;
+ } else {
+ if (!flushTriggeredFlag) {
+ flush();
+ flushTriggeredFlag = true;
+ }
+ }
+ inactivityFireEventPtr->setupNextFire();
+ {
+ timer.add(inactivityFireEventPtr);
+ }
+}
+
+void
+JournalImpl::wr_aio_cb(std::vector<data_tok*>& dtokl)
+{
+ for (std::vector<data_tok*>::const_iterator i=dtokl.begin(); i!=dtokl.end(); i++)
+ {
+ DataTokenImpl* dtokp = static_cast<DataTokenImpl*>(*i);
+ if (/*!is_stopped() &&*/ dtokp->getSourceMessage())
+ {
+ switch (dtokp->wstate())
+ {
+ case data_tok::ENQ:
+ dtokp->getSourceMessage()->enqueueComplete();
+ break;
+ case data_tok::DEQ:
+/* Don't need to signal until we have a way to ack completion of dequeue in AMQP
+ dtokp->getSourceMessage()->dequeueComplete();
+ if ( dtokp->getSourceMessage()->isDequeueComplete() ) // clear id after last dequeue
+ dtokp->getSourceMessage()->setPersistenceId(0);
+*/
+ break;
+ default: ;
+ }
+ }
+ dtokp->release();
+ }
+}
+
+void
+JournalImpl::rd_aio_cb(std::vector<u_int16_t>& /*pil*/)
+{}
+
+void
+JournalImpl::free_read_buffers()
+{
+ if (_xidp) {
+ ::free(_xidp);
+ _xidp = 0;
+ _datap = 0;
+ } else if (_datap) {
+ ::free(_datap);
+ _datap = 0;
+ }
+}
+
+void
+JournalImpl::handleIoResult(const iores r)
+{
+ writeActivityFlag = true;
+ switch (r)
+ {
+ case mrg::journal::RHM_IORES_SUCCESS:
+ return;
+ case mrg::journal::RHM_IORES_ENQCAPTHRESH:
+ {
+ std::ostringstream oss;
+ oss << "Enqueue capacity threshold exceeded on queue \"" << _jid << "\".";
+ log(LOG_WARN, oss.str());
+ if (_agent != 0)
+ _agent->raiseEvent(qmf::org::apache::qpid::legacystore::EventEnqThresholdExceeded(_jid, "Journal enqueue capacity threshold exceeded"),
+ qpid::management::ManagementAgent::SEV_WARN);
+ THROW_STORE_FULL_EXCEPTION(oss.str());
+ }
+ case mrg::journal::RHM_IORES_FULL:
+ {
+ std::ostringstream oss;
+ oss << "Journal full on queue \"" << _jid << "\".";
+ log(LOG_CRITICAL, oss.str());
+ if (_agent != 0)
+ _agent->raiseEvent(qmf::org::apache::qpid::legacystore::EventFull(_jid, "Journal full"), qpid::management::ManagementAgent::SEV_ERROR);
+ THROW_STORE_FULL_EXCEPTION(oss.str());
+ }
+ default:
+ {
+ std::ostringstream oss;
+ oss << "Unexpected I/O response (" << mrg::journal::iores_str(r) << ") on queue " << _jid << "\".";
+ log(LOG_ERROR, oss.str());
+ THROW_STORE_FULL_EXCEPTION(oss.str());
+ }
+ }
+}
+
+qpid::management::Manageable::status_t JournalImpl::ManagementMethod (uint32_t methodId,
+ qpid::management::Args& /*args*/,
+ std::string& /*text*/)
+{
+ Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
+
+ switch (methodId)
+ {
+ case _qmf::Journal::METHOD_EXPAND :
+ //_qmf::ArgsJournalExpand& eArgs = (_qmf::ArgsJournalExpand&) args;
+
+ // Implement "expand" using eArgs.i_by (expand-by argument)
+
+ status = Manageable::STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ return status;
+}
diff --git a/cpp/src/qpid/legacystore/JournalImpl.h b/cpp/src/qpid/legacystore/JournalImpl.h
new file mode 100644
index 0000000000..7227b2ffd4
--- /dev/null
+++ b/cpp/src/qpid/legacystore/JournalImpl.h
@@ -0,0 +1,265 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifndef QPID_LEGACYSTORE_JOURNALIMPL_H
+#define QPID_LEGACYSTORE_JOURNALIMPL_H
+
+#include <set>
+#include "qpid/legacystore/jrnl/enums.h"
+#include "qpid/legacystore/jrnl/jcntl.h"
+#include "qpid/legacystore/DataTokenImpl.h"
+#include "qpid/legacystore/PreparedTransaction.h"
+#include "qpid/broker/PersistableQueue.h"
+#include "qpid/sys/Timer.h"
+#include "qpid/sys/Time.h"
+#include <boost/ptr_container/ptr_list.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include "qpid/management/Manageable.h"
+#include "qmf/org/apache/qpid/legacystore/Journal.h"
+
+namespace qpid { namespace sys {
+class Timer;
+}}
+
+namespace mrg {
+namespace msgstore {
+
+class JournalImpl;
+
+class InactivityFireEvent : public qpid::sys::TimerTask
+{
+ JournalImpl* _parent;
+ qpid::sys::Mutex _ife_lock;
+
+ public:
+ InactivityFireEvent(JournalImpl* p, const qpid::sys::Duration timeout);
+ virtual ~InactivityFireEvent() {}
+ void fire();
+ inline void cancel() { qpid::sys::Mutex::ScopedLock sl(_ife_lock); _parent = 0; }
+};
+
+class GetEventsFireEvent : public qpid::sys::TimerTask
+{
+ JournalImpl* _parent;
+ qpid::sys::Mutex _gefe_lock;
+
+ public:
+ GetEventsFireEvent(JournalImpl* p, const qpid::sys::Duration timeout);
+ virtual ~GetEventsFireEvent() {}
+ void fire();
+ inline void cancel() { qpid::sys::Mutex::ScopedLock sl(_gefe_lock); _parent = 0; }
+};
+
+class JournalImpl : public qpid::broker::ExternalQueueStore, public mrg::journal::jcntl, public mrg::journal::aio_callback
+{
+ public:
+ typedef boost::function<void (JournalImpl&)> DeleteCallback;
+
+ private:
+// static qpid::sys::Mutex _static_lock;
+// static u_int32_t cnt;
+
+ qpid::sys::Timer& timer;
+ bool getEventsTimerSetFlag;
+ boost::intrusive_ptr<qpid::sys::TimerTask> getEventsFireEventsPtr;
+ qpid::sys::Mutex _getf_lock;
+ qpid::sys::Mutex _read_lock;
+
+ u_int64_t lastReadRid; // rid of last read msg for loadMsgContent() - detects out-of-order read requests
+ std::vector<u_int64_t> oooRidList; // list of out-of-order rids (greater than current rid) encountered during read sequence
+
+ bool writeActivityFlag;
+ bool flushTriggeredFlag;
+ boost::intrusive_ptr<qpid::sys::TimerTask> inactivityFireEventPtr;
+
+ // temp local vars for loadMsgContent below
+ void* _xidp;
+ void* _datap;
+ size_t _dlen;
+ mrg::journal::data_tok _dtok;
+ bool _external;
+
+ qpid::management::ManagementAgent* _agent;
+ qmf::org::apache::qpid::legacystore::Journal::shared_ptr _mgmtObject;
+ DeleteCallback deleteCallback;
+
+ public:
+
+ JournalImpl(qpid::sys::Timer& timer,
+ const std::string& journalId,
+ const std::string& journalDirectory,
+ const std::string& journalBaseFilename,
+ const qpid::sys::Duration getEventsTimeout,
+ const qpid::sys::Duration flushTimeout,
+ qpid::management::ManagementAgent* agent,
+ DeleteCallback deleteCallback=DeleteCallback() );
+
+ virtual ~JournalImpl();
+
+ void initManagement(qpid::management::ManagementAgent* agent);
+
+ void initialize(const u_int16_t num_jfiles,
+ const bool auto_expand,
+ const u_int16_t ae_max_jfiles,
+ const u_int32_t jfsize_sblks,
+ const u_int16_t wcache_num_pages,
+ const u_int32_t wcache_pgsize_sblks,
+ mrg::journal::aio_callback* const cbp);
+
+ inline void initialize(const u_int16_t num_jfiles,
+ const bool auto_expand,
+ const u_int16_t ae_max_jfiles,
+ const u_int32_t jfsize_sblks,
+ const u_int16_t wcache_num_pages,
+ const u_int32_t wcache_pgsize_sblks) {
+ initialize(num_jfiles, auto_expand, ae_max_jfiles, jfsize_sblks, wcache_num_pages, wcache_pgsize_sblks,
+ this);
+ }
+
+ void recover(const u_int16_t num_jfiles,
+ const bool auto_expand,
+ const u_int16_t ae_max_jfiles,
+ const u_int32_t jfsize_sblks,
+ const u_int16_t wcache_num_pages,
+ const u_int32_t wcache_pgsize_sblks,
+ mrg::journal::aio_callback* const cbp,
+ boost::ptr_list<msgstore::PreparedTransaction>* prep_tx_list_ptr,
+ u_int64_t& highest_rid,
+ u_int64_t queue_id);
+
+ inline void recover(const u_int16_t num_jfiles,
+ const bool auto_expand,
+ const u_int16_t ae_max_jfiles,
+ const u_int32_t jfsize_sblks,
+ const u_int16_t wcache_num_pages,
+ const u_int32_t wcache_pgsize_sblks,
+ boost::ptr_list<msgstore::PreparedTransaction>* prep_tx_list_ptr,
+ u_int64_t& highest_rid,
+ u_int64_t queue_id) {
+ recover(num_jfiles, auto_expand, ae_max_jfiles, jfsize_sblks, wcache_num_pages, wcache_pgsize_sblks,
+ this, prep_tx_list_ptr, highest_rid, queue_id);
+ }
+
+ void recover_complete();
+
+ // Temporary fn to read and save last msg read from journal so it can be assigned
+ // in chunks. To be replaced when coding to do this direct from the journal is ready.
+ // Returns true if the record is extern, false if local.
+ bool loadMsgContent(u_int64_t rid, std::string& data, size_t length, size_t offset = 0);
+
+ // Overrides for write inactivity timer
+ void enqueue_data_record(const void* const data_buff, const size_t tot_data_len,
+ const size_t this_data_len, mrg::journal::data_tok* dtokp,
+ const bool transient = false);
+
+ void enqueue_extern_data_record(const size_t tot_data_len, mrg::journal::data_tok* dtokp,
+ const bool transient = false);
+
+ void enqueue_txn_data_record(const void* const data_buff, const size_t tot_data_len,
+ const size_t this_data_len, mrg::journal::data_tok* dtokp, const std::string& xid,
+ const bool transient = false);
+
+ void enqueue_extern_txn_data_record(const size_t tot_data_len, mrg::journal::data_tok* dtokp,
+ const std::string& xid, const bool transient = false);
+
+ void dequeue_data_record(mrg::journal::data_tok* const dtokp, const bool txn_coml_commit = false);
+
+ void dequeue_txn_data_record(mrg::journal::data_tok* const dtokp, const std::string& xid, const bool txn_coml_commit = false);
+
+ void txn_abort(mrg::journal::data_tok* const dtokp, const std::string& xid);
+
+ void txn_commit(mrg::journal::data_tok* const dtokp, const std::string& xid);
+
+ void stop(bool block_till_aio_cmpl = false);
+
+ // Logging
+ void log(mrg::journal::log_level level, const std::string& log_stmt) const;
+ void log(mrg::journal::log_level level, const char* const log_stmt) const;
+
+ // Overrides for get_events timer
+ mrg::journal::iores flush(const bool block_till_aio_cmpl = false);
+
+ // TimerTask callback
+ void getEventsFire();
+ void flushFire();
+
+ // AIO callbacks
+ virtual void wr_aio_cb(std::vector<mrg::journal::data_tok*>& dtokl);
+ virtual void rd_aio_cb(std::vector<u_int16_t>& pil);
+
+ qpid::management::ManagementObject::shared_ptr GetManagementObject (void) const
+ { return _mgmtObject; }
+
+ qpid::management::Manageable::status_t ManagementMethod (uint32_t,
+ qpid::management::Args&,
+ std::string&);
+
+ void resetDeleteCallback() { deleteCallback = DeleteCallback(); }
+
+ private:
+ void free_read_buffers();
+
+ inline void setGetEventTimer()
+ {
+ getEventsFireEventsPtr->setupNextFire();
+ timer.add(getEventsFireEventsPtr);
+ getEventsTimerSetFlag = true;
+ }
+ void handleIoResult(const mrg::journal::iores r);
+
+ // Management instrumentation callbacks overridden from jcntl
+ inline void instr_incr_outstanding_aio_cnt() {
+ if (_mgmtObject.get() != 0) _mgmtObject->inc_outstandingAIOs();
+ }
+ inline void instr_decr_outstanding_aio_cnt() {
+ if (_mgmtObject.get() != 0) _mgmtObject->dec_outstandingAIOs();
+ }
+
+}; // class JournalImpl
+
+class TplJournalImpl : public JournalImpl
+{
+ public:
+ TplJournalImpl(qpid::sys::Timer& timer,
+ const std::string& journalId,
+ const std::string& journalDirectory,
+ const std::string& journalBaseFilename,
+ const qpid::sys::Duration getEventsTimeout,
+ const qpid::sys::Duration flushTimeout,
+ qpid::management::ManagementAgent* agent) :
+ JournalImpl(timer, journalId, journalDirectory, journalBaseFilename, getEventsTimeout, flushTimeout, agent)
+ {}
+
+ virtual ~TplJournalImpl() {}
+
+ // Special version of read_data_record that ignores transactions - needed when reading the TPL
+ inline mrg::journal::iores read_data_record(void** const datapp, std::size_t& dsize,
+ void** const xidpp, std::size_t& xidsize, bool& transient, bool& external,
+ mrg::journal::data_tok* const dtokp) {
+ return JournalImpl::read_data_record(datapp, dsize, xidpp, xidsize, transient, external, dtokp, true);
+ }
+ inline void read_reset() { _rmgr.invalidate(); }
+}; // class TplJournalImpl
+
+} // namespace msgstore
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JOURNALIMPL_H
diff --git a/cpp/src/qpid/legacystore/MessageStoreImpl.cpp b/cpp/src/qpid/legacystore/MessageStoreImpl.cpp
new file mode 100644
index 0000000000..69e9f48a17
--- /dev/null
+++ b/cpp/src/qpid/legacystore/MessageStoreImpl.cpp
@@ -0,0 +1,1732 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/legacystore/MessageStoreImpl.h"
+
+#include "qpid/legacystore/BindingDbt.h"
+#include "qpid/legacystore/BufferValue.h"
+#include "qpid/legacystore/IdDbt.h"
+#include "qpid/legacystore/jrnl/txn_map.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/log/Statement.h"
+#include "qmf/org/apache/qpid/legacystore/Package.h"
+#include "qpid/legacystore/StoreException.h"
+#include <dirent.h>
+#include <db.h>
+
+#define MAX_AIO_SLEEPS 100000 // tot: ~1 sec
+#define AIO_SLEEP_TIME_US 10 // 0.01 ms
+
+namespace _qmf = qmf::org::apache::qpid::legacystore;
+
+namespace mrg {
+namespace msgstore {
+
+
+const std::string MessageStoreImpl::storeTopLevelDir("rhm"); // Sets the top-level store dir name
+// FIXME aconway 2010-03-09: was 10
+qpid::sys::Duration MessageStoreImpl::defJournalGetEventsTimeout(1 * qpid::sys::TIME_MSEC); // 10ms
+qpid::sys::Duration MessageStoreImpl::defJournalFlushTimeout(500 * qpid::sys::TIME_MSEC); // 0.5s
+qpid::sys::Mutex TxnCtxt::globalSerialiser;
+
+MessageStoreImpl::TplRecoverStruct::TplRecoverStruct(const u_int64_t _rid,
+ const bool _deq_flag,
+ const bool _commit_flag,
+ const bool _tpc_flag) :
+ rid(_rid),
+ deq_flag(_deq_flag),
+ commit_flag(_commit_flag),
+ tpc_flag(_tpc_flag)
+{}
+
+MessageStoreImpl::MessageStoreImpl(qpid::broker::Broker* broker_, const char* envpath) :
+ numJrnlFiles(0),
+ autoJrnlExpand(false),
+ autoJrnlExpandMaxFiles(0),
+ jrnlFsizeSblks(0),
+ truncateFlag(false),
+ wCachePgSizeSblks(0),
+ wCacheNumPages(0),
+ tplNumJrnlFiles(0),
+ tplJrnlFsizeSblks(0),
+ tplWCachePgSizeSblks(0),
+ tplWCacheNumPages(0),
+ highestRid(0),
+ isInit(false),
+ envPath(envpath),
+ broker(broker_),
+ mgmtObject(),
+ agent(0)
+{}
+
+u_int16_t MessageStoreImpl::chkJrnlNumFilesParam(const u_int16_t param, const std::string paramName)
+{
+ u_int16_t p = param;
+ if (p < JRNL_MIN_NUM_FILES) {
+ p = JRNL_MIN_NUM_FILES;
+ QPID_LOG(warning, "parameter " << paramName << " (" << param << ") is below allowable minimum (" << JRNL_MIN_NUM_FILES << "); changing this parameter to minimum value.");
+ } else if (p > JRNL_MAX_NUM_FILES) {
+ p = JRNL_MAX_NUM_FILES;
+ QPID_LOG(warning, "parameter " << paramName << " (" << param << ") is above allowable maximum (" << JRNL_MAX_NUM_FILES << "); changing this parameter to maximum value.");
+ }
+ return p;
+}
+
+u_int32_t MessageStoreImpl::chkJrnlFileSizeParam(const u_int32_t param, const std::string paramName, const u_int32_t wCachePgSizeSblks)
+{
+ u_int32_t p = param;
+ u_int32_t min = JRNL_MIN_FILE_SIZE / JRNL_RMGR_PAGE_SIZE;
+ u_int32_t max = JRNL_MAX_FILE_SIZE / JRNL_RMGR_PAGE_SIZE;
+ if (p < min) {
+ p = min;
+ QPID_LOG(warning, "parameter " << paramName << " (" << param << ") is below allowable minimum (" << min << "); changing this parameter to minimum value.");
+ } else if (p > max) {
+ p = max;
+ QPID_LOG(warning, "parameter " << paramName << " (" << param << ") is above allowable maximum (" << max << "); changing this parameter to maximum value.");
+ }
+ if (wCachePgSizeSblks > p * JRNL_RMGR_PAGE_SIZE) {
+ std::ostringstream oss;
+ oss << "Cannot create store with file size less than write page cache size. [file size = " << p << " (" << (p * JRNL_RMGR_PAGE_SIZE / 2) << " kB); write page cache = " << (wCachePgSizeSblks / 2) << " kB]";
+ THROW_STORE_EXCEPTION(oss.str());
+ }
+ return p;
+}
+
+u_int32_t MessageStoreImpl::chkJrnlWrPageCacheSize(const u_int32_t param, const std::string paramName, const u_int16_t jrnlFsizePgs)
+{
+ u_int32_t p = param;
+ switch (p)
+ {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case 16:
+ case 32:
+ case 64:
+ case 128:
+ if (jrnlFsizePgs == 1) {
+ p = 64;
+ QPID_LOG(warning, "parameter " << paramName << " (" << param << ") cannot set a page size greater than the journal file size; changing this parameter to the journal file size (" << p << ")");
+ }
+ break;
+ default:
+ if (p == 0) {
+ // For zero value, use default
+ p = JRNL_WMGR_DEF_PAGE_SIZE * JRNL_DBLK_SIZE * JRNL_SBLK_SIZE / 1024;
+ QPID_LOG(warning, "parameter " << paramName << " (" << param << ") must be a power of 2 between 1 and 128; changing this parameter to default value (" << p << ")");
+ } else {
+ // For any positive value, use closest value
+ if (p < 6) p = 4;
+ else if (p < 12) p = 8;
+ else if (p < 24) p = 16;
+ else if (p < 48) p = 32;
+ else if (p < 96) p = 64;
+ else p = 128;
+ QPID_LOG(warning, "parameter " << paramName << " (" << param << ") must be a power of 2 between 1 and 128; changing this parameter to closest allowable value (" << p << ")");
+ }
+ }
+ return p;
+}
+
+u_int16_t MessageStoreImpl::getJrnlWrNumPages(const u_int32_t wrPageSizeKib)
+{
+ u_int32_t wrPageSizeSblks = wrPageSizeKib * 1024 / JRNL_DBLK_SIZE / JRNL_SBLK_SIZE; // convert from KiB to number sblks
+ u_int32_t defTotWCacheSize = JRNL_WMGR_DEF_PAGE_SIZE * JRNL_WMGR_DEF_PAGES; // in sblks. Currently 2014 sblks (1 MiB).
+ switch (wrPageSizeKib)
+ {
+ case 1:
+ case 2:
+ case 4:
+ // 256 KiB total cache
+ return defTotWCacheSize / wrPageSizeSblks / 4;
+ case 8:
+ case 16:
+ // 512 KiB total cache
+ return defTotWCacheSize / wrPageSizeSblks / 2;
+ default: // 32, 64, 128
+ // 1 MiB total cache
+ return defTotWCacheSize / wrPageSizeSblks;
+ }
+}
+
+void MessageStoreImpl::chkJrnlAutoExpandOptions(const StoreOptions* opts,
+ bool& autoJrnlExpand,
+ u_int16_t& autoJrnlExpandMaxFiles,
+ const std::string& autoJrnlExpandMaxFilesParamName,
+ const u_int16_t numJrnlFiles,
+ const std::string& numJrnlFilesParamName)
+{
+ if (!opts->autoJrnlExpand) {
+ // auto-expand disabled
+ autoJrnlExpand = false;
+ autoJrnlExpandMaxFiles = 0;
+ return;
+ }
+ u_int16_t p = opts->autoJrnlExpandMaxFiles;
+ if (numJrnlFiles == JRNL_MAX_NUM_FILES) {
+ // num-jfiles at max; disable auto-expand
+ autoJrnlExpand = false;
+ autoJrnlExpandMaxFiles = 0;
+ QPID_LOG(warning, "parameter " << autoJrnlExpandMaxFilesParamName << " (" << p << ") must be higher than parameter "
+ << numJrnlFilesParamName << " (" << numJrnlFiles << ") which is at the maximum allowable value; disabling auto-expand.");
+ return;
+ }
+ if (p > JRNL_MAX_NUM_FILES) {
+ // auto-expand-max-jfiles higher than max allowable, adjust
+ autoJrnlExpand = true;
+ autoJrnlExpandMaxFiles = JRNL_MAX_NUM_FILES;
+ QPID_LOG(warning, "parameter " << autoJrnlExpandMaxFilesParamName << " (" << p << ") is above allowable maximum ("
+ << JRNL_MAX_NUM_FILES << "); changing this parameter to maximum value.");
+ return;
+ }
+ if (p && p == defAutoJrnlExpandMaxFiles && numJrnlFiles != defTplNumJrnlFiles) {
+ // num-jfiles is different from the default AND max-auto-expand-jfiles is still at default
+ // change value of max-auto-expand-jfiles
+ autoJrnlExpand = true;
+ if (2 * numJrnlFiles <= JRNL_MAX_NUM_FILES) {
+ autoJrnlExpandMaxFiles = 2 * numJrnlFiles <= JRNL_MAX_NUM_FILES ? 2 * numJrnlFiles : JRNL_MAX_NUM_FILES;
+ QPID_LOG(warning, "parameter " << autoJrnlExpandMaxFilesParamName << " adjusted from its default value ("
+ << defAutoJrnlExpandMaxFiles << ") to twice that of parameter " << numJrnlFilesParamName << " (" << autoJrnlExpandMaxFiles << ").");
+ } else {
+ autoJrnlExpandMaxFiles = 2 * numJrnlFiles <= JRNL_MAX_NUM_FILES ? 2 * numJrnlFiles : JRNL_MAX_NUM_FILES;
+ QPID_LOG(warning, "parameter " << autoJrnlExpandMaxFilesParamName << " adjusted from its default to maximum allowable value ("
+ << JRNL_MAX_NUM_FILES << ") because of the value of " << numJrnlFilesParamName << " (" << numJrnlFiles << ").");
+ }
+ return;
+ }
+ // No adjustments req'd, set values
+ autoJrnlExpand = true;
+ autoJrnlExpandMaxFiles = p;
+}
+
+void MessageStoreImpl::initManagement ()
+{
+ if (broker != 0) {
+ agent = broker->getManagementAgent();
+ if (agent != 0) {
+ _qmf::Package packageInitializer(agent);
+ mgmtObject = _qmf::Store::shared_ptr (
+ new _qmf::Store(agent, this, broker));
+
+ mgmtObject->set_location(storeDir);
+ mgmtObject->set_defaultInitialFileCount(numJrnlFiles);
+ mgmtObject->set_defaultDataFileSize(jrnlFsizeSblks / JRNL_RMGR_PAGE_SIZE);
+ mgmtObject->set_tplIsInitialized(false);
+ mgmtObject->set_tplDirectory(getTplBaseDir());
+ mgmtObject->set_tplWritePageSize(tplWCachePgSizeSblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE);
+ mgmtObject->set_tplWritePages(tplWCacheNumPages);
+ mgmtObject->set_tplInitialFileCount(tplNumJrnlFiles);
+ mgmtObject->set_tplDataFileSize(tplJrnlFsizeSblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE);
+ mgmtObject->set_tplCurrentFileCount(tplNumJrnlFiles);
+
+ agent->addObject(mgmtObject, 0, true);
+
+ // Initialize all existing queues (ie those recovered before management was initialized)
+ for (JournalListMapItr i=journalList.begin(); i!=journalList.end(); i++) {
+ i->second->initManagement(agent);
+ }
+ }
+ }
+}
+
+bool MessageStoreImpl::init(const qpid::Options* options)
+{
+ // Extract and check options
+ const StoreOptions* opts = static_cast<const StoreOptions*>(options);
+ u_int16_t numJrnlFiles = chkJrnlNumFilesParam(opts->numJrnlFiles, "num-jfiles");
+ u_int32_t jrnlFsizePgs = chkJrnlFileSizeParam(opts->jrnlFsizePgs, "jfile-size-pgs");
+ u_int32_t jrnlWrCachePageSizeKib = chkJrnlWrPageCacheSize(opts->wCachePageSizeKib, "wcache-page-size", jrnlFsizePgs);
+ u_int16_t tplNumJrnlFiles = chkJrnlNumFilesParam(opts->tplNumJrnlFiles, "tpl-num-jfiles");
+ u_int32_t tplJrnlFSizePgs = chkJrnlFileSizeParam(opts->tplJrnlFsizePgs, "tpl-jfile-size-pgs");
+ u_int32_t tplJrnlWrCachePageSizeKib = chkJrnlWrPageCacheSize(opts->tplWCachePageSizeKib, "tpl-wcache-page-size", tplJrnlFSizePgs);
+ bool autoJrnlExpand;
+ u_int16_t autoJrnlExpandMaxFiles;
+ chkJrnlAutoExpandOptions(opts, autoJrnlExpand, autoJrnlExpandMaxFiles, "auto-expand-max-jfiles", numJrnlFiles, "num-jfiles");
+
+ // Pass option values to init(...)
+ return init(opts->storeDir, numJrnlFiles, jrnlFsizePgs, opts->truncateFlag, jrnlWrCachePageSizeKib, tplNumJrnlFiles, tplJrnlFSizePgs, tplJrnlWrCachePageSizeKib, autoJrnlExpand, autoJrnlExpandMaxFiles);
+}
+
+// These params, taken from options, are assumed to be correct and verified
+bool MessageStoreImpl::init(const std::string& dir,
+ u_int16_t jfiles,
+ u_int32_t jfileSizePgs,
+ const bool truncateFlag,
+ u_int32_t wCachePageSizeKib,
+ u_int16_t tplJfiles,
+ u_int32_t tplJfileSizePgs,
+ u_int32_t tplWCachePageSizeKib,
+ bool autoJExpand,
+ u_int16_t autoJExpandMaxFiles)
+{
+ if (isInit) return true;
+
+ // Set geometry members (converting to correct units where req'd)
+ numJrnlFiles = jfiles;
+ jrnlFsizeSblks = jfileSizePgs * JRNL_RMGR_PAGE_SIZE;
+ wCachePgSizeSblks = wCachePageSizeKib * 1024 / JRNL_DBLK_SIZE / JRNL_SBLK_SIZE; // convert from KiB to number sblks
+ wCacheNumPages = getJrnlWrNumPages(wCachePageSizeKib);
+ tplNumJrnlFiles = tplJfiles;
+ tplJrnlFsizeSblks = tplJfileSizePgs * JRNL_RMGR_PAGE_SIZE;
+ tplWCachePgSizeSblks = tplWCachePageSizeKib * 1024 / JRNL_DBLK_SIZE / JRNL_SBLK_SIZE; // convert from KiB to number sblks
+ tplWCacheNumPages = getJrnlWrNumPages(tplWCachePageSizeKib);
+ autoJrnlExpand = autoJExpand;
+ autoJrnlExpandMaxFiles = autoJExpandMaxFiles;
+ if (dir.size()>0) storeDir = dir;
+
+ if (truncateFlag)
+ truncateInit(false);
+ else
+ init();
+
+ QPID_LOG(notice, "Store module initialized; store-dir=" << dir);
+ QPID_LOG(info, "> Default files per journal: " << jfiles);
+// TODO: Uncomment these lines when auto-expand is enabled.
+// QPID_LOG(info, "> Auto-expand " << (autoJrnlExpand ? "enabled" : "disabled"));
+// if (autoJrnlExpand) QPID_LOG(info, "> Max auto-expand journal files: " << autoJrnlExpandMaxFiles);
+ QPID_LOG(info, "> Default journal file size: " << jfileSizePgs << " (wpgs)");
+ QPID_LOG(info, "> Default write cache page size: " << wCachePageSizeKib << " (KiB)");
+ QPID_LOG(info, "> Default number of write cache pages: " << wCacheNumPages);
+ QPID_LOG(info, "> TPL files per journal: " << tplNumJrnlFiles);
+ QPID_LOG(info, "> TPL journal file size: " << tplJfileSizePgs << " (wpgs)");
+ QPID_LOG(info, "> TPL write cache page size: " << tplWCachePageSizeKib << " (KiB)");
+ QPID_LOG(info, "> TPL number of write cache pages: " << tplWCacheNumPages);
+
+ return isInit;
+}
+
+void MessageStoreImpl::init()
+{
+ const int retryMax = 3;
+ int bdbRetryCnt = 0;
+ do {
+ if (bdbRetryCnt++ > 0)
+ {
+ closeDbs();
+ ::usleep(1000000); // 1 sec delay
+ QPID_LOG(error, "Previoius BDB store initialization failed, retrying (" << bdbRetryCnt << " of " << retryMax << ")...");
+ }
+
+ try {
+ journal::jdir::create_dir(getBdbBaseDir());
+
+ dbenv.reset(new DbEnv(0));
+ dbenv->set_errpfx("msgstore");
+ dbenv->set_lg_regionmax(256000); // default = 65000
+ dbenv->open(getBdbBaseDir().c_str(), DB_THREAD | DB_CREATE | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_USE_ENVIRON | DB_RECOVER, 0);
+
+ // Databases are constructed here instead of the constructor so that the DB_RECOVER flag can be used
+ // against the database environment. Recover can only be performed if no databases have been created
+ // against the environment at the time of recovery, as recovery invalidates the environment.
+ queueDb.reset(new Db(dbenv.get(), 0));
+ dbs.push_back(queueDb);
+ configDb.reset(new Db(dbenv.get(), 0));
+ dbs.push_back(configDb);
+ exchangeDb.reset(new Db(dbenv.get(), 0));
+ dbs.push_back(exchangeDb);
+ mappingDb.reset(new Db(dbenv.get(), 0));
+ dbs.push_back(mappingDb);
+ bindingDb.reset(new Db(dbenv.get(), 0));
+ dbs.push_back(bindingDb);
+ generalDb.reset(new Db(dbenv.get(), 0));
+ dbs.push_back(generalDb);
+
+ TxnCtxt txn;
+ txn.begin(dbenv.get(), false);
+ try {
+ open(queueDb, txn.get(), "queues.db", false);
+ open(configDb, txn.get(), "config.db", false);
+ open(exchangeDb, txn.get(), "exchanges.db", false);
+ open(mappingDb, txn.get(), "mappings.db", true);
+ open(bindingDb, txn.get(), "bindings.db", true);
+ open(generalDb, txn.get(), "general.db", false);
+ txn.commit();
+ } catch (...) { txn.abort(); throw; }
+ // NOTE: during normal initialization, agent == 0 because the store is initialized before the management infrastructure.
+ // However during a truncated initialization in a cluster, agent != 0. We always pass 0 as the agent for the
+ // TplStore to keep things consistent in a cluster. See https://bugzilla.redhat.com/show_bug.cgi?id=681026
+ tplStorePtr.reset(new TplJournalImpl(broker->getTimer(), "TplStore", getTplBaseDir(), "tpl", defJournalGetEventsTimeout, defJournalFlushTimeout, 0));
+ isInit = true;
+ } catch (const DbException& e) {
+ if (e.get_errno() == DB_VERSION_MISMATCH)
+ {
+ QPID_LOG(error, "Database environment mismatch: This version of db4 does not match that which created the store database.: " << e.what());
+ THROW_STORE_EXCEPTION_2("Database environment mismatch: This version of db4 does not match that which created the store database. "
+ "(If recovery is not important, delete the contents of the store directory. Otherwise, try upgrading the database using "
+ "db_upgrade or using db_recover - but the db4-utils package must also be installed to use these utilities.)", e);
+ }
+ QPID_LOG(error, "BDB exception occurred while initializing store: " << e.what());
+ if (bdbRetryCnt >= retryMax)
+ THROW_STORE_EXCEPTION_2("BDB exception occurred while initializing store", e);
+ } catch (const StoreException&) {
+ throw;
+ } catch (const journal::jexception& e) {
+ QPID_LOG(error, "Journal Exception occurred while initializing store: " << e);
+ THROW_STORE_EXCEPTION_2("Journal Exception occurred while initializing store", e.what());
+ } catch (...) {
+ QPID_LOG(error, "Unknown exception occurred while initializing store.");
+ throw;
+ }
+ } while (!isInit);
+}
+
+void MessageStoreImpl::finalize()
+{
+ if (tplStorePtr.get() && tplStorePtr->is_ready()) tplStorePtr->stop(true);
+ {
+ qpid::sys::Mutex::ScopedLock sl(journalListLock);
+ for (JournalListMapItr i = journalList.begin(); i != journalList.end(); i++)
+ {
+ JournalImpl* jQueue = i->second;
+ jQueue->resetDeleteCallback();
+ if (jQueue->is_ready()) jQueue->stop(true);
+ }
+ }
+
+ if (mgmtObject.get() != 0) {
+ mgmtObject->resourceDestroy();
+ mgmtObject.reset();
+ }
+}
+
+void MessageStoreImpl::truncateInit(const bool saveStoreContent)
+{
+ if (isInit) {
+ {
+ qpid::sys::Mutex::ScopedLock sl(journalListLock);
+ if (journalList.size()) { // check no queues exist
+ std::ostringstream oss;
+ oss << "truncateInit() called with " << journalList.size() << " queues still in existence";
+ THROW_STORE_EXCEPTION(oss.str());
+ }
+ }
+ closeDbs();
+ dbs.clear();
+ if (tplStorePtr->is_ready()) tplStorePtr->stop(true);
+ dbenv->close(0);
+ isInit = false;
+ }
+ std::ostringstream oss;
+ oss << storeDir << "/" << storeTopLevelDir;
+ if (saveStoreContent) {
+ std::string dir = mrg::journal::jdir::push_down(storeDir, storeTopLevelDir, "cluster");
+ QPID_LOG(notice, "Store directory " << oss.str() << " was pushed down (saved) into directory " << dir << ".");
+ } else {
+ mrg::journal::jdir::delete_dir(oss.str().c_str());
+ QPID_LOG(notice, "Store directory " << oss.str() << " was truncated.");
+ }
+ init();
+}
+
+void MessageStoreImpl::chkTplStoreInit()
+{
+ // Prevent multiple threads from late-initializing the TPL
+ qpid::sys::Mutex::ScopedLock sl(tplInitLock);
+ if (!tplStorePtr->is_ready()) {
+ journal::jdir::create_dir(getTplBaseDir());
+ tplStorePtr->initialize(tplNumJrnlFiles, false, 0, tplJrnlFsizeSblks, tplWCacheNumPages, tplWCachePgSizeSblks);
+ if (mgmtObject.get() != 0) mgmtObject->set_tplIsInitialized(true);
+ }
+}
+
+void MessageStoreImpl::open(db_ptr db,
+ DbTxn* txn,
+ const char* file,
+ bool dupKey)
+{
+ if(dupKey) db->set_flags(DB_DUPSORT);
+ db->open(txn, file, 0, DB_BTREE, DB_CREATE | DB_THREAD, 0);
+}
+
+void MessageStoreImpl::closeDbs()
+{
+ for (std::list<db_ptr >::iterator i = dbs.begin(); i != dbs.end(); i++) {
+ (*i)->close(0);
+ }
+ dbs.clear();
+}
+
+MessageStoreImpl::~MessageStoreImpl()
+{
+ finalize();
+ try {
+ closeDbs();
+ } catch (const DbException& e) {
+ QPID_LOG(error, "Error closing BDB databases: " << e.what());
+ } catch (const journal::jexception& e) {
+ QPID_LOG(error, "Error: " << e.what());
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Error: " << e.what());
+ } catch (...) {
+ QPID_LOG(error, "Unknown error in MessageStoreImpl::~MessageStoreImpl()");
+ }
+
+ if (mgmtObject.get() != 0) {
+ mgmtObject->resourceDestroy();
+ mgmtObject.reset();
+ }
+}
+
+void MessageStoreImpl::create(qpid::broker::PersistableQueue& queue,
+ const qpid::framing::FieldTable& args)
+{
+ checkInit();
+ if (queue.getPersistenceId()) {
+ THROW_STORE_EXCEPTION("Queue already created: " + queue.getName());
+ }
+ JournalImpl* jQueue = 0;
+ qpid::framing::FieldTable::ValuePtr value;
+
+ u_int16_t localFileCount = numJrnlFiles;
+ bool localAutoExpandFlag = autoJrnlExpand;
+ u_int16_t localAutoExpandMaxFileCount = autoJrnlExpandMaxFiles;
+ u_int32_t localFileSizeSblks = jrnlFsizeSblks;
+
+ value = args.get("qpid.file_count");
+ if (value.get() != 0 && !value->empty() && value->convertsTo<int>())
+ localFileCount = chkJrnlNumFilesParam((u_int16_t) value->get<int>(), "qpid.file_count");
+
+ value = args.get("qpid.file_size");
+ if (value.get() != 0 && !value->empty() && value->convertsTo<int>())
+ localFileSizeSblks = chkJrnlFileSizeParam((u_int32_t) value->get<int>(), "qpid.file_size", wCachePgSizeSblks) * JRNL_RMGR_PAGE_SIZE;
+
+ if (queue.getName().size() == 0)
+ {
+ QPID_LOG(error, "Cannot create store for empty (null) queue name - ignoring and attempting to continue.");
+ return;
+ }
+
+ jQueue = new JournalImpl(broker->getTimer(), queue.getName(), getJrnlDir(queue), std::string("JournalData"),
+ defJournalGetEventsTimeout, defJournalFlushTimeout, agent,
+ boost::bind(&MessageStoreImpl::journalDeleted, this, _1));
+ {
+ qpid::sys::Mutex::ScopedLock sl(journalListLock);
+ journalList[queue.getName()]=jQueue;
+ }
+
+ value = args.get("qpid.auto_expand");
+ if (value.get() != 0 && !value->empty() && value->convertsTo<bool>())
+ localAutoExpandFlag = (bool) value->get<bool>();
+
+ value = args.get("qpid.auto_expand_max_jfiles");
+ if (value.get() != 0 && !value->empty() && value->convertsTo<int>())
+ localAutoExpandMaxFileCount = (u_int16_t) value->get<int>();
+
+ queue.setExternalQueueStore(dynamic_cast<qpid::broker::ExternalQueueStore*>(jQueue));
+ try {
+ // init will create the deque's for the init...
+ jQueue->initialize(localFileCount, localAutoExpandFlag, localAutoExpandMaxFileCount, localFileSizeSblks, wCacheNumPages, wCachePgSizeSblks);
+ } catch (const journal::jexception& e) {
+ THROW_STORE_EXCEPTION(std::string("Queue ") + queue.getName() + ": create() failed: " + e.what());
+ }
+ try {
+ if (!create(queueDb, queueIdSequence, queue)) {
+ THROW_STORE_EXCEPTION("Queue already exists: " + queue.getName());
+ }
+ } catch (const DbException& e) {
+ THROW_STORE_EXCEPTION_2("Error creating queue named " + queue.getName(), e);
+ }
+}
+
+void MessageStoreImpl::destroy(qpid::broker::PersistableQueue& queue)
+{
+ checkInit();
+ destroy(queueDb, queue);
+ deleteBindingsForQueue(queue);
+ qpid::broker::ExternalQueueStore* eqs = queue.getExternalQueueStore();
+ if (eqs) {
+ JournalImpl* jQueue = static_cast<JournalImpl*>(eqs);
+ jQueue->delete_jrnl_files();
+ queue.setExternalQueueStore(0); // will delete the journal if exists
+ {
+ qpid::sys::Mutex::ScopedLock sl(journalListLock);
+ journalList.erase(queue.getName());
+ }
+ }
+}
+
+void MessageStoreImpl::create(const qpid::broker::PersistableExchange& exchange,
+ const qpid::framing::FieldTable& /*args*/)
+{
+ checkInit();
+ if (exchange.getPersistenceId()) {
+ THROW_STORE_EXCEPTION("Exchange already created: " + exchange.getName());
+ }
+ try {
+ if (!create(exchangeDb, exchangeIdSequence, exchange)) {
+ THROW_STORE_EXCEPTION("Exchange already exists: " + exchange.getName());
+ }
+ } catch (const DbException& e) {
+ THROW_STORE_EXCEPTION_2("Error creating exchange named " + exchange.getName(), e);
+ }
+}
+
+void MessageStoreImpl::destroy(const qpid::broker::PersistableExchange& exchange)
+{
+ checkInit();
+ destroy(exchangeDb, exchange);
+ //need to also delete bindings
+ IdDbt key(exchange.getPersistenceId());
+ bindingDb->del(0, &key, DB_AUTO_COMMIT);
+}
+
+void MessageStoreImpl::create(const qpid::broker::PersistableConfig& general)
+{
+ checkInit();
+ if (general.getPersistenceId()) {
+ THROW_STORE_EXCEPTION("General configuration item already created");
+ }
+ try {
+ if (!create(generalDb, generalIdSequence, general)) {
+ THROW_STORE_EXCEPTION("General configuration already exists");
+ }
+ } catch (const DbException& e) {
+ THROW_STORE_EXCEPTION_2("Error creating general configuration", e);
+ }
+}
+
+void MessageStoreImpl::destroy(const qpid::broker::PersistableConfig& general)
+{
+ checkInit();
+ destroy(generalDb, general);
+}
+
+bool MessageStoreImpl::create(db_ptr db,
+ IdSequence& seq,
+ const qpid::broker::Persistable& p)
+{
+ u_int64_t id (seq.next());
+ Dbt key(&id, sizeof(id));
+ BufferValue value (p);
+
+ int status;
+ TxnCtxt txn;
+ txn.begin(dbenv.get(), true);
+ try {
+ status = db->put(txn.get(), &key, &value, DB_NOOVERWRITE);
+ txn.commit();
+ } catch (...) {
+ txn.abort();
+ throw;
+ }
+ if (status == DB_KEYEXIST) {
+ return false;
+ } else {
+ p.setPersistenceId(id);
+ return true;
+ }
+}
+
+void MessageStoreImpl::destroy(db_ptr db, const qpid::broker::Persistable& p)
+{
+ qpid::sys::Mutex::ScopedLock sl(bdbLock);
+ IdDbt key(p.getPersistenceId());
+ db->del(0, &key, DB_AUTO_COMMIT);
+}
+
+
+void MessageStoreImpl::bind(const qpid::broker::PersistableExchange& e,
+ const qpid::broker::PersistableQueue& q,
+ const std::string& k,
+ const qpid::framing::FieldTable& a)
+{
+ checkInit();
+ IdDbt key(e.getPersistenceId());
+ BindingDbt value(e, q, k, a);
+ TxnCtxt txn;
+ txn.begin(dbenv.get(), true);
+ try {
+ put(bindingDb, txn.get(), key, value);
+ txn.commit();
+ } catch (...) {
+ txn.abort();
+ throw;
+ }
+}
+
+void MessageStoreImpl::unbind(const qpid::broker::PersistableExchange& e,
+ const qpid::broker::PersistableQueue& q,
+ const std::string& k,
+ const qpid::framing::FieldTable&)
+{
+ checkInit();
+ deleteBinding(e, q, k);
+}
+
+void MessageStoreImpl::recover(qpid::broker::RecoveryManager& registry)
+{
+ checkInit();
+ txn_list prepared;
+ recoverLockedMappings(prepared);
+
+ queue_index queues;//id->queue
+ exchange_index exchanges;//id->exchange
+ message_index messages;//id->message
+
+ TxnCtxt txn;
+ txn.begin(dbenv.get(), false);
+ try {
+ //read all queues, calls recoversMessages
+ recoverQueues(txn, registry, queues, prepared, messages);
+
+ //recover exchange & bindings:
+ recoverExchanges(txn, registry, exchanges);
+ recoverBindings(txn, exchanges, queues);
+
+ //recover general-purpose configuration
+ recoverGeneral(txn, registry);
+
+ txn.commit();
+ } catch (const DbException& e) {
+ txn.abort();
+ THROW_STORE_EXCEPTION_2("Error on recovery", e);
+ } catch (...) {
+ txn.abort();
+ throw;
+ }
+
+ //recover transactions:
+ for (txn_list::iterator i = prepared.begin(); i != prepared.end(); i++) {
+ const PreparedTransaction pt = *i;
+ if (mgmtObject.get() != 0) {
+ mgmtObject->inc_tplTransactionDepth();
+ mgmtObject->inc_tplTxnPrepares();
+ }
+
+ std::string xid = pt.xid;
+
+ // Restore data token state in TxnCtxt
+ TplRecoverMapCitr citr = tplRecoverMap.find(xid);
+ if (citr == tplRecoverMap.end()) THROW_STORE_EXCEPTION("XID not found in tplRecoverMap");
+
+ // If a record is found that is dequeued but not committed/aborted from tplStore, then a complete() call
+ // was interrupted part way through committing/aborting the impacted queues. Complete this process.
+ bool incomplTplTxnFlag = citr->second.deq_flag;
+
+ if (citr->second.tpc_flag) {
+ // Dtx (2PC) transaction
+ TPCTxnCtxt* tpcc = new TPCTxnCtxt(xid, &messageIdSequence);
+ std::auto_ptr<qpid::broker::TPCTransactionContext> txn(tpcc);
+ tpcc->recoverDtok(citr->second.rid, xid);
+ tpcc->prepare(tplStorePtr.get());
+
+ qpid::broker::RecoverableTransaction::shared_ptr dtx;
+ if (!incomplTplTxnFlag) dtx = registry.recoverTransaction(xid, txn);
+ if (pt.enqueues.get()) {
+ for (LockedMappings::iterator j = pt.enqueues->begin(); j != pt.enqueues->end(); j++) {
+ tpcc->addXidRecord(queues[j->first]->getExternalQueueStore());
+ if (!incomplTplTxnFlag) dtx->enqueue(queues[j->first], messages[j->second]);
+ }
+ }
+ if (pt.dequeues.get()) {
+ for (LockedMappings::iterator j = pt.dequeues->begin(); j != pt.dequeues->end(); j++) {
+ tpcc->addXidRecord(queues[j->first]->getExternalQueueStore());
+ if (!incomplTplTxnFlag) dtx->dequeue(queues[j->first], messages[j->second]);
+ }
+ }
+
+ if (incomplTplTxnFlag) {
+ tpcc->complete(citr->second.commit_flag);
+ }
+ } else {
+ // Local (1PC) transaction
+ boost::shared_ptr<TxnCtxt> opcc(new TxnCtxt(xid, &messageIdSequence));
+ opcc->recoverDtok(citr->second.rid, xid);
+ opcc->prepare(tplStorePtr.get());
+
+ if (pt.enqueues.get()) {
+ for (LockedMappings::iterator j = pt.enqueues->begin(); j != pt.enqueues->end(); j++) {
+ opcc->addXidRecord(queues[j->first]->getExternalQueueStore());
+ }
+ }
+ if (pt.dequeues.get()) {
+ for (LockedMappings::iterator j = pt.dequeues->begin(); j != pt.dequeues->end(); j++) {
+ opcc->addXidRecord(queues[j->first]->getExternalQueueStore());
+ }
+ }
+ if (incomplTplTxnFlag) {
+ opcc->complete(citr->second.commit_flag);
+ } else {
+ completed(*opcc.get(), citr->second.commit_flag);
+ }
+ }
+ }
+ registry.recoveryComplete();
+}
+
+void MessageStoreImpl::recoverQueues(TxnCtxt& txn,
+ qpid::broker::RecoveryManager& registry,
+ queue_index& queue_index,
+ txn_list& prepared,
+ message_index& messages)
+{
+ Cursor queues;
+ queues.open(queueDb, txn.get());
+
+ u_int64_t maxQueueId(1);
+
+ IdDbt key;
+ Dbt value;
+ //read all queues
+ while (queues.next(key, value)) {
+ qpid::framing::Buffer buffer(reinterpret_cast<char*>(value.get_data()), value.get_size());
+ //create a Queue instance
+ qpid::broker::RecoverableQueue::shared_ptr queue = registry.recoverQueue(buffer);
+ //set the persistenceId and update max as required
+ queue->setPersistenceId(key.id);
+
+ const std::string queueName = queue->getName().c_str();
+ JournalImpl* jQueue = 0;
+ if (queueName.size() == 0)
+ {
+ QPID_LOG(error, "Cannot recover empty (null) queue name - ignoring and attempting to continue.");
+ break;
+ }
+ jQueue = new JournalImpl(broker->getTimer(), queueName, getJrnlHashDir(queueName), std::string("JournalData"),
+ defJournalGetEventsTimeout, defJournalFlushTimeout, agent,
+ boost::bind(&MessageStoreImpl::journalDeleted, this, _1));
+ {
+ qpid::sys::Mutex::ScopedLock sl(journalListLock);
+ journalList[queueName] = jQueue;
+ }
+ queue->setExternalQueueStore(dynamic_cast<qpid::broker::ExternalQueueStore*>(jQueue));
+
+ try
+ {
+ long rcnt = 0L; // recovered msg count
+ long idcnt = 0L; // in-doubt msg count
+ u_int64_t thisHighestRid = 0ULL;
+ jQueue->recover(numJrnlFiles, autoJrnlExpand, autoJrnlExpandMaxFiles, jrnlFsizeSblks, wCacheNumPages, wCachePgSizeSblks, &prepared, thisHighestRid, key.id); // start recovery
+ if (highestRid == 0ULL)
+ highestRid = thisHighestRid;
+ else if (thisHighestRid - highestRid < 0x8000000000000000ULL) // RFC 1982 comparison for unsigned 64-bit
+ highestRid = thisHighestRid;
+ recoverMessages(txn, registry, queue, prepared, messages, rcnt, idcnt);
+ QPID_LOG(info, "Recovered queue \"" << queueName << "\": " << rcnt << " messages recovered; " << idcnt << " messages in-doubt.");
+ jQueue->recover_complete(); // start journal.
+ } catch (const journal::jexception& e) {
+ THROW_STORE_EXCEPTION(std::string("Queue ") + queueName + ": recoverQueues() failed: " + e.what());
+ }
+ //read all messages: done on a per queue basis if using Journal
+
+ queue_index[key.id] = queue;
+ maxQueueId = std::max(key.id, maxQueueId);
+ }
+
+ // NOTE: highestRid is set by both recoverQueues() and recoverTplStore() as
+ // the messageIdSequence is used for both queue journals and the tpl journal.
+ messageIdSequence.reset(highestRid + 1);
+ QPID_LOG(info, "Most recent persistence id found: 0x" << std::hex << highestRid << std::dec);
+
+ queueIdSequence.reset(maxQueueId + 1);
+}
+
+
+void MessageStoreImpl::recoverExchanges(TxnCtxt& txn,
+ qpid::broker::RecoveryManager& registry,
+ exchange_index& index)
+{
+ //TODO: this is a copy&paste from recoverQueues - refactor!
+ Cursor exchanges;
+ exchanges.open(exchangeDb, txn.get());
+
+ u_int64_t maxExchangeId(1);
+ IdDbt key;
+ Dbt value;
+ //read all exchanges
+ while (exchanges.next(key, value)) {
+ qpid::framing::Buffer buffer(reinterpret_cast<char*>(value.get_data()), value.get_size());
+ //create a Exchange instance
+ qpid::broker::RecoverableExchange::shared_ptr exchange = registry.recoverExchange(buffer);
+ if (exchange) {
+ //set the persistenceId and update max as required
+ exchange->setPersistenceId(key.id);
+ index[key.id] = exchange;
+ QPID_LOG(info, "Recovered exchange \"" << exchange->getName() << '"');
+ }
+ maxExchangeId = std::max(key.id, maxExchangeId);
+ }
+ exchangeIdSequence.reset(maxExchangeId + 1);
+}
+
+void MessageStoreImpl::recoverBindings(TxnCtxt& txn,
+ exchange_index& exchanges,
+ queue_index& queues)
+{
+ Cursor bindings;
+ bindings.open(bindingDb, txn.get());
+
+ IdDbt key;
+ Dbt value;
+ while (bindings.next(key, value)) {
+ qpid::framing::Buffer buffer(reinterpret_cast<char*>(value.get_data()), value.get_size());
+ if (buffer.available() < 8) {
+ QPID_LOG(error, "Not enough data for binding: " << buffer.available());
+ THROW_STORE_EXCEPTION("Not enough data for binding");
+ }
+ uint64_t queueId = buffer.getLongLong();
+ std::string queueName;
+ std::string routingkey;
+ qpid::framing::FieldTable args;
+ buffer.getShortString(queueName);
+ buffer.getShortString(routingkey);
+ buffer.get(args);
+ exchange_index::iterator exchange = exchanges.find(key.id);
+ queue_index::iterator queue = queues.find(queueId);
+ if (exchange != exchanges.end() && queue != queues.end()) {
+ //could use the recoverable queue here rather than the name...
+ exchange->second->bind(queueName, routingkey, args);
+ QPID_LOG(info, "Recovered binding exchange=" << exchange->second->getName()
+ << " key=" << routingkey
+ << " queue=" << queueName);
+ } else {
+ //stale binding, delete it
+ QPID_LOG(warning, "Deleting stale binding");
+ bindings->del(0);
+ }
+ }
+}
+
+void MessageStoreImpl::recoverGeneral(TxnCtxt& txn,
+ qpid::broker::RecoveryManager& registry)
+{
+ Cursor items;
+ items.open(generalDb, txn.get());
+
+ u_int64_t maxGeneralId(1);
+ IdDbt key;
+ Dbt value;
+ //read all items
+ while (items.next(key, value)) {
+ qpid::framing::Buffer buffer(reinterpret_cast<char*>(value.get_data()), value.get_size());
+ //create instance
+ qpid::broker::RecoverableConfig::shared_ptr config = registry.recoverConfig(buffer);
+ //set the persistenceId and update max as required
+ config->setPersistenceId(key.id);
+ maxGeneralId = std::max(key.id, maxGeneralId);
+ }
+ generalIdSequence.reset(maxGeneralId + 1);
+}
+
+void MessageStoreImpl::recoverMessages(TxnCtxt& /*txn*/,
+ qpid::broker::RecoveryManager& recovery,
+ qpid::broker::RecoverableQueue::shared_ptr& queue,
+ txn_list& prepared,
+ message_index& messages,
+ long& rcnt,
+ long& idcnt)
+{
+ size_t preambleLength = sizeof(u_int32_t)/*header size*/;
+
+ JournalImpl* jc = static_cast<JournalImpl*>(queue->getExternalQueueStore());
+ DataTokenImpl dtok;
+ size_t readSize = 0;
+ unsigned msg_count = 0;
+
+ // TODO: This optimization to skip reading if there are no enqueued messages to read
+ // breaks the python system test in phase 6 with "Exception: Cannot write lock file"
+ // Figure out what is breaking.
+ //bool read = jc->get_enq_cnt() > 0;
+ bool read = true;
+
+ void* dbuff = NULL; size_t dbuffSize = 0;
+ void* xidbuff = NULL; size_t xidbuffSize = 0;
+ bool transientFlag = false;
+ bool externalFlag = false;
+
+ dtok.set_wstate(DataTokenImpl::ENQ);
+
+ // Read the message from the Journal.
+ try {
+ unsigned aio_sleep_cnt = 0;
+ while (read) {
+ mrg::journal::iores res = jc->read_data_record(&dbuff, dbuffSize, &xidbuff, xidbuffSize, transientFlag, externalFlag, &dtok);
+ readSize = dtok.dsize();
+
+ switch (res)
+ {
+ case mrg::journal::RHM_IORES_SUCCESS: {
+ msg_count++;
+ qpid::broker::RecoverableMessage::shared_ptr msg;
+ char* data = (char*)dbuff;
+
+ unsigned headerSize;
+ if (externalFlag) {
+ msg = getExternMessage(recovery, dtok.rid(), headerSize); // large message external to jrnl
+ } else {
+ headerSize = qpid::framing::Buffer(data, preambleLength).getLong();
+ qpid::framing::Buffer headerBuff(data+ preambleLength, headerSize); /// do we want read size or header size ????
+ msg = recovery.recoverMessage(headerBuff);
+ }
+ msg->setPersistenceId(dtok.rid());
+ // At some future point if delivery attempts are stored, then this call would
+ // become optional depending on that information.
+ msg->setRedelivered();
+ // Reset the TTL for the recovered message
+ msg->computeExpiration(broker->getExpiryPolicy());
+
+ u_int32_t contentOffset = headerSize + preambleLength;
+ u_int64_t contentSize = readSize - contentOffset;
+ if (msg->loadContent(contentSize) && !externalFlag) {
+ //now read the content
+ qpid::framing::Buffer contentBuff(data + contentOffset, contentSize);
+ msg->decodeContent(contentBuff);
+ }
+
+ PreparedTransaction::list::iterator i = PreparedTransaction::getLockedPreparedTransaction(prepared, queue->getPersistenceId(), dtok.rid());
+ if (i == prepared.end()) { // not in prepared list
+ rcnt++;
+ queue->recover(msg);
+ } else {
+ u_int64_t rid = dtok.rid();
+ std::string xid(i->xid);
+ TplRecoverMapCitr citr = tplRecoverMap.find(xid);
+ if (citr == tplRecoverMap.end()) THROW_STORE_EXCEPTION("XID not found in tplRecoverMap");
+
+ // deq present in prepared list: this xid is part of incomplete txn commit/abort
+ // or this is a 1PC txn that must be rolled forward
+ if (citr->second.deq_flag || !citr->second.tpc_flag) {
+ if (jc->is_enqueued(rid, true)) {
+ // Enqueue is non-tx, dequeue tx
+ assert(jc->is_locked(rid)); // This record MUST be locked by a txn dequeue
+ if (!citr->second.commit_flag) {
+ rcnt++;
+ queue->recover(msg); // recover message in abort case only
+ }
+ } else {
+ // Enqueue and/or dequeue tx
+ journal::txn_map& tmap = jc->get_txn_map();
+ journal::txn_data_list txnList = tmap.get_tdata_list(xid); // txnList will be empty if xid not found
+ bool enq = false;
+ bool deq = false;
+ for (journal::tdl_itr j = txnList.begin(); j<txnList.end(); j++) {
+ if (j->_enq_flag && j->_rid == rid) enq = true;
+ else if (!j->_enq_flag && j->_drid == rid) deq = true;
+ }
+ if (enq && !deq && citr->second.commit_flag) {
+ rcnt++;
+ queue->recover(msg); // recover txn message in commit case only
+ }
+ }
+ } else {
+ idcnt++;
+ messages[rid] = msg;
+ }
+ }
+
+ dtok.reset();
+ dtok.set_wstate(DataTokenImpl::ENQ);
+
+ if (xidbuff)
+ ::free(xidbuff);
+ else if (dbuff)
+ ::free(dbuff);
+ aio_sleep_cnt = 0;
+ break;
+ }
+ case mrg::journal::RHM_IORES_PAGE_AIOWAIT:
+ if (++aio_sleep_cnt > MAX_AIO_SLEEPS)
+ THROW_STORE_EXCEPTION("Timeout waiting for AIO in MessageStoreImpl::recoverMessages()");
+ ::usleep(AIO_SLEEP_TIME_US);
+ break;
+ case mrg::journal::RHM_IORES_EMPTY:
+ read = false;
+ break; // done with all messages. (add call in jrnl to test that _emap is empty.)
+ default:
+ std::ostringstream oss;
+ oss << "recoverMessages(): Queue: " << queue->getName() << ": Unexpected return from journal read: " << mrg::journal::iores_str(res);
+ THROW_STORE_EXCEPTION(oss.str());
+ } // switch
+ } // while
+ } catch (const journal::jexception& e) {
+ THROW_STORE_EXCEPTION(std::string("Queue ") + queue->getName() + ": recoverMessages() failed: " + e.what());
+ }
+}
+
+qpid::broker::RecoverableMessage::shared_ptr MessageStoreImpl::getExternMessage(qpid::broker::RecoveryManager& /*recovery*/,
+ uint64_t /*messageId*/,
+ unsigned& /*headerSize*/)
+{
+ throw mrg::journal::jexception(mrg::journal::jerrno::JERR__NOTIMPL, "MessageStoreImpl", "getExternMessage");
+}
+
+int MessageStoreImpl::enqueueMessage(TxnCtxt& txn,
+ IdDbt& msgId,
+ qpid::broker::RecoverableMessage::shared_ptr& msg,
+ queue_index& index,
+ txn_list& prepared,
+ message_index& messages)
+{
+ Cursor mappings;
+ mappings.open(mappingDb, txn.get());
+
+ IdDbt value;
+
+ int count(0);
+ for (int status = mappings->get(&msgId, &value, DB_SET); status == 0; status = mappings->get(&msgId, &value, DB_NEXT_DUP)) {
+ if (index.find(value.id) == index.end()) {
+ QPID_LOG(warning, "Recovered message for queue that no longer exists");
+ mappings->del(0);
+ } else {
+ qpid::broker::RecoverableQueue::shared_ptr queue = index[value.id];
+ if (PreparedTransaction::isLocked(prepared, value.id, msgId.id)) {
+ messages[msgId.id] = msg;
+ } else {
+ queue->recover(msg);
+ }
+ count++;
+ }
+ }
+ mappings.close();
+ return count;
+}
+
+void MessageStoreImpl::readTplStore()
+{
+ tplRecoverMap.clear();
+ journal::txn_map& tmap = tplStorePtr->get_txn_map();
+ DataTokenImpl dtok;
+ void* dbuff = NULL; size_t dbuffSize = 0;
+ void* xidbuff = NULL; size_t xidbuffSize = 0;
+ bool transientFlag = false;
+ bool externalFlag = false;
+ bool done = false;
+ try {
+ unsigned aio_sleep_cnt = 0;
+ while (!done) {
+ dtok.reset();
+ dtok.set_wstate(DataTokenImpl::ENQ);
+ mrg::journal::iores res = tplStorePtr->read_data_record(&dbuff, dbuffSize, &xidbuff, xidbuffSize, transientFlag, externalFlag, &dtok);
+ switch (res) {
+ case mrg::journal::RHM_IORES_SUCCESS: {
+ // Every TPL record contains both data and an XID
+ assert(dbuffSize>0);
+ assert(xidbuffSize>0);
+ std::string xid(static_cast<const char*>(xidbuff), xidbuffSize);
+ bool is2PC = *(static_cast<char*>(dbuff)) != 0;
+
+ // Check transaction details; add to recover map
+ journal::txn_data_list txnList = tmap.get_tdata_list(xid); // txnList will be empty if xid not found
+ if (!txnList.empty()) { // xid found in tmap
+ unsigned enqCnt = 0;
+ unsigned deqCnt = 0;
+ u_int64_t rid = 0;
+
+ // Assume commit (roll forward) in cases where only prepare has been called - ie only enqueue record exists.
+ // Note: will apply to both 1PC and 2PC transactions.
+ bool commitFlag = true;
+
+ for (journal::tdl_itr j = txnList.begin(); j<txnList.end(); j++) {
+ if (j->_enq_flag) {
+ rid = j->_rid;
+ enqCnt++;
+ } else {
+ commitFlag = j->_commit_flag;
+ deqCnt++;
+ }
+ }
+ assert(enqCnt == 1);
+ assert(deqCnt <= 1);
+ tplRecoverMap.insert(TplRecoverMapPair(xid, TplRecoverStruct(rid, deqCnt == 1, commitFlag, is2PC)));
+ }
+
+ ::free(xidbuff);
+ aio_sleep_cnt = 0;
+ break;
+ }
+ case mrg::journal::RHM_IORES_PAGE_AIOWAIT:
+ if (++aio_sleep_cnt > MAX_AIO_SLEEPS)
+ THROW_STORE_EXCEPTION("Timeout waiting for AIO in MessageStoreImpl::recoverTplStore()");
+ ::usleep(AIO_SLEEP_TIME_US);
+ break;
+ case mrg::journal::RHM_IORES_EMPTY:
+ done = true;
+ break; // done with all messages. (add call in jrnl to test that _emap is empty.)
+ default:
+ std::ostringstream oss;
+ oss << "readTplStore(): Unexpected result from journal read: " << mrg::journal::iores_str(res);
+ THROW_STORE_EXCEPTION(oss.str());
+ } // switch
+ }
+ } catch (const journal::jexception& e) {
+ THROW_STORE_EXCEPTION(std::string("TPL recoverTplStore() failed: ") + e.what());
+ }
+}
+
+void MessageStoreImpl::recoverTplStore()
+{
+ if (journal::jdir::exists(tplStorePtr->jrnl_dir() + tplStorePtr->base_filename() + ".jinf")) {
+ u_int64_t thisHighestRid = 0ULL;
+ tplStorePtr->recover(tplNumJrnlFiles, false, 0, tplJrnlFsizeSblks, tplWCachePgSizeSblks, tplWCacheNumPages, 0, thisHighestRid, 0);
+ if (highestRid == 0ULL)
+ highestRid = thisHighestRid;
+ else if (thisHighestRid - highestRid < 0x8000000000000000ULL) // RFC 1982 comparison for unsigned 64-bit
+ highestRid = thisHighestRid;
+
+ // Load tplRecoverMap by reading the TPL store
+ readTplStore();
+
+ tplStorePtr->recover_complete(); // start journal.
+ }
+}
+
+void MessageStoreImpl::recoverLockedMappings(txn_list& txns)
+{
+ if (!tplStorePtr->is_ready())
+ recoverTplStore();
+
+ // Abort unprepared xids and populate the locked maps
+ for (TplRecoverMapCitr i = tplRecoverMap.begin(); i != tplRecoverMap.end(); i++) {
+ LockedMappings::shared_ptr enq_ptr;
+ enq_ptr.reset(new LockedMappings);
+ LockedMappings::shared_ptr deq_ptr;
+ deq_ptr.reset(new LockedMappings);
+ txns.push_back(new PreparedTransaction(i->first, enq_ptr, deq_ptr));
+ }
+}
+
+void MessageStoreImpl::collectPreparedXids(std::set<std::string>& xids)
+{
+ if (tplStorePtr->is_ready()) {
+ tplStorePtr->read_reset();
+ readTplStore();
+ } else {
+ recoverTplStore();
+ }
+ for (TplRecoverMapCitr i = tplRecoverMap.begin(); i != tplRecoverMap.end(); i++) {
+ // Discard all txns that are to be rolled forward/back and 1PC transactions
+ if (!i->second.deq_flag && i->second.tpc_flag)
+ xids.insert(i->first);
+ }
+}
+
+void MessageStoreImpl::stage(const boost::intrusive_ptr<qpid::broker::PersistableMessage>& /*msg*/)
+{
+ throw mrg::journal::jexception(mrg::journal::jerrno::JERR__NOTIMPL, "MessageStoreImpl", "stage");
+}
+
+void MessageStoreImpl::destroy(qpid::broker::PersistableMessage& /*msg*/)
+{
+ throw mrg::journal::jexception(mrg::journal::jerrno::JERR__NOTIMPL, "MessageStoreImpl", "destroy");
+}
+
+void MessageStoreImpl::appendContent(const boost::intrusive_ptr<const qpid::broker::PersistableMessage>& /*msg*/,
+ const std::string& /*data*/)
+{
+ throw mrg::journal::jexception(mrg::journal::jerrno::JERR__NOTIMPL, "MessageStoreImpl", "appendContent");
+}
+
+void MessageStoreImpl::loadContent(const qpid::broker::PersistableQueue& queue,
+ const boost::intrusive_ptr<const qpid::broker::PersistableMessage>& msg,
+ std::string& data,
+ u_int64_t offset,
+ u_int32_t length)
+{
+ checkInit();
+ u_int64_t messageId (msg->getPersistenceId());
+
+ if (messageId != 0) {
+ try {
+ JournalImpl* jc = static_cast<JournalImpl*>(queue.getExternalQueueStore());
+ if (jc && jc->is_enqueued(messageId) ) {
+ if (!jc->loadMsgContent(messageId, data, length, offset)) {
+ std::ostringstream oss;
+ oss << "Queue " << queue.getName() << ": loadContent() failed: Message " << messageId << " is extern";
+ THROW_STORE_EXCEPTION(oss.str());
+ }
+ } else {
+ std::ostringstream oss;
+ oss << "Queue " << queue.getName() << ": loadContent() failed: Message " << messageId << " not enqueued";
+ THROW_STORE_EXCEPTION(oss.str());
+ }
+ } catch (const journal::jexception& e) {
+ THROW_STORE_EXCEPTION(std::string("Queue ") + queue.getName() + ": loadContent() failed: " + e.what());
+ }
+ } else {
+ THROW_STORE_EXCEPTION("Cannot load content. Message not known to store!");
+ }
+}
+
+void MessageStoreImpl::flush(const qpid::broker::PersistableQueue& queue)
+{
+ if (queue.getExternalQueueStore() == 0) return;
+ checkInit();
+ std::string qn = queue.getName();
+ try {
+ JournalImpl* jc = static_cast<JournalImpl*>(queue.getExternalQueueStore());
+ if (jc) {
+ // TODO: check if this result should be used...
+ /*mrg::journal::iores res =*/ jc->flush();
+ }
+ } catch (const journal::jexception& e) {
+ THROW_STORE_EXCEPTION(std::string("Queue ") + qn + ": flush() failed: " + e.what() );
+ }
+}
+
+void MessageStoreImpl::enqueue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<qpid::broker::PersistableMessage>& msg,
+ const qpid::broker::PersistableQueue& queue)
+{
+ checkInit();
+ u_int64_t queueId (queue.getPersistenceId());
+ u_int64_t messageId (msg->getPersistenceId());
+ if (queueId == 0) {
+ THROW_STORE_EXCEPTION("Queue not created: " + queue.getName());
+ }
+
+ TxnCtxt implicit;
+ TxnCtxt* txn = 0;
+ if (ctxt) {
+ txn = check(ctxt);
+ } else {
+ txn = &implicit;
+ }
+
+ bool newId = false;
+ if (messageId == 0) {
+ messageId = messageIdSequence.next();
+ msg->setPersistenceId(messageId);
+ newId = true;
+ }
+ store(&queue, txn, msg, newId);
+
+ // add queue* to the txn map..
+ if (ctxt) txn->addXidRecord(queue.getExternalQueueStore());
+}
+
+u_int64_t MessageStoreImpl::msgEncode(std::vector<char>& buff, const boost::intrusive_ptr<qpid::broker::PersistableMessage>& message)
+{
+ u_int32_t headerSize = message->encodedHeaderSize();
+ u_int64_t size = message->encodedSize() + sizeof(u_int32_t);
+ try { buff = std::vector<char>(size); } // long + headers + content
+ catch (const std::exception& e) {
+ std::ostringstream oss;
+ oss << "Unable to allocate memory for encoding message; requested size: " << size << "; error: " << e.what();
+ THROW_STORE_EXCEPTION(oss.str());
+ }
+ qpid::framing::Buffer buffer(&buff[0],size);
+ buffer.putLong(headerSize);
+ message->encode(buffer);
+ return size;
+}
+
+void MessageStoreImpl::store(const qpid::broker::PersistableQueue* queue,
+ TxnCtxt* txn,
+ const boost::intrusive_ptr<qpid::broker::PersistableMessage>& message,
+ bool /*newId*/)
+{
+ std::vector<char> buff;
+ u_int64_t size = msgEncode(buff, message);
+
+ try {
+ if (queue) {
+ boost::intrusive_ptr<DataTokenImpl> dtokp(new DataTokenImpl);
+ dtokp->addRef();
+ dtokp->setSourceMessage(message);
+ dtokp->set_external_rid(true);
+ dtokp->set_rid(message->getPersistenceId()); // set the messageID into the Journal header (record-id)
+
+ JournalImpl* jc = static_cast<JournalImpl*>(queue->getExternalQueueStore());
+ if (txn->getXid().empty()) {
+ jc->enqueue_data_record(&buff[0], size, size, dtokp.get(), !message->isPersistent());
+ } else {
+ jc->enqueue_txn_data_record(&buff[0], size, size, dtokp.get(), txn->getXid(), !message->isPersistent());
+ }
+ } else {
+ THROW_STORE_EXCEPTION(std::string("MessageStoreImpl::store() failed: queue NULL."));
+ }
+ } catch (const journal::jexception& e) {
+ THROW_STORE_EXCEPTION(std::string("Queue ") + queue->getName() + ": MessageStoreImpl::store() failed: " +
+ e.what());
+ }
+}
+
+void MessageStoreImpl::dequeue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<qpid::broker::PersistableMessage>& msg,
+ const qpid::broker::PersistableQueue& queue)
+{
+ checkInit();
+ u_int64_t queueId (queue.getPersistenceId());
+ u_int64_t messageId (msg->getPersistenceId());
+ if (queueId == 0) {
+ THROW_STORE_EXCEPTION("Queue \"" + queue.getName() + "\" has null queue Id (has not been created)");
+ }
+ if (messageId == 0) {
+ THROW_STORE_EXCEPTION("Queue \"" + queue.getName() + "\": Dequeuing message with null persistence Id.");
+ }
+
+ TxnCtxt implicit;
+ TxnCtxt* txn = 0;
+ if (ctxt) {
+ txn = check(ctxt);
+ } else {
+ txn = &implicit;
+ }
+
+ // add queue* to the txn map..
+ if (ctxt) txn->addXidRecord(queue.getExternalQueueStore());
+ async_dequeue(ctxt, msg, queue);
+
+ msg->dequeueComplete();
+}
+
+void MessageStoreImpl::async_dequeue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<qpid::broker::PersistableMessage>& msg,
+ const qpid::broker::PersistableQueue& queue)
+{
+ boost::intrusive_ptr<DataTokenImpl> ddtokp(new DataTokenImpl);
+ ddtokp->setSourceMessage(msg);
+ ddtokp->set_external_rid(true);
+ ddtokp->set_rid(messageIdSequence.next());
+ ddtokp->set_dequeue_rid(msg->getPersistenceId());
+ ddtokp->set_wstate(DataTokenImpl::ENQ);
+ std::string tid;
+ if (ctxt) {
+ TxnCtxt* txn = check(ctxt);
+ tid = txn->getXid();
+ }
+ // Manually increase the ref count, as raw pointers are used beyond this point
+ ddtokp->addRef();
+ try {
+ JournalImpl* jc = static_cast<JournalImpl*>(queue.getExternalQueueStore());
+ if (tid.empty()) {
+ jc->dequeue_data_record(ddtokp.get());
+ } else {
+ jc->dequeue_txn_data_record(ddtokp.get(), tid);
+ }
+ } catch (const journal::jexception& e) {
+ ddtokp->release();
+ THROW_STORE_EXCEPTION(std::string("Queue ") + queue.getName() + ": async_dequeue() failed: " + e.what());
+ }
+}
+
+u_int32_t MessageStoreImpl::outstandingQueueAIO(const qpid::broker::PersistableQueue& /*queue*/)
+{
+ checkInit();
+ return 0;
+}
+
+void MessageStoreImpl::completed(TxnCtxt& txn,
+ bool commit)
+{
+ try {
+ chkTplStoreInit(); // Late initialize (if needed)
+
+ // Nothing to do if not prepared
+ if (txn.getDtok()->is_enqueued()) {
+ txn.incrDtokRef();
+ DataTokenImpl* dtokp = txn.getDtok();
+ dtokp->set_dequeue_rid(dtokp->rid());
+ dtokp->set_rid(messageIdSequence.next());
+ tplStorePtr->dequeue_txn_data_record(txn.getDtok(), txn.getXid(), commit);
+ }
+ txn.complete(commit);
+ if (mgmtObject.get() != 0) {
+ mgmtObject->dec_tplTransactionDepth();
+ if (commit)
+ mgmtObject->inc_tplTxnCommits();
+ else
+ mgmtObject->inc_tplTxnAborts();
+ }
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Error completing xid " << txn.getXid() << ": " << e.what());
+ throw;
+ }
+}
+
+std::auto_ptr<qpid::broker::TransactionContext> MessageStoreImpl::begin()
+{
+ checkInit();
+ // pass sequence number for c/a
+ return std::auto_ptr<qpid::broker::TransactionContext>(new TxnCtxt(&messageIdSequence));
+}
+
+std::auto_ptr<qpid::broker::TPCTransactionContext> MessageStoreImpl::begin(const std::string& xid)
+{
+ checkInit();
+ IdSequence* jtx = &messageIdSequence;
+ // pass sequence number for c/a
+ return std::auto_ptr<qpid::broker::TPCTransactionContext>(new TPCTxnCtxt(xid, jtx));
+}
+
+void MessageStoreImpl::prepare(qpid::broker::TPCTransactionContext& ctxt)
+{
+ checkInit();
+ TxnCtxt* txn = dynamic_cast<TxnCtxt*>(&ctxt);
+ if(!txn) throw qpid::broker::InvalidTransactionContextException();
+ localPrepare(txn);
+}
+
+void MessageStoreImpl::localPrepare(TxnCtxt* ctxt)
+{
+ try {
+ chkTplStoreInit(); // Late initialize (if needed)
+
+ // This sync is required to ensure multi-queue atomicity - ie all txn data
+ // must hit the disk on *all* queues before the TPL prepare (enq) is written.
+ ctxt->sync();
+
+ ctxt->incrDtokRef();
+ DataTokenImpl* dtokp = ctxt->getDtok();
+ dtokp->set_external_rid(true);
+ dtokp->set_rid(messageIdSequence.next());
+ char tpcFlag = static_cast<char>(ctxt->isTPC());
+ tplStorePtr->enqueue_txn_data_record(&tpcFlag, sizeof(char), sizeof(char), dtokp, ctxt->getXid(), false);
+ ctxt->prepare(tplStorePtr.get());
+ // make sure all the data is written to disk before returning
+ ctxt->sync();
+ if (mgmtObject.get() != 0) {
+ mgmtObject->inc_tplTransactionDepth();
+ mgmtObject->inc_tplTxnPrepares();
+ }
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Error preparing xid " << ctxt->getXid() << ": " << e.what());
+ throw;
+ }
+}
+
+void MessageStoreImpl::commit(qpid::broker::TransactionContext& ctxt)
+{
+ checkInit();
+ TxnCtxt* txn(check(&ctxt));
+ if (!txn->isTPC()) {
+ if (txn->impactedQueuesEmpty()) return;
+ localPrepare(dynamic_cast<TxnCtxt*>(txn));
+ }
+ completed(*dynamic_cast<TxnCtxt*>(txn), true);
+}
+
+void MessageStoreImpl::abort(qpid::broker::TransactionContext& ctxt)
+{
+ checkInit();
+ TxnCtxt* txn(check(&ctxt));
+ if (!txn->isTPC()) {
+ if (txn->impactedQueuesEmpty()) return;
+ localPrepare(dynamic_cast<TxnCtxt*>(txn));
+ }
+ completed(*dynamic_cast<TxnCtxt*>(txn), false);
+}
+
+TxnCtxt* MessageStoreImpl::check(qpid::broker::TransactionContext* ctxt)
+{
+ TxnCtxt* txn = dynamic_cast<TxnCtxt*>(ctxt);
+ if(!txn) throw qpid::broker::InvalidTransactionContextException();
+ return txn;
+}
+
+void MessageStoreImpl::put(db_ptr db,
+ DbTxn* txn,
+ Dbt& key,
+ Dbt& value)
+{
+ try {
+ int status = db->put(txn, &key, &value, DB_NODUPDATA);
+ if (status == DB_KEYEXIST) {
+ THROW_STORE_EXCEPTION("duplicate data");
+ } else if (status) {
+ THROW_STORE_EXCEPTION(DbEnv::strerror(status));
+ }
+ } catch (const DbException& e) {
+ THROW_STORE_EXCEPTION(e.what());
+ }
+}
+
+void MessageStoreImpl::deleteBindingsForQueue(const qpid::broker::PersistableQueue& queue)
+{
+ TxnCtxt txn;
+ txn.begin(dbenv.get(), true);
+ try {
+ {
+ Cursor bindings;
+ bindings.open(bindingDb, txn.get());
+
+ IdDbt key;
+ Dbt value;
+ while (bindings.next(key, value)) {
+ qpid::framing::Buffer buffer(reinterpret_cast<char*>(value.get_data()), value.get_size());
+ if (buffer.available() < 8) {
+ THROW_STORE_EXCEPTION("Not enough data for binding");
+ }
+ uint64_t queueId = buffer.getLongLong();
+ if (queue.getPersistenceId() == queueId) {
+ bindings->del(0);
+ QPID_LOG(debug, "Deleting binding for " << queue.getName() << " " << key.id << "->" << queueId);
+ }
+ }
+ }
+ txn.commit();
+ } catch (const std::exception& e) {
+ txn.abort();
+ THROW_STORE_EXCEPTION_2("Error deleting bindings", e.what());
+ } catch (...) {
+ txn.abort();
+ throw;
+ }
+ QPID_LOG(debug, "Deleted all bindings for " << queue.getName() << ":" << queue.getPersistenceId());
+}
+
+void MessageStoreImpl::deleteBinding(const qpid::broker::PersistableExchange& exchange,
+ const qpid::broker::PersistableQueue& queue,
+ const std::string& bkey)
+{
+ TxnCtxt txn;
+ txn.begin(dbenv.get(), true);
+ try {
+ {
+ Cursor bindings;
+ bindings.open(bindingDb, txn.get());
+
+ IdDbt key(exchange.getPersistenceId());
+ Dbt value;
+
+ for (int status = bindings->get(&key, &value, DB_SET); status == 0; status = bindings->get(&key, &value, DB_NEXT_DUP)) {
+ qpid::framing::Buffer buffer(reinterpret_cast<char*>(value.get_data()), value.get_size());
+ if (buffer.available() < 8) {
+ THROW_STORE_EXCEPTION("Not enough data for binding");
+ }
+ uint64_t queueId = buffer.getLongLong();
+ if (queue.getPersistenceId() == queueId) {
+ std::string q;
+ std::string k;
+ buffer.getShortString(q);
+ buffer.getShortString(k);
+ if (bkey == k) {
+ bindings->del(0);
+ QPID_LOG(debug, "Deleting binding for " << queue.getName() << " " << key.id << "->" << queueId);
+ }
+ }
+ }
+ }
+ txn.commit();
+ } catch (const std::exception& e) {
+ txn.abort();
+ THROW_STORE_EXCEPTION_2("Error deleting bindings", e.what());
+ } catch (...) {
+ txn.abort();
+ throw;
+ }
+}
+
+std::string MessageStoreImpl::getJrnlBaseDir()
+{
+ std::ostringstream dir;
+ dir << storeDir << "/" << storeTopLevelDir << "/jrnl/" ;
+ return dir.str();
+}
+
+std::string MessageStoreImpl::getBdbBaseDir()
+{
+ std::ostringstream dir;
+ dir << storeDir << "/" << storeTopLevelDir << "/dat/" ;
+ return dir.str();
+}
+
+std::string MessageStoreImpl::getTplBaseDir()
+{
+ std::ostringstream dir;
+ dir << storeDir << "/" << storeTopLevelDir << "/tpl/" ;
+ return dir.str();
+}
+
+std::string MessageStoreImpl::getJrnlDir(const qpid::broker::PersistableQueue& queue) //for exmaple /var/rhm/ + queueDir/
+{
+ return getJrnlHashDir(queue.getName().c_str());
+}
+
+u_int32_t MessageStoreImpl::bHash(const std::string str)
+{
+ // Daniel Bernstein hash fn
+ u_int32_t h = 0;
+ for (std::string::const_iterator i = str.begin(); i < str.end(); i++)
+ h = 33*h + *i;
+ return h;
+}
+
+std::string MessageStoreImpl::getJrnlHashDir(const std::string& queueName) //for exmaple /var/rhm/ + queueDir/
+{
+ std::stringstream dir;
+ dir << getJrnlBaseDir() << std::hex << std::setfill('0') << std::setw(4);
+ dir << (bHash(queueName.c_str()) % 29); // Use a prime number for better distribution across dirs
+ dir << "/" << queueName << "/";
+ return dir.str();
+}
+
+std::string MessageStoreImpl::getStoreDir() const { return storeDir; }
+
+void MessageStoreImpl::journalDeleted(JournalImpl& j) {
+ qpid::sys::Mutex::ScopedLock sl(journalListLock);
+ journalList.erase(j.id());
+}
+
+MessageStoreImpl::StoreOptions::StoreOptions(const std::string& name) :
+ qpid::Options(name),
+ numJrnlFiles(defNumJrnlFiles),
+ autoJrnlExpand(defAutoJrnlExpand),
+ autoJrnlExpandMaxFiles(defAutoJrnlExpandMaxFiles),
+ jrnlFsizePgs(defJrnlFileSizePgs),
+ truncateFlag(defTruncateFlag),
+ wCachePageSizeKib(defWCachePageSize),
+ tplNumJrnlFiles(defTplNumJrnlFiles),
+ tplJrnlFsizePgs(defTplJrnlFileSizePgs),
+ tplWCachePageSizeKib(defTplWCachePageSize)
+{
+ std::ostringstream oss1;
+ oss1 << "Default number of files for each journal instance (queue). [Allowable values: " <<
+ JRNL_MIN_NUM_FILES << " - " << JRNL_MAX_NUM_FILES << "]";
+ std::ostringstream oss2;
+ oss2 << "Default size for each journal file in multiples of read pages (1 read page = 64KiB). [Allowable values: " <<
+ JRNL_MIN_FILE_SIZE / JRNL_RMGR_PAGE_SIZE << " - " << JRNL_MAX_FILE_SIZE / JRNL_RMGR_PAGE_SIZE << "]";
+ std::ostringstream oss3;
+ oss3 << "Number of files for transaction prepared list journal instance. [Allowable values: " <<
+ JRNL_MIN_NUM_FILES << " - " << JRNL_MAX_NUM_FILES << "]";
+ std::ostringstream oss4;
+ oss4 << "Size of each transaction prepared list journal file in multiples of read pages (1 read page = 64KiB) [Allowable values: " <<
+ JRNL_MIN_FILE_SIZE / JRNL_RMGR_PAGE_SIZE << " - " << JRNL_MAX_FILE_SIZE / JRNL_RMGR_PAGE_SIZE << "]";
+ addOptions()
+ ("store-dir", qpid::optValue(storeDir, "DIR"),
+ "Store directory location for persistence (instead of using --data-dir value). "
+ "Required if --no-data-dir is also used.")
+ ("num-jfiles", qpid::optValue(numJrnlFiles, "N"), oss1.str().c_str())
+ ("jfile-size-pgs", qpid::optValue(jrnlFsizePgs, "N"), oss2.str().c_str())
+// TODO: Uncomment these lines when auto-expand is enabled.
+// ("auto-expand", qpid::optValue(autoJrnlExpand, "yes|no"),
+// "If yes|true|1, allows journal to auto-expand by adding additional journal files as needed. "
+// "If no|false|0, the number of journal files will remain fixed (num-jfiles).")
+// ("max-auto-expand-jfiles", qpid::optValue(autoJrnlExpandMaxFiles, "N"),
+// "Maximum number of journal files allowed from auto-expanding; must be greater than --num-jfiles parameter.")
+ ("truncate", qpid::optValue(truncateFlag, "yes|no"),
+ "If yes|true|1, will truncate the store (discard any existing records). If no|false|0, will preserve "
+ "the existing store files for recovery.")
+ ("wcache-page-size", qpid::optValue(wCachePageSizeKib, "N"),
+ "Size of the pages in the write page cache in KiB. "
+ "Allowable values - powers of 2: 1, 2, 4, ... , 128. "
+ "Lower values decrease latency at the expense of throughput.")
+ ("tpl-num-jfiles", qpid::optValue(tplNumJrnlFiles, "N"), oss3.str().c_str())
+ ("tpl-jfile-size-pgs", qpid::optValue(tplJrnlFsizePgs, "N"), oss4.str().c_str())
+ ("tpl-wcache-page-size", qpid::optValue(tplWCachePageSizeKib, "N"),
+ "Size of the pages in the transaction prepared list write page cache in KiB. "
+ "Allowable values - powers of 2: 1, 2, 4, ... , 128. "
+ "Lower values decrease latency at the expense of throughput.")
+ ;
+}
+
+}}
diff --git a/cpp/src/qpid/legacystore/MessageStoreImpl.h b/cpp/src/qpid/legacystore/MessageStoreImpl.h
new file mode 100644
index 0000000000..68aceedfbb
--- /dev/null
+++ b/cpp/src/qpid/legacystore/MessageStoreImpl.h
@@ -0,0 +1,380 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifndef QPID_LEGACYSTORE_MESSAGESTOREIMPL_H
+#define QPID_LEGACYSTORE_MESSAGESTOREIMPL_H
+
+#include <string>
+
+#include "db-inc.h"
+#include "qpid/legacystore/Cursor.h"
+#include "qpid/legacystore/IdDbt.h"
+#include "qpid/legacystore/IdSequence.h"
+#include "qpid/legacystore/JournalImpl.h"
+#include "qpid/legacystore/jrnl/jcfg.h"
+#include "qpid/legacystore/PreparedTransaction.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/management/Manageable.h"
+#include "qmf/org/apache/qpid/legacystore/Store.h"
+#include "qpid/legacystore/TxnCtxt.h"
+
+// Assume DB_VERSION_MAJOR == 4
+#if (DB_VERSION_MINOR == 2)
+#include <errno.h>
+#define DB_BUFFER_SMALL ENOMEM
+#endif
+
+namespace qpid { namespace sys {
+class Timer;
+}}
+
+namespace mrg {
+namespace msgstore {
+
+/**
+ * An implementation of the MessageStore interface based on Berkeley DB
+ */
+class MessageStoreImpl : public qpid::broker::MessageStore, public qpid::management::Manageable
+{
+ public:
+ typedef boost::shared_ptr<Db> db_ptr;
+ typedef boost::shared_ptr<DbEnv> dbEnv_ptr;
+
+ struct StoreOptions : public qpid::Options {
+ StoreOptions(const std::string& name="Store Options");
+ std::string clusterName;
+ std::string storeDir;
+ u_int16_t numJrnlFiles;
+ bool autoJrnlExpand;
+ u_int16_t autoJrnlExpandMaxFiles;
+ u_int32_t jrnlFsizePgs;
+ bool truncateFlag;
+ u_int32_t wCachePageSizeKib;
+ u_int16_t tplNumJrnlFiles;
+ u_int32_t tplJrnlFsizePgs;
+ u_int32_t tplWCachePageSizeKib;
+ };
+
+ protected:
+ typedef std::map<u_int64_t, qpid::broker::RecoverableQueue::shared_ptr> queue_index;
+ typedef std::map<u_int64_t, qpid::broker::RecoverableExchange::shared_ptr> exchange_index;
+ typedef std::map<u_int64_t, qpid::broker::RecoverableMessage::shared_ptr> message_index;
+
+ typedef LockedMappings::map txn_lock_map;
+ typedef boost::ptr_list<PreparedTransaction> txn_list;
+
+ // Structs for Transaction Recover List (TPL) recover state
+ struct TplRecoverStruct {
+ u_int64_t rid; // rid of TPL record
+ bool deq_flag;
+ bool commit_flag;
+ bool tpc_flag;
+ TplRecoverStruct(const u_int64_t _rid, const bool _deq_flag, const bool _commit_flag, const bool _tpc_flag);
+ };
+ typedef TplRecoverStruct TplRecover;
+ typedef std::pair<std::string, TplRecover> TplRecoverMapPair;
+ typedef std::map<std::string, TplRecover> TplRecoverMap;
+ typedef TplRecoverMap::const_iterator TplRecoverMapCitr;
+
+ typedef std::map<std::string, JournalImpl*> JournalListMap;
+ typedef JournalListMap::iterator JournalListMapItr;
+
+ // Default store settings
+ static const u_int16_t defNumJrnlFiles = 8;
+ static const u_int32_t defJrnlFileSizePgs = 24;
+ static const bool defTruncateFlag = false;
+ static const u_int32_t defWCachePageSize = JRNL_WMGR_DEF_PAGE_SIZE * JRNL_DBLK_SIZE * JRNL_SBLK_SIZE / 1024;
+ static const u_int16_t defTplNumJrnlFiles = 8;
+ static const u_int32_t defTplJrnlFileSizePgs = 24;
+ static const u_int32_t defTplWCachePageSize = defWCachePageSize / 8;
+ // TODO: set defAutoJrnlExpand to true and defAutoJrnlExpandMaxFiles to 16 when auto-expand comes on-line
+ static const bool defAutoJrnlExpand = false;
+ static const u_int16_t defAutoJrnlExpandMaxFiles = 0;
+
+ static const std::string storeTopLevelDir;
+ static qpid::sys::Duration defJournalGetEventsTimeout;
+ static qpid::sys::Duration defJournalFlushTimeout;
+
+ std::list<db_ptr> dbs;
+ dbEnv_ptr dbenv;
+ db_ptr queueDb;
+ db_ptr configDb;
+ db_ptr exchangeDb;
+ db_ptr mappingDb;
+ db_ptr bindingDb;
+ db_ptr generalDb;
+
+ // Pointer to Transaction Prepared List (TPL) journal instance
+ boost::shared_ptr<TplJournalImpl> tplStorePtr;
+ TplRecoverMap tplRecoverMap;
+ qpid::sys::Mutex tplInitLock;
+ JournalListMap journalList;
+ qpid::sys::Mutex journalListLock;
+ qpid::sys::Mutex bdbLock;
+
+ IdSequence queueIdSequence;
+ IdSequence exchangeIdSequence;
+ IdSequence generalIdSequence;
+ IdSequence messageIdSequence;
+ std::string storeDir;
+ u_int16_t numJrnlFiles;
+ bool autoJrnlExpand;
+ u_int16_t autoJrnlExpandMaxFiles;
+ u_int32_t jrnlFsizeSblks;
+ bool truncateFlag;
+ u_int32_t wCachePgSizeSblks;
+ u_int16_t wCacheNumPages;
+ u_int16_t tplNumJrnlFiles;
+ u_int32_t tplJrnlFsizeSblks;
+ u_int32_t tplWCachePgSizeSblks;
+ u_int16_t tplWCacheNumPages;
+ u_int64_t highestRid;
+ bool isInit;
+ const char* envPath;
+ qpid::broker::Broker* broker;
+
+ qmf::org::apache::qpid::legacystore::Store::shared_ptr mgmtObject;
+ qpid::management::ManagementAgent* agent;
+
+
+ // Parameter validation and calculation
+ static u_int16_t chkJrnlNumFilesParam(const u_int16_t param,
+ const std::string paramName);
+ static u_int32_t chkJrnlFileSizeParam(const u_int32_t param,
+ const std::string paramName,
+ const u_int32_t wCachePgSizeSblks = 0);
+ static u_int32_t chkJrnlWrPageCacheSize(const u_int32_t param,
+ const std::string paramName,
+ const u_int16_t jrnlFsizePgs);
+ static u_int16_t getJrnlWrNumPages(const u_int32_t wrPageSizeKib);
+ void chkJrnlAutoExpandOptions(const MessageStoreImpl::StoreOptions* opts,
+ bool& autoJrnlExpand,
+ u_int16_t& autoJrnlExpandMaxFiles,
+ const std::string& autoJrnlExpandMaxFilesParamName,
+ const u_int16_t numJrnlFiles,
+ const std::string& numJrnlFilesParamName);
+
+ void init();
+
+ void recoverQueues(TxnCtxt& txn,
+ qpid::broker::RecoveryManager& recovery,
+ queue_index& index,
+ txn_list& locked,
+ message_index& messages);
+ void recoverMessages(TxnCtxt& txn,
+ qpid::broker::RecoveryManager& recovery,
+ queue_index& index,
+ txn_list& locked,
+ message_index& prepared);
+ void recoverMessages(TxnCtxt& txn,
+ qpid::broker::RecoveryManager& recovery,
+ qpid::broker::RecoverableQueue::shared_ptr& queue,
+ txn_list& locked,
+ message_index& prepared,
+ long& rcnt,
+ long& idcnt);
+ qpid::broker::RecoverableMessage::shared_ptr getExternMessage(qpid::broker::RecoveryManager& recovery,
+ uint64_t mId,
+ unsigned& headerSize);
+ void recoverExchanges(TxnCtxt& txn,
+ qpid::broker::RecoveryManager& recovery,
+ exchange_index& index);
+ void recoverBindings(TxnCtxt& txn,
+ exchange_index& exchanges,
+ queue_index& queues);
+ void recoverGeneral(TxnCtxt& txn,
+ qpid::broker::RecoveryManager& recovery);
+ int enqueueMessage(TxnCtxt& txn,
+ IdDbt& msgId,
+ qpid::broker::RecoverableMessage::shared_ptr& msg,
+ queue_index& index,
+ txn_list& locked,
+ message_index& prepared);
+ void readTplStore();
+ void recoverTplStore();
+ void recoverLockedMappings(txn_list& txns);
+ TxnCtxt* check(qpid::broker::TransactionContext* ctxt);
+ u_int64_t msgEncode(std::vector<char>& buff, const boost::intrusive_ptr<qpid::broker::PersistableMessage>& message);
+ void store(const qpid::broker::PersistableQueue* queue,
+ TxnCtxt* txn,
+ const boost::intrusive_ptr<qpid::broker::PersistableMessage>& message,
+ bool newId);
+ void async_dequeue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<qpid::broker::PersistableMessage>& msg,
+ const qpid::broker::PersistableQueue& queue);
+ void destroy(db_ptr db,
+ const qpid::broker::Persistable& p);
+ bool create(db_ptr db,
+ IdSequence& seq,
+ const qpid::broker::Persistable& p);
+ void completed(TxnCtxt& txn,
+ bool commit);
+ void deleteBindingsForQueue(const qpid::broker::PersistableQueue& queue);
+ void deleteBinding(const qpid::broker::PersistableExchange& exchange,
+ const qpid::broker::PersistableQueue& queue,
+ const std::string& key);
+
+ void put(db_ptr db,
+ DbTxn* txn,
+ Dbt& key,
+ Dbt& value);
+ void open(db_ptr db,
+ DbTxn* txn,
+ const char* file,
+ bool dupKey);
+ void closeDbs();
+
+ // journal functions
+ void createJrnlQueue(const qpid::broker::PersistableQueue& queue);
+ u_int32_t bHash(const std::string str);
+ std::string getJrnlDir(const qpid::broker::PersistableQueue& queue); //for exmaple /var/rhm/ + queueDir/
+ std::string getJrnlHashDir(const std::string& queueName);
+ std::string getJrnlBaseDir();
+ std::string getBdbBaseDir();
+ std::string getTplBaseDir();
+ inline void checkInit() {
+ // TODO: change the default dir to ~/.qpidd
+ if (!isInit) { init("/tmp"); isInit = true; }
+ }
+ void chkTplStoreInit();
+
+ // debug aid for printing XIDs that may contain non-printable chars
+ static std::string xid2str(const std::string xid) {
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0');
+ for (unsigned i=0; i<xid.size(); i++) {
+ if (isprint(xid[i]))
+ oss << xid[i];
+ else
+ oss << "/" << std::setw(2) << (int)((char)xid[i]);
+ }
+ return oss.str();
+ }
+
+ public:
+ typedef boost::shared_ptr<MessageStoreImpl> shared_ptr;
+
+ MessageStoreImpl(qpid::broker::Broker* broker, const char* envpath = 0);
+
+ virtual ~MessageStoreImpl();
+
+ bool init(const qpid::Options* options);
+
+ bool init(const std::string& dir,
+ u_int16_t jfiles = defNumJrnlFiles,
+ u_int32_t jfileSizePgs = defJrnlFileSizePgs,
+ const bool truncateFlag = false,
+ u_int32_t wCachePageSize = defWCachePageSize,
+ u_int16_t tplJfiles = defTplNumJrnlFiles,
+ u_int32_t tplJfileSizePgs = defTplJrnlFileSizePgs,
+ u_int32_t tplWCachePageSize = defTplWCachePageSize,
+ bool autoJExpand = defAutoJrnlExpand,
+ u_int16_t autoJExpandMaxFiles = defAutoJrnlExpandMaxFiles);
+
+ void truncateInit(const bool saveStoreContent = false);
+
+ void initManagement ();
+
+ void finalize();
+
+ void create(qpid::broker::PersistableQueue& queue,
+ const qpid::framing::FieldTable& args);
+
+ void destroy(qpid::broker::PersistableQueue& queue);
+
+ void create(const qpid::broker::PersistableExchange& queue,
+ const qpid::framing::FieldTable& args);
+
+ void destroy(const qpid::broker::PersistableExchange& queue);
+
+ void bind(const qpid::broker::PersistableExchange& exchange,
+ const qpid::broker::PersistableQueue& queue,
+ const std::string& key,
+ const qpid::framing::FieldTable& args);
+
+ void unbind(const qpid::broker::PersistableExchange& exchange,
+ const qpid::broker::PersistableQueue& queue,
+ const std::string& key,
+ const qpid::framing::FieldTable& args);
+
+ void create(const qpid::broker::PersistableConfig& config);
+
+ void destroy(const qpid::broker::PersistableConfig& config);
+
+ void recover(qpid::broker::RecoveryManager& queues);
+
+ void stage(const boost::intrusive_ptr<qpid::broker::PersistableMessage>& msg);
+
+ void destroy(qpid::broker::PersistableMessage& msg);
+
+ void appendContent(const boost::intrusive_ptr<const qpid::broker::PersistableMessage>& msg,
+ const std::string& data);
+
+ void loadContent(const qpid::broker::PersistableQueue& queue,
+ const boost::intrusive_ptr<const qpid::broker::PersistableMessage>& msg,
+ std::string& data,
+ uint64_t offset,
+ uint32_t length);
+
+ void enqueue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<qpid::broker::PersistableMessage>& msg,
+ const qpid::broker::PersistableQueue& queue);
+
+ void dequeue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<qpid::broker::PersistableMessage>& msg,
+ const qpid::broker::PersistableQueue& queue);
+
+ void flush(const qpid::broker::PersistableQueue& queue);
+
+ u_int32_t outstandingQueueAIO(const qpid::broker::PersistableQueue& queue);
+
+ void collectPreparedXids(std::set<std::string>& xids);
+
+ std::auto_ptr<qpid::broker::TransactionContext> begin();
+
+ std::auto_ptr<qpid::broker::TPCTransactionContext> begin(const std::string& xid);
+
+ void prepare(qpid::broker::TPCTransactionContext& ctxt);
+
+ void localPrepare(TxnCtxt* ctxt);
+
+ void commit(qpid::broker::TransactionContext& ctxt);
+
+ void abort(qpid::broker::TransactionContext& ctxt);
+
+ qpid::management::ManagementObject::shared_ptr GetManagementObject (void) const
+ { return mgmtObject; }
+
+ inline qpid::management::Manageable::status_t ManagementMethod (u_int32_t, qpid::management::Args&, std::string&)
+ { return qpid::management::Manageable::STATUS_OK; }
+
+ std::string getStoreDir() const;
+
+ private:
+ void journalDeleted(JournalImpl&);
+
+}; // class MessageStoreImpl
+
+} // namespace msgstore
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_MESSAGESTOREIMPL_H
diff --git a/cpp/src/qpid/legacystore/PreparedTransaction.cpp b/cpp/src/qpid/legacystore/PreparedTransaction.cpp
new file mode 100644
index 0000000000..50b81e2824
--- /dev/null
+++ b/cpp/src/qpid/legacystore/PreparedTransaction.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/legacystore/PreparedTransaction.h"
+#include <algorithm>
+
+using namespace mrg::msgstore;
+using std::string;
+
+void LockedMappings::add(queue_id queue, message_id message)
+{
+ locked.push_back(std::make_pair(queue, message));
+}
+
+bool LockedMappings::isLocked(queue_id queue, message_id message)
+{
+ idpair op( std::make_pair(queue, message) );
+ return find(locked.begin(), locked.end(), op) != locked.end();
+}
+
+void LockedMappings::add(LockedMappings::map& map, std::string& key, queue_id queue, message_id message)
+{
+ LockedMappings::map::iterator i = map.find(key);
+ if (i == map.end()) {
+ LockedMappings::shared_ptr ptr(new LockedMappings());
+ i = map.insert(std::make_pair(key, ptr)).first;
+ }
+ i->second->add(queue, message);
+}
+
+bool PreparedTransaction::isLocked(queue_id queue, message_id message)
+{
+ return (enqueues.get() && enqueues->isLocked(queue, message))
+ || (dequeues.get() && dequeues->isLocked(queue, message));
+}
+
+
+bool PreparedTransaction::isLocked(PreparedTransaction::list& txns, queue_id queue, message_id message)
+{
+ for (PreparedTransaction::list::iterator i = txns.begin(); i != txns.end(); i++) {
+ if (i->isLocked(queue, message)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+PreparedTransaction::list::iterator PreparedTransaction::getLockedPreparedTransaction(PreparedTransaction::list& txns, queue_id queue, message_id message)
+{
+ for (PreparedTransaction::list::iterator i = txns.begin(); i != txns.end(); i++) {
+ if (i->isLocked(queue, message)) {
+ return i;
+ }
+ }
+ return txns.end();
+}
+
+PreparedTransaction::PreparedTransaction(const std::string& _xid,
+ LockedMappings::shared_ptr _enqueues,
+ LockedMappings::shared_ptr _dequeues)
+
+ : xid(_xid), enqueues(_enqueues), dequeues(_dequeues) {}
+
diff --git a/cpp/src/qpid/legacystore/PreparedTransaction.h b/cpp/src/qpid/legacystore/PreparedTransaction.h
new file mode 100644
index 0000000000..c5f7b9458a
--- /dev/null
+++ b/cpp/src/qpid/legacystore/PreparedTransaction.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifndef QPID_LEGACYSTORE_PREPAREDTRANSACTION_H
+#define QPID_LEGACYSTORE_PREPAREDTRANSACTION_H
+
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include <boost/ptr_container/ptr_list.hpp>
+
+namespace mrg{
+namespace msgstore{
+
+typedef u_int64_t queue_id;
+typedef u_int64_t message_id;
+
+class LockedMappings
+{
+public:
+ typedef boost::shared_ptr<LockedMappings> shared_ptr;
+ typedef std::map<std::string, shared_ptr> map;
+ typedef std::pair<queue_id, message_id> idpair;
+ typedef std::list<idpair>::iterator iterator;
+
+ void add(queue_id queue, message_id message);
+ bool isLocked(queue_id queue, message_id message);
+ std::size_t size() { return locked.size(); }
+ iterator begin() { return locked.begin(); }
+ iterator end() { return locked.end(); }
+
+ static void add(LockedMappings::map& map, std::string& key, queue_id queue, message_id message);
+
+private:
+ std::list<idpair> locked;
+};
+
+struct PreparedTransaction
+{
+ typedef boost::ptr_list<PreparedTransaction> list;
+
+ const std::string xid;
+ const LockedMappings::shared_ptr enqueues;
+ const LockedMappings::shared_ptr dequeues;
+
+ PreparedTransaction(const std::string& xid, LockedMappings::shared_ptr enqueues, LockedMappings::shared_ptr dequeues);
+ bool isLocked(queue_id queue, message_id message);
+ static bool isLocked(PreparedTransaction::list& txns, queue_id queue, message_id message);
+ static PreparedTransaction::list::iterator getLockedPreparedTransaction(PreparedTransaction::list& txns, queue_id queue, message_id message);
+};
+
+}}
+
+#endif // ifndef QPID_LEGACYSTORE_PREPAREDTRANSACTION_H
diff --git a/cpp/src/qpid/legacystore/StoreException.h b/cpp/src/qpid/legacystore/StoreException.h
new file mode 100644
index 0000000000..6624aafd5a
--- /dev/null
+++ b/cpp/src/qpid/legacystore/StoreException.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifndef QPID_LEGACYSTORE_STOREEXCEPTION_H
+#define QPID_LEGACYSTORE_STOREEXCEPTION_H
+
+#include "qpid/legacystore/IdDbt.h"
+#include <boost/format.hpp>
+
+namespace mrg{
+namespace msgstore{
+
+class StoreException : public std::exception
+{
+ std::string text;
+public:
+ StoreException(const std::string& _text) : text(_text) {}
+ StoreException(const std::string& _text, const DbException& cause) : text(_text + ": " + cause.what()) {}
+ virtual ~StoreException() throw() {}
+ virtual const char* what() const throw() { return text.c_str(); }
+};
+
+class StoreFullException : public StoreException
+{
+public:
+ StoreFullException(const std::string& _text) : StoreException(_text) {}
+ StoreFullException(const std::string& _text, const DbException& cause) : StoreException(_text, cause) {}
+ virtual ~StoreFullException() throw() {}
+
+};
+
+#define THROW_STORE_EXCEPTION(MESSAGE) throw StoreException(boost::str(boost::format("%s (%s:%d)") % (MESSAGE) % __FILE__ % __LINE__))
+#define THROW_STORE_EXCEPTION_2(MESSAGE, EXCEPTION) throw StoreException(boost::str(boost::format("%s (%s:%d)") % (MESSAGE) % __FILE__ % __LINE__), EXCEPTION)
+#define THROW_STORE_FULL_EXCEPTION(MESSAGE) throw StoreFullException(boost::str(boost::format("%s (%s:%d)") % (MESSAGE) % __FILE__ % __LINE__))
+
+}}
+
+#endif // ifndef QPID_LEGACYSTORE_STOREEXCEPTION_H
diff --git a/cpp/src/qpid/legacystore/StorePlugin.cpp b/cpp/src/qpid/legacystore/StorePlugin.cpp
new file mode 100644
index 0000000000..f9b77ce02c
--- /dev/null
+++ b/cpp/src/qpid/legacystore/StorePlugin.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/Broker.h"
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include "qpid/DataDir.h"
+#include "qpid/log/Statement.h"
+#include "qpid/legacystore/MessageStoreImpl.h"
+
+using mrg::msgstore::MessageStoreImpl;
+
+namespace qpid {
+namespace broker {
+
+using namespace std;
+
+struct StorePlugin : public Plugin {
+
+ MessageStoreImpl::StoreOptions options;
+ boost::shared_ptr<MessageStoreImpl> store;
+
+ Options* getOptions() { return &options; }
+
+ void earlyInitialize (Plugin::Target& target)
+ {
+ Broker* broker = dynamic_cast<Broker*>(&target);
+ if (!broker) return;
+ store.reset(new MessageStoreImpl(broker));
+ DataDir& dataDir = broker->getDataDir ();
+ if (options.storeDir.empty ())
+ {
+ if (!dataDir.isEnabled ())
+ throw Exception ("msgstore: If --data-dir is blank or --no-data-dir is specified, --store-dir must be present.");
+
+ options.storeDir = dataDir.getPath ();
+ }
+ store->init(&options);
+ boost::shared_ptr<qpid::broker::MessageStore> brokerStore(store);
+ broker->setStore(brokerStore);
+ target.addFinalizer(boost::bind(&StorePlugin::finalize, this));
+ }
+
+ void initialize(Plugin::Target& target)
+ {
+ Broker* broker = dynamic_cast<Broker*>(&target);
+ if (!broker) return;
+ if (!store) return;
+ QPID_LOG(info, "Enabling management instrumentation for the store.");
+ store->initManagement();
+ }
+
+ void finalize()
+ {
+ store.reset();
+ }
+
+ const char* id() {return "StorePlugin";}
+};
+
+static StorePlugin instance; // Static initialization.
+
+}} // namespace qpid::broker
diff --git a/cpp/src/qpid/legacystore/TxnCtxt.cpp b/cpp/src/qpid/legacystore/TxnCtxt.cpp
new file mode 100644
index 0000000000..1db41f4c70
--- /dev/null
+++ b/cpp/src/qpid/legacystore/TxnCtxt.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/legacystore/TxnCtxt.h"
+
+#include <sstream>
+
+#include "qpid/legacystore/jrnl/jexception.h"
+#include "qpid/legacystore/StoreException.h"
+
+namespace mrg {
+namespace msgstore {
+
+void TxnCtxt::completeTxn(bool commit) {
+ sync();
+ for (ipqItr i = impactedQueues.begin(); i != impactedQueues.end(); i++) {
+ commitTxn(static_cast<JournalImpl*>(*i), commit);
+ }
+ impactedQueues.clear();
+ if (preparedXidStorePtr)
+ commitTxn(preparedXidStorePtr, commit);
+}
+
+void TxnCtxt::commitTxn(JournalImpl* jc, bool commit) {
+ if (jc && loggedtx) { /* if using journal */
+ boost::intrusive_ptr<DataTokenImpl> dtokp(new DataTokenImpl);
+ dtokp->addRef();
+ dtokp->set_external_rid(true);
+ dtokp->set_rid(loggedtx->next());
+ try {
+ if (commit) {
+ jc->txn_commit(dtokp.get(), getXid());
+ sync();
+ } else {
+ jc->txn_abort(dtokp.get(), getXid());
+ }
+ } catch (const journal::jexception& e) {
+ THROW_STORE_EXCEPTION(std::string("Error commit") + e.what());
+ }
+ }
+}
+
+// static
+uuid_t TxnCtxt::uuid;
+
+// static
+IdSequence TxnCtxt::uuidSeq;
+
+// static
+bool TxnCtxt::staticInit = TxnCtxt::setUuid();
+
+// static
+bool TxnCtxt::setUuid() {
+ ::uuid_generate(uuid);
+ return true;
+}
+
+TxnCtxt::TxnCtxt(IdSequence* _loggedtx) : loggedtx(_loggedtx), dtokp(new DataTokenImpl), preparedXidStorePtr(0), txn(0) {
+ if (loggedtx) {
+// // Human-readable tid: 53 bytes
+// // uuit_t is a char[16]
+// tid.reserve(53);
+// u_int64_t* u1 = (u_int64_t*)uuid;
+// u_int64_t* u2 = (u_int64_t*)(uuid + sizeof(u_int64_t));
+// std::stringstream s;
+// s << "tid:" << std::hex << std::setfill('0') << std::setw(16) << uuidSeq.next() << ":" << std::setw(16) << *u1 << std::setw(16) << *u2;
+// tid.assign(s.str());
+
+ // Binary tid: 24 bytes
+ tid.reserve(24);
+ u_int64_t c = uuidSeq.next();
+ tid.append((char*)&c, sizeof(c));
+ tid.append((char*)&uuid, sizeof(uuid));
+ }
+}
+
+TxnCtxt::TxnCtxt(std::string _tid, IdSequence* _loggedtx) : loggedtx(_loggedtx), dtokp(new DataTokenImpl), preparedXidStorePtr(0), tid(_tid), txn(0) {}
+
+TxnCtxt::~TxnCtxt() { abort(); }
+
+void TxnCtxt::sync() {
+ if (loggedtx) {
+ try {
+ for (ipqItr i = impactedQueues.begin(); i != impactedQueues.end(); i++)
+ jrnl_flush(static_cast<JournalImpl*>(*i));
+ if (preparedXidStorePtr)
+ jrnl_flush(preparedXidStorePtr);
+ for (ipqItr i = impactedQueues.begin(); i != impactedQueues.end(); i++)
+ jrnl_sync(static_cast<JournalImpl*>(*i), &journal::jcntl::_aio_cmpl_timeout);
+ if (preparedXidStorePtr)
+ jrnl_sync(preparedXidStorePtr, &journal::jcntl::_aio_cmpl_timeout);
+ } catch (const journal::jexception& e) {
+ THROW_STORE_EXCEPTION(std::string("Error during txn sync: ") + e.what());
+ }
+ }
+}
+
+void TxnCtxt::jrnl_flush(JournalImpl* jc) {
+ if (jc && !(jc->is_txn_synced(getXid())))
+ jc->flush();
+}
+
+void TxnCtxt::jrnl_sync(JournalImpl* jc, timespec* timeout) {
+ if (!jc || jc->is_txn_synced(getXid()))
+ return;
+ while (jc->get_wr_aio_evt_rem()) {
+ if (jc->get_wr_events(timeout) == journal::jerrno::AIO_TIMEOUT && timeout)
+ THROW_STORE_EXCEPTION(std::string("Error: timeout waiting for TxnCtxt::jrnl_sync()"));
+ }
+}
+
+void TxnCtxt::begin(DbEnv* env, bool sync) {
+ int err;
+ try { err = env->txn_begin(0, &txn, 0); }
+ catch (const DbException&) { txn = 0; throw; }
+ if (err != 0) {
+ std::ostringstream oss;
+ oss << "Error: Env::txn_begin() returned error code: " << err;
+ THROW_STORE_EXCEPTION(oss.str());
+ }
+ if (sync)
+ globalHolder = AutoScopedLock(new qpid::sys::Mutex::ScopedLock(globalSerialiser));
+}
+
+void TxnCtxt::commit() {
+ if (txn) {
+ txn->commit(0);
+ txn = 0;
+ globalHolder.reset();
+ }
+}
+
+void TxnCtxt::abort(){
+ if (txn) {
+ txn->abort();
+ txn = 0;
+ globalHolder.reset();
+ }
+}
+
+DbTxn* TxnCtxt::get() { return txn; }
+
+bool TxnCtxt::isTPC() { return false; }
+
+const std::string& TxnCtxt::getXid() { return tid; }
+
+void TxnCtxt::addXidRecord(qpid::broker::ExternalQueueStore* queue) { impactedQueues.insert(queue); }
+
+void TxnCtxt::complete(bool commit) { completeTxn(commit); }
+
+bool TxnCtxt::impactedQueuesEmpty() { return impactedQueues.empty(); }
+
+DataTokenImpl* TxnCtxt::getDtok() { return dtokp.get(); }
+
+void TxnCtxt::incrDtokRef() { dtokp->addRef(); }
+
+void TxnCtxt::recoverDtok(const u_int64_t rid, const std::string xid) {
+ dtokp->set_rid(rid);
+ dtokp->set_wstate(DataTokenImpl::ENQ);
+ dtokp->set_xid(xid);
+ dtokp->set_external_rid(true);
+}
+
+TPCTxnCtxt::TPCTxnCtxt(const std::string& _xid, IdSequence* _loggedtx) : TxnCtxt(_loggedtx), xid(_xid) {}
+
+}}
diff --git a/cpp/src/qpid/legacystore/TxnCtxt.h b/cpp/src/qpid/legacystore/TxnCtxt.h
new file mode 100644
index 0000000000..77eaa27cd7
--- /dev/null
+++ b/cpp/src/qpid/legacystore/TxnCtxt.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 QPID_LEGACYSTORE_TXNCTXT_H
+#define QPID_LEGACYSTORE_TXNCTXT_H
+
+#include "db-inc.h"
+#include <memory>
+#include <set>
+#include <string>
+
+#include "qpid/legacystore/DataTokenImpl.h"
+#include "qpid/legacystore/IdSequence.h"
+#include "qpid/legacystore/JournalImpl.h"
+#include "qpid/broker/PersistableQueue.h"
+#include "qpid/broker/TransactionalStore.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/uuid.h"
+
+#include <boost/intrusive_ptr.hpp>
+
+namespace mrg {
+namespace msgstore {
+
+class TxnCtxt : public qpid::broker::TransactionContext
+{
+ protected:
+ static qpid::sys::Mutex globalSerialiser;
+
+ static uuid_t uuid;
+ static IdSequence uuidSeq;
+ static bool staticInit;
+ static bool setUuid();
+
+ typedef std::set<qpid::broker::ExternalQueueStore*> ipqdef;
+ typedef ipqdef::iterator ipqItr;
+ typedef std::auto_ptr<qpid::sys::Mutex::ScopedLock> AutoScopedLock;
+
+ ipqdef impactedQueues; // list of Queues used in the txn
+ IdSequence* loggedtx;
+ boost::intrusive_ptr<DataTokenImpl> dtokp;
+ AutoScopedLock globalHolder;
+ JournalImpl* preparedXidStorePtr;
+
+ /**
+ * local txn id, if non XA.
+ */
+ std::string tid;
+ DbTxn* txn;
+
+ virtual void completeTxn(bool commit);
+ void commitTxn(JournalImpl* jc, bool commit);
+ void jrnl_flush(JournalImpl* jc);
+ void jrnl_sync(JournalImpl* jc, timespec* timeout);
+
+ public:
+ TxnCtxt(IdSequence* _loggedtx=NULL);
+ TxnCtxt(std::string _tid, IdSequence* _loggedtx);
+ virtual ~TxnCtxt();
+
+ /**
+ * Call to make sure all the data for this txn is written to safe store
+ *
+ *@return if the data successfully synced.
+ */
+ void sync();
+ void begin(DbEnv* env, bool sync = false);
+ void commit();
+ void abort();
+ DbTxn* get();
+ virtual bool isTPC();
+ virtual const std::string& getXid();
+
+ void addXidRecord(qpid::broker::ExternalQueueStore* queue);
+ inline void prepare(JournalImpl* _preparedXidStorePtr) { preparedXidStorePtr = _preparedXidStorePtr; }
+ void complete(bool commit);
+ bool impactedQueuesEmpty();
+ DataTokenImpl* getDtok();
+ void incrDtokRef();
+ void recoverDtok(const u_int64_t rid, const std::string xid);
+};
+
+
+class TPCTxnCtxt : public TxnCtxt, public qpid::broker::TPCTransactionContext
+{
+ protected:
+ const std::string xid;
+
+ public:
+ TPCTxnCtxt(const std::string& _xid, IdSequence* _loggedtx);
+ inline virtual bool isTPC() { return true; }
+ inline virtual const std::string& getXid() { return xid; }
+};
+
+}}
+
+#endif // ifndef QPID_LEGACYSTORE_TXNCTXT_H
+
+
diff --git a/cpp/src/qpid/legacystore/jrnl/aio.cpp b/cpp/src/qpid/legacystore/jrnl/aio.cpp
new file mode 100644
index 0000000000..ffbddd887e
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/aio.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.
+ *
+ */
+
+/**
+ * \file aio.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::aio (libaio interface
+ * encapsulation). See comments in file aio.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/aio.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/aio.h b/cpp/src/qpid/legacystore/jrnl/aio.h
new file mode 100644
index 0000000000..b1de5f79f7
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/aio.h
@@ -0,0 +1,153 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file aio.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * This file contains an encapsulation of the libaio interface used
+ * by the journal.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_AIO_H
+#define QPID_LEGACYSTORE_JRNL_AIO_H
+
+#include <libaio.h>
+#include <cstring>
+#include <sys/types.h>
+#include <string.h>
+
+namespace mrg
+{
+namespace journal
+{
+
+typedef iocb aio_cb;
+typedef io_event aio_event;
+
+/**
+ * \brief This class is a C++ wrapper class for the libaio functions used by the journal. Note that only those
+ * functions used by the journal are included here. This is not a complete implementation of all libaio functions.
+ */
+class aio
+{
+public:
+ static inline int queue_init(int maxevents, io_context_t* ctxp)
+ {
+ return ::io_queue_init(maxevents, ctxp);
+ }
+
+ static inline int queue_release(io_context_t ctx)
+ {
+ return ::io_queue_release(ctx);
+ }
+
+ static inline int submit(io_context_t ctx, long nr, aio_cb* aios[])
+ {
+ return ::io_submit(ctx, nr, aios);
+ }
+
+ static inline int getevents(io_context_t ctx, long min_nr, long nr, aio_event* events, timespec* const timeout)
+ {
+ return ::io_getevents(ctx, min_nr, nr, events, timeout);
+ }
+
+ /**
+ * \brief This function allows iocbs to be initialized with a pointer that can be re-used. This prepares an
+ * aio_cb struct for read use. (This is a wrapper for libaio's ::io_prep_pread() function.)
+ *
+ * \param aiocbp Pointer to the aio_cb struct to be prepared.
+ * \param fd File descriptor to be used for read.
+ * \param buf Pointer to buffer in which read data is to be placed.
+ * \param count Number of bytes to read - buffer must be large enough.
+ * \param offset Offset within file from which data will be read.
+ */
+ static inline void prep_pread(aio_cb* aiocbp, int fd, void* buf, std::size_t count, int64_t offset)
+ {
+ ::io_prep_pread(aiocbp, fd, buf, count, offset);
+ }
+
+ /**
+ * \brief Special version of libaio's io_prep_pread() which preserves the value of the data pointer. This allows
+ * iocbs to be initialized with a pointer that can be re-used. This prepares a aio_cb struct for read use.
+ *
+ * \param aiocbp Pointer to the aio_cb struct to be prepared.
+ * \param fd File descriptor to be used for read.
+ * \param buf Pointer to buffer in which read data is to be placed.
+ * \param count Number of bytes to read - buffer must be large enough.
+ * \param offset Offset within file from which data will be read.
+ */
+ static inline void prep_pread_2(aio_cb* aiocbp, int fd, void* buf, std::size_t count, int64_t offset)
+ {
+ std::memset((void*) ((char*) aiocbp + sizeof(void*)), 0, sizeof(aio_cb) - sizeof(void*));
+ aiocbp->aio_fildes = fd;
+ aiocbp->aio_lio_opcode = IO_CMD_PREAD;
+ aiocbp->aio_reqprio = 0;
+ aiocbp->u.c.buf = buf;
+ aiocbp->u.c.nbytes = count;
+ aiocbp->u.c.offset = offset;
+ }
+
+ /**
+ * \brief This function allows iocbs to be initialized with a pointer that can be re-used. This function prepares
+ * an aio_cb struct for write use. (This is a wrapper for libaio's ::io_prep_pwrite() function.)
+ *
+ * \param aiocbp Pointer to the aio_cb struct to be prepared.
+ * \param fd File descriptor to be used for write.
+ * \param buf Pointer to buffer in which data to be written is located.
+ * \param count Number of bytes to write.
+ * \param offset Offset within file to which data will be written.
+ */
+ static inline void prep_pwrite(aio_cb* aiocbp, int fd, void* buf, std::size_t count, int64_t offset)
+ {
+ ::io_prep_pwrite(aiocbp, fd, buf, count, offset);
+ }
+
+ /**
+ * \brief Special version of libaio's io_prep_pwrite() which preserves the value of the data pointer. This allows
+ * iocbs to be initialized with a pointer that can be re-used. This function prepares an aio_cb struct for write
+ * use.
+ *
+ * \param aiocbp Pointer to the aio_cb struct to be prepared.
+ * \param fd File descriptor to be used for write.
+ * \param buf Pointer to buffer in which data to be written is located.
+ * \param count Number of bytes to write.
+ * \param offset Offset within file to which data will be written.
+ */
+ static inline void prep_pwrite_2(aio_cb* aiocbp, int fd, void* buf, std::size_t count, int64_t offset)
+ {
+ std::memset((void*) ((char*) aiocbp + sizeof(void*)), 0, sizeof(aio_cb) - sizeof(void*));
+ aiocbp->aio_fildes = fd;
+ aiocbp->aio_lio_opcode = IO_CMD_PWRITE;
+ aiocbp->aio_reqprio = 0;
+ aiocbp->u.c.buf = buf;
+ aiocbp->u.c.nbytes = count;
+ aiocbp->u.c.offset = offset;
+ }
+};
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_AIO_H
diff --git a/cpp/src/qpid/legacystore/jrnl/aio_callback.h b/cpp/src/qpid/legacystore/jrnl/aio_callback.h
new file mode 100644
index 0000000000..90249278a5
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/aio_callback.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file aio_callback.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * This file contains the definition for the AIO callback function
+ * pointer.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_AIO_CALLBACK_H
+#define QPID_LEGACYSTORE_JRNL_AIO_CALLBACK_H
+
+#include <vector>
+#include <sys/types.h>
+
+namespace mrg
+{
+namespace journal
+{
+
+ class data_tok;
+
+ class aio_callback
+ {
+ public:
+ virtual ~aio_callback() {}
+ virtual void wr_aio_cb(std::vector<data_tok*>& dtokl) = 0;
+ virtual void rd_aio_cb(std::vector<u_int16_t>& pil) = 0;
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_AIO_CALLBACK_H
diff --git a/cpp/src/qpid/legacystore/jrnl/cvar.cpp b/cpp/src/qpid/legacystore/jrnl/cvar.cpp
new file mode 100644
index 0000000000..e4010bf91f
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/cvar.cpp
@@ -0,0 +1,33 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file cvar.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::cvar (condition variable). See
+ * comments in file cvar.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/cvar.h"
diff --git a/cpp/src/qpid/legacystore/jrnl/cvar.h b/cpp/src/qpid/legacystore/jrnl/cvar.h
new file mode 100644
index 0000000000..0498e743a2
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/cvar.h
@@ -0,0 +1,87 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file cvar.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * This file contains a posix condition variable class.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_CVAR_H
+#define QPID_LEGACYSTORE_JRNL_CVAR_H
+
+#include <cstring>
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+#include "qpid/legacystore/jrnl/smutex.h"
+#include "qpid/legacystore/jrnl/time_ns.h"
+#include <pthread.h>
+#include <sstream>
+
+namespace mrg
+{
+namespace journal
+{
+
+ // Ultra-simple thread condition variable class
+ class cvar
+ {
+ private:
+ const smutex& _sm;
+ pthread_cond_t _c;
+ public:
+ inline cvar(const smutex& sm) : _sm(sm) { ::pthread_cond_init(&_c, 0); }
+ inline ~cvar() { ::pthread_cond_destroy(&_c); }
+ inline void wait()
+ {
+ PTHREAD_CHK(::pthread_cond_wait(&_c, _sm.get()), "::pthread_cond_wait", "cvar", "wait");
+ }
+ inline void timedwait(timespec& ts)
+ {
+ PTHREAD_CHK(::pthread_cond_timedwait(&_c, _sm.get(), &ts), "::pthread_cond_timedwait", "cvar", "timedwait");
+ }
+ inline bool waitintvl(const long intvl_ns)
+ {
+ time_ns t; t.now(); t+=intvl_ns;
+ int ret = ::pthread_cond_timedwait(&_c, _sm.get(), &t);
+ if (ret == ETIMEDOUT)
+ return true;
+ PTHREAD_CHK(ret, "::pthread_cond_timedwait", "cvar", "waitintvl");
+ return false;
+ }
+ inline void signal()
+ {
+ PTHREAD_CHK(::pthread_cond_signal(&_c), "::pthread_cond_signal", "cvar", "notify");
+ }
+ inline void broadcast()
+ {
+ PTHREAD_CHK(::pthread_cond_broadcast(&_c), "::pthread_cond_broadcast", "cvar", "broadcast");
+ }
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_CVAR_H
diff --git a/cpp/src/qpid/legacystore/jrnl/data_tok.cpp b/cpp/src/qpid/legacystore/jrnl/data_tok.cpp
new file mode 100644
index 0000000000..ce7206d80d
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/data_tok.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.
+ *
+ */
+
+/**
+ * \file data_tok.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::data_tok (data block token).
+ * See comments in file data_tok.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/data_tok.h"
+
+#include <iomanip>
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+#include "qpid/legacystore/jrnl/slock.h"
+#include <sstream>
+
+namespace mrg
+{
+namespace journal
+{
+
+// Static members
+
+u_int64_t data_tok::_cnt = 0;
+smutex data_tok::_mutex;
+
+data_tok::data_tok():
+ _wstate(NONE),
+ _rstate(UNREAD),
+ _dsize(0),
+ _dblks_written(0),
+ _dblks_read(0),
+ _pg_cnt(0),
+ _fid(0),
+ _rid(0),
+ _xid(),
+ _dequeue_rid(0),
+ _external_rid(false)
+{
+ slock s(_mutex);
+ _icnt = _cnt++;
+}
+
+data_tok::~data_tok() {}
+
+const char*
+data_tok::wstate_str() const
+{
+ return wstate_str(_wstate);
+}
+
+const char*
+data_tok::wstate_str(write_state wstate)
+{
+ switch (wstate)
+ {
+ case NONE:
+ return "NONE";
+ case ENQ_CACHED:
+ return "ENQ_CACHED";
+ case ENQ_PART:
+ return "ENQ_PART";
+ case ENQ_SUBM:
+ return "ENQ_SUBM";
+ case ENQ:
+ return "ENQ";
+ case DEQ_CACHED:
+ return "DEQ_CACHED";
+ case DEQ_PART:
+ return "DEQ_PART";
+ case DEQ_SUBM:
+ return "DEQ_SUBM";
+ case DEQ:
+ return "DEQ";
+ case ABORT_CACHED:
+ return "ABORT_CACHED";
+ case ABORT_PART:
+ return "ABORT_PART";
+ case ABORT_SUBM:
+ return "ABORT_SUBM";
+ case ABORTED:
+ return "ABORTED";
+ case COMMIT_CACHED:
+ return "COMMIT_CACHED";
+ case COMMIT_PART:
+ return "COMMIT_PART";
+ case COMMIT_SUBM:
+ return "COMMIT_SUBM";
+ case COMMITTED:
+ return "COMMITTED";
+ }
+ // Not using default: forces compiler to ensure all cases are covered.
+ return "<wstate unknown>";
+}
+
+const char*
+data_tok::rstate_str() const
+{
+ return rstate_str(_rstate);
+}
+
+const char*
+data_tok::rstate_str(read_state rstate)
+{
+ switch (rstate)
+ {
+ case NONE:
+ return "NONE";
+ case READ_PART:
+ return "READ_PART";
+ case SKIP_PART:
+ return "SKIP_PART";
+ case READ:
+ return "READ";
+ // Not using default: forces compiler to ensure all cases are covered.
+ }
+ return "<rstate unknown>";
+}
+
+void
+data_tok::set_rstate(const read_state rstate)
+{
+ if (_wstate != ENQ && rstate != UNREAD)
+ {
+ std::ostringstream oss;
+ oss << "Attempted to change read state to " << rstate_str(rstate);
+ oss << " while write state is not enqueued (wstate ENQ); wstate=" << wstate_str() << ".";
+ throw jexception(jerrno::JERR_DTOK_ILLEGALSTATE, oss.str(), "data_tok",
+ "set_rstate");
+ }
+ _rstate = rstate;
+}
+
+void
+data_tok::reset()
+{
+ _wstate = NONE;
+ _rstate = UNREAD;
+ _dsize = 0;
+ _dblks_written = 0;
+ _dblks_read = 0;
+ _pg_cnt = 0;
+ _fid = 0;
+ _rid = 0;
+ _xid.clear();
+}
+
+// debug aid
+std::string
+data_tok::status_str() const
+{
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0');
+ oss << "dtok id=0x" << _icnt << "; ws=" << wstate_str() << "; rs=" << rstate_str();
+ oss << "; fid=0x" << _fid << "; rid=0x" << _rid << "; xid=";
+ for (unsigned i=0; i<_xid.size(); i++)
+ {
+ if (isprint(_xid[i]))
+ oss << _xid[i];
+ else
+ oss << "/" << std::setw(2) << (int)((char)_xid[i]);
+ }
+ oss << "; drid=0x" << _dequeue_rid << " extrid=" << (_external_rid?"T":"F");
+ oss << "; ds=0x" << _dsize << "; dw=0x" << _dblks_written << "; dr=0x" << _dblks_read;
+ oss << " pc=0x" << _pg_cnt;
+ return oss.str();
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/data_tok.h b/cpp/src/qpid/legacystore/jrnl/data_tok.h
new file mode 100644
index 0000000000..e35f069399
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/data_tok.h
@@ -0,0 +1,172 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file data_tok.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::data_tok (data block token).
+ * See class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_DATA_TOK_H
+#define QPID_LEGACYSTORE_JRNL_DATA_TOK_H
+
+namespace mrg
+{
+namespace journal
+{
+class data_tok;
+}
+}
+
+#include <cassert>
+#include <cstddef>
+#include "qpid/legacystore/jrnl/smutex.h"
+#include <pthread.h>
+#include <string>
+#include <sys/types.h>
+
+namespace mrg
+{
+
+namespace journal
+{
+
+ /**
+ * \class data_tok
+ * \brief Data block token (data_tok) used to track wstate of a data block through asynchronous
+ * I/O process
+ */
+ class data_tok
+ {
+ public:
+ // TODO: Fix this, separate write state from operation
+ // ie: wstate = NONE, CACHED, PART, SUBM, COMPL
+ // op = ENQUEUE, DEQUEUE, ABORT, COMMIT
+ enum write_state
+ {
+ NONE, ///< Data block not sent to journal
+ ENQ_CACHED, ///< Data block enqueue written to page cache
+ ENQ_PART, ///< Data block part-submitted to AIO, waiting for page buffer to free up
+ ENQ_SUBM, ///< Data block enqueue submitted to AIO
+ ENQ, ///< Data block enqueue AIO write complete (enqueue complete)
+ DEQ_CACHED, ///< Data block dequeue written to page cache
+ DEQ_PART, ///< Data block part-submitted to AIO, waiting for page buffer to free up
+ DEQ_SUBM, ///< Data block dequeue submitted to AIO
+ DEQ, ///< Data block dequeue AIO write complete (dequeue complete)
+ ABORT_CACHED,
+ ABORT_PART,
+ ABORT_SUBM,
+ ABORTED,
+ COMMIT_CACHED,
+ COMMIT_PART,
+ COMMIT_SUBM,
+ COMMITTED
+ };
+
+ enum read_state
+ {
+ UNREAD, ///< Data block not read
+ READ_PART, ///< Data block is part-read; waiting for page buffer to fill
+ SKIP_PART, ///< Prev. dequeued dblock is part-skipped; waiting for page buffer to fill
+ READ ///< Data block is fully read
+ };
+
+ protected:
+ static smutex _mutex;
+ static u_int64_t _cnt;
+ u_int64_t _icnt;
+ write_state _wstate; ///< Enqueued / dequeued state of data
+ read_state _rstate; ///< Read state of data
+ std::size_t _dsize; ///< Data size in bytes
+ u_int32_t _dblks_written; ///< Data blocks read/written
+ u_int32_t _dblks_read; ///< Data blocks read/written
+ u_int32_t _pg_cnt; ///< Page counter - incr for each page containing part of data
+ u_int16_t _fid; ///< FID containing header of enqueue record
+ u_int64_t _rid; ///< RID of data set by enqueue operation
+ std::string _xid; ///< XID set by enqueue operation
+ u_int64_t _dequeue_rid; ///< RID of data set by dequeue operation
+ bool _external_rid; ///< Flag to indicate external setting of rid
+
+ public:
+ data_tok();
+ virtual ~data_tok();
+
+ inline u_int64_t id() const { return _icnt; }
+ inline write_state wstate() const { return _wstate; }
+ const char* wstate_str() const;
+ static const char* wstate_str(write_state wstate);
+ inline read_state rstate() const { return _rstate; }
+ const char* rstate_str() const;
+ static const char* rstate_str(read_state rstate);
+ inline bool is_writable() const { return _wstate == NONE || _wstate == ENQ_PART; }
+ inline bool is_enqueued() const { return _wstate == ENQ; }
+ inline bool is_readable() const { return _wstate == ENQ; }
+ inline bool is_read() const { return _rstate == READ; }
+ inline bool is_dequeueable() const { return _wstate == ENQ || _wstate == DEQ_PART; }
+ inline void set_wstate(const write_state wstate) { _wstate = wstate; }
+ void set_rstate(const read_state rstate);
+ inline std::size_t dsize() const { return _dsize; }
+ inline void set_dsize(std::size_t dsize) { _dsize = dsize; }
+
+ inline u_int32_t dblocks_written() const { return _dblks_written; }
+ inline void incr_dblocks_written(u_int32_t dblks_written)
+ { _dblks_written += dblks_written; }
+ inline void set_dblocks_written(u_int32_t dblks_written) { _dblks_written = dblks_written; }
+
+ inline u_int32_t dblocks_read() const { return _dblks_read; }
+ inline void incr_dblocks_read(u_int32_t dblks_read) { _dblks_read += dblks_read; }
+ inline void set_dblocks_read(u_int32_t dblks_read) { _dblks_read = dblks_read; }
+
+ inline u_int32_t pg_cnt() const { return _pg_cnt; }
+ inline u_int32_t incr_pg_cnt() { return ++_pg_cnt; }
+ inline u_int32_t decr_pg_cnt() { assert(_pg_cnt != 0); return --_pg_cnt; }
+
+ inline u_int16_t fid() const { return _fid; }
+ inline void set_fid(const u_int16_t fid) { _fid = fid; }
+ inline u_int64_t rid() const { return _rid; }
+ inline void set_rid(const u_int64_t rid) { _rid = rid; }
+ inline u_int64_t dequeue_rid() const {return _dequeue_rid; }
+ inline void set_dequeue_rid(const u_int64_t rid) { _dequeue_rid = rid; }
+ inline bool external_rid() const { return _external_rid; }
+ inline void set_external_rid(const bool external_rid) { _external_rid = external_rid; }
+
+ inline bool has_xid() const { return !_xid.empty(); }
+ inline const std::string& xid() const { return _xid; }
+ inline void clear_xid() { _xid.clear(); }
+ inline void set_xid(const std::string& xid) { _xid.assign(xid); }
+ inline void set_xid(const void* xidp, const std::size_t xid_len)
+ { _xid.assign((const char*)xidp, xid_len); }
+
+ void reset();
+
+ // debug aid
+ std::string status_str() const;
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_DATA_TOK_H
diff --git a/cpp/src/qpid/legacystore/jrnl/deq_hdr.h b/cpp/src/qpid/legacystore/jrnl/deq_hdr.h
new file mode 100644
index 0000000000..ae7081eac1
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/deq_hdr.h
@@ -0,0 +1,141 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file deq_hdr.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::deq_hdr (dequeue record),
+ * used to dequeue a previously enqueued record.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_DEQ_HDR_H
+#define QPID_LEGACYSTORE_JRNL_DEQ_HDR_H
+
+#include <cstddef>
+#include "qpid/legacystore/jrnl/rec_hdr.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+#pragma pack(1)
+
+ /**
+ * \brief Struct for dequeue record.
+ *
+ * Struct for dequeue record. If this record has a non-zero xidsize field (i.e., there is a
+ * valid XID), then this header is followed by the XID of xidsize bytes and a rec_tail. If,
+ * on the other hand, this record has a zero xidsize (i.e., there is no XID), then the rec_tail
+ * is absent.
+ *
+ * Note that this record had its own rid distinct from the rid of the record it is dequeueing.
+ * The rid field below is the rid of the dequeue record itself; the deq-rid field is the rid of a
+ * previous enqueue record being dequeued by this record.
+ *
+ * Record header info in binary format (32 bytes):
+ * <pre>
+ * 0 7
+ * +---+---+---+---+---+---+---+---+ -+
+ * | magic | v | e | flags | |
+ * +---+---+---+---+---+---+---+---+ | struct hdr
+ * | rid | |
+ * +---+---+---+---+---+---+---+---+ -+
+ * | deq-rid |
+ * +---+---+---+---+---+---+---+---+
+ * | xidsize |
+ * +---+---+---+---+---+---+---+---+
+ * v = file version (If the format or encoding of this file changes, then this
+ * number should be incremented)
+ * e = endian flag, false (0x00) for little endian, true (0x01) for big endian
+ * </pre>
+ *
+ * Note that journal files should be transferable between 32- and 64-bit
+ * hardware of the same endianness, but not between hardware of opposite
+ * entianness without some sort of binary conversion utility. Thus buffering
+ * will be needed for types that change size between 32- and 64-bit compiles.
+ */
+ struct deq_hdr : rec_hdr
+ {
+ u_int64_t _deq_rid; ///< Record ID of dequeued record
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ u_int32_t _filler0; ///< Big-endian filler for 32-bit size_t
+#endif
+ std::size_t _xidsize; ///< XID size
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ u_int32_t _filler0; ///< Little-endian filler for 32-bit size_t
+#endif
+ static const u_int16_t DEQ_HDR_TXNCMPLCOMMIT_MASK = 0x10;
+
+ /**
+ * \brief Default constructor, which sets all values to 0.
+ */
+ inline deq_hdr(): rec_hdr(), _deq_rid(0),
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ _filler0(0),
+#endif
+ _xidsize(0)
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ , _filler0(0)
+#endif
+ {}
+
+ /**
+ * \brief Convenience constructor which initializes values during construction.
+ */
+ inline deq_hdr(const u_int32_t magic, const u_int8_t version, const u_int64_t rid,
+ const u_int64_t deq_rid, const std::size_t xidsize, const bool owi,
+ const bool txn_coml_commit = false):
+ rec_hdr(magic, version, rid, owi), _deq_rid(deq_rid),
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ _filler0(0),
+#endif
+ _xidsize(xidsize)
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ , _filler0(0)
+#endif
+ { set_txn_coml_commit(txn_coml_commit); }
+
+
+ inline bool is_txn_coml_commit() const { return _uflag & DEQ_HDR_TXNCMPLCOMMIT_MASK; }
+
+ inline void set_txn_coml_commit(const bool commit)
+ {
+ _uflag = commit ? _uflag | DEQ_HDR_TXNCMPLCOMMIT_MASK :
+ _uflag & (~DEQ_HDR_TXNCMPLCOMMIT_MASK);
+ }
+
+ /**
+ * \brief Returns the size of the header in bytes.
+ */
+ inline static std::size_t size() { return sizeof(deq_hdr); }
+ };
+
+#pragma pack()
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_DEQ_HDR_H
diff --git a/cpp/src/qpid/legacystore/jrnl/deq_rec.cpp b/cpp/src/qpid/legacystore/jrnl/deq_rec.cpp
new file mode 100644
index 0000000000..4de412c201
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/deq_rec.cpp
@@ -0,0 +1,459 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file deq_rec.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * This file contains the code for the mrg::journal::deq_rec (journal dequeue
+ * record) class. See comments in file deq_rec.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "jrnl/deq_rec.h"
+
+#include <cassert>
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <iomanip>
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+#include <sstream>
+
+namespace mrg
+{
+namespace journal
+{
+
+deq_rec::deq_rec():
+ _deq_hdr(RHM_JDAT_DEQ_MAGIC, RHM_JDAT_VERSION, 0, 0, 0, false),
+ _xidp(0),
+ _buff(0),
+ _deq_tail(_deq_hdr)
+{}
+
+deq_rec::deq_rec(const u_int64_t rid, const u_int64_t drid, const void* const xidp,
+ const std::size_t xidlen, const bool owi, const bool txn_coml_commit):
+ _deq_hdr(RHM_JDAT_DEQ_MAGIC, RHM_JDAT_VERSION, rid, drid, xidlen, owi, txn_coml_commit),
+ _xidp(xidp),
+ _buff(0),
+ _deq_tail(_deq_hdr)
+{}
+
+deq_rec::~deq_rec()
+{
+ clean();
+}
+
+void
+deq_rec::reset()
+{
+ _deq_hdr._rid = 0;
+ _deq_hdr.set_owi(false);
+ _deq_hdr.set_txn_coml_commit(false);
+ _deq_hdr._deq_rid = 0;
+ _deq_hdr._xidsize = 0;
+ _deq_tail._rid = 0;
+ _xidp = 0;
+ _buff = 0;
+}
+
+void
+deq_rec::reset(const u_int64_t rid, const u_int64_t drid, const void* const xidp,
+ const std::size_t xidlen, const bool owi, const bool txn_coml_commit)
+{
+ _deq_hdr._rid = rid;
+ _deq_hdr.set_owi(owi);
+ _deq_hdr.set_txn_coml_commit(txn_coml_commit);
+ _deq_hdr._deq_rid = drid;
+ _deq_hdr._xidsize = xidlen;
+ _deq_tail._rid = rid;
+ _xidp = xidp;
+ _buff = 0;
+}
+
+u_int32_t
+deq_rec::encode(void* wptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks)
+{
+ assert(wptr != 0);
+ assert(max_size_dblks > 0);
+ if (_xidp == 0)
+ assert(_deq_hdr._xidsize == 0);
+
+ std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE;
+ std::size_t rem = max_size_dblks * JRNL_DBLK_SIZE;
+ std::size_t wr_cnt = 0;
+ if (rec_offs_dblks) // Continuation of split dequeue record (over 2 or more pages)
+ {
+ if (size_dblks(rec_size()) - rec_offs_dblks > max_size_dblks) // Further split required
+ {
+ rec_offs -= sizeof(_deq_hdr);
+ std::size_t wsize = _deq_hdr._xidsize > rec_offs ? _deq_hdr._xidsize - rec_offs : 0;
+ std::size_t wsize2 = wsize;
+ if (wsize)
+ {
+ if (wsize > rem)
+ wsize = rem;
+ std::memcpy(wptr, (const char*)_xidp + rec_offs, wsize);
+ wr_cnt += wsize;
+ rem -= wsize;
+ }
+ rec_offs -= _deq_hdr._xidsize - wsize2;
+ if (rem)
+ {
+ wsize = sizeof(_deq_tail) > rec_offs ? sizeof(_deq_tail) - rec_offs : 0;
+ wsize2 = wsize;
+ if (wsize)
+ {
+ if (wsize > rem)
+ wsize = rem;
+ std::memcpy((char*)wptr + wr_cnt, (char*)&_deq_tail + rec_offs, wsize);
+ wr_cnt += wsize;
+ rem -= wsize;
+ }
+ rec_offs -= sizeof(_deq_tail) - wsize2;
+ }
+ assert(rem == 0);
+ assert(rec_offs == 0);
+ }
+ else // No further split required
+ {
+ rec_offs -= sizeof(_deq_hdr);
+ std::size_t wsize = _deq_hdr._xidsize > rec_offs ? _deq_hdr._xidsize - rec_offs : 0;
+ if (wsize)
+ {
+ std::memcpy(wptr, (const char*)_xidp + rec_offs, wsize);
+ wr_cnt += wsize;
+ }
+ rec_offs -= _deq_hdr._xidsize - wsize;
+ wsize = sizeof(_deq_tail) > rec_offs ? sizeof(_deq_tail) - rec_offs : 0;
+ if (wsize)
+ {
+ std::memcpy((char*)wptr + wr_cnt, (char*)&_deq_tail + rec_offs, wsize);
+ wr_cnt += wsize;
+#ifdef RHM_CLEAN
+ std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE;
+ std::size_t dblk_rec_size = size_dblks(rec_size() - rec_offs) * JRNL_DBLK_SIZE;
+ std::memset((char*)wptr + wr_cnt, RHM_CLEAN_CHAR, dblk_rec_size - wr_cnt);
+#endif
+ }
+ rec_offs -= sizeof(_deq_tail) - wsize;
+ assert(rec_offs == 0);
+ }
+ }
+ else // Start at beginning of data record
+ {
+ // Assumption: the header will always fit into the first dblk
+ std::memcpy(wptr, (void*)&_deq_hdr, sizeof(_deq_hdr));
+ wr_cnt = sizeof(_deq_hdr);
+ if (size_dblks(rec_size()) > max_size_dblks) // Split required - can only occur with xid
+ {
+ std::size_t wsize;
+ rem -= sizeof(_deq_hdr);
+ if (rem)
+ {
+ wsize = rem >= _deq_hdr._xidsize ? _deq_hdr._xidsize : rem;
+ std::memcpy((char*)wptr + wr_cnt, _xidp, wsize);
+ wr_cnt += wsize;
+ rem -= wsize;
+ }
+ if (rem)
+ {
+ wsize = rem >= sizeof(_deq_tail) ? sizeof(_deq_tail) : rem;
+ std::memcpy((char*)wptr + wr_cnt, (void*)&_deq_tail, wsize);
+ wr_cnt += wsize;
+ rem -= wsize;
+ }
+ assert(rem == 0);
+ }
+ else // No split required
+ {
+ if (_deq_hdr._xidsize)
+ {
+ std::memcpy((char*)wptr + wr_cnt, _xidp, _deq_hdr._xidsize);
+ wr_cnt += _deq_hdr._xidsize;
+ std::memcpy((char*)wptr + wr_cnt, (void*)&_deq_tail, sizeof(_deq_tail));
+ wr_cnt += sizeof(_deq_tail);
+ }
+#ifdef RHM_CLEAN
+ std::size_t dblk_rec_size = size_dblks(rec_size()) * JRNL_DBLK_SIZE;
+ std::memset((char*)wptr + wr_cnt, RHM_CLEAN_CHAR, dblk_rec_size - wr_cnt);
+#endif
+ }
+ }
+ return size_dblks(wr_cnt);
+}
+
+u_int32_t
+deq_rec::decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks)
+{
+ assert(rptr != 0);
+ assert(max_size_dblks > 0);
+
+ std::size_t rd_cnt = 0;
+ if (rec_offs_dblks) // Continuation of record on new page
+ {
+ const u_int32_t hdr_xid_dblks = size_dblks(deq_hdr::size() + _deq_hdr._xidsize);
+ const u_int32_t hdr_xid_tail_dblks = size_dblks(deq_hdr::size() + _deq_hdr._xidsize +
+ rec_tail::size());
+ const std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE;
+
+ if (hdr_xid_tail_dblks - rec_offs_dblks <= max_size_dblks)
+ {
+ // Remainder of xid fits within this page
+ if (rec_offs - deq_hdr::size() < _deq_hdr._xidsize)
+ {
+ // Part of xid still outstanding, copy remainder of xid and tail
+ const std::size_t xid_offs = rec_offs - deq_hdr::size();
+ const std::size_t xid_rem = _deq_hdr._xidsize - xid_offs;
+ std::memcpy((char*)_buff + xid_offs, rptr, xid_rem);
+ rd_cnt = xid_rem;
+ std::memcpy((void*)&_deq_tail, ((char*)rptr + rd_cnt), sizeof(_deq_tail));
+ chk_tail();
+ rd_cnt += sizeof(_deq_tail);
+ }
+ else
+ {
+ // Tail or part of tail only outstanding, complete tail
+ const std::size_t tail_offs = rec_offs - deq_hdr::size() - _deq_hdr._xidsize;
+ const std::size_t tail_rem = rec_tail::size() - tail_offs;
+ std::memcpy((char*)&_deq_tail + tail_offs, rptr, tail_rem);
+ chk_tail();
+ rd_cnt = tail_rem;
+ }
+ }
+ else if (hdr_xid_dblks - rec_offs_dblks <= max_size_dblks)
+ {
+ // Remainder of xid fits within this page, tail split
+ const std::size_t xid_offs = rec_offs - deq_hdr::size();
+ const std::size_t xid_rem = _deq_hdr._xidsize - xid_offs;
+ std::memcpy((char*)_buff + xid_offs, rptr, xid_rem);
+ rd_cnt += xid_rem;
+ const std::size_t tail_rem = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt;
+ if (tail_rem)
+ {
+ std::memcpy((void*)&_deq_tail, ((char*)rptr + xid_rem), tail_rem);
+ rd_cnt += tail_rem;
+ }
+ }
+ else
+ {
+ // Remainder of xid split
+ const std::size_t xid_cp_size = (max_size_dblks * JRNL_DBLK_SIZE);
+ std::memcpy((char*)_buff + rec_offs - deq_hdr::size(), rptr, xid_cp_size);
+ rd_cnt += xid_cp_size;
+ }
+ }
+ else // Start of record
+ {
+ // Get and check header
+ _deq_hdr.hdr_copy(h);
+ rd_cnt = sizeof(rec_hdr);
+ _deq_hdr._deq_rid = *(u_int64_t*)((char*)rptr + rd_cnt);
+ rd_cnt += sizeof(u_int64_t);
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ rd_cnt += sizeof(u_int32_t); // Filler 0
+#endif
+ _deq_hdr._xidsize = *(std::size_t*)((char*)rptr + rd_cnt);
+ rd_cnt = _deq_hdr.size();
+ chk_hdr();
+ if (_deq_hdr._xidsize)
+ {
+ _buff = std::malloc(_deq_hdr._xidsize);
+ MALLOC_CHK(_buff, "_buff", "deq_rec", "decode");
+ const u_int32_t hdr_xid_dblks = size_dblks(deq_hdr::size() + _deq_hdr._xidsize);
+ const u_int32_t hdr_xid_tail_dblks = size_dblks(deq_hdr::size() + _deq_hdr._xidsize +
+ rec_tail::size());
+
+ // Check if record (header + xid + tail) fits within this page, we can check the
+ // tail before the expense of copying data to memory
+ if (hdr_xid_tail_dblks <= max_size_dblks)
+ {
+ // Entire header, xid and tail fits within this page
+ std::memcpy(_buff, (char*)rptr + rd_cnt, _deq_hdr._xidsize);
+ rd_cnt += _deq_hdr._xidsize;
+ std::memcpy((void*)&_deq_tail, (char*)rptr + rd_cnt, sizeof(_deq_tail));
+ rd_cnt += sizeof(_deq_tail);
+ chk_tail();
+ }
+ else if (hdr_xid_dblks <= max_size_dblks)
+ {
+ // Entire header and xid fit within this page, tail split
+ std::memcpy(_buff, (char*)rptr + rd_cnt, _deq_hdr._xidsize);
+ rd_cnt += _deq_hdr._xidsize;
+ const std::size_t tail_rem = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt;
+ if (tail_rem)
+ {
+ std::memcpy((void*)&_deq_tail, (char*)rptr + rd_cnt, tail_rem);
+ rd_cnt += tail_rem;
+ }
+ }
+ else
+ {
+ // Header fits within this page, xid split
+ const std::size_t xid_cp_size = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt;
+ std::memcpy(_buff, (char*)rptr + rd_cnt, xid_cp_size);
+ rd_cnt += xid_cp_size;
+ }
+ }
+ }
+ return size_dblks(rd_cnt);
+}
+
+bool
+deq_rec::rcv_decode(rec_hdr h, std::ifstream* ifsp, std::size_t& rec_offs)
+{
+ if (rec_offs == 0)
+ {
+ _deq_hdr.hdr_copy(h);
+ ifsp->read((char*)&_deq_hdr._deq_rid, sizeof(u_int64_t));
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ ifsp->ignore(sizeof(u_int32_t)); // _filler0
+#endif
+ ifsp->read((char*)&_deq_hdr._xidsize, sizeof(std::size_t));
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ ifsp->ignore(sizeof(u_int32_t)); // _filler0
+#endif
+ rec_offs = sizeof(_deq_hdr);
+ // Read header, allocate (if req'd) for xid
+ if (_deq_hdr._xidsize)
+ {
+ _buff = std::malloc(_deq_hdr._xidsize);
+ MALLOC_CHK(_buff, "_buff", "enq_rec", "rcv_decode");
+ }
+ }
+ if (rec_offs < sizeof(_deq_hdr) + _deq_hdr._xidsize)
+ {
+ // Read xid (or continue reading xid)
+ std::size_t offs = rec_offs - sizeof(_deq_hdr);
+ ifsp->read((char*)_buff + offs, _deq_hdr._xidsize - offs);
+ std::size_t size_read = ifsp->gcount();
+ rec_offs += size_read;
+ if (size_read < _deq_hdr._xidsize - offs)
+ {
+ assert(ifsp->eof());
+ // As we may have read past eof, turn off fail bit
+ ifsp->clear(ifsp->rdstate()&(~std::ifstream::failbit));
+ assert(!ifsp->fail() && !ifsp->bad());
+ return false;
+ }
+ }
+ if (rec_offs < sizeof(_deq_hdr) +
+ (_deq_hdr._xidsize ? _deq_hdr._xidsize + sizeof(rec_tail) : 0))
+ {
+ // Read tail (or continue reading tail)
+ std::size_t offs = rec_offs - sizeof(_deq_hdr) - _deq_hdr._xidsize;
+ ifsp->read((char*)&_deq_tail + offs, sizeof(rec_tail) - offs);
+ std::size_t size_read = ifsp->gcount();
+ rec_offs += size_read;
+ if (size_read < sizeof(rec_tail) - offs)
+ {
+ assert(ifsp->eof());
+ // As we may have read past eof, turn off fail bit
+ ifsp->clear(ifsp->rdstate()&(~std::ifstream::failbit));
+ assert(!ifsp->fail() && !ifsp->bad());
+ return false;
+ }
+ }
+ ifsp->ignore(rec_size_dblks() * JRNL_DBLK_SIZE - rec_size());
+ if (_deq_hdr._xidsize)
+ chk_tail(); // Throws if tail invalid or record incomplete
+ assert(!ifsp->fail() && !ifsp->bad());
+ return true;
+}
+
+std::size_t
+deq_rec::get_xid(void** const xidpp)
+{
+ if (!_buff)
+ {
+ *xidpp = 0;
+ return 0;
+ }
+ *xidpp = _buff;
+ return _deq_hdr._xidsize;
+}
+
+std::string&
+deq_rec::str(std::string& str) const
+{
+ std::ostringstream oss;
+ oss << "deq_rec: m=" << _deq_hdr._magic;
+ oss << " v=" << (int)_deq_hdr._version;
+ oss << " rid=" << _deq_hdr._rid;
+ oss << " drid=" << _deq_hdr._deq_rid;
+ if (_xidp)
+ oss << " xid=\"" << _xidp << "\"";
+ str.append(oss.str());
+ return str;
+}
+
+std::size_t
+deq_rec::xid_size() const
+{
+ return _deq_hdr._xidsize;
+}
+
+std::size_t
+deq_rec::rec_size() const
+{
+ return deq_hdr::size() + (_deq_hdr._xidsize ? _deq_hdr._xidsize + rec_tail::size() : 0);
+}
+
+void
+deq_rec::chk_hdr() const
+{
+ jrec::chk_hdr(_deq_hdr);
+ if (_deq_hdr._magic != RHM_JDAT_DEQ_MAGIC)
+ {
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0');
+ oss << "deq magic: rid=0x" << std::setw(16) << _deq_hdr._rid;
+ oss << ": expected=0x" << std::setw(8) << RHM_JDAT_DEQ_MAGIC;
+ oss << " read=0x" << std::setw(2) << (int)_deq_hdr._magic;
+ throw jexception(jerrno::JERR_JREC_BADRECHDR, oss.str(), "deq_rec", "chk_hdr");
+ }
+}
+
+void
+deq_rec::chk_hdr(u_int64_t rid) const
+{
+ chk_hdr();
+ jrec::chk_rid(_deq_hdr, rid);
+}
+
+void
+deq_rec::chk_tail() const
+{
+ jrec::chk_tail(_deq_tail, _deq_hdr);
+}
+
+void
+deq_rec::clean()
+{
+ // clean up allocated memory here
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/deq_rec.h b/cpp/src/qpid/legacystore/jrnl/deq_rec.h
new file mode 100644
index 0000000000..d870b658da
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/deq_rec.h
@@ -0,0 +1,103 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file deq_rec.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * This file contains the code for the mrg::journal::deq_rec (journal dequeue
+ * record) class. See class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_DEQ_REQ_H
+#define QPID_LEGACYSTORE_JRNL_DEQ_REQ_H
+
+namespace mrg
+{
+namespace journal
+{
+class deq_rec;
+}
+}
+
+#include <cstddef>
+#include "qpid/legacystore/jrnl/deq_hdr.h"
+#include "qpid/legacystore/jrnl/jrec.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \class deq_rec
+ * \brief Class to handle a single journal dequeue record.
+ */
+ class deq_rec : public jrec
+ {
+ private:
+ deq_hdr _deq_hdr; ///< Dequeue header
+ const void* _xidp; ///< xid pointer for encoding (writing to disk)
+ void* _buff; ///< Pointer to buffer to receive data read from disk
+ rec_tail _deq_tail; ///< Record tail, only encoded if XID is present
+
+ public:
+ // constructor used for read operations and xid will have memory allocated
+ deq_rec();
+ // constructor used for write operations, where xid already exists
+ deq_rec(const u_int64_t rid, const u_int64_t drid, const void* const xidp,
+ const std::size_t xidlen, const bool owi, const bool txn_coml_commit);
+ virtual ~deq_rec();
+
+ // Prepare instance for use in reading data from journal
+ void reset();
+ // Prepare instance for use in writing data to journal
+ void reset(const u_int64_t rid, const u_int64_t drid, const void* const xidp,
+ const std::size_t xidlen, const bool owi, const bool txn_coml_commit);
+ u_int32_t encode(void* wptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks);
+ u_int32_t decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks,
+ u_int32_t max_size_dblks);
+ // Decode used for recover
+ bool rcv_decode(rec_hdr h, std::ifstream* ifsp, std::size_t& rec_offs);
+
+ inline bool is_txn_coml_commit() const { return _deq_hdr.is_txn_coml_commit(); }
+ inline u_int64_t rid() const { return _deq_hdr._rid; }
+ inline u_int64_t deq_rid() const { return _deq_hdr._deq_rid; }
+ std::size_t get_xid(void** const xidpp);
+ std::string& str(std::string& str) const;
+ inline std::size_t data_size() const { return 0; } // This record never carries data
+ std::size_t xid_size() const;
+ std::size_t rec_size() const;
+
+ private:
+ virtual void chk_hdr() const;
+ virtual void chk_hdr(u_int64_t rid) const;
+ virtual void chk_tail() const;
+ virtual void clean();
+ }; // class deq_rec
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_DEQ_REQ_H
diff --git a/cpp/src/qpid/legacystore/jrnl/enq_hdr.h b/cpp/src/qpid/legacystore/jrnl/enq_hdr.h
new file mode 100644
index 0000000000..0d1e6116be
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/enq_hdr.h
@@ -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.
+ *
+ */
+
+/**
+ * \file enq_hdr.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::enq_hdr (enueue header),
+ * used to start an enqueue record in the journal.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_ENQ_HDR_H
+#define QPID_LEGACYSTORE_JRNL_ENQ_HDR_H
+
+#include <cstddef>
+#include "qpid/legacystore/jrnl/rec_hdr.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+#pragma pack(1)
+
+ /**
+ * \brief Struct for enqueue record.
+ *
+ * Struct for enqueue record. In addition to the common data, this header includes both the
+ * xid and data blob sizes.
+ *
+ * This header precedes all enqueue data in journal files.
+ *
+ * Record header info in binary format (32 bytes):
+ * <pre>
+ * 0 7
+ * +---+---+---+---+---+---+---+---+ -+
+ * | magic | v | e | flags | |
+ * +---+---+---+---+---+---+---+---+ | struct hdr
+ * | rid | |
+ * +---+---+---+---+---+---+---+---+ -+
+ * | xidsize |
+ * +---+---+---+---+---+---+---+---+
+ * | dsize |
+ * +---+---+---+---+---+---+---+---+
+ * v = file version (If the format or encoding of this file changes, then this
+ * number should be incremented)
+ * e = endian flag, false (0x00) for little endian, true (0x01) for big endian
+ * </pre>
+ *
+ * Note that journal files should be transferable between 32- and 64-bit
+ * hardware of the same endianness, but not between hardware of opposite
+ * entianness without some sort of binary conversion utility. Thus buffering
+ * will be needed for types that change size between 32- and 64-bit compiles.
+ */
+ struct enq_hdr : rec_hdr
+ {
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ u_int32_t _filler0; ///< Big-endian filler for 32-bit size_t
+#endif
+ std::size_t _xidsize; ///< XID size
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ u_int32_t _filler0; ///< Little-endian filler for 32-bit size_t
+#endif
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ u_int32_t _filler1; ///< Big-endian filler for 32-bit size_t
+#endif
+ std::size_t _dsize; ///< Record data size
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ u_int32_t _filler1; ///< Little-endian filler for 32-bit size_t
+#endif
+ static const u_int16_t ENQ_HDR_TRANSIENT_MASK = 0x10;
+ static const u_int16_t ENQ_HDR_EXTERNAL_MASK = 0x20;
+
+ /**
+ * \brief Default constructor, which sets all values to 0.
+ */
+ inline enq_hdr(): rec_hdr(),
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ _filler0(0),
+#endif
+ _xidsize(0),
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ _filler0(0),
+#endif
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ _filler1(0),
+#endif
+ _dsize(0)
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ , _filler1(0)
+#endif
+ {}
+
+ /**
+ * \brief Convenience constructor which initializes values during construction.
+ */
+ inline enq_hdr(const u_int32_t magic, const u_int8_t version, const u_int64_t rid,
+ const std::size_t xidsize, const std::size_t dsize, const bool owi,
+ const bool transient = false): rec_hdr(magic, version, rid, owi),
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ _filler0(0),
+#endif
+ _xidsize(xidsize),
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ _filler0(0),
+#endif
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ _filler1(0),
+#endif
+ _dsize(dsize)
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ , _filler1(0)
+#endif
+ { set_transient(transient); }
+
+
+ inline bool is_transient() const { return _uflag & ENQ_HDR_TRANSIENT_MASK; }
+
+ inline void set_transient(const bool transient)
+ {
+ _uflag = transient ? _uflag | ENQ_HDR_TRANSIENT_MASK :
+ _uflag & (~ENQ_HDR_TRANSIENT_MASK);
+ }
+
+ inline bool is_external() const { return _uflag & ENQ_HDR_EXTERNAL_MASK; }
+
+ inline void set_external(const bool external)
+ {
+ _uflag = external ? _uflag | ENQ_HDR_EXTERNAL_MASK :
+ _uflag & (~ENQ_HDR_EXTERNAL_MASK);
+ }
+
+ /**
+ * \brief Returns the size of the header in bytes.
+ */
+ inline static std::size_t size() { return sizeof(enq_hdr); }
+ };
+
+#pragma pack()
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_ENQ_HDR_H
diff --git a/cpp/src/qpid/legacystore/jrnl/enq_map.cpp b/cpp/src/qpid/legacystore/jrnl/enq_map.cpp
new file mode 100644
index 0000000000..d024b704a7
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/enq_map.cpp
@@ -0,0 +1,183 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file enq_map.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::enq_map (enqueue map). See
+ * comments in file enq_map.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/enq_map.h"
+
+#include <iomanip>
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/slock.h"
+#include <sstream>
+
+
+namespace mrg
+{
+namespace journal
+{
+
+// static return/error codes
+int16_t enq_map::EMAP_DUP_RID = -3;
+int16_t enq_map::EMAP_LOCKED = -2;
+int16_t enq_map::EMAP_RID_NOT_FOUND = -1;
+int16_t enq_map::EMAP_OK = 0;
+int16_t enq_map::EMAP_FALSE = 0;
+int16_t enq_map::EMAP_TRUE = 1;
+
+enq_map::enq_map():
+ _map(),
+ _pfid_enq_cnt()
+{}
+
+enq_map::~enq_map() {}
+
+void
+enq_map::set_num_jfiles(const u_int16_t num_jfiles)
+{
+ _pfid_enq_cnt.resize(num_jfiles, 0);
+}
+
+
+int16_t
+enq_map::insert_pfid(const u_int64_t rid, const u_int16_t pfid)
+{
+ return insert_pfid(rid, pfid, false);
+}
+
+int16_t
+enq_map::insert_pfid(const u_int64_t rid, const u_int16_t pfid, const bool locked)
+{
+ std::pair<emap_itr, bool> ret;
+ emap_data_struct rec(pfid, locked);
+ {
+ slock s(_mutex);
+ ret = _map.insert(emap_param(rid, rec));
+ }
+ if (ret.second == false)
+ return EMAP_DUP_RID;
+ _pfid_enq_cnt.at(pfid)++;
+ return EMAP_OK;
+}
+
+int16_t
+enq_map::get_pfid(const u_int64_t rid)
+{
+ slock s(_mutex);
+ emap_itr itr = _map.find(rid);
+ if (itr == _map.end()) // not found in map
+ return EMAP_RID_NOT_FOUND;
+ if (itr->second._lock)
+ return EMAP_LOCKED;
+ return itr->second._pfid;
+}
+
+int16_t
+enq_map::get_remove_pfid(const u_int64_t rid, const bool txn_flag)
+{
+ slock s(_mutex);
+ emap_itr itr = _map.find(rid);
+ if (itr == _map.end()) // not found in map
+ return EMAP_RID_NOT_FOUND;
+ if (itr->second._lock && !txn_flag) // locked, but not a commit/abort
+ return EMAP_LOCKED;
+ u_int16_t pfid = itr->second._pfid;
+ _map.erase(itr);
+ _pfid_enq_cnt.at(pfid)--;
+ return pfid;
+}
+
+bool
+enq_map::is_enqueued(const u_int64_t rid, bool ignore_lock)
+{
+ slock s(_mutex);
+ emap_itr itr = _map.find(rid);
+ if (itr == _map.end()) // not found in map
+ return false;
+ if (!ignore_lock && itr->second._lock) // locked
+ return false;
+ return true;
+}
+
+int16_t
+enq_map::lock(const u_int64_t rid)
+{
+ slock s(_mutex);
+ emap_itr itr = _map.find(rid);
+ if (itr == _map.end()) // not found in map
+ return EMAP_RID_NOT_FOUND;
+ itr->second._lock = true;
+ return EMAP_OK;
+}
+
+int16_t
+enq_map::unlock(const u_int64_t rid)
+{
+ slock s(_mutex);
+ emap_itr itr = _map.find(rid);
+ if (itr == _map.end()) // not found in map
+ return EMAP_RID_NOT_FOUND;
+ itr->second._lock = false;
+ return EMAP_OK;
+}
+
+int16_t
+enq_map::is_locked(const u_int64_t rid)
+{
+ slock s(_mutex);
+ emap_itr itr = _map.find(rid);
+ if (itr == _map.end()) // not found in map
+ return EMAP_RID_NOT_FOUND;
+ return itr->second._lock ? EMAP_TRUE : EMAP_FALSE;
+}
+
+void
+enq_map::rid_list(std::vector<u_int64_t>& rv)
+{
+ rv.clear();
+ {
+ slock s(_mutex);
+ for (emap_itr itr = _map.begin(); itr != _map.end(); itr++)
+ rv.push_back(itr->first);
+ }
+}
+
+void
+enq_map::pfid_list(std::vector<u_int16_t>& fv)
+{
+ fv.clear();
+ {
+ slock s(_mutex);
+ for (emap_itr itr = _map.begin(); itr != _map.end(); itr++)
+ fv.push_back(itr->second._pfid);
+ }
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/enq_map.h b/cpp/src/qpid/legacystore/jrnl/enq_map.h
new file mode 100644
index 0000000000..75404afebe
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/enq_map.h
@@ -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.
+ *
+ */
+
+/**
+ * \file enq_map.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::enq_map (enqueue map).
+ * See class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_ENQ_MAP_H
+#define QPID_LEGACYSTORE_JRNL_ENQ_MAP_H
+
+namespace mrg
+{
+namespace journal
+{
+class enq_map;
+}
+}
+
+#include "qpid/legacystore/jrnl/jexception.h"
+#include "qpid/legacystore/jrnl/smutex.h"
+#include <map>
+#include <pthread.h>
+#include <vector>
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \class enq_map
+ * \brief Class for storing the physical file id (pfid) and a transaction locked flag for each enqueued
+ * data block using the record id (rid) as a key. This is the primary mechanism for
+ * deterimining the enqueue low water mark: if a pfid exists in this map, then there is
+ * at least one still-enqueued record in that file. (The transaction map must also be
+ * clear, however.)
+ *
+ * Map rids against pfid and lock status. As records are enqueued, they are added to this
+ * map, and as they are dequeued, they are removed. An enqueue is locked when a transactional
+ * dequeue is pending that has been neither committed nor aborted.
+ * <pre>
+ * key data
+ *
+ * rid1 --- [ pfid, txn_lock ]
+ * rid2 --- [ pfid, txn_lock ]
+ * rid3 --- [ pfid, txn_lock ]
+ * ...
+ * </pre>
+ */
+ class enq_map
+ {
+ public:
+ // return/error codes
+ static int16_t EMAP_DUP_RID;
+ static int16_t EMAP_LOCKED;
+ static int16_t EMAP_RID_NOT_FOUND;
+ static int16_t EMAP_OK;
+ static int16_t EMAP_FALSE;
+ static int16_t EMAP_TRUE;
+
+ private:
+
+ struct emap_data_struct
+ {
+ u_int16_t _pfid;
+ bool _lock;
+ emap_data_struct(const u_int16_t pfid, const bool lock) : _pfid(pfid), _lock(lock) {}
+ };
+ typedef std::pair<u_int64_t, emap_data_struct> emap_param;
+ typedef std::map<u_int64_t, emap_data_struct> emap;
+ typedef emap::iterator emap_itr;
+
+ emap _map;
+ smutex _mutex;
+ std::vector<u_int32_t> _pfid_enq_cnt;
+
+ public:
+ enq_map();
+ virtual ~enq_map();
+
+ void set_num_jfiles(const u_int16_t num_jfiles);
+ inline u_int32_t get_enq_cnt(const u_int16_t pfid) const { return _pfid_enq_cnt.at(pfid); };
+
+ int16_t insert_pfid(const u_int64_t rid, const u_int16_t pfid); // 0=ok; -3=duplicate rid;
+ int16_t insert_pfid(const u_int64_t rid, const u_int16_t pfid, const bool locked); // 0=ok; -3=duplicate rid;
+ int16_t get_pfid(const u_int64_t rid); // >=0=pfid; -1=rid not found; -2=locked
+ int16_t get_remove_pfid(const u_int64_t rid, const bool txn_flag = false); // >=0=pfid; -1=rid not found; -2=locked
+ bool is_enqueued(const u_int64_t rid, bool ignore_lock = false);
+ int16_t lock(const u_int64_t rid); // 0=ok; -1=rid not found
+ int16_t unlock(const u_int64_t rid); // 0=ok; -1=rid not found
+ int16_t is_locked(const u_int64_t rid); // 1=true; 0=false; -1=rid not found
+ inline void clear() { _map.clear(); }
+ inline bool empty() const { return _map.empty(); }
+ inline u_int32_t size() const { return u_int32_t(_map.size()); }
+ void rid_list(std::vector<u_int64_t>& rv);
+ void pfid_list(std::vector<u_int16_t>& fv);
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_ENQ_MAP_H
diff --git a/cpp/src/qpid/legacystore/jrnl/enq_rec.cpp b/cpp/src/qpid/legacystore/jrnl/enq_rec.cpp
new file mode 100644
index 0000000000..468599836b
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/enq_rec.cpp
@@ -0,0 +1,638 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 enq_rec.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * This file contains the code for the mrg::journal::enq_rec (journal enqueue
+ * record) class. See comments in file enq_rec.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/enq_rec.h"
+
+#include <cassert>
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <iomanip>
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+#include <sstream>
+
+namespace mrg
+{
+namespace journal
+{
+
+// Constructor used for read operations, where buf contains preallocated space to receive data.
+enq_rec::enq_rec():
+ jrec(), // superclass
+ _enq_hdr(RHM_JDAT_ENQ_MAGIC, RHM_JDAT_VERSION, 0, 0, 0, false, false),
+ _xidp(0),
+ _data(0),
+ _buff(0),
+ _enq_tail(_enq_hdr)
+{}
+
+// Constructor used for transactional write operations, where dbuf contains data to be written.
+enq_rec::enq_rec(const u_int64_t rid, const void* const dbuf, const std::size_t dlen,
+ const void* const xidp, const std::size_t xidlen, const bool owi, const bool transient):
+ jrec(), // superclass
+ _enq_hdr(RHM_JDAT_ENQ_MAGIC, RHM_JDAT_VERSION, rid, xidlen, dlen, owi, transient),
+ _xidp(xidp),
+ _data(dbuf),
+ _buff(0),
+ _enq_tail(_enq_hdr)
+{}
+
+enq_rec::~enq_rec()
+{
+ clean();
+}
+
+// Prepare instance for use in reading data from journal, where buf contains preallocated space
+// to receive data.
+void
+enq_rec::reset()
+{
+ _enq_hdr._rid = 0;
+ _enq_hdr.set_owi(false);
+ _enq_hdr.set_transient(false);
+ _enq_hdr._xidsize = 0;
+ _enq_hdr._dsize = 0;
+ _xidp = 0;
+ _data = 0;
+ _buff = 0;
+ _enq_tail._rid = 0;
+}
+
+// Prepare instance for use in writing transactional data to journal, where dbuf contains data to
+// be written.
+void
+enq_rec::reset(const u_int64_t rid, const void* const dbuf, const std::size_t dlen,
+ const void* const xidp, const std::size_t xidlen, const bool owi, const bool transient,
+ const bool external)
+{
+ _enq_hdr._rid = rid;
+ _enq_hdr.set_owi(owi);
+ _enq_hdr.set_transient(transient);
+ _enq_hdr.set_external(external);
+ _enq_hdr._xidsize = xidlen;
+ _enq_hdr._dsize = dlen;
+ _xidp = xidp;
+ _data = dbuf;
+ _buff = 0;
+ _enq_tail._rid = rid;
+}
+
+u_int32_t
+enq_rec::encode(void* wptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks)
+{
+ assert(wptr != 0);
+ assert(max_size_dblks > 0);
+ if (_xidp == 0)
+ assert(_enq_hdr._xidsize == 0);
+
+ std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE;
+ std::size_t rem = max_size_dblks * JRNL_DBLK_SIZE;
+ std::size_t wr_cnt = 0;
+ if (rec_offs_dblks) // Continuation of split data record (over 2 or more pages)
+ {
+ if (size_dblks(rec_size()) - rec_offs_dblks > max_size_dblks) // Further split required
+ {
+ rec_offs -= sizeof(_enq_hdr);
+ std::size_t wsize = _enq_hdr._xidsize > rec_offs ? _enq_hdr._xidsize - rec_offs : 0;
+ std::size_t wsize2 = wsize;
+ if (wsize)
+ {
+ if (wsize > rem)
+ wsize = rem;
+ std::memcpy(wptr, (const char*)_xidp + rec_offs, wsize);
+ wr_cnt = wsize;
+ rem -= wsize;
+ }
+ rec_offs -= _enq_hdr._xidsize - wsize2;
+ if (rem && !_enq_hdr.is_external())
+ {
+ wsize = _enq_hdr._dsize > rec_offs ? _enq_hdr._dsize - rec_offs : 0;
+ wsize2 = wsize;
+ if (wsize)
+ {
+ if (wsize > rem)
+ wsize = rem;
+ std::memcpy((char*)wptr + wr_cnt, (const char*)_data + rec_offs, wsize);
+ wr_cnt += wsize;
+ rem -= wsize;
+ }
+ rec_offs -= _enq_hdr._dsize - wsize2;
+ }
+ if (rem)
+ {
+ wsize = sizeof(_enq_tail) > rec_offs ? sizeof(_enq_tail) - rec_offs : 0;
+ wsize2 = wsize;
+ if (wsize)
+ {
+ if (wsize > rem)
+ wsize = rem;
+ std::memcpy((char*)wptr + wr_cnt, (char*)&_enq_tail + rec_offs, wsize);
+ wr_cnt += wsize;
+ rem -= wsize;
+ }
+ rec_offs -= sizeof(_enq_tail) - wsize2;
+ }
+ assert(rem == 0);
+ assert(rec_offs == 0);
+ }
+ else // No further split required
+ {
+ rec_offs -= sizeof(_enq_hdr);
+ std::size_t wsize = _enq_hdr._xidsize > rec_offs ? _enq_hdr._xidsize - rec_offs : 0;
+ if (wsize)
+ {
+ std::memcpy(wptr, (const char*)_xidp + rec_offs, wsize);
+ wr_cnt += wsize;
+ }
+ rec_offs -= _enq_hdr._xidsize - wsize;
+ wsize = _enq_hdr._dsize > rec_offs ? _enq_hdr._dsize - rec_offs : 0;
+ if (wsize && !_enq_hdr.is_external())
+ {
+ std::memcpy((char*)wptr + wr_cnt, (const char*)_data + rec_offs, wsize);
+ wr_cnt += wsize;
+ }
+ rec_offs -= _enq_hdr._dsize - wsize;
+ wsize = sizeof(_enq_tail) > rec_offs ? sizeof(_enq_tail) - rec_offs : 0;
+ if (wsize)
+ {
+ std::memcpy((char*)wptr + wr_cnt, (char*)&_enq_tail + rec_offs, wsize);
+ wr_cnt += wsize;
+#ifdef RHM_CLEAN
+ std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE;
+ std::size_t dblk_rec_size = size_dblks(rec_size() - rec_offs) * JRNL_DBLK_SIZE;
+ std::memset((char*)wptr + wr_cnt, RHM_CLEAN_CHAR, dblk_rec_size - wr_cnt);
+#endif
+ }
+ rec_offs -= sizeof(_enq_tail) - wsize;
+ assert(rec_offs == 0);
+ }
+ }
+ else // Start at beginning of data record
+ {
+ // Assumption: the header will always fit into the first dblk
+ std::memcpy(wptr, (void*)&_enq_hdr, sizeof(_enq_hdr));
+ wr_cnt = sizeof(_enq_hdr);
+ if (size_dblks(rec_size()) > max_size_dblks) // Split required
+ {
+ std::size_t wsize;
+ rem -= sizeof(_enq_hdr);
+ if (rem)
+ {
+ wsize = rem >= _enq_hdr._xidsize ? _enq_hdr._xidsize : rem;
+ std::memcpy((char*)wptr + wr_cnt, _xidp, wsize);
+ wr_cnt += wsize;
+ rem -= wsize;
+ }
+ if (rem && !_enq_hdr.is_external())
+ {
+ wsize = rem >= _enq_hdr._dsize ? _enq_hdr._dsize : rem;
+ std::memcpy((char*)wptr + wr_cnt, _data, wsize);
+ wr_cnt += wsize;
+ rem -= wsize;
+ }
+ if (rem)
+ {
+ wsize = rem >= sizeof(_enq_tail) ? sizeof(_enq_tail) : rem;
+ std::memcpy((char*)wptr + wr_cnt, (void*)&_enq_tail, wsize);
+ wr_cnt += wsize;
+ rem -= wsize;
+ }
+ assert(rem == 0);
+ }
+ else // No split required
+ {
+ if (_enq_hdr._xidsize)
+ {
+ std::memcpy((char*)wptr + wr_cnt, _xidp, _enq_hdr._xidsize);
+ wr_cnt += _enq_hdr._xidsize;
+ }
+ if (!_enq_hdr.is_external())
+ {
+ std::memcpy((char*)wptr + wr_cnt, _data, _enq_hdr._dsize);
+ wr_cnt += _enq_hdr._dsize;
+ }
+ std::memcpy((char*)wptr + wr_cnt, (void*)&_enq_tail, sizeof(_enq_tail));
+ wr_cnt += sizeof(_enq_tail);
+#ifdef RHM_CLEAN
+ std::size_t dblk_rec_size = size_dblks(rec_size()) * JRNL_DBLK_SIZE;
+ std::memset((char*)wptr + wr_cnt, RHM_CLEAN_CHAR, dblk_rec_size - wr_cnt);
+#endif
+ }
+ }
+ return size_dblks(wr_cnt);
+}
+
+u_int32_t
+enq_rec::decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks)
+{
+ assert(rptr != 0);
+ assert(max_size_dblks > 0);
+
+ std::size_t rd_cnt = 0;
+ if (rec_offs_dblks) // Continuation of record on new page
+ {
+ const u_int32_t hdr_xid_data_size = enq_hdr::size() + _enq_hdr._xidsize +
+ (_enq_hdr.is_external() ? 0 : _enq_hdr._dsize);
+ const u_int32_t hdr_xid_data_tail_size = hdr_xid_data_size + rec_tail::size();
+ const u_int32_t hdr_data_dblks = size_dblks(hdr_xid_data_size);
+ const u_int32_t hdr_tail_dblks = size_dblks(hdr_xid_data_tail_size);
+ const std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE;
+ const std::size_t offs = rec_offs - enq_hdr::size();
+
+ if (hdr_tail_dblks - rec_offs_dblks <= max_size_dblks)
+ {
+ // Remainder of record fits within this page
+ if (offs < _enq_hdr._xidsize)
+ {
+ // some XID still outstanding, copy remainder of XID, data and tail
+ const std::size_t rem = _enq_hdr._xidsize + _enq_hdr._dsize - offs;
+ std::memcpy((char*)_buff + offs, rptr, rem);
+ rd_cnt += rem;
+ std::memcpy((void*)&_enq_tail, ((char*)rptr + rd_cnt), sizeof(_enq_tail));
+ chk_tail();
+ rd_cnt += sizeof(_enq_tail);
+ }
+ else if (offs < _enq_hdr._xidsize + _enq_hdr._dsize && !_enq_hdr.is_external())
+ {
+ // some data still outstanding, copy remainder of data and tail
+ const std::size_t data_offs = offs - _enq_hdr._xidsize;
+ const std::size_t data_rem = _enq_hdr._dsize - data_offs;
+ std::memcpy((char*)_buff + offs, rptr, data_rem);
+ rd_cnt += data_rem;
+ std::memcpy((void*)&_enq_tail, ((char*)rptr + rd_cnt), sizeof(_enq_tail));
+ chk_tail();
+ rd_cnt += sizeof(_enq_tail);
+ }
+ else
+ {
+ // Tail or part of tail only outstanding, complete tail
+ const std::size_t tail_offs = rec_offs - enq_hdr::size() - _enq_hdr._xidsize -
+ _enq_hdr._dsize;
+ const std::size_t tail_rem = rec_tail::size() - tail_offs;
+ std::memcpy((char*)&_enq_tail + tail_offs, rptr, tail_rem);
+ chk_tail();
+ rd_cnt = tail_rem;
+ }
+ }
+ else if (hdr_data_dblks - rec_offs_dblks <= max_size_dblks)
+ {
+ // Remainder of xid & data fits within this page; tail split
+
+ /*
+ * TODO: This section needs revision. Since it is known that the end of the page falls within the
+ * tail record, it is only necessary to write from the current offset to the end of the page under
+ * all circumstances. The multiple if/else combinations may be eliminated, as well as one memcpy()
+ * operation.
+ *
+ * Also note that Coverity has detected a possible memory overwrite in this block. It occurs if
+ * both the following two if() stmsts (numbered) are false. With rd_cnt = 0, this would result in
+ * the value of tail_rem > sizeof(tail_rec). Practically, this could only happen if the start and
+ * end of a page both fall within the same tail record, in which case the tail would have to be
+ * (much!) larger. However, the logic here does not account for this possibility.
+ *
+ * If the optimization above is undertaken, this code would probably be removed.
+ */
+ if (offs < _enq_hdr._xidsize) // 1
+ {
+ // some XID still outstanding, copy remainder of XID and data
+ const std::size_t rem = _enq_hdr._xidsize + _enq_hdr._dsize - offs;
+ std::memcpy((char*)_buff + offs, rptr, rem);
+ rd_cnt += rem;
+ }
+ else if (offs < _enq_hdr._xidsize + _enq_hdr._dsize && !_enq_hdr.is_external()) // 2
+ {
+ // some data still outstanding, copy remainder of data
+ const std::size_t data_offs = offs - _enq_hdr._xidsize;
+ const std::size_t data_rem = _enq_hdr._dsize - data_offs;
+ std::memcpy((char*)_buff + offs, rptr, data_rem);
+ rd_cnt += data_rem;
+ }
+ const std::size_t tail_rem = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt;
+ if (tail_rem)
+ {
+ std::memcpy((void*)&_enq_tail, ((char*)rptr + rd_cnt), tail_rem);
+ rd_cnt += tail_rem;
+ }
+ }
+ else
+ {
+ // Since xid and data are contiguous, both fit within current page - copy whole page
+ const std::size_t data_cp_size = (max_size_dblks * JRNL_DBLK_SIZE);
+ std::memcpy((char*)_buff + offs, rptr, data_cp_size);
+ rd_cnt += data_cp_size;
+ }
+ }
+ else // Start of record
+ {
+ // Get and check header
+ _enq_hdr.hdr_copy(h);
+ rd_cnt = sizeof(rec_hdr);
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ rd_cnt += sizeof(u_int32_t); // Filler 0
+#endif
+ _enq_hdr._xidsize = *(std::size_t*)((char*)rptr + rd_cnt);
+ rd_cnt += sizeof(std::size_t);
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ rd_cnt += sizeof(u_int32_t); // Filler 0
+#endif
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ rd_cnt += sizeof(u_int32_t); // Filler 1
+#endif
+ _enq_hdr._dsize = *(std::size_t*)((char*)rptr + rd_cnt);
+ rd_cnt = _enq_hdr.size();
+ chk_hdr();
+ if (_enq_hdr._xidsize + (_enq_hdr.is_external() ? 0 : _enq_hdr._dsize))
+ {
+ _buff = std::malloc(_enq_hdr._xidsize + (_enq_hdr.is_external() ? 0 : _enq_hdr._dsize));
+ MALLOC_CHK(_buff, "_buff", "enq_rec", "decode");
+
+ const u_int32_t hdr_xid_size = enq_hdr::size() + _enq_hdr._xidsize;
+ const u_int32_t hdr_xid_data_size = hdr_xid_size + (_enq_hdr.is_external() ? 0 : _enq_hdr._dsize);
+ const u_int32_t hdr_xid_data_tail_size = hdr_xid_data_size + rec_tail::size();
+ const u_int32_t hdr_xid_dblks = size_dblks(hdr_xid_size);
+ const u_int32_t hdr_data_dblks = size_dblks(hdr_xid_data_size);
+ const u_int32_t hdr_tail_dblks = size_dblks(hdr_xid_data_tail_size);
+ // Check if record (header + data + tail) fits within this page, we can check the
+ // tail before the expense of copying data to memory
+ if (hdr_tail_dblks <= max_size_dblks)
+ {
+ // Header, xid, data and tail fits within this page
+ if (_enq_hdr._xidsize)
+ {
+ std::memcpy(_buff, (char*)rptr + rd_cnt, _enq_hdr._xidsize);
+ rd_cnt += _enq_hdr._xidsize;
+ }
+ if (_enq_hdr._dsize && !_enq_hdr.is_external())
+ {
+ std::memcpy((char*)_buff + _enq_hdr._xidsize, (char*)rptr + rd_cnt,
+ _enq_hdr._dsize);
+ rd_cnt += _enq_hdr._dsize;
+ }
+ std::memcpy((void*)&_enq_tail, (char*)rptr + rd_cnt, sizeof(_enq_tail));
+ chk_tail();
+ rd_cnt += sizeof(_enq_tail);
+ }
+ else if (hdr_data_dblks <= max_size_dblks)
+ {
+ // Header, xid and data fit within this page, tail split or separated
+ if (_enq_hdr._xidsize)
+ {
+ std::memcpy(_buff, (char*)rptr + rd_cnt, _enq_hdr._xidsize);
+ rd_cnt += _enq_hdr._xidsize;
+ }
+ if (_enq_hdr._dsize && !_enq_hdr.is_external())
+ {
+ std::memcpy((char*)_buff + _enq_hdr._xidsize, (char*)rptr + rd_cnt,
+ _enq_hdr._dsize);
+ rd_cnt += _enq_hdr._dsize;
+ }
+ const std::size_t tail_rem = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt;
+ if (tail_rem)
+ {
+ std::memcpy((void*)&_enq_tail, (char*)rptr + rd_cnt, tail_rem);
+ rd_cnt += tail_rem;
+ }
+ }
+ else if (hdr_xid_dblks <= max_size_dblks)
+ {
+ // Header and xid fits within this page, data split or separated
+ if (_enq_hdr._xidsize)
+ {
+ std::memcpy(_buff, (char*)rptr + rd_cnt, _enq_hdr._xidsize);
+ rd_cnt += _enq_hdr._xidsize;
+ }
+ if (_enq_hdr._dsize && !_enq_hdr.is_external())
+ {
+ const std::size_t data_cp_size = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt;
+ std::memcpy((char*)_buff + _enq_hdr._xidsize, (char*)rptr + rd_cnt, data_cp_size);
+ rd_cnt += data_cp_size;
+ }
+ }
+ else
+ {
+ // Header fits within this page, xid split or separated
+ const std::size_t data_cp_size = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt;
+ std::memcpy(_buff, (char*)rptr + rd_cnt, data_cp_size);
+ rd_cnt += data_cp_size;
+ }
+ }
+ }
+ return size_dblks(rd_cnt);
+}
+
+bool
+enq_rec::rcv_decode(rec_hdr h, std::ifstream* ifsp, std::size_t& rec_offs)
+{
+ if (rec_offs == 0)
+ {
+ // Read header, allocate (if req'd) for xid
+ _enq_hdr.hdr_copy(h);
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ ifsp->ignore(sizeof(u_int32_t)); // _filler0
+#endif
+ ifsp->read((char*)&_enq_hdr._xidsize, sizeof(std::size_t));
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ ifsp->ignore(sizeof(u_int32_t)); // _filler0
+#endif
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ ifsp->ignore(sizeof(u_int32_t)); // _filler1
+#endif
+ ifsp->read((char*)&_enq_hdr._dsize, sizeof(std::size_t));
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ ifsp->ignore(sizeof(u_int32_t)); // _filler1
+#endif
+ rec_offs = sizeof(_enq_hdr);
+ if (_enq_hdr._xidsize)
+ {
+ _buff = std::malloc(_enq_hdr._xidsize);
+ MALLOC_CHK(_buff, "_buff", "enq_rec", "rcv_decode");
+ }
+ }
+ if (rec_offs < sizeof(_enq_hdr) + _enq_hdr._xidsize)
+ {
+ // Read xid (or continue reading xid)
+ std::size_t offs = rec_offs - sizeof(_enq_hdr);
+ ifsp->read((char*)_buff + offs, _enq_hdr._xidsize - offs);
+ std::size_t size_read = ifsp->gcount();
+ rec_offs += size_read;
+ if (size_read < _enq_hdr._xidsize - offs)
+ {
+ assert(ifsp->eof());
+ // As we may have read past eof, turn off fail bit
+ ifsp->clear(ifsp->rdstate()&(~std::ifstream::failbit));
+ assert(!ifsp->fail() && !ifsp->bad());
+ return false;
+ }
+ }
+ if (!_enq_hdr.is_external())
+ {
+ if (rec_offs < sizeof(_enq_hdr) + _enq_hdr._xidsize + _enq_hdr._dsize)
+ {
+ // Ignore data (or continue ignoring data)
+ std::size_t offs = rec_offs - sizeof(_enq_hdr) - _enq_hdr._xidsize;
+ ifsp->ignore(_enq_hdr._dsize - offs);
+ std::size_t size_read = ifsp->gcount();
+ rec_offs += size_read;
+ if (size_read < _enq_hdr._dsize - offs)
+ {
+ assert(ifsp->eof());
+ // As we may have read past eof, turn off fail bit
+ ifsp->clear(ifsp->rdstate()&(~std::ifstream::failbit));
+ assert(!ifsp->fail() && !ifsp->bad());
+ return false;
+ }
+ }
+ }
+ if (rec_offs < sizeof(_enq_hdr) + _enq_hdr._xidsize +
+ (_enq_hdr.is_external() ? 0 : _enq_hdr._dsize) + sizeof(rec_tail))
+ {
+ // Read tail (or continue reading tail)
+ std::size_t offs = rec_offs - sizeof(_enq_hdr) - _enq_hdr._xidsize;
+ if (!_enq_hdr.is_external())
+ offs -= _enq_hdr._dsize;
+ ifsp->read((char*)&_enq_tail + offs, sizeof(rec_tail) - offs);
+ std::size_t size_read = ifsp->gcount();
+ rec_offs += size_read;
+ if (size_read < sizeof(rec_tail) - offs)
+ {
+ assert(ifsp->eof());
+ // As we may have read past eof, turn off fail bit
+ ifsp->clear(ifsp->rdstate()&(~std::ifstream::failbit));
+ assert(!ifsp->fail() && !ifsp->bad());
+ return false;
+ }
+ }
+ ifsp->ignore(rec_size_dblks() * JRNL_DBLK_SIZE - rec_size());
+ chk_tail(); // Throws if tail invalid or record incomplete
+ assert(!ifsp->fail() && !ifsp->bad());
+ return true;
+}
+
+std::size_t
+enq_rec::get_xid(void** const xidpp)
+{
+ if (!_buff || !_enq_hdr._xidsize)
+ {
+ *xidpp = 0;
+ return 0;
+ }
+ *xidpp = _buff;
+ return _enq_hdr._xidsize;
+}
+
+std::size_t
+enq_rec::get_data(void** const datapp)
+{
+ if (!_buff)
+ {
+ *datapp = 0;
+ return 0;
+ }
+ if (_enq_hdr.is_external())
+ *datapp = 0;
+ else
+ *datapp = (void*)((char*)_buff + _enq_hdr._xidsize);
+ return _enq_hdr._dsize;
+}
+
+std::string&
+enq_rec::str(std::string& str) const
+{
+ std::ostringstream oss;
+ oss << "enq_rec: m=" << _enq_hdr._magic;
+ oss << " v=" << (int)_enq_hdr._version;
+ oss << " rid=" << _enq_hdr._rid;
+ if (_xidp)
+ oss << " xid=\"" << _xidp << "\"";
+ oss << " len=" << _enq_hdr._dsize;
+ str.append(oss.str());
+ return str;
+}
+
+std::size_t
+enq_rec::rec_size() const
+{
+ return rec_size(_enq_hdr._xidsize, _enq_hdr._dsize, _enq_hdr.is_external());
+}
+
+std::size_t
+enq_rec::rec_size(const std::size_t xidsize, const std::size_t dsize, const bool external)
+{
+ if (external)
+ return enq_hdr::size() + xidsize + rec_tail::size();
+ return enq_hdr::size() + xidsize + dsize + rec_tail::size();
+}
+
+void
+enq_rec::set_rid(const u_int64_t rid)
+{
+ _enq_hdr._rid = rid;
+ _enq_tail._rid = rid;
+}
+
+void
+enq_rec::chk_hdr() const
+{
+ jrec::chk_hdr(_enq_hdr);
+ if (_enq_hdr._magic != RHM_JDAT_ENQ_MAGIC)
+ {
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0');
+ oss << "enq magic: rid=0x" << std::setw(16) << _enq_hdr._rid;
+ oss << ": expected=0x" << std::setw(8) << RHM_JDAT_ENQ_MAGIC;
+ oss << " read=0x" << std::setw(2) << (int)_enq_hdr._magic;
+ throw jexception(jerrno::JERR_JREC_BADRECHDR, oss.str(), "enq_rec", "chk_hdr");
+ }
+}
+
+void
+enq_rec::chk_hdr(u_int64_t rid) const
+{
+ chk_hdr();
+ jrec::chk_rid(_enq_hdr, rid);
+}
+
+void
+enq_rec::chk_tail() const
+{
+ jrec::chk_tail(_enq_tail, _enq_hdr);
+}
+
+void
+enq_rec::clean()
+{
+ // clean up allocated memory here
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/enq_rec.h b/cpp/src/qpid/legacystore/jrnl/enq_rec.h
new file mode 100644
index 0000000000..805a96a1aa
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/enq_rec.h
@@ -0,0 +1,116 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file enq_rec.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * This file contains the code for the mrg::journal::enq_rec (journal enqueue
+ * record) class. See class documentation for details.
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_ENQ_REC_H
+#define QPID_LEGACYSTORE_JRNL_ENQ_REC_H
+
+namespace mrg
+{
+namespace journal
+{
+class enq_rec;
+}
+}
+
+#include <cstddef>
+#include "qpid/legacystore/jrnl/enq_hdr.h"
+#include "qpid/legacystore/jrnl/jrec.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \class enq_rec
+ * \brief Class to handle a single journal enqueue record.
+ */
+ class enq_rec : public jrec
+ {
+ private:
+ enq_hdr _enq_hdr;
+ const void* _xidp; ///< xid pointer for encoding (for writing to disk)
+ const void* _data; ///< Pointer to data to be written to disk
+ void* _buff; ///< Pointer to buffer to receive data read from disk
+ rec_tail _enq_tail;
+
+ public:
+ /**
+ * \brief Constructor used for read operations.
+ */
+ enq_rec();
+
+ /**
+ * \brief Constructor used for write operations, where mbuf contains data to be written.
+ */
+ enq_rec(const u_int64_t rid, const void* const dbuf, const std::size_t dlen,
+ const void* const xidp, const std::size_t xidlen, const bool owi, const bool transient);
+
+ /**
+ * \brief Destructor
+ */
+ virtual ~enq_rec();
+
+ // Prepare instance for use in reading data from journal, xid and data will be allocated
+ void reset();
+ // Prepare instance for use in writing data to journal
+ void reset(const u_int64_t rid, const void* const dbuf, const std::size_t dlen,
+ const void* const xidp, const std::size_t xidlen, const bool owi, const bool transient,
+ const bool external);
+
+ u_int32_t encode(void* wptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks);
+ u_int32_t decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks,
+ u_int32_t max_size_dblks);
+ // Decode used for recover
+ bool rcv_decode(rec_hdr h, std::ifstream* ifsp, std::size_t& rec_offs);
+
+ std::size_t get_xid(void** const xidpp);
+ std::size_t get_data(void** const datapp);
+ inline bool is_transient() const { return _enq_hdr.is_transient(); }
+ inline bool is_external() const { return _enq_hdr.is_external(); }
+ std::string& str(std::string& str) const;
+ inline std::size_t data_size() const { return _enq_hdr._dsize; }
+ inline std::size_t xid_size() const { return _enq_hdr._xidsize; }
+ std::size_t rec_size() const;
+ static std::size_t rec_size(const std::size_t xidsize, const std::size_t dsize, const bool external);
+ inline u_int64_t rid() const { return _enq_hdr._rid; }
+ void set_rid(const u_int64_t rid);
+
+ private:
+ void chk_hdr() const;
+ void chk_hdr(u_int64_t rid) const;
+ void chk_tail() const;
+ virtual void clean();
+ }; // class enq_rec
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_ENQ_REC_H
diff --git a/cpp/src/qpid/legacystore/jrnl/enums.h b/cpp/src/qpid/legacystore/jrnl/enums.h
new file mode 100644
index 0000000000..169a13fa4d
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/enums.h
@@ -0,0 +1,108 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file enums.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing definitions for namespace mrg::journal enums.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_ENUMS_H
+#define QPID_LEGACYSTORE_JRNL_ENUMS_H
+
+namespace mrg
+{
+namespace journal
+{
+
+ // TODO: Change this to flags, as multiple of these conditions may exist simultaneously
+ /**
+ * \brief Enumeration of possilbe return states from journal read and write operations.
+ */
+ enum _iores
+ {
+ RHM_IORES_SUCCESS = 0, ///< Success: IO operation completed noramlly.
+ RHM_IORES_PAGE_AIOWAIT, ///< IO operation suspended - next page is waiting for AIO.
+ RHM_IORES_FILE_AIOWAIT, ///< IO operation suspended - next file is waiting for AIO.
+ RHM_IORES_EMPTY, ///< During read operations, nothing further is available to read.
+ RHM_IORES_RCINVALID, ///< Read page cache is invalid (ie obsolete or uninitialized)
+ RHM_IORES_ENQCAPTHRESH, ///< Enqueue capacity threshold (limit) reached.
+ RHM_IORES_FULL, ///< During write operations, the journal files are full.
+ RHM_IORES_BUSY, ///< Another blocking operation is in progress.
+ RHM_IORES_TXPENDING, ///< Operation blocked by pending transaction.
+ RHM_IORES_NOTIMPL ///< Function is not yet implemented.
+ };
+ typedef _iores iores;
+
+ static inline const char* iores_str(iores res)
+ {
+ switch (res)
+ {
+ case RHM_IORES_SUCCESS: return "RHM_IORES_SUCCESS";
+ case RHM_IORES_PAGE_AIOWAIT: return "RHM_IORES_PAGE_AIOWAIT";
+ case RHM_IORES_FILE_AIOWAIT: return "RHM_IORES_FILE_AIOWAIT";
+ case RHM_IORES_EMPTY: return "RHM_IORES_EMPTY";
+ case RHM_IORES_RCINVALID: return "RHM_IORES_RCINVALID";
+ case RHM_IORES_ENQCAPTHRESH: return "RHM_IORES_ENQCAPTHRESH";
+ case RHM_IORES_FULL: return "RHM_IORES_FULL";
+ case RHM_IORES_BUSY: return "RHM_IORES_BUSY";
+ case RHM_IORES_TXPENDING: return "RHM_IORES_TXPENDING";
+ case RHM_IORES_NOTIMPL: return "RHM_IORES_NOTIMPL";
+ }
+ return "<iores unknown>";
+ }
+
+ enum _log_level
+ {
+ LOG_TRACE = 0,
+ LOG_DEBUG,
+ LOG_INFO,
+ LOG_NOTICE,
+ LOG_WARN,
+ LOG_ERROR,
+ LOG_CRITICAL
+ };
+ typedef _log_level log_level;
+
+ static inline const char* log_level_str(log_level ll)
+ {
+ switch (ll)
+ {
+ case LOG_TRACE: return "TRACE";
+ case LOG_DEBUG: return "DEBUG";
+ case LOG_INFO: return "INFO";
+ case LOG_NOTICE: return "NOTICE";
+ case LOG_WARN: return "WARN";
+ case LOG_ERROR: return "ERROR";
+ case LOG_CRITICAL: return "CRITICAL";
+ }
+ return "<log level unknown>";
+ }
+
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_ENUMS_H
diff --git a/cpp/src/qpid/legacystore/jrnl/fcntl.cpp b/cpp/src/qpid/legacystore/jrnl/fcntl.cpp
new file mode 100644
index 0000000000..fbb176667e
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/fcntl.cpp
@@ -0,0 +1,375 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file fcntl.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::fcntl (non-logging file
+ * handle), used for controlling journal log files. See comments in file
+ * fcntl.h for details.
+ */
+
+#include "qpid/legacystore/jrnl/fcntl.h"
+
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <fcntl.h>
+#include <iomanip>
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+#include <sstream>
+#include <unistd.h>
+
+namespace mrg
+{
+namespace journal
+{
+
+fcntl::fcntl(const std::string& fbasename, const u_int16_t pfid, const u_int16_t lfid, const u_int32_t jfsize_sblks,
+ const rcvdat* const ro):
+ _fname(),
+ _pfid(pfid),
+ _lfid(lfid),
+ _ffull_dblks(JRNL_SBLK_SIZE * (jfsize_sblks + 1)),
+ _wr_fh(-1),
+ _rec_enqcnt(0),
+ _rd_subm_cnt_dblks(0),
+ _rd_cmpl_cnt_dblks(0),
+ _wr_subm_cnt_dblks(0),
+ _wr_cmpl_cnt_dblks(0),
+ _aio_cnt(0),
+ _fhdr_wr_aio_outstanding(false)
+{
+ initialize(fbasename, pfid, lfid, jfsize_sblks, ro);
+ open_wr_fh();
+}
+
+fcntl::~fcntl()
+{
+ close_wr_fh();
+}
+
+bool
+fcntl::reset(const rcvdat* const ro)
+{
+ rd_reset();
+ return wr_reset(ro);
+}
+
+void
+fcntl::rd_reset()
+{
+ _rd_subm_cnt_dblks = 0;
+ _rd_cmpl_cnt_dblks = 0;
+}
+
+bool
+fcntl::wr_reset(const rcvdat* const ro)
+{
+ if (ro)
+ {
+ if (!ro->_jempty)
+ {
+ if (ro->_lfid == _pfid)
+ {
+ _wr_subm_cnt_dblks = ro->_eo/JRNL_DBLK_SIZE;
+ _wr_cmpl_cnt_dblks = ro->_eo/JRNL_DBLK_SIZE;
+ }
+ else
+ {
+ _wr_subm_cnt_dblks = _ffull_dblks;
+ _wr_cmpl_cnt_dblks = _ffull_dblks;
+ }
+ _rec_enqcnt = ro->_enq_cnt_list[_pfid];
+ return true;
+ }
+ }
+ // Journal overflow test - checks if the file to be reset still contains enqueued records
+ // or outstanding aios
+ if (_rec_enqcnt || _aio_cnt)
+ return false;
+ _wr_subm_cnt_dblks = 0;
+ _wr_cmpl_cnt_dblks = 0;
+ return true;
+}
+
+int
+fcntl::open_wr_fh()
+{
+ if (_wr_fh < 0)
+ {
+ _wr_fh = ::open(_fname.c_str(), O_WRONLY | O_DIRECT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); // 0644 -rw-r--r--
+ if (_wr_fh < 0)
+ {
+ std::ostringstream oss;
+ oss << "pfid=" << _pfid << " lfid=" << _lfid << " file=\"" << _fname << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_FCNTL_OPENWR, oss.str(), "fcntl", "open_fh");
+ }
+ }
+ return _wr_fh;
+}
+
+void
+fcntl::close_wr_fh()
+{
+ if (_wr_fh >= 0)
+ {
+ ::close(_wr_fh);
+ _wr_fh = -1;
+ }
+}
+
+u_int32_t
+fcntl::add_enqcnt(u_int32_t a)
+{
+ _rec_enqcnt += a;
+ return _rec_enqcnt;
+}
+
+u_int32_t
+fcntl::decr_enqcnt()
+{
+ if (_rec_enqcnt == 0)
+ {
+ std::ostringstream oss;
+ oss << "pfid=" << _pfid << " lfid=" << _lfid;
+ throw jexception(jerrno::JERR__UNDERFLOW, oss.str(), "fcntl", "decr_enqcnt");
+ }
+ return --_rec_enqcnt;
+}
+
+u_int32_t
+fcntl::subtr_enqcnt(u_int32_t s)
+{
+ if (_rec_enqcnt < s)
+ {
+ std::ostringstream oss;
+ oss << "pfid=" << _pfid << " lfid=" << _lfid << " rec_enqcnt=" << _rec_enqcnt << " decr=" << s;
+ throw jexception(jerrno::JERR__UNDERFLOW, oss.str(), "fcntl", "subtr_enqcnt");
+ }
+ _rec_enqcnt -= s;
+ return _rec_enqcnt;
+}
+
+u_int32_t
+fcntl::add_rd_subm_cnt_dblks(u_int32_t a)
+{
+ if (_rd_subm_cnt_dblks + a > _wr_subm_cnt_dblks)
+ {
+ std::ostringstream oss;
+ oss << "pfid=" << _pfid << " lfid=" << _lfid << " rd_subm_cnt_dblks=" << _rd_subm_cnt_dblks << " incr=" << a;
+ oss << " wr_subm_cnt_dblks=" << _wr_subm_cnt_dblks;
+ throw jexception(jerrno::JERR_FCNTL_RDOFFSOVFL, oss.str(), "fcntl", "add_rd_subm_cnt_dblks");
+ }
+ _rd_subm_cnt_dblks += a;
+ return _rd_subm_cnt_dblks;
+}
+
+u_int32_t
+fcntl::add_rd_cmpl_cnt_dblks(u_int32_t a)
+{
+ if (_rd_cmpl_cnt_dblks + a > _rd_subm_cnt_dblks)
+ {
+ std::ostringstream oss;
+ oss << "pfid=" << _pfid << " lfid=" << _lfid << " rd_cmpl_cnt_dblks=" << _rd_cmpl_cnt_dblks << " incr=" << a;
+ oss << " rd_subm_cnt_dblks=" << _rd_subm_cnt_dblks;
+ throw jexception(jerrno::JERR_FCNTL_CMPLOFFSOVFL, oss.str(), "fcntl", "add_rd_cmpl_cnt_dblks");
+ }
+ _rd_cmpl_cnt_dblks += a;
+ return _rd_cmpl_cnt_dblks;
+}
+
+u_int32_t
+fcntl::add_wr_subm_cnt_dblks(u_int32_t a)
+{
+ if (_wr_subm_cnt_dblks + a > _ffull_dblks) // Allow for file header
+ {
+ std::ostringstream oss;
+ oss << "pfid=" << _pfid << " lfid=" << _lfid << " wr_subm_cnt_dblks=" << _wr_subm_cnt_dblks << " incr=" << a;
+ oss << " fsize=" << _ffull_dblks << " dblks";
+ throw jexception(jerrno::JERR_FCNTL_FILEOFFSOVFL, oss.str(), "fcntl", "add_wr_subm_cnt_dblks");
+ }
+ _wr_subm_cnt_dblks += a;
+ return _wr_subm_cnt_dblks;
+}
+
+u_int32_t
+fcntl::add_wr_cmpl_cnt_dblks(u_int32_t a)
+{
+ if (_wr_cmpl_cnt_dblks + a > _wr_subm_cnt_dblks)
+ {
+ std::ostringstream oss;
+ oss << "pfid=" << _pfid << " lfid=" << _lfid << " wr_cmpl_cnt_dblks=" << _wr_cmpl_cnt_dblks << " incr=" << a;
+ oss << " wr_subm_cnt_dblks=" << _wr_subm_cnt_dblks;
+ throw jexception(jerrno::JERR_FCNTL_CMPLOFFSOVFL, oss.str(), "fcntl", "add_wr_cmpl_cnt_dblks");
+ }
+ _wr_cmpl_cnt_dblks += a;
+ return _wr_cmpl_cnt_dblks;
+}
+
+u_int16_t
+fcntl::decr_aio_cnt()
+{
+ if(_aio_cnt == 0)
+ {
+ std::ostringstream oss;
+ oss << "pfid=" << _pfid << " lfid=" << _lfid << " Decremented aio_cnt to below zero";
+ throw jexception(jerrno::JERR__UNDERFLOW, oss.str(), "fcntl", "decr_aio_cnt");
+ }
+ return --_aio_cnt;
+}
+
+// Debug function
+const std::string
+fcntl::status_str() const
+{
+ std::ostringstream oss;
+ oss << "pfid=" << _pfid << " ws=" << _wr_subm_cnt_dblks << " wc=" << _wr_cmpl_cnt_dblks;
+ oss << " rs=" << _rd_subm_cnt_dblks << " rc=" << _rd_cmpl_cnt_dblks;
+ oss << " ec=" << _rec_enqcnt << " ac=" << _aio_cnt;
+ return oss.str();
+}
+
+// Protected functions
+
+void
+fcntl::initialize(const std::string& fbasename, const u_int16_t pfid, const u_int16_t lfid, const u_int32_t jfsize_sblks,
+ const rcvdat* const ro)
+{
+ _pfid = pfid;
+ _lfid = lfid;
+ _fname = filename(fbasename, pfid);
+
+#ifdef RHM_JOWRITE
+ // In test mode, only create file if it does not exist
+ struct stat s;
+ if (::stat(_fname.c_str(), &s))
+ {
+#endif
+ if (ro) // Recovery initialization: set counters only
+ {
+ if (!ro->_jempty)
+ {
+ // For last file only, set write counters to end of last record (the
+ // continuation point); for all others, set to eof.
+ if (ro->_lfid == _pfid)
+ {
+ _wr_subm_cnt_dblks = ro->_eo/JRNL_DBLK_SIZE;
+ _wr_cmpl_cnt_dblks = ro->_eo/JRNL_DBLK_SIZE;
+ }
+ else
+ {
+ _wr_subm_cnt_dblks = _ffull_dblks;
+ _wr_cmpl_cnt_dblks = _ffull_dblks;
+ }
+ // Set the number of enqueued records for this file.
+ _rec_enqcnt = ro->_enq_cnt_list[_pfid];
+ }
+ }
+ else // Normal initialization: create empty journal files
+ create_jfile(jfsize_sblks);
+#ifdef RHM_JOWRITE
+ }
+#endif
+}
+
+std::string
+fcntl::filename(const std::string& fbasename, const u_int16_t pfid)
+{
+ std::ostringstream oss;
+ oss << fbasename << ".";
+ oss << std::setw(4) << std::setfill('0') << std::hex << pfid;
+ oss << "." << JRNL_DATA_EXTENSION;
+ return oss.str();
+}
+
+void
+fcntl::clean_file(const u_int32_t jfsize_sblks)
+{
+ // NOTE: The journal file size is always one sblock bigger than the specified journal
+ // file size, which is the data content size. The extra block is for the journal file
+ // header which precedes all data on each file and is exactly one sblock in size.
+ u_int32_t nsblks = jfsize_sblks + 1;
+
+ // TODO - look at more efficient alternatives to allocating a null block:
+ // 1. mmap() against /dev/zero, but can alignment for O_DIRECT be assured?
+ // 2. ftruncate(), but does this result in a sparse file? If so, then this is no good.
+
+ // Create temp null block for writing
+ const std::size_t sblksize = JRNL_DBLK_SIZE * JRNL_SBLK_SIZE;
+ void* nullbuf = 0;
+ // Allocate no more than 2MB (4096 sblks) as a null buffer
+ const u_int32_t nullbuffsize_sblks = nsblks > 4096 ? 4096 : nsblks;
+ const std::size_t nullbuffsize = nullbuffsize_sblks * sblksize;
+ if (::posix_memalign(&nullbuf, sblksize, nullbuffsize))
+ {
+ std::ostringstream oss;
+ oss << "posix_memalign() failed: size=" << nullbuffsize << " blk_size=" << sblksize;
+ oss << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR__MALLOC, oss.str(), "fcntl", "clean_file");
+ }
+ std::memset(nullbuf, 0, nullbuffsize);
+
+ int fh = ::open(_fname.c_str(), O_WRONLY | O_CREAT | O_DIRECT,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); // 0644 -rw-r--r--
+ if (fh < 0)
+ {
+ std::free(nullbuf);
+ std::ostringstream oss;
+ oss << "open() failed:" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_FCNTL_OPENWR, oss.str(), "fcntl", "clean_file");
+ }
+
+ while (nsblks > 0)
+ {
+ u_int32_t this_write_sblks = nsblks >= nullbuffsize_sblks ? nullbuffsize_sblks : nsblks;
+ if (::write(fh, nullbuf, this_write_sblks * sblksize) == -1)
+ {
+ ::close(fh);
+ std::free(nullbuf);
+ std::ostringstream oss;
+ oss << "wr_size=" << (this_write_sblks * sblksize) << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_FCNTL_WRITE, oss.str(), "fcntl", "clean_file");
+ }
+ nsblks -= this_write_sblks;
+ }
+
+ // Clean up
+ std::free(nullbuf);
+ if (::close(fh))
+ {
+ std::ostringstream oss;
+ oss << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_FCNTL_CLOSE, oss.str(), "fcntl", "clean_file");
+ }
+}
+
+void
+fcntl::create_jfile(const u_int32_t jfsize_sblks)
+{
+ clean_file(jfsize_sblks);
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/fcntl.h b/cpp/src/qpid/legacystore/jrnl/fcntl.h
new file mode 100644
index 0000000000..a75e3bc84d
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/fcntl.h
@@ -0,0 +1,156 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file fcntl.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::fcntl (non-logging file
+ * handle), used for controlling journal log files. See class documentation for
+ * details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_FCNTL_H
+#define QPID_LEGACYSTORE_JRNL_FCNTL_H
+
+namespace mrg
+{
+namespace journal
+{
+class fcntl;
+}
+}
+
+#include <cstddef>
+#include <string>
+#include "qpid/legacystore/jrnl/rcvdat.h"
+#include <sys/types.h>
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \class fcntl
+ * \brief Journal file controller. There is one instance per journal file.
+ */
+ class fcntl
+ {
+ protected:
+ std::string _fname; ///< File name
+ u_int16_t _pfid; ///< Physical file ID (file number in order of creation)
+ u_int16_t _lfid; ///< Logical file ID (ordinal number in ring store)
+ const u_int32_t _ffull_dblks; ///< File size in dblks (incl. file header)
+ int _wr_fh; ///< Write file handle
+ u_int32_t _rec_enqcnt; ///< Count of enqueued records
+ u_int32_t _rd_subm_cnt_dblks; ///< Read file count (data blocks) for submitted AIO
+ u_int32_t _rd_cmpl_cnt_dblks; ///< Read file count (data blocks) for completed AIO
+ u_int32_t _wr_subm_cnt_dblks; ///< Write file count (data blocks) for submitted AIO
+ u_int32_t _wr_cmpl_cnt_dblks; ///< Write file count (data blocks) for completed AIO
+ u_int16_t _aio_cnt; ///< Outstanding AIO operations on this file
+ bool _fhdr_wr_aio_outstanding; ///< Outstanding file header write on this file
+
+ public:
+ // Constructors with implicit initialize() and open()
+ fcntl(const std::string& fbasename, const u_int16_t pfid, const u_int16_t lfid, const u_int32_t jfsize_sblks,
+ const rcvdat* const ro);
+ virtual ~fcntl();
+
+ virtual bool reset(const rcvdat* const ro = 0);
+ virtual void rd_reset();
+ virtual bool wr_reset(const rcvdat* const ro = 0);
+
+ virtual int open_wr_fh();
+ virtual void close_wr_fh();
+ inline bool is_wr_fh_open() const { return _wr_fh >= 0; }
+
+ inline const std::string& fname() const { return _fname; }
+ inline u_int16_t pfid() const { return _pfid; }
+ inline u_int16_t lfid() const { return _lfid; }
+ inline void set_lfid(const u_int16_t lfid) { _lfid = lfid; }
+ inline int wr_fh() const { return _wr_fh; }
+ inline u_int32_t enqcnt() const { return _rec_enqcnt; }
+ inline u_int32_t incr_enqcnt() { return ++_rec_enqcnt; }
+ u_int32_t add_enqcnt(u_int32_t a);
+ u_int32_t decr_enqcnt();
+ u_int32_t subtr_enqcnt(u_int32_t s);
+
+ inline u_int32_t rd_subm_cnt_dblks() const { return _rd_subm_cnt_dblks; }
+ inline std::size_t rd_subm_offs() const { return _rd_subm_cnt_dblks * JRNL_DBLK_SIZE; }
+ u_int32_t add_rd_subm_cnt_dblks(u_int32_t a);
+
+ inline u_int32_t rd_cmpl_cnt_dblks() const { return _rd_cmpl_cnt_dblks; }
+ inline std::size_t rd_cmpl_offs() const { return _rd_cmpl_cnt_dblks * JRNL_DBLK_SIZE; }
+ u_int32_t add_rd_cmpl_cnt_dblks(u_int32_t a);
+
+ inline u_int32_t wr_subm_cnt_dblks() const { return _wr_subm_cnt_dblks; }
+ inline std::size_t wr_subm_offs() const { return _wr_subm_cnt_dblks * JRNL_DBLK_SIZE; }
+ u_int32_t add_wr_subm_cnt_dblks(u_int32_t a);
+
+ inline u_int32_t wr_cmpl_cnt_dblks() const { return _wr_cmpl_cnt_dblks; }
+ inline std::size_t wr_cmpl_offs() const { return _wr_cmpl_cnt_dblks * JRNL_DBLK_SIZE; }
+ u_int32_t add_wr_cmpl_cnt_dblks(u_int32_t a);
+
+ inline u_int16_t aio_cnt() const { return _aio_cnt; }
+ inline u_int16_t incr_aio_cnt() { return ++_aio_cnt; }
+ u_int16_t decr_aio_cnt();
+
+ inline bool wr_fhdr_aio_outstanding() { return _fhdr_wr_aio_outstanding; }
+ inline void set_wr_fhdr_aio_outstanding(const bool wfao) { _fhdr_wr_aio_outstanding = wfao; }
+
+ // Derived helper functions
+
+ inline bool rd_void() const { return _wr_cmpl_cnt_dblks == 0; }
+ inline bool rd_empty() const { return _wr_cmpl_cnt_dblks <= JRNL_SBLK_SIZE; }
+ inline u_int32_t rd_remaining_dblks() const { return _wr_cmpl_cnt_dblks - _rd_subm_cnt_dblks; }
+ inline bool is_rd_full() const { return _wr_cmpl_cnt_dblks == _rd_subm_cnt_dblks; }
+ inline bool is_rd_compl() const { return _wr_cmpl_cnt_dblks == _rd_cmpl_cnt_dblks; }
+ inline u_int32_t rd_aio_outstanding_dblks() const { return _rd_subm_cnt_dblks - _rd_cmpl_cnt_dblks; }
+ inline bool rd_file_rotate() const { return is_rd_full() && is_wr_compl(); }
+
+ inline bool wr_void() const { return _wr_subm_cnt_dblks == 0; }
+ inline bool wr_empty() const { return _wr_subm_cnt_dblks <= JRNL_SBLK_SIZE; }
+ inline u_int32_t wr_remaining_dblks() const { return _ffull_dblks - _wr_subm_cnt_dblks; }
+ inline bool is_wr_full() const { return _ffull_dblks == _wr_subm_cnt_dblks; }
+ inline bool is_wr_compl() const { return _ffull_dblks == _wr_cmpl_cnt_dblks; }
+ inline u_int32_t wr_aio_outstanding_dblks() const { return _wr_subm_cnt_dblks - _wr_cmpl_cnt_dblks; }
+ inline bool wr_file_rotate() const { return is_wr_full(); }
+
+ // Debug aid
+ const std::string status_str() const;
+
+ protected:
+ virtual void initialize(const std::string& fbasename, const u_int16_t pfid, const u_int16_t lfid,
+ const u_int32_t jfsize_sblks, const rcvdat* const ro);
+
+ static std::string filename(const std::string& fbasename, const u_int16_t pfid);
+ void clean_file(const u_int32_t jfsize_sblks);
+ void create_jfile(const u_int32_t jfsize_sblks);
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_FCNTL_H
diff --git a/cpp/src/qpid/legacystore/jrnl/file_hdr.h b/cpp/src/qpid/legacystore/jrnl/file_hdr.h
new file mode 100644
index 0000000000..db20834cbb
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/file_hdr.h
@@ -0,0 +1,211 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file file_hdr.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::file_hdr (file
+ * record header), used to start a journal file. It contains some
+ * file metadata and information to aid journal recovery.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_FILE_HDR_H
+#define QPID_LEGACYSTORE_JRNL_FILE_HDR_H
+
+#include <cerrno>
+#include <ctime>
+#include "qpid/legacystore/jrnl/rec_hdr.h"
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+#include <sstream>
+
+namespace mrg
+{
+namespace journal
+{
+
+#pragma pack(1)
+
+ /**
+ * \brief Struct for data common to the head of all journal files. In addition to
+ * the common data, this includes the record ID and offset of the first record in
+ * the file.
+ *
+ * This header precedes all data in journal files and occupies the first complete
+ * block in the file. The record ID and offset are updated on each overwrite of the
+ * file.
+ *
+ * File header info in binary format (48 bytes):
+ * <pre>
+ * 0 7
+ * +---+---+---+---+---+---+---+---+ -+
+ * | magic | v | e | flags | |
+ * +---+---+---+---+---+---+---+---+ | struct hdr
+ * | first rid in file | |
+ * +---+---+---+---+---+---+---+---+ -+
+ * | pfid | lfid | reserved (0) |
+ * +---+---+---+---+---+---+---+---+
+ * | fro |
+ * +---+---+---+---+---+---+---+---+
+ * | timestamp (sec) |
+ * +---+---+---+---+---+---+---+---+
+ * | timestamp (ns) |
+ * +---+---+---+---+---+---+---+---+
+ * v = file version (If the format or encoding of this file changes, then this
+ * number should be incremented)
+ * e = endian flag, false (0x00) for little endian, true (0x01) for big endian
+ * pfid = File ID (number used in naming file)
+ * lfid = Logical ID (order used in circular buffer)
+ * fro = First record offset, offset from start of file to first record header
+ * </pre>
+ *
+ * Note that journal files should be transferable between 32- and 64-bit
+ * hardware of the same endianness, but not between hardware of opposite
+ * entianness without some sort of binary conversion utility. Thus buffering
+ * will be needed for types that change size between 32- and 64-bit compiles.
+ */
+ struct file_hdr : rec_hdr
+ {
+ u_int16_t _pfid; ///< Physical file ID (pfid)
+ u_int16_t _lfid; ///< Logical file ID (lfid)
+ u_int32_t _res; ///< Reserved (for alignment/flags)
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ u_int32_t _filler0; ///< Big-endian filler for 32-bit size_t
+#endif
+ std::size_t _fro; ///< First record offset
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ u_int32_t _filler0; ///< Little-endian filler for 32-bit size_t
+#endif
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ u_int32_t _filler1; ///< Big-endian filler for 32-bit time_t
+#endif
+ std::time_t _ts_sec; ///< Timestamp of journal initilailization
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ u_int32_t _filler1; ///< Little-endian filler for 32-bit time_t
+#endif
+#if defined(JRNL_BIG_ENDIAN)
+ u_int32_t _filler2; ///< Big endian filler for u_int32_t
+#endif
+ u_int32_t _ts_nsec; ///< Timestamp of journal initilailization
+#if defined(JRNL_LITTLE_ENDIAN)
+ u_int32_t _filler2; ///< Little-endian filler for u_int32_t
+#endif
+
+ /**
+ * \brief Default constructor, which sets all values to 0.
+ */
+ inline file_hdr(): rec_hdr(), _pfid(0), _lfid(0), _res(0),
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ _filler0(0),
+#endif
+ _fro(0),
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ _filler0(0),
+#endif
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ _filler1(0),
+#endif
+ _ts_sec(0),
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ _filler1(0),
+#endif
+#if defined(JRNL_BIG_ENDIAN)
+ _filler2(0),
+#endif
+ _ts_nsec(0)
+#if defined(JRNL_LITTLE_ENDIAN)
+ , _filler2(0)
+#endif
+ {}
+
+ /**
+ * \brief Convenience constructor which initializes values during construction.
+ */
+ inline file_hdr(const u_int32_t magic, const u_int8_t version, const u_int64_t rid,
+ const u_int16_t pfid, const u_int16_t lfid, const std::size_t fro,
+ const bool owi, const bool settime = false):
+ rec_hdr(magic, version, rid, owi), _pfid(pfid), _lfid(lfid), _res(0),
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ _filler0(0),
+#endif
+ _fro(fro),
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ _filler0(0),
+#endif
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ _filler1(0),
+#endif
+ _ts_sec(0),
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ _filler1(0),
+#endif
+#if defined(JRNL_BIG_ENDIAN)
+ _filler2(0),
+#endif
+ _ts_nsec(0)
+#if defined(JRNL_LITTLE_ENDIAN)
+ , _filler2(0)
+#endif
+ { if (settime) set_time(); }
+
+ /**
+ * \brief Gets the current time from the system clock and sets the timestamp in the struct.
+ */
+ inline void set_time()
+ {
+ // TODO: Standardize on method for getting time that does not requrie a context switch.
+ timespec ts;
+ if (::clock_gettime(CLOCK_REALTIME, &ts))
+ {
+ std::ostringstream oss;
+ oss << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR__RTCLOCK, oss.str(), "file_hdr", "set_time");
+ }
+ _ts_sec = ts.tv_sec;
+ _ts_nsec = ts.tv_nsec;
+ }
+
+ /**
+ * \brief Sets the timestamp in the struct to the provided value (in seconds and
+ * nanoseconds).
+ */
+ inline void set_time(timespec& ts)
+ {
+ _ts_sec = ts.tv_sec;
+ _ts_nsec = ts.tv_nsec;
+ }
+
+ /**
+ * \brief Returns the size of the header in bytes.
+ */
+ inline static std::size_t size() { return sizeof(file_hdr); }
+ }; // struct file_hdr
+
+#pragma pack()
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_FILE_HDR_H
diff --git a/cpp/src/qpid/legacystore/jrnl/jcfg.h b/cpp/src/qpid/legacystore/jrnl/jcfg.h
new file mode 100644
index 0000000000..0a0d0df28d
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/jcfg.h
@@ -0,0 +1,91 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file jcfg.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * This file contains \#defines that control the implementation details of
+ * the journal.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_JCFG_H
+#define QPID_LEGACYSTORE_JRNL_JCFG_H
+
+#if defined(__i386__) /* little endian, 32 bits */
+#define JRNL_LITTLE_ENDIAN
+#define JRNL_32_BIT
+#elif defined(__PPC__) || defined(__s390__) /* big endian, 32 bits */
+#define JRNL_BIG_ENDIAN
+#define JRNL_32_BIT
+#elif defined(__ia64__) || defined(__x86_64__) || defined(__alpha__) /* little endian, 64 bits */
+#define JRNL_LITTLE_ENDIAN
+#define JRNL_64_BIT
+#elif defined(__powerpc64__) || defined(__s390x__) /* big endian, 64 bits */
+#define JRNL_BIG_ENDIAN
+#define JRNL_64_BIT
+#else
+#error endian?
+#endif
+
+
+/**
+* <b>Rule:</b> Data block size (JRNL_DBLK_SIZE) MUST be a power of 2 such that
+* <pre>
+* JRNL_DBLK_SIZE * JRNL_SBLK_SIZE == n * 512 (n = 1,2,3...)
+* </pre>
+* (The disk softblock size is 512 for Linux kernels >= 2.6)
+*/
+#define JRNL_DBLK_SIZE 128 ///< Data block size in bytes (CANNOT BE LESS THAN 32!)
+#define JRNL_SBLK_SIZE 4 ///< Disk softblock size in multiples of JRNL_DBLK_SIZE
+#define JRNL_MIN_FILE_SIZE 128 ///< Min. jrnl file size in sblks (excl. file_hdr)
+#define JRNL_MAX_FILE_SIZE 4194176 ///< Max. jrnl file size in sblks (excl. file_hdr)
+#define JRNL_MIN_NUM_FILES 4 ///< Min. number of journal files
+#define JRNL_MAX_NUM_FILES 64 ///< Max. number of journal files
+#define JRNL_ENQ_THRESHOLD 80 ///< Percent full when enqueue connection will be closed
+
+#define JRNL_RMGR_PAGE_SIZE 128 ///< Journal page size in softblocks
+#define JRNL_RMGR_PAGES 16 ///< Number of pages to use in wmgr
+
+#define JRNL_WMGR_DEF_PAGE_SIZE 64 ///< Journal write page size in softblocks (default)
+#define JRNL_WMGR_DEF_PAGES 32 ///< Number of pages to use in wmgr (default)
+
+#define JRNL_WMGR_MAXDTOKPP 1024 ///< Max. dtoks (data blocks) per page in wmgr
+#define JRNL_WMGR_MAXWAITUS 100 ///< Max. wait time (us) before submitting AIO
+
+#define JRNL_INFO_EXTENSION "jinf" ///< Extension for journal info files
+#define JRNL_DATA_EXTENSION "jdat" ///< Extension for journal data files
+#define RHM_JDAT_TXA_MAGIC 0x614d4852 ///< ("RHMa" in little endian) Magic for dtx abort hdrs
+#define RHM_JDAT_TXC_MAGIC 0x634d4852 ///< ("RHMc" in little endian) Magic for dtx commit hdrs
+#define RHM_JDAT_DEQ_MAGIC 0x644d4852 ///< ("RHMd" in little endian) Magic for deq rec hdrs
+#define RHM_JDAT_ENQ_MAGIC 0x654d4852 ///< ("RHMe" in little endian) Magic for enq rec hdrs
+#define RHM_JDAT_FILE_MAGIC 0x664d4852 ///< ("RHMf" in little endian) Magic for file hdrs
+#define RHM_JDAT_EMPTY_MAGIC 0x784d4852 ///< ("RHMx" in little endian) Magic for empty dblk
+#define RHM_JDAT_VERSION 0x01 ///< Version (of file layout)
+#define RHM_CLEAN_CHAR 0xff ///< Char used to clear empty space on disk
+
+#define RHM_LENDIAN_FLAG 0 ///< Value of little endian flag on disk
+#define RHM_BENDIAN_FLAG 1 ///< Value of big endian flag on disk
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_JCFG_H
diff --git a/cpp/src/qpid/legacystore/jrnl/jcntl.cpp b/cpp/src/qpid/legacystore/jrnl/jcntl.cpp
new file mode 100644
index 0000000000..a03076dca5
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/jcntl.cpp
@@ -0,0 +1,984 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 jcntl.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * Messaging journal top-level control and interface class
+ * mrg::journal::jcntl. See comments in file jcntl.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+
+#include "qpid/legacystore/jrnl/jcntl.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include "qpid/legacystore/jrnl/file_hdr.h"
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jinf.h"
+#include <limits>
+#include <sstream>
+#include <unistd.h>
+
+namespace mrg
+{
+namespace journal
+{
+
+#define AIO_CMPL_TIMEOUT_SEC 5
+#define AIO_CMPL_TIMEOUT_NSEC 0
+#define FINAL_AIO_CMPL_TIMEOUT_SEC 15
+#define FINAL_AIO_CMPL_TIMEOUT_NSEC 0
+
+// Static
+timespec jcntl::_aio_cmpl_timeout; ///< Timeout for blocking libaio returns
+timespec jcntl::_final_aio_cmpl_timeout; ///< Timeout for blocking libaio returns when stopping or finalizing
+bool jcntl::_init = init_statics();
+bool jcntl::init_statics()
+{
+ _aio_cmpl_timeout.tv_sec = AIO_CMPL_TIMEOUT_SEC;
+ _aio_cmpl_timeout.tv_nsec = AIO_CMPL_TIMEOUT_NSEC;
+ _final_aio_cmpl_timeout.tv_sec = FINAL_AIO_CMPL_TIMEOUT_SEC;
+ _final_aio_cmpl_timeout.tv_nsec = FINAL_AIO_CMPL_TIMEOUT_NSEC;
+ return true;
+}
+
+
+// Functions
+
+jcntl::jcntl(const std::string& jid, const std::string& jdir, const std::string& base_filename):
+ _jid(jid),
+ _jdir(jdir, base_filename),
+ _base_filename(base_filename),
+ _init_flag(false),
+ _stop_flag(false),
+ _readonly_flag(false),
+ _autostop(true),
+ _jfsize_sblks(0),
+ _lpmgr(),
+ _emap(),
+ _tmap(),
+ _rrfc(&_lpmgr),
+ _wrfc(&_lpmgr),
+ _rmgr(this, _emap, _tmap, _rrfc),
+ _wmgr(this, _emap, _tmap, _wrfc),
+ _rcvdat()
+{}
+
+jcntl::~jcntl()
+{
+ if (_init_flag && !_stop_flag)
+ try { stop(true); }
+ catch (const jexception& e) { std::cerr << e << std::endl; }
+ _lpmgr.finalize();
+}
+
+void
+jcntl::initialize(const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles,
+ const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks,
+ aio_callback* const cbp)
+{
+ _init_flag = false;
+ _stop_flag = false;
+ _readonly_flag = false;
+
+ _emap.clear();
+ _tmap.clear();
+
+ _lpmgr.finalize();
+
+ // Set new file geometry parameters
+ assert(num_jfiles >= JRNL_MIN_NUM_FILES);
+ assert(num_jfiles <= JRNL_MAX_NUM_FILES);
+ _emap.set_num_jfiles(num_jfiles);
+ _tmap.set_num_jfiles(num_jfiles);
+
+ assert(jfsize_sblks >= JRNL_MIN_FILE_SIZE);
+ assert(jfsize_sblks <= JRNL_MAX_FILE_SIZE);
+ _jfsize_sblks = jfsize_sblks;
+
+ // Clear any existing journal files
+ _jdir.clear_dir();
+ _lpmgr.initialize(num_jfiles, ae, ae_max_jfiles, this, &new_fcntl);
+
+ _wrfc.initialize(_jfsize_sblks);
+ _rrfc.initialize();
+ _rrfc.set_findex(0);
+ _rmgr.initialize(cbp);
+ _wmgr.initialize(cbp, wcache_pgsize_sblks, wcache_num_pages, JRNL_WMGR_MAXDTOKPP, JRNL_WMGR_MAXWAITUS);
+
+ // Write info file (<basename>.jinf) to disk
+ write_infofile();
+
+ _init_flag = true;
+}
+
+void
+jcntl::recover(const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles,
+ const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks,
+// const rd_aio_cb rd_cb, const wr_aio_cb wr_cb, const std::vector<std::string>* prep_txn_list_ptr,
+ aio_callback* const cbp, const std::vector<std::string>* prep_txn_list_ptr,
+ u_int64_t& highest_rid)
+{
+ _init_flag = false;
+ _stop_flag = false;
+ _readonly_flag = false;
+
+ _emap.clear();
+ _tmap.clear();
+
+ _lpmgr.finalize();
+
+ assert(num_jfiles >= JRNL_MIN_NUM_FILES);
+ assert(num_jfiles <= JRNL_MAX_NUM_FILES);
+ assert(jfsize_sblks >= JRNL_MIN_FILE_SIZE);
+ assert(jfsize_sblks <= JRNL_MAX_FILE_SIZE);
+ _jfsize_sblks = jfsize_sblks;
+
+ // Verify journal dir and journal files
+ _jdir.verify_dir();
+ _rcvdat.reset(num_jfiles, ae, ae_max_jfiles);
+
+ rcvr_janalyze(_rcvdat, prep_txn_list_ptr);
+ highest_rid = _rcvdat._h_rid;
+ if (_rcvdat._jfull)
+ throw jexception(jerrno::JERR_JCNTL_RECOVERJFULL, "jcntl", "recover");
+ this->log(LOG_DEBUG, _rcvdat.to_log(_jid));
+
+ _lpmgr.recover(_rcvdat, this, &new_fcntl);
+
+ _wrfc.initialize(_jfsize_sblks, &_rcvdat);
+ _rrfc.initialize();
+ _rrfc.set_findex(_rcvdat.ffid());
+ _rmgr.initialize(cbp);
+ _wmgr.initialize(cbp, wcache_pgsize_sblks, wcache_num_pages, JRNL_WMGR_MAXDTOKPP, JRNL_WMGR_MAXWAITUS,
+ (_rcvdat._lffull ? 0 : _rcvdat._eo));
+
+ _readonly_flag = true;
+ _init_flag = true;
+}
+
+void
+jcntl::recover_complete()
+{
+ if (!_readonly_flag)
+ throw jexception(jerrno::JERR_JCNTL_NOTRECOVERED, "jcntl", "recover_complete");
+ for (u_int16_t i=0; i<_lpmgr.num_jfiles(); i++)
+ _lpmgr.get_fcntlp(i)->reset(&_rcvdat);
+ _wrfc.initialize(_jfsize_sblks, &_rcvdat);
+ _rrfc.initialize();
+ _rrfc.set_findex(_rcvdat.ffid());
+ _rmgr.recover_complete();
+ _readonly_flag = false;
+}
+
+void
+jcntl::delete_jrnl_files()
+{
+ stop(true); // wait for AIO to complete
+ _jdir.delete_dir();
+}
+
+
+iores
+jcntl::enqueue_data_record(const void* const data_buff, const std::size_t tot_data_len,
+ const std::size_t this_data_len, data_tok* dtokp, const bool transient)
+{
+ iores r;
+ check_wstatus("enqueue_data_record");
+ {
+ slock s(_wr_mutex);
+ while (handle_aio_wait(_wmgr.enqueue(data_buff, tot_data_len, this_data_len, dtokp, 0, 0, transient, false), r,
+ dtokp)) ;
+ }
+ return r;
+}
+
+iores
+jcntl::enqueue_extern_data_record(const std::size_t tot_data_len, data_tok* dtokp, const bool transient)
+{
+ iores r;
+ check_wstatus("enqueue_extern_data_record");
+ {
+ slock s(_wr_mutex);
+ while (handle_aio_wait(_wmgr.enqueue(0, tot_data_len, 0, dtokp, 0, 0, transient, true), r, dtokp)) ;
+ }
+ return r;
+}
+
+iores
+jcntl::enqueue_txn_data_record(const void* const data_buff, const std::size_t tot_data_len,
+ const std::size_t this_data_len, data_tok* dtokp, const std::string& xid,
+ const bool transient)
+{
+ iores r;
+ check_wstatus("enqueue_tx_data_record");
+ {
+ slock s(_wr_mutex);
+ while (handle_aio_wait(_wmgr.enqueue(data_buff, tot_data_len, this_data_len, dtokp, xid.data(), xid.size(),
+ transient, false), r, dtokp)) ;
+ }
+ return r;
+}
+
+iores
+jcntl::enqueue_extern_txn_data_record(const std::size_t tot_data_len, data_tok* dtokp,
+ const std::string& xid, const bool transient)
+{
+ iores r;
+ check_wstatus("enqueue_extern_txn_data_record");
+ {
+ slock s(_wr_mutex);
+ while (handle_aio_wait(_wmgr.enqueue(0, tot_data_len, 0, dtokp, xid.data(), xid.size(), transient, true), r,
+ dtokp)) ;
+ }
+ return r;
+}
+
+/* TODO
+iores
+jcntl::get_data_record(const u_int64_t& rid, const std::size_t& dsize, const std::size_t& dsize_avail,
+ const void** const data, bool auto_discard)
+{
+ check_rstatus("get_data_record");
+ return _rmgr.get(rid, dsize, dsize_avail, data, auto_discard);
+} */
+
+/* TODO
+iores
+jcntl::discard_data_record(data_tok* const dtokp)
+{
+ check_rstatus("discard_data_record");
+ return _rmgr.discard(dtokp);
+} */
+
+iores
+jcntl::read_data_record(void** const datapp, std::size_t& dsize, void** const xidpp, std::size_t& xidsize,
+ bool& transient, bool& external, data_tok* const dtokp, bool ignore_pending_txns)
+{
+ check_rstatus("read_data");
+ iores res = _rmgr.read(datapp, dsize, xidpp, xidsize, transient, external, dtokp, ignore_pending_txns);
+ if (res == RHM_IORES_RCINVALID)
+ {
+ get_wr_events(0); // check for outstanding write events
+ iores sres = _rmgr.synchronize(); // flushes all outstanding read events
+ if (sres != RHM_IORES_SUCCESS)
+ return sres;
+ _rmgr.wait_for_validity(&_aio_cmpl_timeout, true); // throw if timeout occurs
+ res = _rmgr.read(datapp, dsize, xidpp, xidsize, transient, external, dtokp, ignore_pending_txns);
+ }
+ return res;
+}
+
+iores
+jcntl::dequeue_data_record(data_tok* const dtokp, const bool txn_coml_commit)
+{
+ iores r;
+ check_wstatus("dequeue_data");
+ {
+ slock s(_wr_mutex);
+ while (handle_aio_wait(_wmgr.dequeue(dtokp, 0, 0, txn_coml_commit), r, dtokp)) ;
+ }
+ return r;
+}
+
+iores
+jcntl::dequeue_txn_data_record(data_tok* const dtokp, const std::string& xid, const bool txn_coml_commit)
+{
+ iores r;
+ check_wstatus("dequeue_data");
+ {
+ slock s(_wr_mutex);
+ while (handle_aio_wait(_wmgr.dequeue(dtokp, xid.data(), xid.size(), txn_coml_commit), r, dtokp)) ;
+ }
+ return r;
+}
+
+iores
+jcntl::txn_abort(data_tok* const dtokp, const std::string& xid)
+{
+ iores r;
+ check_wstatus("txn_abort");
+ {
+ slock s(_wr_mutex);
+ while (handle_aio_wait(_wmgr.abort(dtokp, xid.data(), xid.size()), r, dtokp)) ;
+ }
+ return r;
+}
+
+iores
+jcntl::txn_commit(data_tok* const dtokp, const std::string& xid)
+{
+ iores r;
+ check_wstatus("txn_commit");
+ {
+ slock s(_wr_mutex);
+ while (handle_aio_wait(_wmgr.commit(dtokp, xid.data(), xid.size()), r, dtokp)) ;
+ }
+ return r;
+}
+
+bool
+jcntl::is_txn_synced(const std::string& xid)
+{
+ slock s(_wr_mutex);
+ bool res = _wmgr.is_txn_synced(xid);
+ return res;
+}
+
+int32_t
+jcntl::get_wr_events(timespec* const timeout)
+{
+ stlock t(_wr_mutex);
+ if (!t.locked())
+ return jerrno::LOCK_TAKEN;
+ int32_t res = _wmgr.get_events(pmgr::UNUSED, timeout);
+ return res;
+}
+
+int32_t
+jcntl::get_rd_events(timespec* const timeout)
+{
+ return _rmgr.get_events(pmgr::AIO_COMPLETE, timeout);
+}
+
+void
+jcntl::stop(const bool block_till_aio_cmpl)
+{
+ if (_readonly_flag)
+ check_rstatus("stop");
+ else
+ check_wstatus("stop");
+ _stop_flag = true;
+ if (!_readonly_flag)
+ flush(block_till_aio_cmpl);
+ _rrfc.finalize();
+ _lpmgr.finalize();
+}
+
+u_int16_t
+jcntl::get_earliest_fid()
+{
+ u_int16_t ffid = _wrfc.earliest_index();
+ u_int16_t fid = _wrfc.index();
+ while ( _emap.get_enq_cnt(ffid) == 0 && _tmap.get_txn_pfid_cnt(ffid) == 0 && ffid != fid)
+ {
+ if (++ffid >= _lpmgr.num_jfiles())
+ ffid = 0;
+ }
+ if (!_rrfc.is_active())
+ _rrfc.set_findex(ffid);
+ return ffid;
+}
+
+iores
+jcntl::flush(const bool block_till_aio_cmpl)
+{
+ if (!_init_flag)
+ return RHM_IORES_SUCCESS;
+ if (_readonly_flag)
+ throw jexception(jerrno::JERR_JCNTL_READONLY, "jcntl", "flush");
+ iores res;
+ {
+ slock s(_wr_mutex);
+ res = _wmgr.flush();
+ }
+ if (block_till_aio_cmpl)
+ aio_cmpl_wait();
+ return res;
+}
+
+void
+jcntl::log(log_level ll, const std::string& log_stmt) const
+{
+ log(ll, log_stmt.c_str());
+}
+
+void
+jcntl::log(log_level ll, const char* const log_stmt) const
+{
+ if (ll > LOG_INFO)
+ {
+ std::cout << log_level_str(ll) << ": Journal \"" << _jid << "\": " << log_stmt << std::endl;
+ }
+}
+
+void
+jcntl::chk_wr_frot()
+{
+ if (_wrfc.index() == _rrfc.index())
+ _rmgr.invalidate();
+}
+
+void
+jcntl::fhdr_wr_sync(const u_int16_t lid)
+{
+ fcntl* fcntlp = _lpmgr.get_fcntlp(lid);
+ while (fcntlp->wr_fhdr_aio_outstanding())
+ {
+ if (get_wr_events(&_aio_cmpl_timeout) == jerrno::AIO_TIMEOUT)
+ throw jexception(jerrno::JERR_JCNTL_AIOCMPLWAIT, "jcntl", "fhdr_wr_sync");
+ }
+}
+
+fcntl*
+jcntl::new_fcntl(jcntl* const jcp, const u_int16_t lid, const u_int16_t fid, const rcvdat* const rdp)
+{
+ if (!jcp) return 0;
+ std::ostringstream oss;
+ oss << jcp->jrnl_dir() << "/" << jcp->base_filename();
+ return new fcntl(oss.str(), fid, lid, jcp->jfsize_sblks(), rdp);
+}
+
+// Protected/Private functions
+
+void
+jcntl::check_wstatus(const char* fn_name) const
+{
+ if (!_init_flag)
+ throw jexception(jerrno::JERR__NINIT, "jcntl", fn_name);
+ if (_readonly_flag)
+ throw jexception(jerrno::JERR_JCNTL_READONLY, "jcntl", fn_name);
+ if (_stop_flag)
+ throw jexception(jerrno::JERR_JCNTL_STOPPED, "jcntl", fn_name);
+}
+
+void
+jcntl::check_rstatus(const char* fn_name) const
+{
+ if (!_init_flag)
+ throw jexception(jerrno::JERR__NINIT, "jcntl", fn_name);
+ if (_stop_flag)
+ throw jexception(jerrno::JERR_JCNTL_STOPPED, "jcntl", fn_name);
+}
+
+void
+jcntl::write_infofile() const
+{
+ timespec ts;
+ if (::clock_gettime(CLOCK_REALTIME, &ts))
+ {
+ std::ostringstream oss;
+ oss << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR__RTCLOCK, oss.str(), "jcntl", "write_infofile");
+ }
+ jinf ji(_jid, _jdir.dirname(), _base_filename, _lpmgr.num_jfiles(), _lpmgr.is_ae(), _lpmgr.ae_max_jfiles(),
+ _jfsize_sblks, _wmgr.cache_pgsize_sblks(), _wmgr.cache_num_pages(), ts);
+ ji.write();
+}
+
+void
+jcntl::aio_cmpl_wait()
+{
+ //while (_wmgr.get_aio_evt_rem())
+ while (true)
+ {
+ u_int32_t aer;
+ {
+ slock s(_wr_mutex);
+ aer = _wmgr.get_aio_evt_rem();
+ }
+ if (aer == 0) break; // no events left
+ if (get_wr_events(&_aio_cmpl_timeout) == jerrno::AIO_TIMEOUT)
+ throw jexception(jerrno::JERR_JCNTL_AIOCMPLWAIT, "jcntl", "aio_cmpl_wait");
+ }
+}
+
+bool
+jcntl::handle_aio_wait(const iores res, iores& resout, const data_tok* dtp)
+{
+ resout = res;
+ if (res == RHM_IORES_PAGE_AIOWAIT)
+ {
+ while (_wmgr.curr_pg_blocked())
+ {
+ if (_wmgr.get_events(pmgr::UNUSED, &_aio_cmpl_timeout) == jerrno::AIO_TIMEOUT)
+ {
+ std::ostringstream oss;
+ oss << "get_events() returned JERR_JCNTL_AIOCMPLWAIT; wmgr_status: " << _wmgr.status_str();
+ this->log(LOG_CRITICAL, oss.str());
+ throw jexception(jerrno::JERR_JCNTL_AIOCMPLWAIT, "jcntl", "handle_aio_wait");
+ }
+ }
+ return true;
+ }
+ else if (res == RHM_IORES_FILE_AIOWAIT)
+ {
+ while (_wmgr.curr_file_blocked())
+ {
+ if (_wmgr.get_events(pmgr::UNUSED, &_aio_cmpl_timeout) == jerrno::AIO_TIMEOUT)
+ {
+ std::ostringstream oss;
+ oss << "get_events() returned JERR_JCNTL_AIOCMPLWAIT; wmgr_status: " << _wmgr.status_str();
+ this->log(LOG_CRITICAL, oss.str());
+ throw jexception(jerrno::JERR_JCNTL_AIOCMPLWAIT, "jcntl", "handle_aio_wait");
+ }
+ }
+ _wrfc.wr_reset();
+ resout = RHM_IORES_SUCCESS;
+ data_tok::write_state ws = dtp->wstate();
+ return ws == data_tok::ENQ_PART || ws == data_tok::DEQ_PART || ws == data_tok::ABORT_PART ||
+ ws == data_tok::COMMIT_PART;
+ }
+ return false;
+}
+
+void
+jcntl::rcvr_janalyze(rcvdat& rd, const std::vector<std::string>* prep_txn_list_ptr)
+{
+ jinf ji(_jdir.dirname() + "/" + _base_filename + "." + JRNL_INFO_EXTENSION, true);
+
+ // If the number of files does not tie up with the jinf file from the journal being recovered,
+ // use the jinf data.
+ if (rd._njf != ji.num_jfiles())
+ {
+ std::ostringstream oss;
+ oss << "Recovery found " << ji.num_jfiles() <<
+ " files (different from --num-jfiles value of " << rd._njf << ").";
+ this->log(LOG_WARN, oss.str());
+ rd._njf = ji.num_jfiles();
+ _rcvdat._enq_cnt_list.resize(rd._njf);
+ }
+ _emap.set_num_jfiles(rd._njf);
+ _tmap.set_num_jfiles(rd._njf);
+ if (_jfsize_sblks != ji.jfsize_sblks())
+ {
+ std::ostringstream oss;
+ oss << "Recovery found file size = " << (ji.jfsize_sblks() / JRNL_RMGR_PAGE_SIZE) <<
+ " (different from --jfile-size-pgs value of " <<
+ (_jfsize_sblks / JRNL_RMGR_PAGE_SIZE) << ").";
+ this->log(LOG_WARN, oss.str());
+ _jfsize_sblks = ji.jfsize_sblks();
+ }
+ if (_jdir.dirname().compare(ji.jdir()))
+ {
+ std::ostringstream oss;
+ oss << "Journal file location change: original = \"" << ji.jdir() <<
+ "\"; current = \"" << _jdir.dirname() << "\"";
+ this->log(LOG_WARN, oss.str());
+ ji.set_jdir(_jdir.dirname());
+ }
+
+ try
+ {
+ rd._ffid = ji.get_first_pfid();
+ rd._lfid = ji.get_last_pfid();
+ rd._owi = ji.get_initial_owi();
+ rd._frot = ji.get_frot();
+ rd._jempty = false;
+ ji.get_normalized_pfid_list(rd._fid_list); // _pfid_list
+ }
+ catch (const jexception& e)
+ {
+ if (e.err_code() != jerrno::JERR_JINF_JDATEMPTY) throw;
+ }
+
+ // Restore all read and write pointers and transactions
+ if (!rd._jempty)
+ {
+ u_int16_t fid = rd._ffid;
+ std::ifstream ifs;
+ bool lowi = rd._owi; // local copy of owi to be used during analysis
+ while (rcvr_get_next_record(fid, &ifs, lowi, rd)) ;
+ if (ifs.is_open()) ifs.close();
+
+ // Remove all txns from tmap that are not in the prepared list
+ if (prep_txn_list_ptr)
+ {
+ std::vector<std::string> xid_list;
+ _tmap.xid_list(xid_list);
+ for (std::vector<std::string>::iterator itr = xid_list.begin(); itr != xid_list.end(); itr++)
+ {
+ std::vector<std::string>::const_iterator pitr =
+ std::find(prep_txn_list_ptr->begin(), prep_txn_list_ptr->end(), *itr);
+ if (pitr == prep_txn_list_ptr->end()) // not found in prepared list
+ {
+ txn_data_list tdl = _tmap.get_remove_tdata_list(*itr); // tdl will be empty if xid not found
+ // Unlock any affected enqueues in emap
+ for (tdl_itr i=tdl.begin(); i<tdl.end(); i++)
+ {
+ if (i->_enq_flag) // enq op - decrement enqueue count
+ rd._enq_cnt_list[i->_pfid]--;
+ else if (_emap.is_enqueued(i->_drid, true)) // deq op - unlock enq record
+ {
+ int16_t ret = _emap.unlock(i->_drid);
+ if (ret < enq_map::EMAP_OK) // fail
+ {
+ // enq_map::unlock()'s only error is enq_map::EMAP_RID_NOT_FOUND
+ std::ostringstream oss;
+ oss << std::hex << "_emap.unlock(): drid=0x\"" << i->_drid;
+ throw jexception(jerrno::JERR_MAP_NOTFOUND, oss.str(), "jcntl", "rcvr_janalyze");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Check for file full condition - add one to _jfsize_sblks to account for file header
+ rd._lffull = rd._eo == (1 + _jfsize_sblks) * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE;
+
+ // Check for journal full condition
+ u_int16_t next_wr_fid = (rd._lfid + 1) % rd._njf;
+ rd._jfull = rd._ffid == next_wr_fid && rd._enq_cnt_list[next_wr_fid] && rd._lffull;
+ }
+}
+
+bool
+jcntl::rcvr_get_next_record(u_int16_t& fid, std::ifstream* ifsp, bool& lowi, rcvdat& rd)
+{
+ std::size_t cum_size_read = 0;
+ void* xidp = 0;
+ rec_hdr h;
+
+ bool hdr_ok = false;
+ std::streampos file_pos;
+ while (!hdr_ok)
+ {
+ if (!ifsp->is_open())
+ {
+ if (!jfile_cycle(fid, ifsp, lowi, rd, true))
+ return false;
+ }
+ file_pos = ifsp->tellg();
+ ifsp->read((char*)&h, sizeof(rec_hdr));
+ if (ifsp->gcount() == sizeof(rec_hdr))
+ hdr_ok = true;
+ else
+ {
+ if (!jfile_cycle(fid, ifsp, lowi, rd, true))
+ return false;
+ }
+ }
+
+ switch(h._magic)
+ {
+ case RHM_JDAT_ENQ_MAGIC:
+ {
+ enq_rec er;
+ u_int16_t start_fid = fid; // fid may increment in decode() if record folds over file boundary
+ if (!decode(er, fid, ifsp, cum_size_read, h, lowi, rd, file_pos))
+ return false;
+ if (!er.is_transient()) // Ignore transient msgs
+ {
+ rd._enq_cnt_list[start_fid]++;
+ if (er.xid_size())
+ {
+ er.get_xid(&xidp);
+ assert(xidp != 0);
+ std::string xid((char*)xidp, er.xid_size());
+ _tmap.insert_txn_data(xid, txn_data(h._rid, 0, start_fid, true));
+ if (_tmap.set_aio_compl(xid, h._rid) < txn_map::TMAP_OK) // fail - xid or rid not found
+ {
+ std::ostringstream oss;
+ oss << std::hex << "_tmap.set_aio_compl: txn_enq xid=\"" << xid << "\" rid=0x" << h._rid;
+ throw jexception(jerrno::JERR_MAP_NOTFOUND, oss.str(), "jcntl", "rcvr_get_next_record");
+ }
+ std::free(xidp);
+ }
+ else
+ {
+ if (_emap.insert_pfid(h._rid, start_fid) < enq_map::EMAP_OK) // fail
+ {
+ // The only error code emap::insert_pfid() returns is enq_map::EMAP_DUP_RID.
+ std::ostringstream oss;
+ oss << std::hex << "rid=0x" << h._rid << " _pfid=0x" << start_fid;
+ throw jexception(jerrno::JERR_MAP_DUPLICATE, oss.str(), "jcntl", "rcvr_get_next_record");
+ }
+ }
+ }
+ }
+ break;
+ case RHM_JDAT_DEQ_MAGIC:
+ {
+ deq_rec dr;
+ u_int16_t start_fid = fid; // fid may increment in decode() if record folds over file boundary
+ if (!decode(dr, fid, ifsp, cum_size_read, h, lowi, rd, file_pos))
+ return false;
+ if (dr.xid_size())
+ {
+ // If the enqueue is part of a pending txn, it will not yet be in emap
+ _emap.lock(dr.deq_rid()); // ignore not found error
+ dr.get_xid(&xidp);
+ assert(xidp != 0);
+ std::string xid((char*)xidp, dr.xid_size());
+ _tmap.insert_txn_data(xid, txn_data(dr.rid(), dr.deq_rid(), start_fid, false,
+ dr.is_txn_coml_commit()));
+ if (_tmap.set_aio_compl(xid, dr.rid()) < txn_map::TMAP_OK) // fail - xid or rid not found
+ {
+ std::ostringstream oss;
+ oss << std::hex << "_tmap.set_aio_compl: txn_deq xid=\"" << xid << "\" rid=0x" << dr.rid();
+ throw jexception(jerrno::JERR_MAP_NOTFOUND, oss.str(), "jcntl", "rcvr_get_next_record");
+ }
+ std::free(xidp);
+ }
+ else
+ {
+ int16_t enq_fid = _emap.get_remove_pfid(dr.deq_rid(), true);
+ if (enq_fid >= enq_map::EMAP_OK) // ignore not found error
+ rd._enq_cnt_list[enq_fid]--;
+ }
+ }
+ break;
+ case RHM_JDAT_TXA_MAGIC:
+ {
+ txn_rec ar;
+ if (!decode(ar, fid, ifsp, cum_size_read, h, lowi, rd, file_pos))
+ return false;
+ // Delete this txn from tmap, unlock any locked records in emap
+ ar.get_xid(&xidp);
+ assert(xidp != 0);
+ std::string xid((char*)xidp, ar.xid_size());
+ txn_data_list tdl = _tmap.get_remove_tdata_list(xid); // tdl will be empty if xid not found
+ for (tdl_itr itr = tdl.begin(); itr != tdl.end(); itr++)
+ {
+ if (itr->_enq_flag)
+ rd._enq_cnt_list[itr->_pfid]--;
+ else
+ _emap.unlock(itr->_drid); // ignore not found error
+ }
+ std::free(xidp);
+ }
+ break;
+ case RHM_JDAT_TXC_MAGIC:
+ {
+ txn_rec cr;
+ if (!decode(cr, fid, ifsp, cum_size_read, h, lowi, rd, file_pos))
+ return false;
+ // Delete this txn from tmap, process records into emap
+ cr.get_xid(&xidp);
+ assert(xidp != 0);
+ std::string xid((char*)xidp, cr.xid_size());
+ txn_data_list tdl = _tmap.get_remove_tdata_list(xid); // tdl will be empty if xid not found
+ for (tdl_itr itr = tdl.begin(); itr != tdl.end(); itr++)
+ {
+ if (itr->_enq_flag) // txn enqueue
+ {
+ if (_emap.insert_pfid(itr->_rid, itr->_pfid) < enq_map::EMAP_OK) // fail
+ {
+ // The only error code emap::insert_pfid() returns is enq_map::EMAP_DUP_RID.
+ std::ostringstream oss;
+ oss << std::hex << "rid=0x" << itr->_rid << " _pfid=0x" << itr->_pfid;
+ throw jexception(jerrno::JERR_MAP_DUPLICATE, oss.str(), "jcntl", "rcvr_get_next_record");
+ }
+ }
+ else // txn dequeue
+ {
+ int16_t enq_fid = _emap.get_remove_pfid(itr->_drid, true);
+ if (enq_fid >= enq_map::EMAP_OK)
+ rd._enq_cnt_list[enq_fid]--;
+ }
+ }
+ std::free(xidp);
+ }
+ break;
+ case RHM_JDAT_EMPTY_MAGIC:
+ {
+ u_int32_t rec_dblks = jrec::size_dblks(sizeof(rec_hdr));
+ ifsp->ignore(rec_dblks * JRNL_DBLK_SIZE - sizeof(rec_hdr));
+ assert(!ifsp->fail() && !ifsp->bad());
+ if (!jfile_cycle(fid, ifsp, lowi, rd, false))
+ return false;
+ }
+ break;
+ case 0:
+ check_journal_alignment(fid, file_pos, rd);
+ return false;
+ default:
+ // Stop as this is the overwrite boundary.
+ check_journal_alignment(fid, file_pos, rd);
+ return false;
+ }
+ return true;
+}
+
+bool
+jcntl::decode(jrec& rec, u_int16_t& fid, std::ifstream* ifsp, std::size_t& cum_size_read,
+ rec_hdr& h, bool& lowi, rcvdat& rd, std::streampos& file_offs)
+{
+ u_int16_t start_fid = fid;
+ std::streampos start_file_offs = file_offs;
+ if (!check_owi(fid, h, lowi, rd, file_offs))
+ return false;
+ bool done = false;
+ while (!done)
+ {
+ try { done = rec.rcv_decode(h, ifsp, cum_size_read); }
+ catch (const jexception& e)
+ {
+// TODO - review this logic and tidy up how rd._lfid is assigned. See new jinf.get_end_file() fn.
+// Original
+// if (e.err_code() != jerrno::JERR_JREC_BADRECTAIL ||
+// fid != (rd._ffid ? rd._ffid - 1 : _num_jfiles - 1)) throw;
+// Tried this, but did not work
+// if (e.err_code() != jerrno::JERR_JREC_BADRECTAIL || h._magic != 0) throw;
+ check_journal_alignment(start_fid, start_file_offs, rd);
+// rd._lfid = start_fid;
+ return false;
+ }
+ if (!done && !jfile_cycle(fid, ifsp, lowi, rd, false))
+ {
+ check_journal_alignment(start_fid, start_file_offs, rd);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+jcntl::jfile_cycle(u_int16_t& fid, std::ifstream* ifsp, bool& lowi, rcvdat& rd, const bool jump_fro)
+{
+ if (ifsp->is_open())
+ {
+ if (ifsp->eof() || !ifsp->good())
+ {
+ ifsp->clear();
+ rd._eo = ifsp->tellg(); // remember file offset before closing
+ assert(rd._eo != std::numeric_limits<std::size_t>::max()); // Check for error code -1
+ ifsp->close();
+ if (++fid >= rd._njf)
+ {
+ fid = 0;
+ lowi = !lowi; // Flip local owi
+ }
+ if (fid == rd._ffid) // used up all journal files
+ return false;
+ }
+ }
+ if (!ifsp->is_open())
+ {
+ std::ostringstream oss;
+ oss << _jdir.dirname() << "/" << _base_filename << ".";
+ oss << std::hex << std::setfill('0') << std::setw(4) << fid << "." << JRNL_DATA_EXTENSION;
+ ifsp->clear(); // clear eof flag, req'd for older versions of c++
+ ifsp->open(oss.str().c_str(), std::ios_base::in | std::ios_base::binary);
+ if (!ifsp->good())
+ throw jexception(jerrno::JERR__FILEIO, oss.str(), "jcntl", "jfile_cycle");
+
+ // Read file header
+ file_hdr fhdr;
+ ifsp->read((char*)&fhdr, sizeof(fhdr));
+ assert(ifsp->good());
+ if (fhdr._magic == RHM_JDAT_FILE_MAGIC)
+ {
+ assert(fhdr._lfid == fid);
+ if (!rd._fro)
+ rd._fro = fhdr._fro;
+ std::streamoff foffs = jump_fro ? fhdr._fro : JRNL_DBLK_SIZE * JRNL_SBLK_SIZE;
+ ifsp->seekg(foffs);
+ }
+ else
+ {
+ ifsp->close();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+jcntl::check_owi(const u_int16_t fid, rec_hdr& h, bool& lowi, rcvdat& rd, std::streampos& file_pos)
+{
+ if (rd._ffid ? h.get_owi() == lowi : h.get_owi() != lowi) // Overwrite indicator changed
+ {
+ u_int16_t expected_fid = rd._ffid ? rd._ffid - 1 : rd._njf - 1;
+ if (fid == expected_fid)
+ {
+ check_journal_alignment(fid, file_pos, rd);
+ return false;
+ }
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0') << "Magic=0x" << std::setw(8) << h._magic;
+ oss << " fid=0x" << std::setw(4) << fid << " rid=0x" << std::setw(8) << h._rid;
+ oss << " foffs=0x" << std::setw(8) << file_pos;
+ oss << " expected_fid=0x" << std::setw(4) << expected_fid;
+ throw jexception(jerrno::JERR_JCNTL_OWIMISMATCH, oss.str(), "jcntl",
+ "check_owi");
+ }
+ if (rd._h_rid == 0)
+ rd._h_rid = h._rid;
+ else if (h._rid - rd._h_rid < 0x8000000000000000ULL) // RFC 1982 comparison for unsigned 64-bit
+ rd._h_rid = h._rid;
+ return true;
+}
+
+
+void
+jcntl::check_journal_alignment(const u_int16_t fid, std::streampos& file_pos, rcvdat& rd)
+{
+ unsigned sblk_offs = file_pos % (JRNL_DBLK_SIZE * JRNL_SBLK_SIZE);
+ if (sblk_offs)
+ {
+ {
+ std::ostringstream oss;
+ oss << std::hex << "Bad record alignment found at fid=0x" << fid;
+ oss << " offs=0x" << file_pos << " (likely journal overwrite boundary); " << std::dec;
+ oss << (JRNL_SBLK_SIZE - (sblk_offs/JRNL_DBLK_SIZE)) << " filler record(s) required.";
+ this->log(LOG_WARN, oss.str());
+ }
+ const u_int32_t xmagic = RHM_JDAT_EMPTY_MAGIC;
+ std::ostringstream oss;
+ oss << _jdir.dirname() << "/" << _base_filename << ".";
+ oss << std::hex << std::setfill('0') << std::setw(4) << fid << "." << JRNL_DATA_EXTENSION;
+ std::ofstream ofsp(oss.str().c_str(),
+ std::ios_base::in | std::ios_base::out | std::ios_base::binary);
+ if (!ofsp.good())
+ throw jexception(jerrno::JERR__FILEIO, oss.str(), "jcntl", "check_journal_alignment");
+ ofsp.seekp(file_pos);
+ void* buff = std::malloc(JRNL_DBLK_SIZE);
+ assert(buff != 0);
+ std::memcpy(buff, (const void*)&xmagic, sizeof(xmagic));
+ // Normally, RHM_CLEAN must be set before these fills are done, but this is a recover
+ // situation (i.e. performance is not an issue), and it makes the location of the write
+ // clear should inspection of the file be required.
+ std::memset((char*)buff + sizeof(xmagic), RHM_CLEAN_CHAR, JRNL_DBLK_SIZE - sizeof(xmagic));
+
+ while (file_pos % (JRNL_DBLK_SIZE * JRNL_SBLK_SIZE))
+ {
+ ofsp.write((const char*)buff, JRNL_DBLK_SIZE);
+ assert(!ofsp.fail());
+ std::ostringstream oss;
+ oss << std::hex << "Recover phase write: Wrote filler record: fid=0x" << fid << " offs=0x" << file_pos;
+ this->log(LOG_NOTICE, oss.str());
+ file_pos = ofsp.tellp();
+ }
+ ofsp.close();
+ std::free(buff);
+ rd._lfid = fid;
+ if (!rd._frot)
+ rd._ffid = (fid + 1) % rd._njf;
+ this->log(LOG_INFO, "Bad record alignment fixed.");
+ }
+ rd._eo = file_pos;
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/jcntl.h b/cpp/src/qpid/legacystore/jrnl/jcntl.h
new file mode 100644
index 0000000000..294e9ced05
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/jcntl.h
@@ -0,0 +1,722 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 jcntl.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * Messaging journal top-level control and interface class
+ * mrg::journal::jcntl. See class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_JCNTL_H
+#define QPID_LEGACYSTORE_JRNL_JCNTL_H
+
+namespace mrg
+{
+namespace journal
+{
+ class jcntl;
+}
+}
+
+#include <cstddef>
+#include <deque>
+#include "qpid/legacystore/jrnl/jdir.h"
+#include "qpid/legacystore/jrnl/fcntl.h"
+#include "qpid/legacystore/jrnl/lpmgr.h"
+#include "qpid/legacystore/jrnl/rcvdat.h"
+#include "qpid/legacystore/jrnl/slock.h"
+#include "qpid/legacystore/jrnl/smutex.h"
+#include "qpid/legacystore/jrnl/rmgr.h"
+#include "qpid/legacystore/jrnl/wmgr.h"
+#include "qpid/legacystore/jrnl/wrfc.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \brief Access and control interface for the journal. This is the top-level class for the
+ * journal.
+ *
+ * This is the top-level journal class; one instance of this class controls one instance of the
+ * journal and all its files and associated control structures. Besides this class, the only
+ * other class that needs to be used at a higher level is the data_tok class, one instance of
+ * which is used per data block written to the journal, and is used to track its status through
+ * the AIO enqueue, read and dequeue process.
+ */
+ class jcntl
+ {
+ protected:
+ /**
+ * \brief Journal ID
+ *
+ * This string uniquely identifies this journal instance. It will most likely be associated
+ * with the identity of the message queue with which it is associated.
+ */
+ // TODO: This is not included in any files at present, add to file_hdr?
+ std::string _jid;
+
+ /**
+ * \brief Journal directory
+ *
+ * This string stores the path to the journal directory. It may be absolute or relative, and
+ * should not end in a file separator character. (e.g. "/fastdisk/jdata" is correct,
+ * "/fastdisk/jdata/" is not.)
+ */
+ jdir _jdir;
+
+ /**
+ * \brief Base filename
+ *
+ * This string contains the base filename used for the journal files. The filenames will
+ * start with this base, and have various sections added to it to derive the final file names
+ * that will be written to disk. No file separator characters should be included here, but
+ * all other legal filename characters are valid.
+ */
+ std::string _base_filename;
+
+ /**
+ * \brief Initialized flag
+ *
+ * This flag starts out set to false, is set to true once this object has been initialized,
+ * either by calling initialize() or recover().
+ */
+ bool _init_flag;
+
+ /**
+ * \brief Stopped flag
+ *
+ * This flag starts out false, and is set to true when stop() is called. At this point, the
+ * journal will no longer accept messages until either initialize() or recover() is called.
+ * There is no way other than through initialization to reset this flag.
+ */
+ // TODO: It would be helpful to distinguish between states stopping and stopped. If stop(true) is called,
+ // then we are stopping, but must wait for all outstanding aios to return before being finally stopped. During
+ // this period, however, no new enqueue/dequeue/read requests may be accepted.
+ bool _stop_flag;
+
+ /**
+ * \brief Read-only state flag used during recover.
+ *
+ * When true, this flag prevents journal write operations (enqueue and dequeue), but
+ * allows read to occur. It is used during recovery, and is reset when recovered() is
+ * called.
+ */
+ bool _readonly_flag;
+
+ /**
+ * \brief If set, calls stop() if the jouranl write pointer overruns dequeue low water
+ * marker. If not set, then attempts to write will throw exceptions until the journal
+ * file low water marker moves to the next journal file.
+ */
+ bool _autostop; ///< Autostop flag - stops journal when overrun occurs
+
+ // Journal control structures
+ u_int32_t _jfsize_sblks; ///< Journal file size in sblks
+ lpmgr _lpmgr; ///< LFID-PFID manager tracks inserted journal files
+ enq_map _emap; ///< Enqueue map for low water mark management
+ txn_map _tmap; ///< Transaction map open transactions
+ rrfc _rrfc; ///< Read journal rotating file controller
+ wrfc _wrfc; ///< Write journal rotating file controller
+ rmgr _rmgr; ///< Read page manager which manages AIO
+ wmgr _wmgr; ///< Write page manager which manages AIO
+ rcvdat _rcvdat; ///< Recovery data used for recovery
+ smutex _wr_mutex; ///< Mutex for journal writes
+
+ public:
+ static timespec _aio_cmpl_timeout; ///< Timeout for blocking libaio returns
+ static timespec _final_aio_cmpl_timeout; ///< Timeout for blocking libaio returns when stopping or finalizing
+
+ /**
+ * \brief Journal constructor.
+ *
+ * Constructor which sets the physical file location and base name.
+ *
+ * \param jid A unique identifier for this journal instance.
+ * \param jdir The directory which will contain the journal files.
+ * \param base_filename The string which will be used to start all journal filenames.
+ */
+ jcntl(const std::string& jid, const std::string& jdir, const std::string& base_filename);
+
+ /**
+ * \brief Destructor.
+ */
+ virtual ~jcntl();
+
+ inline const std::string& id() const { return _jid; }
+ inline const std::string& jrnl_dir() const { return _jdir.dirname(); }
+
+ /**
+ * \brief Initialize the journal for storing data.
+ *
+ * Initialize the journal by creating new journal data files and initializing internal
+ * control structures. When complete, the journal will be empty, and ready to store data.
+ *
+ * <b>NOTE: Any existing journal will be ignored by this operation.</b> To use recover
+ * the data from an existing journal, use recover().
+ *
+ * <b>NOTE: If <i>NULL</i> is passed to the deque pointers, they will be internally created
+ * and deleted.</b>
+ *
+ * <b>NOTE: If <i>NULL</i> is passed to the callbacks, internal default callbacks will be
+ * used.</b>
+ *
+ * \param num_jfiles The number of journal files to be created.
+ * \param auto_expand If true, allows journal file auto-expansion. In this mode, the journal will automatically
+ * add files to the journal if it runs out of space. No more than ae_max_jfiles may be added. If false, then
+ * no files are added and an exception will be thrown if the journal runs out of file space.
+ * \param ae_max_jfiles Upper limit of journal files for auto-expand mode. When auto_expand is true, this is the
+ * maximum total number of files allowed in the journal (original plus those added by auto-expand mode). If
+ * this number of files exist and the journal runs out of space, an exception will be thrown. This number
+ * must be greater than the num_jfiles parameter value but cannot exceed the maximum number of files for a
+ * single journal; if num_jfiles is already at its maximum value, then auto-expand will be disabled.
+ * \param jfsize_sblks The size of each journal file expressed in softblocks.
+ * \param wcache_num_pages The number of write cache pages to create.
+ * \param wcache_pgsize_sblks The size in sblks of each write cache page.
+ * \param cbp Pointer to object containing callback functions for read and write operations. May be 0 (NULL).
+ *
+ * \exception TODO
+ */
+ void initialize(const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles,
+ const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks,
+ aio_callback* const cbp);
+
+ /**
+ * /brief Initialize journal by recovering state from previously written journal.
+ *
+ * Initialize journal by recovering state from previously written journal. The journal files
+ * are analyzed, and all records that have not been dequeued and that remain in the journal
+ * will be available for reading. The journal is placed in a read-only state until
+ * recovered() is called; any calls to enqueue or dequeue will fail with an exception
+ * in this state.
+ *
+ * <b>NOTE: If <i>NULL</i> is passed to the deque pointers, they will be internally created
+ * and deleted.</b>
+ *
+ * <b>NOTE: If <i>NULL</i> is passed to the callbacks, internal default callbacks will be
+ * used.</b>
+ *
+ * \param num_jfiles The number of journal files to be created.
+ * \param auto_expand If true, allows journal file auto-expansion. In this mode, the journal will automatically
+ * add files to the journal if it runs out of space. No more than ae_max_jfiles may be added. If false, then
+ * no files are added and an exception will be thrown if the journal runs out of file space.
+ * \param ae_max_jfiles Upper limit of journal files for auto-expand mode. When auto_expand is true, this is the
+ * maximum total number of files allowed in the journal (original plus those added by auto-expand mode). If
+ * this number of files exist and the journal runs out of space, an exception will be thrown. This number
+ * must be greater than the num_jfiles parameter value but cannot exceed the maximum number of files for a
+ * single journal; if num_jfiles is already at its maximum value, then auto-expand will be disabled.
+ * \param jfsize_sblks The size of each journal file expressed in softblocks.
+ * \param wcache_num_pages The number of write cache pages to create.
+ * \param wcache_pgsize_sblks The size in sblks of each write cache page.
+ * \param cbp Pointer to object containing callback functions for read and write operations. May be 0 (NULL).
+ * \param prep_txn_list_ptr
+ * \param highest_rid Returns the highest rid found in the journal during recover
+ *
+ * \exception TODO
+ */
+ void recover(const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles,
+ const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks,
+ aio_callback* const cbp, const std::vector<std::string>* prep_txn_list_ptr, u_int64_t& highest_rid);
+
+ /**
+ * \brief Notification to the journal that recovery is complete and that normal operation
+ * may resume.
+ *
+ * This call notifies the journal that recovery is complete and that normal operation
+ * may resume. The read pointers are reset so that all records read as a part of recover
+ * may be re-read during normal operation. The read-only flag is then reset, allowing
+ * enqueue and dequeue operations to resume.
+ *
+ * \exception TODO
+ */
+ void recover_complete();
+
+ /**
+ * \brief Stops journal and deletes all journal files.
+ *
+ * Clear the journal directory of all journal files matching the base filename.
+ *
+ * \exception TODO
+ */
+ void delete_jrnl_files();
+
+ /**
+ * \brief Enqueue data.
+ *
+ * Enqueue data or part thereof. If a large data block is being written, then it may be
+ * enqueued in parts by setting this_data_len to the size of the data being written in this
+ * call. The total data size must be known in advance, however, as this is written into the
+ * record header on the first record write. The state of the write (i.e. how much has been
+ * written so far) is maintained in the data token dtokp. Partial writes will return in state
+ * ENQ_PART.
+ *
+ * Note that a return value of anything other than RHM_IORES_SUCCESS implies that this write
+ * operation did not complete successfully or was partially completed. The action taken under
+ * these conditions depends on the value of the return. For example, RHM_IORES_AIO_WAIT
+ * implies that all pages in the write page cache are waiting for AIO operations to return,
+ * and that the call should be remade after waiting a bit.
+ *
+ * Example: If a write of 99 kB is divided into three equal parts, then the following states
+ * and returns would characterize a successful operation:
+ * <pre>
+ * dtok. dtok. dtok.
+ * Pperation Return wstate() dsize() written() Comment
+ * -----------------+--------+--------+-------+---------+------------------------------------
+ * NONE 0 0 Value of dtok before op
+ * edr(99000, 33000) SUCCESS ENQ_PART 99000 33000 Enqueue part 1
+ * edr(99000, 33000) AIO_WAIT ENQ_PART 99000 50000 Enqueue part 2, not completed
+ * edr(99000, 33000) SUCCESS ENQ_PART 99000 66000 Enqueue part 2 again
+ * edr(99000, 33000) SUCCESS ENQ 99000 99000 Enqueue part 3
+ * </pre>
+ *
+ * \param data_buff Pointer to data to be enqueued for this enqueue operation.
+ * \param tot_data_len Total data length.
+ * \param this_data_len Amount to be written in this enqueue operation.
+ * \param dtokp Pointer to data token which contains the details of the enqueue operation.
+ * \param transient Flag indicating transient persistence (ie, ignored on recover).
+ *
+ * \exception TODO
+ */
+ iores enqueue_data_record(const void* const data_buff, const std::size_t tot_data_len,
+ const std::size_t this_data_len, data_tok* dtokp, const bool transient = false);
+
+ iores enqueue_extern_data_record(const std::size_t tot_data_len, data_tok* dtokp,
+ const bool transient = false);
+
+ /**
+ * \brief Enqueue data.
+ *
+ * \param data_buff Pointer to data to be enqueued for this enqueue operation.
+ * \param tot_data_len Total data length.
+ * \param this_data_len Amount to be written in this enqueue operation.
+ * \param dtokp Pointer to data token which contains the details of the enqueue operation.
+ * \param xid String containing xid. An empty string (i.e. length=0) will be considered
+ * non-transactional.
+ * \param transient Flag indicating transient persistence (ie, ignored on recover).
+ *
+ * \exception TODO
+ */
+ iores enqueue_txn_data_record(const void* const data_buff, const std::size_t tot_data_len,
+ const std::size_t this_data_len, data_tok* dtokp, const std::string& xid,
+ const bool transient = false);
+ iores enqueue_extern_txn_data_record(const std::size_t tot_data_len, data_tok* dtokp,
+ const std::string& xid, const bool transient = false);
+
+ /* TODO
+ **
+ * \brief Retrieve details of next record to be read without consuming the record.
+ *
+ * Retrieve information about current read record. A pointer to the data is returned, along
+ * with the data size and available data size. Data is considered "available" when the AIO
+ * operations to fill page-cache pages from disk have returned, and is ready for consumption.
+ *
+ * If <i>dsize_avail</i> &lt; <i>dsize</i>, then not all of the data is available or part of
+ * the data is in non-contiguous memory, and a subsequent call will update both the pointer
+ * and <i>dsize_avail</i> if more pages have returned from AIO.
+ *
+ * The <i>dsize_avail</i> parameter will return the amount of data from this record that is
+ * available in the page cache as contiguous memory, even if it spans page cache boundaries.
+ * However, if a record spans the end of the page cache and continues at the beginning, even
+ * if both parts are ready for consumption, then this must be divided into at least two
+ * get_data_record() operations, as the data is contained in at least two non-contiguous
+ * segments of the page cache.
+ *
+ * Once all the available data for a record is exposed, it can not be read again using
+ * this function. It must be consumed prior to getting the next record. This can be done by
+ * calling discard_data_record() or read_data_record(). However, if parameter
+ * <i>auto_discard</i> is set to <b><i>true</i></b>, then this record will be automatically
+ * consumed when the entire record has become available without having to explicitly call
+ * discard_next_data_record() or read_data_record().
+ *
+ * If the current record is an open transactional record, then it cannot be read until it is
+ * committed. If it is aborted, it can never be read. Under this condition, get_data_record()
+ * will return RHM_IORES_TXPENDING, the data pointer will be set to NULL and all data
+ * lengths will be set to 0.
+ *
+ * Example: Read a record of 30k. Assume a read page cache of 10 pages of size 10k starting
+ * at address base_ptr (page0 = base_ptr, page1 = page_ptr+10k, etc.). The first 15k of
+ * the record falls at the end of the page cache, the remaining 15k folded to the beginning.
+ * The current page (page 8) containing 5k is available, the remaining pages which contain
+ * this record are pending AIO return:
+ * <pre>
+ * call dsize
+ * no. dsize avail data ptr Return Comment
+ * ----+-----+-----+------------+--------+--------------------------------------------------
+ * 1 30k 5k base_ptr+85k SUCCESS Initial call, read first 5k
+ * 2 30k 0k base_ptr+90k AIO_WAIT AIO still pending; no further pages avail
+ * 3 30k 10k base_ptr+90k SUCCESS AIO now returned; now read till end of page cache
+ * 4 30k 15k base_ptr SUCCESS data_ptr now pointing to start of page cache
+ * </pre>
+ *
+ * \param rid Reference that returns the record ID (rid)
+ * \param dsize Reference that returns the total data size of the record data .
+ * \param dsize_avail Reference that returns the amount of the data that is available for
+ * consumption.
+ * \param data Pointer to data pointer which will point to the first byte of the next record
+ * data.
+ * \param auto_discard If <b><i>true</i></b>, automatically discard the record being read if
+ * the entire record is available (i.e. dsize == dsize_avail). Otherwise
+ * discard_next_data_record() must be explicitly called.
+ *
+ * \exception TODO
+ *
+ // *** NOT YET IMPLEMENTED ***
+ iores get_data_record(const u_int64_t& rid, const std::size_t& dsize,
+ const std::size_t& dsize_avail, const void** const data, bool auto_discard = false);
+ */
+
+ /* TODO
+ **
+ * \brief Discard (skip) next record to be read without reading or retrieving it.
+ *
+ * \exception TODO
+ *
+ // *** NOT YET IMPLEMENTED ***
+ iores discard_data_record(data_tok* const dtokp);
+ */
+
+ /**
+ * \brief Reads data from the journal. It is the responsibility of the reader to free
+ * the memory that is allocated through this call - see below for details.
+ *
+ * Reads the next non-dequeued data record from the journal.
+ *
+ * <b>Note</b> that this call allocates memory into which the data and XID are copied. It
+ * is the responsibility of the caller to free this memory. The memory for the data and
+ * XID are allocated in a single call, and the XID precedes the data in the memory space.
+ * Thus, where an XID exists, freeing the XID pointer will free both the XID and data memory.
+ * However, if an XID does not exist for the message, the XID pointer xidpp is set to NULL,
+ * and it is the data pointer datapp that must be freed. Should neither an XID nor data be
+ * present (ie an empty record), then no memory is allocated, and both pointers will be NULL.
+ * In this case, there is no need to free memory.
+ *
+ * TODO: Fix this lousy interface. The caller should NOT be required to clean up these
+ * pointers! Rather use a struct, or better still, let the data token carry the data and
+ * xid pointers and lengths, and have the data token both allocate and delete.
+ *
+ * \param datapp Pointer to pointer that will be set to point to memory allocated and
+ * containing the data. Will be set to NULL if the call fails or there is no data
+ * in the record.
+ * \param dsize Ref that will be set to the size of the data. Will be set to 0 if the call
+ * fails or if there is no data in the record.
+ * \param xidpp Pointer to pointer that will be set to point to memory allocated and
+ * containing the XID. Will be set to NULL if the call fails or there is no XID attached
+ * to this record.
+ * \param xidsize Ref that will be set to the size of the XID.
+ * \param transient Ref that will be set true if record is transient.
+ * \param external Ref that will be set true if record is external. In this case, the data
+ * pointer datapp will be set to NULL, but dsize will contain the size of the data.
+ * NOTE: If there is an xid, then xidpp must be freed.
+ * \param dtokp Pointer to data_tok instance for this data, used to track state of data
+ * through journal.
+ * \param ignore_pending_txns When false (default), if the next record to be read is locked
+ * by a pending transaction, the read fails with RHM_IORES_TXPENDING. However, if set
+ * to true, then locks are ignored. This is required for reading of the Transaction
+ * Prepared List (TPL) which may have its entries locked, but may be read from
+ * time-to-time, and needs all its records (locked and unlocked) to be available.
+ *
+ * \exception TODO
+ */
+ iores read_data_record(void** const datapp, std::size_t& dsize, void** const xidpp,
+ std::size_t& xidsize, bool& transient, bool& external, data_tok* const dtokp,
+ bool ignore_pending_txns = false);
+
+ /**
+ * \brief Dequeues (marks as no longer needed) data record in journal.
+ *
+ * Dequeues (marks as no longer needed) data record in journal. Note that it is possible
+ * to use the same data token instance used to enqueue this data; it contains the record ID
+ * needed to correctly mark this data as dequeued in the journal. Otherwise the RID of the
+ * record to be dequeued and the write state of ENQ must be manually set in a new or reset
+ * instance of data_tok.
+ *
+ * \param dtokp Pointer to data_tok instance for this data, used to track state of data
+ * through journal.
+ * \param txn_coml_commit Only used for preparedXID journal. When used for dequeueing
+ * prepared XID list items, sets whether the complete() was called in commit or abort
+ * mode.
+ *
+ * \exception TODO
+ */
+ iores dequeue_data_record(data_tok* const dtokp, const bool txn_coml_commit = false);
+
+ /**
+ * \brief Dequeues (marks as no longer needed) data record in journal.
+ *
+ * Dequeues (marks as no longer needed) data record in journal as part of a transaction.
+ * Note that it is possible to use the same data token instance used to enqueue this data;
+ * it contains the RID needed to correctly mark this data as dequeued in the journal.
+ * Otherwise the RID of the record to be dequeued and the write state of ENQ must be
+ * manually set in a new or reset instance of data_tok.
+ *
+ * \param dtokp Pointer to data_tok instance for this data, used to track state of data
+ * through journal.
+ * \param xid String containing xid. An empty string (i.e. length=0) will be considered
+ * non-transactional.
+ * \param txn_coml_commit Only used for preparedXID journal. When used for dequeueing
+ * prepared XID list items, sets whether the complete() was called in commit or abort
+ * mode.
+ *
+ * \exception TODO
+ */
+ iores dequeue_txn_data_record(data_tok* const dtokp, const std::string& xid, const bool txn_coml_commit = false);
+
+ /**
+ * \brief Abort the transaction for all records enqueued or dequeued with the matching xid.
+ *
+ * Abort the transaction for all records enqueued with the matching xid. All enqueued records
+ * are effectively deleted from the journal, and can not be read. All dequeued records remain
+ * as though they had never been dequeued.
+ *
+ * \param dtokp Pointer to data_tok instance for this data, used to track state of data
+ * through journal.
+ * \param xid String containing xid.
+ *
+ * \exception TODO
+ */
+ iores txn_abort(data_tok* const dtokp, const std::string& xid);
+
+ /**
+ * \brief Commit the transaction for all records enqueued or dequeued with the matching xid.
+ *
+ * Commit the transaction for all records enqueued with the matching xid. All enqueued
+ * records are effectively released for reading and dequeueing. All dequeued records are
+ * removed and can no longer be accessed.
+ *
+ * \param dtokp Pointer to data_tok instance for this data, used to track state of data
+ * through journal.
+ * \param xid String containing xid.
+ *
+ * \exception TODO
+ */
+ iores txn_commit(data_tok* const dtokp, const std::string& xid);
+
+ /**
+ * \brief Check whether all the enqueue records for the given xid have reached disk.
+ *
+ * \param xid String containing xid.
+ *
+ * \exception TODO
+ */
+ bool is_txn_synced(const std::string& xid);
+
+ /**
+ * \brief Forces a check for returned AIO write events.
+ *
+ * Forces a check for returned AIO write events. This is normally performed by enqueue() and
+ * dequeue() operations, but if these operations cease, then this call needs to be made to
+ * force the processing of any outstanding AIO operations.
+ */
+ int32_t get_wr_events(timespec* const timeout);
+
+ /**
+ * \brief Forces a check for returned AIO read events.
+ *
+ * Forces a check for returned AIO read events. This is normally performed by read_data()
+ * operations, but if these operations cease, then this call needs to be made to force the
+ * processing of any outstanding AIO operations.
+ */
+ int32_t get_rd_events(timespec* const timeout);
+
+ /**
+ * \brief Stop the journal from accepting any further requests to read or write data.
+ *
+ * This operation is used to stop the journal. This is the normal mechanism for bringing the
+ * journal to an orderly stop. Any outstanding AIO operations or partially written pages in
+ * the write page cache will by flushed and will complete.
+ *
+ * <b>Note:</b> The journal cannot be restarted without either initializing it or restoring
+ * it.
+ *
+ * \param block_till_aio_cmpl If true, will block the thread while waiting for all
+ * outstanding AIO operations to complete.
+ */
+ void stop(const bool block_till_aio_cmpl = false);
+
+ /**
+ * \brief Force a flush of the write page cache, creating a single AIO write operation.
+ */
+ iores flush(const bool block_till_aio_cmpl = false);
+
+ inline u_int32_t get_enq_cnt() const { return _emap.size(); }
+
+ inline u_int32_t get_wr_aio_evt_rem() const { slock l(_wr_mutex); return _wmgr.get_aio_evt_rem(); }
+
+ inline u_int32_t get_rd_aio_evt_rem() const { return _rmgr.get_aio_evt_rem(); }
+
+ inline u_int32_t get_wr_outstanding_aio_dblks() const
+ { return _wrfc.aio_outstanding_dblks(); }
+
+ inline u_int32_t get_wr_outstanding_aio_dblks(u_int16_t lfid) const
+ { return _lpmgr.get_fcntlp(lfid)->wr_aio_outstanding_dblks(); }
+
+ inline u_int32_t get_rd_outstanding_aio_dblks() const
+ { return _rrfc.aio_outstanding_dblks(); }
+
+ inline u_int32_t get_rd_outstanding_aio_dblks(u_int16_t lfid) const
+ { return _lpmgr.get_fcntlp(lfid)->rd_aio_outstanding_dblks(); }
+
+ inline u_int16_t get_rd_fid() const { return _rrfc.index(); }
+ inline u_int16_t get_wr_fid() const { return _wrfc.index(); }
+ u_int16_t get_earliest_fid();
+
+ /**
+ * \brief Check if a particular rid is enqueued. Note that this function will return
+ * false if the rid is transactionally enqueued and is not committed, or if it is
+ * locked (i.e. transactionally dequeued, but the dequeue has not been committed).
+ */
+ inline bool is_enqueued(const u_int64_t rid, bool ignore_lock = false)
+ { return _emap.is_enqueued(rid, ignore_lock); }
+ inline bool is_locked(const u_int64_t rid)
+ { if (_emap.is_enqueued(rid, true) < enq_map::EMAP_OK) return false; return _emap.is_locked(rid) == enq_map::EMAP_TRUE; }
+ inline void enq_rid_list(std::vector<u_int64_t>& rids) { _emap.rid_list(rids); }
+ inline void enq_xid_list(std::vector<std::string>& xids) { _tmap.xid_list(xids); }
+ inline u_int32_t get_open_txn_cnt() const { return _tmap.size(); }
+ // TODO Make this a const, but txn_map must support const first.
+ inline txn_map& get_txn_map() { return _tmap; }
+
+ /**
+ * \brief Check if the journal is stopped.
+ *
+ * \return <b><i>true</i></b> if the jouranl is stopped;
+ * <b><i>false</i></b> otherwise.
+ */
+ inline bool is_stopped() { return _stop_flag; }
+
+ /**
+ * \brief Check if the journal is ready to read and write data.
+ *
+ * Checks if the journal is ready to read and write data. This function will return
+ * <b><i>true</i></b> if the journal has been either initialized or restored, and the stop()
+ * function has not been called since the initialization.
+ *
+ * Note that the journal may also be stopped if an internal error occurs (such as running out
+ * of data journal file space).
+ *
+ * \return <b><i>true</i></b> if the journal is ready to read and write data;
+ * <b><i>false</i></b> otherwise.
+ */
+ inline bool is_ready() const { return _init_flag && !_stop_flag; }
+
+ inline bool is_read_only() const { return _readonly_flag; }
+
+ /**
+ * \brief Get the journal directory.
+ *
+ * This returns the journal directory as set during initialization. This is the directory
+ * into which the journal files will be written.
+ */
+ inline const std::string& dirname() const { return _jdir.dirname(); }
+
+ /**
+ * \brief Get the journal base filename.
+ *
+ * Get the journal base filename as set during initialization. This is the prefix used in all
+ * journal files of this instance. Note that if more than one instance of the journal shares
+ * the same directory, their base filenames <b>MUST</b> be different or else the instances
+ * will overwrite one another.
+ */
+ inline const std::string& base_filename() const { return _base_filename; }
+
+ inline u_int16_t num_jfiles() const { return _lpmgr.num_jfiles(); }
+
+ inline fcntl* get_fcntlp(const u_int16_t lfid) const { return _lpmgr.get_fcntlp(lfid); }
+
+ inline u_int32_t jfsize_sblks() const { return _jfsize_sblks; }
+
+ // Logging
+ virtual void log(log_level level, const std::string& log_stmt) const;
+ virtual void log(log_level level, const char* const log_stmt) const;
+
+ // FIXME these are _rmgr to _wmgr interactions, remove when _rmgr contains ref to _wmgr:
+ void chk_wr_frot();
+ inline u_int32_t unflushed_dblks() { return _wmgr.unflushed_dblks(); }
+ void fhdr_wr_sync(const u_int16_t lid);
+ inline u_int32_t wr_subm_cnt_dblks(const u_int16_t lfid) const { return _lpmgr.get_fcntlp(lfid)->wr_subm_cnt_dblks(); }
+
+ // Management instrumentation callbacks
+ inline virtual void instr_incr_outstanding_aio_cnt() {}
+ inline virtual void instr_decr_outstanding_aio_cnt() {}
+
+ /**
+ * /brief Static function for creating new fcntl objects for use with obj_arr.
+ */
+ static fcntl* new_fcntl(jcntl* const jcp, const u_int16_t lid, const u_int16_t fid, const rcvdat* const rdp);
+
+ protected:
+ static bool _init;
+ static bool init_statics();
+
+ /**
+ * \brief Check status of journal before allowing write operations.
+ */
+ void check_wstatus(const char* fn_name) const;
+
+ /**
+ * \brief Check status of journal before allowing read operations.
+ */
+ void check_rstatus(const char* fn_name) const;
+
+ /**
+ * \brief Write info file &lt;basefilename&gt;.jinf to disk
+ */
+ void write_infofile() const;
+
+ /**
+ * \brief Call that blocks while waiting for all outstanding AIOs to complete
+ */
+ void aio_cmpl_wait();
+
+ /**
+ * \brief Call that blocks until at least one message returns; used to wait for
+ * AIO wait conditions to clear.
+ */
+ bool handle_aio_wait(const iores res, iores& resout, const data_tok* dtp);
+
+ /**
+ * \brief Analyze journal for recovery.
+ */
+ void rcvr_janalyze(rcvdat& rd, const std::vector<std::string>* prep_txn_list_ptr);
+
+ bool rcvr_get_next_record(u_int16_t& fid, std::ifstream* ifsp, bool& lowi, rcvdat& rd);
+
+ bool decode(jrec& rec, u_int16_t& fid, std::ifstream* ifsp, std::size_t& cum_size_read,
+ rec_hdr& h, bool& lowi, rcvdat& rd, std::streampos& rec_offset);
+
+ bool jfile_cycle(u_int16_t& fid, std::ifstream* ifsp, bool& lowi, rcvdat& rd,
+ const bool jump_fro);
+
+ bool check_owi(const u_int16_t fid, rec_hdr& h, bool& lowi, rcvdat& rd,
+ std::streampos& read_pos);
+
+ void check_journal_alignment(const u_int16_t fid, std::streampos& rec_offset, rcvdat& rd);
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_JCNTL_H
diff --git a/cpp/src/qpid/legacystore/jrnl/jdir.cpp b/cpp/src/qpid/legacystore/jrnl/jdir.cpp
new file mode 100644
index 0000000000..a874c6c945
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/jdir.cpp
@@ -0,0 +1,463 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file jdir.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::jdir (journal data
+ * directory), used for controlling and manipulating journal data
+ * direcories and files. See comments in file jdir.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/jdir.h"
+
+#include <cstdlib>
+#include <cstring>
+#include <cerrno>
+#include <iomanip>
+#include "qpid/legacystore/jrnl/jcfg.h"
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+#include <sstream>
+#include <sys/stat.h>
+#include <unistd.h>
+
+namespace mrg
+{
+namespace journal
+{
+
+jdir::jdir(const std::string& dirname, const std::string& _base_filename):
+ _dirname(dirname),
+ _base_filename(_base_filename)
+{}
+
+jdir::~jdir()
+{}
+
+// === create_dir ===
+
+void
+jdir::create_dir()
+{
+ create_dir(_dirname);
+}
+
+
+void
+jdir::create_dir(const char* dirname)
+{
+ create_dir(std::string(dirname));
+}
+
+
+void
+jdir::create_dir(const std::string& dirname)
+{
+ std::size_t fdp = dirname.find_last_of('/');
+ if (fdp != std::string::npos)
+ {
+ std::string parent_dir = dirname.substr(0, fdp);
+ if (!exists(parent_dir))
+ create_dir(parent_dir);
+ }
+ if (::mkdir(dirname.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH))
+ {
+ if (errno != EEXIST) // Dir exists, ignore
+ {
+ std::ostringstream oss;
+ oss << "dir=\"" << dirname << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_JDIR_MKDIR, oss.str(), "jdir", "create_dir");
+ }
+ }
+}
+
+
+// === clear_dir ===
+
+void
+jdir::clear_dir(const bool create_flag)
+{
+ clear_dir(_dirname, _base_filename, create_flag);
+}
+
+void
+jdir::clear_dir(const char* dirname, const char* base_filename, const bool create_flag)
+{
+ clear_dir(std::string(dirname), std::string(base_filename), create_flag);
+}
+
+
+void
+jdir::clear_dir(const std::string& dirname, const std::string&
+#ifndef RHM_JOWRITE
+ base_filename
+#endif
+ , const bool create_flag)
+{
+ DIR* dir = ::opendir(dirname.c_str());
+ if (!dir)
+ {
+ if (errno == 2 && create_flag) // ENOENT (No such file or dir)
+ {
+ create_dir(dirname);
+ return;
+ }
+ std::ostringstream oss;
+ oss << "dir=\"" << dirname << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_JDIR_OPENDIR, oss.str(), "jdir", "clear_dir");
+ }
+#ifndef RHM_JOWRITE
+ struct dirent* entry;
+ bool found = false;
+ std::string bak_dir;
+ while ((entry = ::readdir(dir)) != 0)
+ {
+ // Ignore . and ..
+ if (std::strcmp(entry->d_name, ".") != 0 && std::strcmp(entry->d_name, "..") != 0)
+ {
+ if (std::strlen(entry->d_name) > base_filename.size())
+ {
+ if (std::strncmp(entry->d_name, base_filename.c_str(), base_filename.size()) == 0)
+ {
+ if (!found)
+ {
+ bak_dir = create_bak_dir(dirname, base_filename);
+ found = true;
+ }
+ std::ostringstream oldname;
+ oldname << dirname << "/" << entry->d_name;
+ std::ostringstream newname;
+ newname << bak_dir << "/" << entry->d_name;
+ if (::rename(oldname.str().c_str(), newname.str().c_str()))
+ {
+ ::closedir(dir);
+ std::ostringstream oss;
+ oss << "file=\"" << oldname.str() << "\" dest=\"" <<
+ newname.str() << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_JDIR_FMOVE, oss.str(), "jdir", "clear_dir");
+ }
+ }
+ }
+ }
+ }
+// FIXME: Find out why this fails with false alarms/errors from time to time...
+// While commented out, there is no error capture from reading dir entries.
+// check_err(errno, dir, dirname, "clear_dir");
+#endif
+ close_dir(dir, dirname, "clear_dir");
+}
+
+// === push_down ===
+
+std::string
+jdir::push_down(const std::string& dirname, const std::string& target_dir, const std::string& bak_dir_base)
+{
+ std::string bak_dir_name = create_bak_dir(dirname, bak_dir_base);
+
+ DIR* dir = ::opendir(dirname.c_str());
+ if (!dir)
+ {
+ std::ostringstream oss;
+ oss << "dir=\"" << dirname << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_JDIR_OPENDIR, oss.str(), "jdir", "push_down");
+ }
+ // Copy contents of targetDirName into bak dir
+ struct dirent* entry;
+ while ((entry = ::readdir(dir)) != 0)
+ {
+ // Search for targetDirName in storeDirName
+ if (std::strcmp(entry->d_name, target_dir.c_str()) == 0)
+ {
+ std::ostringstream oldname;
+ oldname << dirname << "/" << target_dir;
+ std::ostringstream newname;
+ newname << bak_dir_name << "/" << target_dir;
+ if (::rename(oldname.str().c_str(), newname.str().c_str()))
+ {
+ ::closedir(dir);
+ std::ostringstream oss;
+ oss << "file=\"" << oldname.str() << "\" dest=\"" << newname.str() << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_JDIR_FMOVE, oss.str(), "jdir", "push_down");
+ }
+ break;
+ }
+ }
+ close_dir(dir, dirname, "push_down");
+ return bak_dir_name;
+}
+
+// === verify_dir ===
+
+void
+jdir::verify_dir()
+{
+ verify_dir(_dirname, _base_filename);
+}
+
+void
+jdir::verify_dir(const char* dirname, const char* base_filename)
+{
+ verify_dir(std::string(dirname), std::string(base_filename));
+}
+
+
+void
+jdir::verify_dir(const std::string& dirname, const std::string& base_filename)
+{
+ if (!is_dir(dirname))
+ {
+ std::ostringstream oss;
+ oss << "dir=\"" << dirname << "\"";
+ throw jexception(jerrno::JERR_JDIR_NOTDIR, oss.str(), "jdir", "verify_dir");
+ }
+
+ // Read jinf file, then verify all journal files are present
+ jinf ji(dirname + "/" + base_filename + "." + JRNL_INFO_EXTENSION, true);
+ for (u_int16_t fnum=0; fnum < ji.num_jfiles(); fnum++)
+ {
+ std::ostringstream oss;
+ oss << dirname << "/" << base_filename << ".";
+ oss << std::setw(4) << std::setfill('0') << std::hex << fnum;
+ oss << "." << JRNL_DATA_EXTENSION;
+ if (!exists(oss.str()))
+ throw jexception(jerrno::JERR_JDIR_NOSUCHFILE, oss.str(), "jdir", "verify_dir");
+ }
+}
+
+
+// === delete_dir ===
+
+void
+jdir::delete_dir(bool children_only)
+{
+ delete_dir(_dirname, children_only);
+}
+
+void
+jdir::delete_dir(const char* dirname, bool children_only)
+{
+ delete_dir(std::string(dirname), children_only);
+}
+
+void
+jdir::delete_dir(const std::string& dirname, bool children_only)
+{
+ struct dirent* entry;
+ struct stat s;
+ DIR* dir = ::opendir(dirname.c_str());
+ if (!dir)
+ {
+ if (errno == ENOENT) // dir does not exist.
+ return;
+
+ std::ostringstream oss;
+ oss << "dir=\"" << dirname << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_JDIR_OPENDIR, oss.str(), "jdir", "delete_dir");
+ }
+ else
+ {
+ while ((entry = ::readdir(dir)) != 0)
+ {
+ // Ignore . and ..
+ if (std::strcmp(entry->d_name, ".") != 0 && std::strcmp(entry->d_name, "..") != 0)
+ {
+ std::string full_name(dirname + "/" + entry->d_name);
+ if (::lstat(full_name.c_str(), &s))
+ {
+ ::closedir(dir);
+ std::ostringstream oss;
+ oss << "stat: file=\"" << full_name << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_JDIR_STAT, oss.str(), "jdir", "delete_dir");
+ }
+ if (S_ISREG(s.st_mode) || S_ISLNK(s.st_mode)) // This is a file or slink
+ {
+ if(::unlink(full_name.c_str()))
+ {
+ ::closedir(dir);
+ std::ostringstream oss;
+ oss << "unlink: file=\"" << entry->d_name << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_JDIR_UNLINK, oss.str(), "jdir", "delete_dir");
+ }
+ }
+ else if (S_ISDIR(s.st_mode)) // This is a dir
+ {
+ delete_dir(full_name);
+ }
+ else // all other types, throw up!
+ {
+ ::closedir(dir);
+ std::ostringstream oss;
+ oss << "file=\"" << entry->d_name << "\" is not a dir, file or slink.";
+ oss << " (mode=0x" << std::hex << s.st_mode << std::dec << ")";
+ throw jexception(jerrno::JERR_JDIR_BADFTYPE, oss.str(), "jdir", "delete_dir");
+ }
+ }
+ }
+
+// FIXME: Find out why this fails with false alarms/errors from time to time...
+// While commented out, there is no error capture from reading dir entries.
+// check_err(errno, dir, dirname, "delete_dir");
+ }
+ // Now dir is empty, close and delete it
+ close_dir(dir, dirname, "delete_dir");
+
+ if (!children_only)
+ if (::rmdir(dirname.c_str()))
+ {
+ std::ostringstream oss;
+ oss << "dir=\"" << dirname << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_JDIR_RMDIR, oss.str(), "jdir", "delete_dir");
+ }
+}
+
+
+std::string
+jdir::create_bak_dir(const std::string& dirname, const std::string& base_filename)
+{
+ DIR* dir = ::opendir(dirname.c_str());
+ long dir_num = 0L;
+ if (!dir)
+ {
+ std::ostringstream oss;
+ oss << "dir=\"" << dirname << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_JDIR_OPENDIR, oss.str(), "jdir", "create_bak_dir");
+ }
+ struct dirent* entry;
+ while ((entry = ::readdir(dir)) != 0)
+ {
+ // Ignore . and ..
+ if (std::strcmp(entry->d_name, ".") != 0 && std::strcmp(entry->d_name, "..") != 0)
+ {
+ if (std::strlen(entry->d_name) == base_filename.size() + 10) // Format: basename.bak.XXXX
+ {
+ std::ostringstream oss;
+ oss << "_" << base_filename << ".bak.";
+ if (std::strncmp(entry->d_name, oss.str().c_str(), base_filename.size() + 6) == 0)
+ {
+ long this_dir_num = std::strtol(entry->d_name + base_filename.size() + 6, 0, 16);
+ if (this_dir_num > dir_num)
+ dir_num = this_dir_num;
+ }
+ }
+ }
+ }
+// FIXME: Find out why this fails with false alarms/errors from time to time...
+// While commented out, there is no error capture from reading dir entries.
+// check_err(errno, dir, dirname, "create_bak_dir");
+ close_dir(dir, dirname, "create_bak_dir");
+
+ std::ostringstream dn;
+ dn << dirname << "/_" << base_filename << ".bak." << std::hex << std::setw(4) <<
+ std::setfill('0') << ++dir_num;
+ if (::mkdir(dn.str().c_str(), S_IRWXU | S_IRWXG | S_IROTH))
+ {
+ std::ostringstream oss;
+ oss << "dir=\"" << dn.str() << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_JDIR_MKDIR, oss.str(), "jdir", "create_bak_dir");
+ }
+ return std::string(dn.str());
+}
+
+bool
+jdir::is_dir(const char* name)
+{
+ struct stat s;
+ if (::stat(name, &s))
+ {
+ std::ostringstream oss;
+ oss << "file=\"" << name << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_JDIR_STAT, oss.str(), "jdir", "is_dir");
+ }
+ return S_ISDIR(s.st_mode);
+}
+
+bool
+jdir::is_dir(const std::string& name)
+{
+ return is_dir(name.c_str());
+}
+
+bool
+jdir::exists(const char* name)
+{
+ struct stat s;
+ if (::stat(name, &s))
+ {
+ if (errno == ENOENT) // No such dir or file
+ return false;
+ // Throw for any other condition
+ std::ostringstream oss;
+ oss << "file=\"" << name << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_JDIR_STAT, oss.str(), "jdir", "exists");
+ }
+ return true;
+}
+
+bool
+jdir::exists(const std::string& name)
+{
+ return exists(name.c_str());
+}
+
+void
+jdir::check_err(const int err_num, DIR* dir, const std::string& dir_name, const std::string& fn_name)
+{
+ if (err_num)
+ {
+ std::ostringstream oss;
+ oss << "dir=\"" << dir_name << "\"" << FORMAT_SYSERR(err_num);
+ ::closedir(dir); // Try to close, it makes no sense to trap errors here...
+ throw jexception(jerrno::JERR_JDIR_READDIR, oss.str(), "jdir", fn_name);
+ }
+}
+
+void
+jdir::close_dir(DIR* dir, const std::string& dir_name, const std::string& fn_name)
+{
+ if (::closedir(dir))
+ {
+ std::ostringstream oss;
+ oss << "dir=\"" << dir_name << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_JDIR_CLOSEDIR, oss.str(), "jdir", fn_name);
+ }
+}
+
+std::ostream&
+operator<<(std::ostream& os, const jdir& jdir)
+{
+ os << jdir._dirname;
+ return os;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const jdir* jdirPtr)
+{
+ os << jdirPtr->_dirname;
+ return os;
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/jdir.h b/cpp/src/qpid/legacystore/jrnl/jdir.h
new file mode 100644
index 0000000000..e129b794d6
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/jdir.h
@@ -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.
+ *
+ */
+
+/**
+ * \file jdir.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::jdir (%journal data
+ * directory), used for controlling and manipulating %journal data
+ * directories and files. See class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_JDIR_H
+#define QPID_LEGACYSTORE_JRNL_JDIR_H
+
+namespace mrg
+{
+namespace journal
+{
+class jdir;
+}
+}
+
+#include "qpid/legacystore/jrnl/jinf.h"
+#include <dirent.h>
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \class jdir
+ * \brief Class to manage the %journal directory
+ */
+ class jdir
+ {
+ private:
+ std::string _dirname;
+ std::string _base_filename;
+
+ public:
+
+ /**
+ * \brief Sole constructor
+ *
+ * \param dirname Name of directory to be managed.
+ * \param base_filename Filename root used in the creation of %journal files
+ * and sub-directories.
+ */
+ jdir(const std::string& dirname, const std::string& base_filename);
+
+ virtual ~jdir();
+
+
+ /**
+ * \brief Create %journal directory as set in the dirname parameter of the constructor.
+ * Recursive creation is supported.
+ *
+ * \exception jerrno::JERR_JDIR_MKDIR The creation of dirname failed.
+ */
+ void create_dir();
+
+ /**
+ * \brief Static function to create a directory. Recursive creation is supported.
+ *
+ * \param dirname C-string containing name of directory.
+ *
+ * \exception jerrno::JERR_JDIR_MKDIR The creation of dirname failed.
+ */
+ static void create_dir(const char* dirname);
+
+ /**
+ * \brief Static function to create a directory. Recursive creation is supported.
+ *
+ * \param dirname String containing name of directory.
+ *
+ * \exception jerrno::JERR_JDIR_MKDIR The creation of dirname failed.
+ */
+ static void create_dir(const std::string& dirname);
+
+
+ /**
+ * \brief Clear the %journal directory of files matching the base filename
+ * by moving them into a subdirectory. This fn uses the dirname and base_filename
+ * that were set on construction.
+ *
+ * \param create_flag If set, create dirname if it is non-existent, otherwise throw
+ * exception.
+ *
+ * \exception jerrno::JERR_JDIR_OPENDIR The %journal directory could not be opened.
+ * \exception jerrno::JERR_JDIR_FMOVE Moving the files from the %journal directory to the created backup
+ * directory failed.
+ * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed.
+ */
+ void clear_dir(const bool create_flag = true);
+
+ /**
+ * \brief Clear the directory dirname of %journal files matching base_filename
+ * by moving them into a subdirectory.
+ *
+ * \param dirname C-string containing name of %journal directory.
+ * \param base_filename C-string containing base filename of %journal files to be matched
+ * for moving into subdirectory.
+ * \param create_flag If set, create dirname if it is non-existent, otherwise throw
+ * exception
+ *
+ * \exception jerrno::JERR_JDIR_OPENDIR The %journal directory could not be opened.
+ * \exception jerrno::JERR_JDIR_FMOVE Moving the files from the %journal directory to the created backup
+ * directory failed.
+ * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed.
+ */
+ static void clear_dir(const char* dirname, const char* base_filename,
+ const bool create_flag = true);
+
+ /**
+ * \brief Clear the directory dirname of %journal files matching base_filename
+ * by moving them into a subdirectory.
+ *
+ * \param dirname String containing name of %journal directory.
+ * \param base_filename String containing base filename of %journal files to be matched
+ * for moving into subdirectory.
+ * \param create_flag If set, create dirname if it is non-existent, otherwise throw
+ * exception
+ *
+ * \exception jerrno::JERR_JDIR_OPENDIR The %journal directory could not be opened.
+ * \exception jerrno::JERR_JDIR_FMOVE Moving the files from the %journal directory to the created backup
+ * directory failed.
+ * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed.
+ */
+ static void clear_dir(const std::string& dirname, const std::string& base_filename,
+ const bool create_flag = true);
+
+
+
+ /**
+ * \brief Move (push down) the directory target_dir located in directory dirname into a backup directory
+ * named _bak_dir_base.XXXX (note prepended underscore), where XXXX is an increasing hex serial number
+ * starting at 0000.
+ *
+ * \param dirname Full path to directory containing directory to be pushed down.
+ * \param target_dir Name of directory in dirname to be pushed down.
+ * \param bak_dir_base Base name for backup directory to be created in dirname, into which target_dir will be moved.
+ * \return Name of backup dir into which target_dir was pushed.
+ */
+ static std::string push_down(const std::string& dirname, const std::string& target_dir, const std::string& bak_dir_base);
+
+
+ /**
+ * \brief Verify that dirname is a valid %journal directory.
+ *
+ * The validation reads the .%jinf file, and using this information verifies that all the expected %journal
+ * (.jdat) files are present.
+ *
+ * \exception jerrno::JERR_JDIR_NOTDIR dirname is not a directory
+ * \exception jerrno::JERR_JDIR_STAT Could not stat dirname
+ * \exception jerrno::JERR__FILEIO Error reading %jinf file
+ * \exception jerrno::JERR_JINF_CVALIDFAIL Error validating %jinf file
+ * \exception jerrno::JERR_JDIR_NOSUCHFILE Expected jdat file is missing
+ */
+ void verify_dir();
+
+ /**
+ * \brief Verify that dirname is a valid %journal directory.
+ *
+ * The validation reads the .%jinf file, and using this information verifies that all the expected %journal
+ * (.jdat) files are present.
+ *
+ * \param dirname C-string containing name of %journal directory.
+ * \param base_filename C-string containing base filename of %journal files to be matched for moving into sub-directory.
+ *
+ * \exception jerrno::JERR_JDIR_NOTDIR dirname is not a directory
+ * \exception jerrno::JERR_JDIR_STAT Could not stat dirname
+ * \exception jerrno::JERR__FILEIO Error reading %jinf file
+ * \exception jerrno::JERR_JINF_CVALIDFAIL Error validating %jinf file
+ * \exception jerrno::JERR_JDIR_NOSUCHFILE Expected jdat file is missing
+ */
+ static void verify_dir(const char* dirname, const char* base_filename);
+
+ /**
+ * \brief Verify that dirname is a valid %journal directory.
+ *
+ * The validation reads the .%jinf file, and using this information verifies that all the expected %journal
+ * (.jdat) files are present.
+ *
+ * \param dirname String containing name of %journal directory.
+ * \param base_filename String containing base filename of %journal files to be matched for moving into sub-directory.
+ *
+ * \exception jerrno::JERR_JDIR_NOTDIR dirname is not a directory
+ * \exception jerrno::JERR_JDIR_STAT Could not stat dirname
+ * \exception jerrno::JERR__FILEIO Error reading %jinf file
+ * \exception jerrno::JERR_JINF_CVALIDFAIL Error validating %jinf file
+ * \exception jerrno::JERR_JDIR_NOSUCHFILE Expected jdat file is missing
+ */
+ static void verify_dir(const std::string& dirname, const std::string& base_filename);
+
+ /**
+ * \brief Delete the %journal directory and all files and sub--directories that it may
+ * contain. This is equivilent of rm -rf.
+ *
+ * FIXME: links are not handled correctly.
+ *
+ * \param children_only If true, delete only children of dirname, but leave dirname itself.
+ *
+ * \exception jerrno::JERR_JDIR_OPENDIR The %journal directory could not be opened.
+ * \exception jerrno::JERR_JDIR_STAT Could not stat dirname.
+ * \exception jerrno::JERR_JDIR_UNLINK A file could not be deleted.
+ * \exception jerrno::JERR_JDIR_BADFTYPE A dir entry is neiter a file nor a dir.
+ * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed.
+ * \exception jerrno::JERR_JDIR_RMDIR A directory could not be deleted.
+ */
+ void delete_dir(bool children_only = false );
+
+ /**
+ * \brief Delete the %journal directory and all files and sub--directories that it may
+ * contain. This is equivilent of rm -rf.
+ *
+ * FIXME: links are not handled correctly.
+ *
+ * \param dirname C-string containing name of directory to be deleted.
+ * \param children_only If true, delete only children of dirname, but leave dirname itself.
+ *
+ * \exception jerrno::JERR_JDIR_OPENDIR The %journal directory could not be opened.
+ * \exception jerrno::JERR_JDIR_STAT Could not stat dirname.
+ * \exception jerrno::JERR_JDIR_UNLINK A file could not be deleted.
+ * \exception jerrno::JERR_JDIR_BADFTYPE A dir entry is neiter a file nor a dir.
+ * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed.
+ * \exception jerrno::JERR_JDIR_RMDIR A directory could not be deleted.
+ */
+ static void delete_dir(const char* dirname, bool children_only = false);
+
+ /**
+ * \brief Delete the %journal directory and all files and sub--directories that it may
+ * contain. This is equivilent of rm -rf.
+ *
+ * FIXME: links are not handled correctly.
+ *
+ * \param dirname String containing name of directory to be deleted.
+ * \param children_only If true, delete only children of dirname, but leave dirname itself.
+ *
+ * \exception jerrno::JERR_JDIR_OPENDIR The %journal directory could not be opened.
+ * \exception jerrno::JERR_JDIR_STAT Could not stat dirname.
+ * \exception jerrno::JERR_JDIR_UNLINK A file could not be deleted.
+ * \exception jerrno::JERR_JDIR_BADFTYPE A dir entry is neiter a file nor a dir.
+ * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed.
+ * \exception jerrno::JERR_JDIR_RMDIR A directory could not be deleted.
+ */
+ static void delete_dir(const std::string& dirname, bool children_only = false);
+
+ /**
+ * \brief Create bakup directory that is next in sequence and move all %journal files
+ * matching base_filename into it.
+ *
+ * In directory dirname, search for existing backup directory using pattern
+ * "_basename.bak.XXXX" where XXXX is a hexadecimal sequence, and create next directory
+ * based on highest number found. Move all %journal files which match the base_fileaname
+ * parameter into this new backup directory.
+ *
+ * \param dirname String containing name of %journal directory.
+ * \param base_filename String containing base filename of %journal files to be matched
+ * for moving into subdirectory.
+ *
+ * \exception jerrno::JERR_JDIR_OPENDIR The %journal directory could not be opened.
+ * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed.
+ * \exception jerrno::JERR_JDIR_MKDIR The backup directory could not be deleted.
+ */
+ static std::string create_bak_dir(const std::string& dirname,
+ const std::string& base_filename);
+
+ /**
+ * \brief Return the directory name as a string.
+ */
+ inline const std::string& dirname() const { return _dirname; }
+
+ /**
+ * \brief Return the %journal base filename name as a string.
+ */
+ inline const std::string& base_filename() const { return _base_filename; }
+
+ /**
+ * \brief Test whether the named file is a directory.
+ *
+ * \param name Name of file to be tested.
+ * \return <b><i>true</i></b> if the named file is a directory; <b><i>false</i></b>
+ * otherwise.
+ * \exception jerrno::JERR_JDIR_STAT Could not stat name.
+ */
+ static bool is_dir(const char* name);
+
+ /**
+ * \brief Test whether the named file is a directory.
+ *
+ * \param name Name of file to be tested.
+ * \return <b><i>true</i></b> if the named file is a directory; <b><i>false</i></b>
+ * otherwise.
+ * \exception jerrno::JERR_JDIR_STAT Could not stat name.
+ */
+ static bool is_dir(const std::string& name);
+
+
+ /**
+ * \brief Test whether the named entity exists on the filesystem.
+ *
+ * If stat() fails with error ENOENT, then this will return <b><i>false</i></b>. If
+ * stat() succeeds, then <b><i>true</i></b> is returned, irrespective of the file type.
+ * If stat() fails with any other error, an exception is thrown.
+ *
+ * \param name Name of entity to be tested.
+ * \return <b><i>true</i></b> if the named entity exists; <b><i>false</i></b>
+ * otherwise.
+ * \exception jerrno::JERR_JDIR_STAT Could not stat name.
+ */
+ static bool exists(const char* name);
+
+ /**
+ * \brief Test whether the named entity exists on the filesystem.
+ *
+ * If stat() fails with error ENOENT, then this will return <b><i>false</i></b>. If
+ * stat() succeeds, then <b><i>true</i></b> is returned, irrespective of the file type.
+ * If stat() fails with any other error, an exception is thrown.
+ *
+ * \param name Name of entity to be tested.
+ * \return <b><i>true</i></b> if the named entity exists; <b><i>false</i></b>
+ * otherwise.
+ * \exception jerrno::JERR_JDIR_STAT Could not stat name.
+ */
+ static bool exists(const std::string& name);
+
+ /**
+ * \brief Stream operator
+ */
+ friend std::ostream& operator<<(std::ostream& os, const jdir& jdir);
+
+ /**
+ * \brief Stream operator
+ */
+ friend std::ostream& operator<<(std::ostream& os, const jdir* jdirPtr);
+
+ private:
+ /**
+ * \brief Check for error, if non-zero close DIR handle and throw JERR_JDIR_READDIR
+ *
+ * \exception jerrno::JERR_JDIR_READDIR Error while reading contents of dir.
+ */
+ static void check_err(const int err_num, DIR* dir, const std::string& dir_name, const std::string& fn_name);
+
+ /**
+ * \brief Close a DIR handle, throw JERR_JDIR_CLOSEDIR if error occurs during close
+ *
+ * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed.
+ */
+ static void close_dir(DIR* dir, const std::string& dir_name, const std::string& fn_name);
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_JDIR_H
diff --git a/cpp/src/qpid/legacystore/jrnl/jerrno.cpp b/cpp/src/qpid/legacystore/jrnl/jerrno.cpp
new file mode 100644
index 0000000000..4962ce63ab
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/jerrno.cpp
@@ -0,0 +1,253 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 jerrno.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::jerrno (journal error
+ * codes). See comments in file jerrno.h for details.
+ *
+ * See file jerrno.h for class details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/jerrno.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+std::map<u_int32_t, const char*> jerrno::_err_map;
+std::map<u_int32_t, const char*>::iterator jerrno::_err_map_itr;
+bool jerrno::_initialized = jerrno::__init();
+
+// generic errors
+const u_int32_t jerrno::JERR__MALLOC = 0x0100;
+const u_int32_t jerrno::JERR__UNDERFLOW = 0x0101;
+const u_int32_t jerrno::JERR__NINIT = 0x0102;
+const u_int32_t jerrno::JERR__AIO = 0x0103;
+const u_int32_t jerrno::JERR__FILEIO = 0x0104;
+const u_int32_t jerrno::JERR__RTCLOCK = 0x0105;
+const u_int32_t jerrno::JERR__PTHREAD = 0x0106;
+const u_int32_t jerrno::JERR__TIMEOUT = 0x0107;
+const u_int32_t jerrno::JERR__UNEXPRESPONSE = 0x0108;
+const u_int32_t jerrno::JERR__RECNFOUND = 0x0109;
+const u_int32_t jerrno::JERR__NOTIMPL = 0x010a;
+
+// class jcntl
+const u_int32_t jerrno::JERR_JCNTL_STOPPED = 0x0200;
+const u_int32_t jerrno::JERR_JCNTL_READONLY = 0x0201;
+const u_int32_t jerrno::JERR_JCNTL_AIOCMPLWAIT = 0x0202;
+const u_int32_t jerrno::JERR_JCNTL_UNKNOWNMAGIC = 0x0203;
+const u_int32_t jerrno::JERR_JCNTL_NOTRECOVERED = 0x0204;
+const u_int32_t jerrno::JERR_JCNTL_RECOVERJFULL = 0x0205;
+const u_int32_t jerrno::JERR_JCNTL_OWIMISMATCH = 0x0206;
+
+// class jdir
+const u_int32_t jerrno::JERR_JDIR_NOTDIR = 0x0300;
+const u_int32_t jerrno::JERR_JDIR_MKDIR = 0x0301;
+const u_int32_t jerrno::JERR_JDIR_OPENDIR = 0x0302;
+const u_int32_t jerrno::JERR_JDIR_READDIR = 0x0303;
+const u_int32_t jerrno::JERR_JDIR_CLOSEDIR = 0x0304;
+const u_int32_t jerrno::JERR_JDIR_RMDIR = 0x0305;
+const u_int32_t jerrno::JERR_JDIR_NOSUCHFILE = 0x0306;
+const u_int32_t jerrno::JERR_JDIR_FMOVE = 0x0307;
+const u_int32_t jerrno::JERR_JDIR_STAT = 0x0308;
+const u_int32_t jerrno::JERR_JDIR_UNLINK = 0x0309;
+const u_int32_t jerrno::JERR_JDIR_BADFTYPE = 0x030a;
+
+// class fcntl
+const u_int32_t jerrno::JERR_FCNTL_OPENWR = 0x0400;
+const u_int32_t jerrno::JERR_FCNTL_WRITE = 0x0401;
+const u_int32_t jerrno::JERR_FCNTL_CLOSE = 0x0402;
+const u_int32_t jerrno::JERR_FCNTL_FILEOFFSOVFL = 0x0403;
+const u_int32_t jerrno::JERR_FCNTL_CMPLOFFSOVFL = 0x0404;
+const u_int32_t jerrno::JERR_FCNTL_RDOFFSOVFL = 0x0405;
+
+// class lfmgr
+const u_int32_t jerrno::JERR_LFMGR_BADAEFNUMLIM = 0x0500;
+const u_int32_t jerrno::JERR_LFMGR_AEFNUMLIMIT = 0x0501;
+const u_int32_t jerrno::JERR_LFMGR_AEDISABLED = 0x0502;
+
+// class rrfc
+const u_int32_t jerrno::JERR_RRFC_OPENRD = 0x0600;
+
+// class jrec, enq_rec, deq_rec, txn_rec
+const u_int32_t jerrno::JERR_JREC_BADRECHDR = 0x0700;
+const u_int32_t jerrno::JERR_JREC_BADRECTAIL = 0x0701;
+
+// class wmgr
+const u_int32_t jerrno::JERR_WMGR_BADPGSTATE = 0x0801;
+const u_int32_t jerrno::JERR_WMGR_BADDTOKSTATE = 0x0802;
+const u_int32_t jerrno::JERR_WMGR_ENQDISCONT = 0x0803;
+const u_int32_t jerrno::JERR_WMGR_DEQDISCONT = 0x0804;
+const u_int32_t jerrno::JERR_WMGR_DEQRIDNOTENQ = 0x0805;
+
+// class rmgr
+const u_int32_t jerrno::JERR_RMGR_UNKNOWNMAGIC = 0x0900;
+const u_int32_t jerrno::JERR_RMGR_RIDMISMATCH = 0x0901;
+//const u_int32_t jerrno::JERR_RMGR_FIDMISMATCH = 0x0902;
+const u_int32_t jerrno::JERR_RMGR_ENQSTATE = 0x0903;
+const u_int32_t jerrno::JERR_RMGR_BADRECTYPE = 0x0904;
+
+// class data_tok
+const u_int32_t jerrno::JERR_DTOK_ILLEGALSTATE = 0x0a00;
+// const u_int32_t jerrno::JERR_DTOK_RIDNOTSET = 0x0a01;
+
+// class enq_map, txn_map
+const u_int32_t jerrno::JERR_MAP_DUPLICATE = 0x0b00;
+const u_int32_t jerrno::JERR_MAP_NOTFOUND = 0x0b01;
+const u_int32_t jerrno::JERR_MAP_LOCKED = 0x0b02;
+
+// class jinf
+const u_int32_t jerrno::JERR_JINF_CVALIDFAIL = 0x0c00;
+const u_int32_t jerrno::JERR_JINF_NOVALUESTR = 0x0c01;
+const u_int32_t jerrno::JERR_JINF_BADVALUESTR = 0x0c02;
+const u_int32_t jerrno::JERR_JINF_JDATEMPTY = 0x0c03;
+const u_int32_t jerrno::JERR_JINF_TOOMANYFILES = 0x0c04;
+const u_int32_t jerrno::JERR_JINF_INVALIDFHDR = 0x0c05;
+const u_int32_t jerrno::JERR_JINF_STAT = 0x0c06;
+const u_int32_t jerrno::JERR_JINF_NOTREGFILE = 0x0c07;
+const u_int32_t jerrno::JERR_JINF_BADFILESIZE = 0x0c08;
+const u_int32_t jerrno::JERR_JINF_OWIBAD = 0x0c09;
+const u_int32_t jerrno::JERR_JINF_ZEROLENFILE = 0x0c0a;
+
+// Negative returns for some functions
+const int32_t jerrno::AIO_TIMEOUT = -1;
+const int32_t jerrno::LOCK_TAKEN = -2;
+
+
+// static initialization fn
+
+bool
+jerrno::__init()
+{
+ // generic errors
+ _err_map[JERR__MALLOC] = "JERR__MALLOC: Buffer memory allocation failed.";
+ _err_map[JERR__UNDERFLOW] = "JERR__UNDERFLOW: Underflow error";
+ _err_map[JERR__NINIT] = "JERR__NINIT: Operation on uninitialized class.";
+ _err_map[JERR__AIO] = "JERR__AIO: AIO error.";
+ _err_map[JERR__FILEIO] = "JERR__FILEIO: File read or write failure.";
+ _err_map[JERR__RTCLOCK] = "JERR__RTCLOCK: Reading real-time clock failed.";
+ _err_map[JERR__PTHREAD] = "JERR__PTHREAD: pthread failure.";
+ _err_map[JERR__TIMEOUT] = "JERR__TIMEOUT: Timeout waiting for event.";
+ _err_map[JERR__UNEXPRESPONSE] = "JERR__UNEXPRESPONSE: Unexpected response to call or event.";
+ _err_map[JERR__RECNFOUND] = "JERR__RECNFOUND: Record not found.";
+ _err_map[JERR__NOTIMPL] = "JERR__NOTIMPL: Not implemented";
+
+ // class jcntl
+ _err_map[JERR_JCNTL_STOPPED] = "JERR_JCNTL_STOPPED: Operation on stopped journal.";
+ _err_map[JERR_JCNTL_READONLY] = "JERR_JCNTL_READONLY: Write operation on read-only journal (during recovery).";
+ _err_map[JERR_JCNTL_AIOCMPLWAIT] = "JERR_JCNTL_AIOCMPLWAIT: Timeout waiting for AIOs to complete.";
+ _err_map[JERR_JCNTL_UNKNOWNMAGIC] = "JERR_JCNTL_UNKNOWNMAGIC: Found record with unknown magic.";
+ _err_map[JERR_JCNTL_NOTRECOVERED] = "JERR_JCNTL_NOTRECOVERED: Operation requires recover() to be run first.";
+ _err_map[JERR_JCNTL_RECOVERJFULL] = "JERR_JCNTL_RECOVERJFULL: Journal data files full, cannot write.";
+ _err_map[JERR_JCNTL_OWIMISMATCH] = "JERR_JCNTL_OWIMISMATCH: Overwrite Indicator (OWI) change found in unexpected location.";
+
+ // class jdir
+ _err_map[JERR_JDIR_NOTDIR] = "JERR_JDIR_NOTDIR: Directory name exists but is not a directory.";
+ _err_map[JERR_JDIR_MKDIR] = "JERR_JDIR_MKDIR: Directory creation failed.";
+ _err_map[JERR_JDIR_OPENDIR] = "JERR_JDIR_OPENDIR: Directory open failed.";
+ _err_map[JERR_JDIR_READDIR] = "JERR_JDIR_READDIR: Directory read failed.";
+ _err_map[JERR_JDIR_CLOSEDIR] = "JERR_JDIR_CLOSEDIR: Directory close failed.";
+ _err_map[JERR_JDIR_RMDIR] = "JERR_JDIR_RMDIR: Directory delete failed.";
+ _err_map[JERR_JDIR_NOSUCHFILE] = "JERR_JDIR_NOSUCHFILE: File does not exist.";
+ _err_map[JERR_JDIR_FMOVE] = "JERR_JDIR_FMOVE: File move failed.";
+ _err_map[JERR_JDIR_STAT] = "JERR_JDIR_STAT: File stat failed.";
+ _err_map[JERR_JDIR_UNLINK] = "JERR_JDIR_UNLINK: File delete failed.";
+ _err_map[JERR_JDIR_BADFTYPE] = "JERR_JDIR_BADFTYPE: Bad or unknown file type (stat mode).";
+
+ // class fcntl
+ _err_map[JERR_FCNTL_OPENWR] = "JERR_FCNTL_OPENWR: Unable to open file for write.";
+ _err_map[JERR_FCNTL_WRITE] = "JERR_FCNTL_WRITE: Unable to write to file.";
+ _err_map[JERR_FCNTL_CLOSE] = "JERR_FCNTL_CLOSE: File close failed.";
+ _err_map[JERR_FCNTL_FILEOFFSOVFL] = "JERR_FCNTL_FILEOFFSOVFL: Attempted increase file offset past file size.";
+ _err_map[JERR_FCNTL_CMPLOFFSOVFL] = "JERR_FCNTL_CMPLOFFSOVFL: Attempted increase completed file offset past submitted offset.";
+ _err_map[JERR_FCNTL_RDOFFSOVFL] = "JERR_FCNTL_RDOFFSOVFL: Attempted increase read offset past write offset.";
+
+ // class lfmgr
+ _err_map[JERR_LFMGR_BADAEFNUMLIM] = "JERR_LFMGR_BADAEFNUMLIM: Auto-expand file number limit lower than initial number of journal files.";
+ _err_map[JERR_LFMGR_AEFNUMLIMIT] = "JERR_LFMGR_AEFNUMLIMIT: Exceeded auto-expand file number limit.";
+ _err_map[JERR_LFMGR_AEDISABLED] = "JERR_LFMGR_AEDISABLED: Attempted to expand with auto-expand disabled.";
+
+ // class rrfc
+ _err_map[JERR_RRFC_OPENRD] = "JERR_RRFC_OPENRD: Unable to open file for read.";
+
+ // class jrec, enq_rec, deq_rec, txn_rec
+ _err_map[JERR_JREC_BADRECHDR] = "JERR_JREC_BADRECHDR: Invalid data record header.";
+ _err_map[JERR_JREC_BADRECTAIL] = "JERR_JREC_BADRECTAIL: Invalid data record tail.";
+
+ // class wmgr
+ _err_map[JERR_WMGR_BADPGSTATE] = "JERR_WMGR_BADPGSTATE: Page buffer in illegal state for operation.";
+ _err_map[JERR_WMGR_BADDTOKSTATE] = "JERR_WMGR_BADDTOKSTATE: Data token in illegal state for operation.";
+ _err_map[JERR_WMGR_ENQDISCONT] = "JERR_WMGR_ENQDISCONT: Enqueued new dtok when previous enqueue returned partly completed (state ENQ_PART).";
+ _err_map[JERR_WMGR_DEQDISCONT] = "JERR_WMGR_DEQDISCONT: Dequeued new dtok when previous dequeue returned partly completed (state DEQ_PART).";
+ _err_map[JERR_WMGR_DEQRIDNOTENQ] = "JERR_WMGR_DEQRIDNOTENQ: Dequeue rid is not enqueued.";
+
+ // class rmgr
+ _err_map[JERR_RMGR_UNKNOWNMAGIC] = "JERR_RMGR_UNKNOWNMAGIC: Found record with unknown magic.";
+ _err_map[JERR_RMGR_RIDMISMATCH] = "JERR_RMGR_RIDMISMATCH: RID mismatch between current record and dtok RID";
+ //_err_map[JERR_RMGR_FIDMISMATCH] = "JERR_RMGR_FIDMISMATCH: FID mismatch between emap and rrfc";
+ _err_map[JERR_RMGR_ENQSTATE] = "JERR_RMGR_ENQSTATE: Attempted read when data token wstate was not ENQ";
+ _err_map[JERR_RMGR_BADRECTYPE] = "JERR_RMGR_BADRECTYPE: Attempted operation on inappropriate record type";
+
+ // class data_tok
+ _err_map[JERR_DTOK_ILLEGALSTATE] = "JERR_MTOK_ILLEGALSTATE: Attempted to change to illegal state.";
+ //_err_map[JERR_DTOK_RIDNOTSET] = "JERR_DTOK_RIDNOTSET: Record ID not set.";
+
+ // class enq_map, txn_map
+ _err_map[JERR_MAP_DUPLICATE] = "JERR_MAP_DUPLICATE: Attempted to insert record into map using duplicate key.";
+ _err_map[JERR_MAP_NOTFOUND] = "JERR_MAP_NOTFOUND: Key not found in map.";
+ _err_map[JERR_MAP_LOCKED] = "JERR_MAP_LOCKED: Record ID locked by a pending transaction.";
+
+ // class jinf
+ _err_map[JERR_JINF_CVALIDFAIL] = "JERR_JINF_CVALIDFAIL: Journal compatibility validation failure.";
+ _err_map[JERR_JINF_NOVALUESTR] = "JERR_JINF_NOVALUESTR: No value attribute found in jinf file.";
+ _err_map[JERR_JINF_BADVALUESTR] = "JERR_JINF_BADVALUESTR: Bad format for value attribute in jinf file";
+ _err_map[JERR_JINF_JDATEMPTY] = "JERR_JINF_JDATEMPTY: Journal data files empty.";
+ _err_map[JERR_JINF_TOOMANYFILES] = "JERR_JINF_TOOMANYFILES: Too many journal data files.";
+ _err_map[JERR_JINF_INVALIDFHDR] = "JERR_JINF_INVALIDFHDR: Invalid journal data file header";
+ _err_map[JERR_JINF_STAT] = "JERR_JINF_STAT: Error while trying to stat a journal data file";
+ _err_map[JERR_JINF_NOTREGFILE] = "JERR_JINF_NOTREGFILE: Target journal data file is not a regular file";
+ _err_map[JERR_JINF_BADFILESIZE] = "JERR_JINF_BADFILESIZE: Journal data file is of incorrect or unexpected size";
+ _err_map[JERR_JINF_OWIBAD] = "JERR_JINF_OWIBAD: Journal data files have inconsistent OWI flags; >1 transition found in non-auto-expand or min-size journal";
+ _err_map[JERR_JINF_ZEROLENFILE] = "JERR_JINF_ZEROLENFILE: Journal info file zero length";
+
+ //_err_map[] = "";
+
+ return true;
+}
+
+const char*
+jerrno::err_msg(const u_int32_t err_no) throw ()
+{
+ _err_map_itr = _err_map.find(err_no);
+ if (_err_map_itr == _err_map.end())
+ return "<Unknown error code>";
+ return _err_map_itr->second;
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/jerrno.h b/cpp/src/qpid/legacystore/jrnl/jerrno.h
new file mode 100644
index 0000000000..4c8b71c423
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/jerrno.h
@@ -0,0 +1,173 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file jerrno.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::jerrno (journal error
+ * codes). See class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_JERRNO_H
+#define QPID_LEGACYSTORE_JRNL_JERRNO_H
+
+namespace mrg
+{
+namespace journal
+{
+class jerrno;
+}
+}
+
+#include <map>
+#include <string>
+#include <sys/types.h>
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \class jerrno
+ * \brief Class containing static error definitions and static map for error messages.
+ */
+ class jerrno
+ {
+ static std::map<u_int32_t, const char*> _err_map; ///< Map of error messages
+ static std::map<u_int32_t, const char*>::iterator _err_map_itr; ///< Iterator
+ static bool _initialized; ///< Dummy flag, used to initialise map.
+
+ public:
+ // generic errors
+ static const u_int32_t JERR__MALLOC; ///< Buffer memory allocation failed
+ static const u_int32_t JERR__UNDERFLOW; ///< Underflow error
+ static const u_int32_t JERR__NINIT; ///< Operation on uninitialized class
+ static const u_int32_t JERR__AIO; ///< AIO failure
+ static const u_int32_t JERR__FILEIO; ///< File read or write failure
+ static const u_int32_t JERR__RTCLOCK; ///< Reading real-time clock failed
+ static const u_int32_t JERR__PTHREAD; ///< pthread failure
+ static const u_int32_t JERR__TIMEOUT; ///< Timeout waiting for an event
+ static const u_int32_t JERR__UNEXPRESPONSE; ///< Unexpected response to call or event
+ static const u_int32_t JERR__RECNFOUND; ///< Record not found
+ static const u_int32_t JERR__NOTIMPL; ///< Not implemented
+
+ // class jcntl
+ static const u_int32_t JERR_JCNTL_STOPPED; ///< Operation on stopped journal
+ static const u_int32_t JERR_JCNTL_READONLY; ///< Write operation on read-only journal
+ static const u_int32_t JERR_JCNTL_AIOCMPLWAIT; ///< Timeout waiting for AIOs to complete
+ static const u_int32_t JERR_JCNTL_UNKNOWNMAGIC; ///< Found record with unknown magic
+ static const u_int32_t JERR_JCNTL_NOTRECOVERED; ///< Req' recover() to be called first
+ static const u_int32_t JERR_JCNTL_RECOVERJFULL; ///< Journal data files full, cannot write
+ static const u_int32_t JERR_JCNTL_OWIMISMATCH; ///< OWI change found in unexpected location
+
+ // class jdir
+ static const u_int32_t JERR_JDIR_NOTDIR; ///< Exists but is not a directory
+ static const u_int32_t JERR_JDIR_MKDIR; ///< Directory creation failed
+ static const u_int32_t JERR_JDIR_OPENDIR; ///< Directory open failed
+ static const u_int32_t JERR_JDIR_READDIR; ///< Directory read failed
+ static const u_int32_t JERR_JDIR_CLOSEDIR; ///< Directory close failed
+ static const u_int32_t JERR_JDIR_RMDIR; ///< Directory delete failed
+ static const u_int32_t JERR_JDIR_NOSUCHFILE; ///< File does not exist
+ static const u_int32_t JERR_JDIR_FMOVE; ///< File move failed
+ static const u_int32_t JERR_JDIR_STAT; ///< File stat failed
+ static const u_int32_t JERR_JDIR_UNLINK; ///< File delete failed
+ static const u_int32_t JERR_JDIR_BADFTYPE; ///< Bad or unknown file type (stat mode)
+
+ // class fcntl
+ static const u_int32_t JERR_FCNTL_OPENWR; ///< Unable to open file for write
+ static const u_int32_t JERR_FCNTL_WRITE; ///< Unable to write to file
+ static const u_int32_t JERR_FCNTL_CLOSE; ///< File close failed
+ static const u_int32_t JERR_FCNTL_FILEOFFSOVFL; ///< Increased offset past file size
+ static const u_int32_t JERR_FCNTL_CMPLOFFSOVFL; ///< Increased cmpl offs past subm offs
+ static const u_int32_t JERR_FCNTL_RDOFFSOVFL; ///< Increased read offs past write offs
+
+ // class lfmgr
+ static const u_int32_t JERR_LFMGR_BADAEFNUMLIM; ///< Bad auto-expand file number limit
+ static const u_int32_t JERR_LFMGR_AEFNUMLIMIT; ///< Exceeded auto-expand file number limit
+ static const u_int32_t JERR_LFMGR_AEDISABLED; ///< Attempted to expand with auto-expand disabled
+
+ // class rrfc
+ static const u_int32_t JERR_RRFC_OPENRD; ///< Unable to open file for read
+
+ // class jrec, enq_rec, deq_rec, txn_rec
+ static const u_int32_t JERR_JREC_BADRECHDR; ///< Invalid data record header
+ static const u_int32_t JERR_JREC_BADRECTAIL; ///< Invalid data record tail
+
+ // class wmgr
+ static const u_int32_t JERR_WMGR_BADPGSTATE; ///< Page buffer in illegal state.
+ static const u_int32_t JERR_WMGR_BADDTOKSTATE; ///< Data token in illegal state.
+ static const u_int32_t JERR_WMGR_ENQDISCONT; ///< Enq. new dtok when previous part compl.
+ static const u_int32_t JERR_WMGR_DEQDISCONT; ///< Deq. new dtok when previous part compl.
+ static const u_int32_t JERR_WMGR_DEQRIDNOTENQ; ///< Deq. rid not enqueued
+
+ // class rmgr
+ static const u_int32_t JERR_RMGR_UNKNOWNMAGIC; ///< Found record with unknown magic
+ static const u_int32_t JERR_RMGR_RIDMISMATCH; ///< RID mismatch between rec and dtok
+ //static const u_int32_t JERR_RMGR_FIDMISMATCH; ///< FID mismatch between emap and rrfc
+ static const u_int32_t JERR_RMGR_ENQSTATE; ///< Attempted read when wstate not ENQ
+ static const u_int32_t JERR_RMGR_BADRECTYPE; ///< Attempted op on incorrect rec type
+
+ // class data_tok
+ static const u_int32_t JERR_DTOK_ILLEGALSTATE; ///< Attempted to change to illegal state
+// static const u_int32_t JERR_DTOK_RIDNOTSET; ///< Record ID not set
+
+ // class enq_map, txn_map
+ static const u_int32_t JERR_MAP_DUPLICATE; ///< Attempted to insert using duplicate key
+ static const u_int32_t JERR_MAP_NOTFOUND; ///< Key not found in map
+ static const u_int32_t JERR_MAP_LOCKED; ///< rid locked by pending txn
+
+ // class jinf
+ static const u_int32_t JERR_JINF_CVALIDFAIL; ///< Compatibility validation failure
+ static const u_int32_t JERR_JINF_NOVALUESTR; ///< No value attr found in jinf file
+ static const u_int32_t JERR_JINF_BADVALUESTR; ///< Bad format for value attr in jinf file
+ static const u_int32_t JERR_JINF_JDATEMPTY; ///< Journal data files empty
+ static const u_int32_t JERR_JINF_TOOMANYFILES; ///< Too many journal data files
+ static const u_int32_t JERR_JINF_INVALIDFHDR; ///< Invalid file header
+ static const u_int32_t JERR_JINF_STAT; ///< Error while trying to stat a file
+ static const u_int32_t JERR_JINF_NOTREGFILE; ///< Target file is not a regular file
+ static const u_int32_t JERR_JINF_BADFILESIZE; ///< File is of incorrect or unexpected size
+ static const u_int32_t JERR_JINF_OWIBAD; ///< OWI inconsistent (>1 transition in non-ae journal)
+ static const u_int32_t JERR_JINF_ZEROLENFILE; ///< Journal info file is zero length (empty).
+
+ // Negative returns for some functions
+ static const int32_t AIO_TIMEOUT; ///< Timeout waiting for AIO return
+ static const int32_t LOCK_TAKEN; ///< Attempted to take lock, but it was taken by another thread
+ /**
+ * \brief Method to access error message from known error number.
+ */
+ static const char* err_msg(const u_int32_t err_no) throw ();
+
+ private:
+ /**
+ * \brief Static function to initialize map.
+ */
+ static bool __init();
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_JERRNO_H
diff --git a/cpp/src/qpid/legacystore/jrnl/jexception.cpp b/cpp/src/qpid/legacystore/jrnl/jexception.cpp
new file mode 100644
index 0000000000..5c571020e4
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/jexception.cpp
@@ -0,0 +1,183 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file jexception.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * Generic journal exception class mrg::journal::jexception. See comments
+ * in file jexception.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/jexception.h"
+
+#include <iomanip>
+#include <sstream>
+#include "qpid/legacystore/jrnl/jerrno.h"
+
+#define CATLEN(p) MAX_MSG_SIZE - std::strlen(p) - 1
+
+namespace mrg
+{
+namespace journal
+{
+
+jexception::jexception() throw ():
+ std::exception(),
+ _err_code(0)
+{
+ format();
+}
+
+jexception::jexception(const u_int32_t err_code) throw ():
+ std::exception(),
+ _err_code(err_code)
+{
+ format();
+}
+
+jexception::jexception(const char* additional_info) throw ():
+ std::exception(),
+ _err_code(0),
+ _additional_info(additional_info)
+{
+ format();
+}
+
+jexception::jexception(const std::string& additional_info) throw ():
+ std::exception(),
+ _err_code(0),
+ _additional_info(additional_info)
+{
+ format();
+}
+
+jexception::jexception(const u_int32_t err_code, const char* additional_info) throw ():
+ std::exception(),
+ _err_code(err_code),
+ _additional_info(additional_info)
+{
+ format();
+}
+
+jexception::jexception(const u_int32_t err_code, const std::string& additional_info) throw ():
+ std::exception(),
+ _err_code(err_code),
+ _additional_info(additional_info)
+{
+ format();
+}
+
+jexception::jexception(const u_int32_t err_code, const char* throwing_class,
+ const char* throwing_fn) throw ():
+ std::exception(),
+ _err_code(err_code),
+ _throwing_class(throwing_class),
+ _throwing_fn(throwing_fn)
+{
+ format();
+}
+
+jexception::jexception(const u_int32_t err_code, const std::string& throwing_class,
+ const std::string& throwing_fn) throw ():
+ std::exception(),
+ _err_code(err_code),
+ _throwing_class(throwing_class),
+ _throwing_fn(throwing_fn)
+{
+ format();
+}
+
+jexception::jexception(const u_int32_t err_code, const char* additional_info,
+ const char* throwing_class, const char* throwing_fn) throw ():
+ std::exception(),
+ _err_code(err_code),
+ _additional_info(additional_info),
+ _throwing_class(throwing_class),
+ _throwing_fn(throwing_fn)
+{
+ format();
+}
+
+jexception::jexception(const u_int32_t err_code, const std::string& additional_info,
+ const std::string& throwing_class, const std::string& throwing_fn) throw ():
+ std::exception(),
+ _err_code(err_code),
+ _additional_info(additional_info),
+ _throwing_class(throwing_class),
+ _throwing_fn(throwing_fn)
+{
+ format();
+}
+
+jexception::~jexception() throw ()
+{}
+
+void
+jexception::format()
+{
+ const bool ai = !_additional_info.empty();
+ const bool tc = !_throwing_class.empty();
+ const bool tf = !_throwing_fn.empty();
+ std::ostringstream oss;
+ oss << "jexception 0x" << std::hex << std::setfill('0') << std::setw(4) << _err_code << " ";
+ if (tc)
+ {
+ oss << _throwing_class;
+ if (tf)
+ oss << "::";
+ else
+ oss << " ";
+ }
+ if (tf)
+ oss << _throwing_fn << "() ";
+ if (tc || tf)
+ oss << "threw " << jerrno::err_msg(_err_code);
+ if (ai)
+ oss << " (" << _additional_info << ")";
+ _what.assign(oss.str());
+}
+
+const char*
+jexception::what() const throw ()
+{
+ return _what.c_str();
+}
+
+std::ostream&
+operator<<(std::ostream& os, const jexception& je)
+{
+ os << je.what();
+ return os;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const jexception* jePtr)
+{
+ os << jePtr->what();
+ return os;
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/jexception.h b/cpp/src/qpid/legacystore/jrnl/jexception.h
new file mode 100644
index 0000000000..34d8373235
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/jexception.h
@@ -0,0 +1,142 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 jexception.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * Generic journal exception class mrg::journal::jexception (derived
+ * from class std::exception). Intended to serve as a common exception
+ * class for all more speicalized exceptions in the message journal. See
+ * class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_JEXCEPTION_H
+#define QPID_LEGACYSTORE_JRNL_JEXCEPTION_H
+
+namespace mrg
+{
+namespace journal
+{
+class jexception;
+}
+}
+
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <exception>
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include <sstream>
+#include <string>
+#include <sys/types.h>
+
+// Macro for formatting commom system errors
+#define FORMAT_SYSERR(errno) " errno=" << errno << " (" << std::strerror(errno) << ")"
+
+#define MALLOC_CHK(ptr, var, cls, fn) if(ptr == 0) { \
+ clean(); \
+ std::ostringstream oss; \
+ oss << var << ": malloc() failed: " << FORMAT_SYSERR(errno); \
+ throw jexception(jerrno::JERR__MALLOC, oss.str(), cls, fn); \
+ }
+
+// TODO: The following is a temporary bug-tracking aid which forces a core.
+// Replace with the commented out version below when BZ484048 is resolved.
+#define PTHREAD_CHK(err, pfn, cls, fn) if(err != 0) { \
+ std::ostringstream oss; \
+ oss << cls << "::" << fn << "(): " << pfn; \
+ errno = err; \
+ ::perror(oss.str().c_str()); \
+ ::abort(); \
+ }
+/*
+#define PTHREAD_CHK(err, pfn, cls, fn) if(err != 0) { \
+ std::ostringstream oss; \
+ oss << pfn << " failed: " << FORMAT_SYSERR(err); \
+ throw jexception(jerrno::JERR__PTHREAD, oss.str(), cls, fn); \
+ }
+*/
+
+#define ASSERT(cond, msg) if(cond == 0) { \
+ std::cerr << msg << std::endl; \
+ ::abort(); \
+ }
+
+namespace mrg
+{
+namespace journal
+{
+ /**
+ * \class jexception
+ * \brief Generic journal exception class
+ */
+ class jexception : public std::exception
+ {
+ private:
+ u_int32_t _err_code;
+ std::string _additional_info;
+ std::string _throwing_class;
+ std::string _throwing_fn;
+ std::string _what;
+ void format();
+
+ public:
+ jexception() throw ();
+
+ jexception(const u_int32_t err_code) throw ();
+
+ jexception(const char* additional_info) throw ();
+ jexception(const std::string& additional_info) throw ();
+
+ jexception(const u_int32_t err_code, const char* additional_info) throw ();
+ jexception(const u_int32_t err_code, const std::string& additional_info) throw ();
+
+ jexception(const u_int32_t err_code, const char* throwing_class, const char* throwing_fn)
+ throw ();
+ jexception(const u_int32_t err_code, const std::string& throwing_class,
+ const std::string& throwing_fn) throw ();
+
+ jexception(const u_int32_t err_code, const char* additional_info,
+ const char* throwing_class, const char* throwing_fn) throw ();
+ jexception(const u_int32_t err_code, const std::string& additional_info,
+ const std::string& throwing_class, const std::string& throwing_fn) throw ();
+
+ virtual ~jexception() throw ();
+ virtual const char* what() const throw (); // override std::exception::what()
+
+ inline u_int32_t err_code() const throw () { return _err_code; }
+ inline const std::string additional_info() const throw () { return _additional_info; }
+ inline const std::string throwing_class() const throw () { return _throwing_class; }
+ inline const std::string throwing_fn() const throw () { return _throwing_fn; }
+
+ friend std::ostream& operator<<(std::ostream& os, const jexception& je);
+ friend std::ostream& operator<<(std::ostream& os, const jexception* jePtr);
+ }; // class jexception
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_JEXCEPTION_H
diff --git a/cpp/src/qpid/legacystore/jrnl/jinf.cpp b/cpp/src/qpid/legacystore/jrnl/jinf.cpp
new file mode 100644
index 0000000000..4117bd3581
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/jinf.cpp
@@ -0,0 +1,540 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 jinf.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * This file contains the code for the mrg::journal::jinf class.
+ *
+ * See jinf.h comments for details of this class.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "jrnl/jinf.h"
+
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <fstream>
+#include "qpid/legacystore/jrnl/file_hdr.h"
+#include "qpid/legacystore/jrnl/jcntl.h"
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/lp_map.h"
+#include <sstream>
+#include <sys/stat.h>
+
+namespace mrg
+{
+namespace journal
+{
+
+jinf::jinf(const std::string& jinf_filename, bool validate_flag):
+ _jver(0),
+ _filename(jinf_filename),
+ _num_jfiles(0),
+ _ae(false),
+ _ae_max_jfiles(0),
+ _jfsize_sblks(0),
+ _sblk_size_dblks(0),
+ _dblk_size(0),
+ _wcache_pgsize_sblks(0),
+ _wcache_num_pages(0),
+ _rcache_pgsize_sblks(0),
+ _rcache_num_pages(0),
+ _tm_ptr(0),
+ _valid_flag(false),
+ _analyzed_flag(false),
+ _initial_owi(false),
+ _frot(false)
+{
+ read(_filename);
+ if (validate_flag)
+ validate();
+}
+
+jinf::jinf(const std::string& jid, const std::string& jdir, const std::string& base_filename, const u_int16_t num_jfiles,
+ const bool auto_expand, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks,
+ const u_int32_t wcache_pgsize_sblks, const u_int16_t wcache_num_pages, const timespec& ts):
+ _jver(RHM_JDAT_VERSION),
+ _jid(jid),
+ _jdir(jdir),
+ _base_filename(base_filename),
+ _ts(ts),
+ _num_jfiles(num_jfiles),
+ _ae(auto_expand),
+ _ae_max_jfiles(ae_max_jfiles),
+ _jfsize_sblks(jfsize_sblks),
+ _sblk_size_dblks(JRNL_SBLK_SIZE),
+ _dblk_size(JRNL_DBLK_SIZE),
+ _wcache_pgsize_sblks(wcache_pgsize_sblks),
+ _wcache_num_pages(wcache_num_pages),
+ _rcache_pgsize_sblks(JRNL_RMGR_PAGE_SIZE),
+ _rcache_num_pages(JRNL_RMGR_PAGES),
+ _tm_ptr(std::localtime(&ts.tv_sec)),
+ _valid_flag(false),
+ _analyzed_flag(false),
+ _initial_owi(false)
+{
+ set_filename();
+}
+
+jinf::~jinf()
+{}
+
+void
+jinf::validate()
+{
+ bool err = false;
+ std::ostringstream oss;
+ if (_jver != RHM_JDAT_VERSION)
+ {
+ oss << "File \"" << _filename << "\": ";
+ oss << "RHM_JDAT_VERSION mismatch: found=" << (int)_jver;
+ oss << "; required=" << RHM_JDAT_VERSION << std::endl;
+ err = true;
+ }
+ if (_num_jfiles < JRNL_MIN_NUM_FILES)
+ {
+ oss << "File \"" << _filename << "\": ";
+ oss << "Number of journal files too small: found=" << _num_jfiles;
+ oss << "; minimum=" << JRNL_MIN_NUM_FILES << std::endl;
+ err = true;
+ }
+ if (_num_jfiles > JRNL_MAX_NUM_FILES)
+ {
+ oss << "File \"" << _filename << "\": ";
+ oss << "Number of journal files too large: found=" << _num_jfiles;
+ oss << "; maximum=" << JRNL_MAX_NUM_FILES << std::endl;
+ err = true;
+ }
+ if (_ae)
+ {
+ if (_ae_max_jfiles < _num_jfiles)
+ {
+ oss << "File \"" << _filename << "\": ";
+ oss << "Number of journal files exceeds auto-expansion limit: found=" << _num_jfiles;
+ oss << "; maximum=" << _ae_max_jfiles;
+ err = true;
+ }
+ if (_ae_max_jfiles > JRNL_MAX_NUM_FILES)
+ {
+ oss << "File \"" << _filename << "\": ";
+ oss << "Auto-expansion file limit too large: found=" << _ae_max_jfiles;
+ oss << "; maximum=" << JRNL_MAX_NUM_FILES;
+ err = true;
+ }
+ }
+ if (_jfsize_sblks < JRNL_MIN_FILE_SIZE)
+ {
+ oss << "File \"" << _filename << "\": ";
+ oss << "Journal file size too small: found=" << _jfsize_sblks;
+ oss << "; minimum=" << JRNL_MIN_FILE_SIZE << " (sblks)" << std::endl;
+ err = true;
+ }
+ if (_sblk_size_dblks != JRNL_SBLK_SIZE)
+ {
+ oss << "File \"" << _filename << "\": ";
+ oss << "JRNL_SBLK_SIZE mismatch: found=" << _sblk_size_dblks;
+ oss << "; required=" << JRNL_SBLK_SIZE << std::endl;
+ err = true;
+ }
+ if (_dblk_size != JRNL_DBLK_SIZE)
+ {
+ oss << "File \"" << _filename << "\": ";
+ oss << "JRNL_DBLK_SIZE mismatch: found=" << _dblk_size;
+ oss << "; required=" << JRNL_DBLK_SIZE << std::endl;
+ err = true;
+ }
+ if (err)
+ throw jexception(jerrno::JERR_JINF_CVALIDFAIL, oss.str(), "jinf", "validate");
+ _valid_flag = true;
+}
+
+void
+jinf::analyze()
+{
+ lp_map early_map; // map for all owi flags same as pfid 0
+ lp_map late_map; // map for all owi flags opposite to pfid 0
+ bool late_latch = false; // latch for owi switchover
+
+ if (!_valid_flag)
+ validate();
+ bool done = false;
+ for (u_int16_t pfid=0; pfid<_num_jfiles && !done; pfid++)
+ {
+ std::ostringstream oss;
+ if (_jdir.at(_jdir.size() - 1) == '/')
+ oss << _jdir << _base_filename << ".";
+ else
+ oss << _jdir << "/" << _base_filename << ".";
+ oss << std::setw(4) << std::setfill('0') << std::hex << pfid;
+ oss << "." << JRNL_DATA_EXTENSION;
+
+ // Check size of each file is consistent and expected
+ u_int32_t fsize = get_filesize(oss.str());
+ if (fsize != (_jfsize_sblks + 1) * _sblk_size_dblks * _dblk_size)
+ {
+ std::ostringstream oss1;
+ oss1 << "File \"" << oss.str() << "\": size=" << fsize << "; expected=" << ((_jfsize_sblks + 1) * _sblk_size_dblks * _dblk_size);
+ throw jexception(jerrno::JERR_JINF_BADFILESIZE, oss1.str(), "jinf", "analyze");
+ }
+
+ std::ifstream jifs(oss.str().c_str());
+ if (!jifs.good())
+ throw jexception(jerrno::JERR__FILEIO, oss.str(), "jinf", "analyze");
+ file_hdr fhdr;
+ jifs.read((char*)&fhdr, sizeof(fhdr));
+ if (fhdr._magic != RHM_JDAT_FILE_MAGIC) // No file header
+ {
+ if (fhdr._magic != 0)
+ throw jexception(jerrno::JERR_JINF_INVALIDFHDR, oss.str(), "jinf", "analyze");
+ if (!pfid) // pfid 0 == lid 0 cannot be empty
+ throw jexception(jerrno::JERR_JINF_JDATEMPTY, oss.str(), "jinf", "analyze");
+ _frot = true;
+ done = true;
+ }
+ else
+ {
+ assert(pfid == fhdr._pfid);
+ if (pfid == 0)
+ {
+ _initial_owi = fhdr.get_owi();
+ early_map.insert(fhdr._lfid, pfid);
+ }
+ else
+ {
+ if (_initial_owi == fhdr.get_owi())
+ {
+ early_map.insert(fhdr._lfid, pfid);
+ if (late_latch && (!_ae || _num_jfiles == JRNL_MIN_NUM_FILES))
+ throw jexception(jerrno::JERR_JINF_OWIBAD, oss.str(), "jinf", "analyze");
+ }
+ else
+ {
+ late_map.insert(fhdr._lfid, pfid);
+ late_latch = true;
+ }
+ }
+ }
+ jifs.close();
+ } // for (pfid)
+
+ // If this is not the first rotation, all files should be in either early or late maps
+ if (!_frot) assert(early_map.size() + late_map.size() == _num_jfiles);
+
+ _pfid_list.clear();
+ late_map.get_pfid_list(_pfid_list);
+ early_map.get_pfid_list(_pfid_list);
+
+ // Check OWI consistency
+// for (u_int16_t lfid=0; lfid<_num_jfiles && !done; lfid++)
+// {
+// throw jexception(jerrno::JERR_JINF_OWIBAD, oss.str(), "jinf", "analyze");
+// }
+
+ _analyzed_flag = true;
+}
+
+void
+jinf::write()
+{
+ std::ostringstream oss;
+ oss << _jdir << "/" << _base_filename << "." << JRNL_INFO_EXTENSION;
+ std::ofstream of(oss.str().c_str(), std::ofstream::out | std::ofstream::trunc);
+ if (!of.good())
+ throw jexception(jerrno::JERR__FILEIO, oss.str(), "jinf", "write");
+ of << xml_str();
+ of.close();
+}
+
+u_int16_t
+jinf::incr_num_jfiles()
+{
+ if (_num_jfiles >= JRNL_MAX_NUM_FILES)
+ throw jexception(jerrno::JERR_JINF_TOOMANYFILES, "jinf", "incr_num_jfiles");
+ return ++_num_jfiles;
+}
+
+u_int16_t
+jinf::get_first_pfid()
+{
+ if (!_analyzed_flag)
+ analyze();
+ return *_pfid_list.begin();
+}
+
+u_int16_t
+jinf::get_last_pfid()
+{
+ if (!_analyzed_flag)
+ analyze();
+ return *_pfid_list.rbegin();
+}
+
+jinf::pfid_list&
+jinf::get_pfid_list()
+{
+ if (!_analyzed_flag)
+ analyze();
+ return _pfid_list;
+}
+
+void
+jinf::get_normalized_pfid_list(pfid_list& pfid_list)
+{
+ if (!_analyzed_flag)
+ analyze();
+ pfid_list.clear();
+ u_int16_t s = _pfid_list.size();
+ u_int16_t iz = 0; // index of 0 value
+ while (_pfid_list[iz] && iz < s)
+ iz++;
+ assert(_pfid_list[iz] == 0);
+ for (u_int16_t i = iz; i < iz + s; i++)
+ pfid_list.push_back(_pfid_list[i % s]);
+ assert(pfid_list[0] == 0);
+ assert(pfid_list.size() == s);
+}
+
+bool
+jinf::get_initial_owi()
+{
+ if (!_analyzed_flag)
+ analyze();
+ return _initial_owi;
+}
+
+bool
+jinf::get_frot()
+{
+ if (!_analyzed_flag)
+ analyze();
+ return _frot;
+}
+
+std::string
+jinf::to_string() const
+{
+ std::ostringstream oss;
+ oss << std::setfill('0');
+ oss << "Journal ID \"" << _jid << "\" initialized " << (_tm_ptr->tm_year + 1900) << "/";
+ oss << std::setw(2) << (_tm_ptr->tm_mon + 1) << "/" << std::setw(2) << _tm_ptr->tm_mday << " ";
+ oss << std::setw(2) << _tm_ptr->tm_hour << ":" << std::setw(2) << _tm_ptr->tm_min << ":";
+ oss << std::setw(2) << _tm_ptr->tm_sec << "." << std::setw(9) << _ts.tv_nsec << ":" << std::endl;
+ oss << " Journal directory: \"" << _jdir << "\"" << std::endl;
+ oss << " Journal base filename: \"" << _base_filename << "\"" << std::endl;
+ oss << " Journal version: " << (unsigned)_jver << std::endl;
+ oss << " Number of journal files: " << _num_jfiles << std::endl;
+// TODO: Uncomment these lines when auto-expand is enabled.
+// oss << " Auto-expand mode: " << (_ae ? "enabled" : "disabled") << std::endl;
+// if (_ae) oss << " Max. number of journal files (in auto-expand mode): " << _ae_max_jfiles << std::endl;
+ oss << " Journal file size: " << _jfsize_sblks << " sblks" << std::endl;
+ oss << " Softblock size (JRNL_SBLK_SIZE): " << _sblk_size_dblks << " dblks" << std::endl;
+ oss << " Datablock size (JRNL_DBLK_SIZE): " << _dblk_size << " bytes" << std::endl;
+ oss << " Write page size: " << _wcache_pgsize_sblks << " sblks" << std::endl;
+ oss << " Number of write pages: " << _wcache_num_pages << std::endl;
+ oss << " Read page size (JRNL_RMGR_PAGE_SIZE): " << _rcache_pgsize_sblks << " sblks" << std::endl;
+ oss << " Number of read pages (JRNL_RMGR_PAGES): " << _rcache_num_pages << std::endl;
+ return oss.str();
+}
+
+std::string
+jinf::xml_str() const
+{
+ // TODO: This is *not* an XML writer, rather for simplicity, it uses literals. I'm sure a more elegant way can be
+ // found to do this using the real thing...
+
+ std::ostringstream oss;
+ oss << std::setfill('0');
+ oss << "<?xml version=\"1.0\" ?>" << std::endl;
+ oss << "<jrnl>" << std::endl;
+ oss << " <journal_version value=\"" << (unsigned)_jver << "\" />" << std::endl;
+ oss << " <journal_id>" << std::endl;
+ oss << " <id_string value=\"" << _jid << "\" />" << std::endl;
+ oss << " <directory value=\"" << _jdir << "\" />" << std::endl;
+ oss << " <base_filename value=\"" << _base_filename << "\" />" << std::endl;
+ oss << " </journal_id>" << std::endl;
+ oss << " <creation_time>" << std::endl;
+ oss << " <seconds value=\"" << _ts.tv_sec << "\" />" << std::endl;
+ oss << " <nanoseconds value=\"" << _ts.tv_nsec << "\" />" << std::endl;
+ oss << " <string value=\"" << (_tm_ptr->tm_year + 1900) << "/";
+ oss << std::setw(2) << (_tm_ptr->tm_mon + 1) << "/" << std::setw(2) << _tm_ptr->tm_mday << " ";
+ oss << std::setw(2) << _tm_ptr->tm_hour << ":" << std::setw(2) << _tm_ptr->tm_min << ":";
+ oss << std::setw(2) << _tm_ptr->tm_sec << "." << std::setw(9) << _ts.tv_nsec;
+ oss << "\" />" << std::endl;
+ oss << " </creation_time>" << std::endl;
+ oss << " <journal_file_geometry>" << std::endl;
+ oss << " <number_jrnl_files value=\"" << _num_jfiles << "\" />" << std::endl;
+ oss << " <auto_expand value=\"" << (_ae ? "true" : "false") << "\" />" << std::endl;
+ if (_ae) oss << " <auto_expand_max_jrnl_files value=\"" << _ae_max_jfiles << "\" />" << std::endl;
+ oss << " <jrnl_file_size_sblks value=\"" << _jfsize_sblks << "\" />" << std::endl;
+ oss << " <JRNL_SBLK_SIZE value=\"" << _sblk_size_dblks << "\" />" << std::endl;
+ oss << " <JRNL_DBLK_SIZE value=\"" << _dblk_size << "\" />" << std::endl;
+ oss << " </journal_file_geometry>" << std::endl;
+ oss << " <cache_geometry>" << std::endl;
+ oss << " <wcache_pgsize_sblks value=\"" << _wcache_pgsize_sblks << "\" />" << std::endl;
+ oss << " <wcache_num_pages value=\"" << _wcache_num_pages << "\" />" << std::endl;
+ oss << " <JRNL_RMGR_PAGE_SIZE value=\"" << _rcache_pgsize_sblks << "\" />" << std::endl;
+ oss << " <JRNL_RMGR_PAGES value=\"" << _rcache_num_pages << "\" />" << std::endl;
+ oss << " </cache_geometry>" << std::endl;
+ oss << "</jrnl>" << std::endl;
+ return oss.str();
+}
+
+void
+jinf::set_filename()
+{
+ std::ostringstream oss;
+ oss << _jdir << "/" << _base_filename << "." << JRNL_INFO_EXTENSION;
+ _filename = oss.str().c_str();
+}
+
+void
+jinf::read(const std::string& jinf_filename)
+{
+ // TODO: This is *not* an XML reader, rather for simplicity, it is a brute-force line reader which relies on string
+ // recognition. It relies on the format of xml_str() above; it will not handle a XML restructuring.
+ // *** Can it be replaced cheaply by a real XML reader? Should it be, or is this sufficient? ***
+
+ char buff[1024]; // limit of line input length
+ std::ifstream jinfs(jinf_filename.c_str());
+ if (!jinfs.good())
+ throw jexception(jerrno::JERR__FILEIO, jinf_filename.c_str(), "jinf", "read");
+ u_int32_t charcnt = 0;
+ while (jinfs.good())
+ {
+ jinfs.getline(buff, 1023);
+ charcnt += std::strlen(buff);
+ if (std::strstr(buff, "journal_version"))
+ _jver = u_int16_value(buff);
+ else if(std::strstr(buff, "id_string"))
+ string_value(_jid, buff);
+ else if(std::strstr(buff, "directory"))
+ string_value(_jdir, buff);
+ else if(std::strstr(buff, "base_filename"))
+ string_value(_base_filename, buff);
+ else if(std::strstr(buff, "number_jrnl_files"))
+ _num_jfiles = u_int16_value(buff);
+ else if(std::strstr(buff, "auto_expand_max_jrnl_files"))
+ _ae_max_jfiles = u_int16_value(buff);
+ else if(std::strstr(buff, "auto_expand"))
+ _ae = bool_value(buff);
+ else if(std::strstr(buff, "jrnl_file_size_sblks"))
+ _jfsize_sblks = u_int32_value(buff);
+ else if(std::strstr(buff, "JRNL_SBLK_SIZE"))
+ _sblk_size_dblks = u_int16_value(buff);
+ else if(std::strstr(buff, "JRNL_DBLK_SIZE"))
+ _dblk_size = u_int32_value(buff);
+ else if(std::strstr(buff, "wcache_pgsize_sblks"))
+ _wcache_pgsize_sblks = u_int32_value(buff);
+ else if(std::strstr(buff, "wcache_num_pages"))
+ _wcache_num_pages = u_int32_value(buff);
+ else if(std::strstr(buff, "JRNL_RMGR_PAGE_SIZE"))
+ _rcache_pgsize_sblks = u_int32_value(buff);
+ else if(std::strstr(buff, "JRNL_RMGR_PAGES"))
+ _rcache_num_pages = u_int32_value(buff);
+ else if(std::strstr(buff, "nanoseconds"))
+ _ts.tv_nsec = u_int32_value(buff);
+ else if(std::strstr(buff, "seconds"))
+ {
+ _ts.tv_sec = u_int32_value(buff);
+ _tm_ptr = std::localtime(&_ts.tv_sec);
+ }
+ }
+ jinfs.close();
+ if (charcnt == 0)
+ throw jexception(jerrno::JERR_JINF_ZEROLENFILE, jinf_filename.c_str(), "jinf", "read");
+}
+
+bool
+jinf::bool_value(char* line) const
+{
+ return std::strcmp(find_value(line), "true") == 0;
+}
+
+u_int16_t
+jinf::u_int16_value(char* line) const
+{
+ return std::atoi(find_value(line));
+}
+
+u_int32_t
+jinf::u_int32_value(char* line) const
+{
+ return std::atol(find_value(line));
+}
+
+std::string&
+jinf::string_value(std::string& str, char* line) const
+{
+ str.assign(find_value(line));
+ return str;
+}
+
+char*
+jinf::find_value(char* line) const
+{
+ const char* target1_str = "value=\"";
+ int target2_char = '\"';
+ char* t1 = std::strstr(line, target1_str);
+ if (t1 == 0)
+ {
+ std::ostringstream oss;
+ oss << "File \"" << _filename << "\": line=" << line;
+ throw jexception(jerrno::JERR_JINF_NOVALUESTR, oss.str(), "jinf", "find_value");
+ }
+ t1 += std::strlen(target1_str);
+
+ char* t2 = std::strchr(t1, target2_char);
+ if (t2 == 0)
+ {
+ std::ostringstream oss;
+ oss << "File \"" << _filename << "\": line=" << line;
+ throw jexception(jerrno::JERR_JINF_BADVALUESTR, oss.str(), "jinf", "find_value");
+ }
+ *t2 = '\0';
+ return t1;
+}
+
+u_int32_t
+jinf::get_filesize(const std::string& file_name) const
+{
+ struct stat s;
+ if (::stat(file_name.c_str(), &s))
+ {
+ std::ostringstream oss;
+ oss << "stat: file=\"" << file_name << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_JINF_STAT, oss.str(), "jinf", "get_filesize");
+ }
+ if (!S_ISREG(s.st_mode)) // not a regular file,
+ {
+ std::ostringstream oss;
+ oss << "File \"" << file_name << "\" is not a regular file: mode=0x" << std::hex << s.st_mode;
+ throw jexception(jerrno::JERR_JINF_NOTREGFILE, oss.str(), "jinf", "get_filesize");
+ }
+ return u_int32_t(s.st_size);
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/jinf.h b/cpp/src/qpid/legacystore/jrnl/jinf.h
new file mode 100644
index 0000000000..73f5386a19
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/jinf.h
@@ -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.
+ *
+ */
+
+/**
+ * \file jinf.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * This file contains the code for the mrg::journal::jinf class.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_JINF_H
+#define QPID_LEGACYSTORE_JRNL_JINF_H
+
+#include <ctime>
+#include <string>
+#include <sys/types.h>
+#include <vector>
+
+namespace mrg
+{
+namespace journal
+{
+ /**
+ * \class jinf
+ * \brief Class to handle the journal information file &lt;basename&gt;.jinf.
+ */
+ class jinf
+ {
+ public:
+ typedef std::vector<u_int16_t> pfid_list; // pfids
+ typedef pfid_list::const_iterator pfidl_citr;
+
+ private:
+ u_int8_t _jver;
+ std::string _jid;
+ std::string _jdir;
+ std::string _base_filename;
+ std::string _filename;
+ timespec _ts;
+ u_int16_t _num_jfiles;
+ bool _ae;
+ u_int32_t _ae_max_jfiles;
+ u_int32_t _jfsize_sblks;
+ u_int16_t _sblk_size_dblks;
+ u_int32_t _dblk_size;
+ u_int32_t _wcache_pgsize_sblks;
+ u_int16_t _wcache_num_pages;
+ u_int32_t _rcache_pgsize_sblks;
+ u_int16_t _rcache_num_pages;
+ std::tm* _tm_ptr;
+ bool _valid_flag;
+ bool _analyzed_flag;
+ pfid_list _pfid_list;
+ bool _initial_owi;
+ bool _frot;
+
+ public:
+ // constructor for reading existing jinf file
+ jinf(const std::string& jinf_filename, bool validate_flag);
+ // constructor for writing jinf file
+ jinf(const std::string& jid, const std::string& jdir, const std::string& base_filename,
+ const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles,
+ const u_int32_t jfsize_sblks, const u_int32_t wcache_pgsize_sblks, const u_int16_t wcache_num_pages,
+ const timespec& ts);
+ virtual ~jinf();
+
+ void validate();
+ void analyze();
+ void write();
+
+ inline u_int8_t jver() const { return _jver; }
+ inline const std::string& jid() const { return _jid; }
+ inline const std::string& jdir() const { return _jdir; }
+ inline void set_jdir(const std::string& jdir) { _jdir = jdir; }
+ inline const std::string& base_filename() const { return _base_filename; }
+ inline const timespec& ts() const { return _ts; }
+ inline u_int16_t num_jfiles() const { return _num_jfiles; }
+ u_int16_t incr_num_jfiles();
+ inline bool is_ae() const { return _ae; }
+ inline u_int16_t ae_max_jfiles() const { return _ae_max_jfiles; }
+ inline u_int32_t jfsize_sblks() const { return _jfsize_sblks; }
+ inline u_int16_t sblk_size_dblks() const { return _sblk_size_dblks; }
+ inline u_int32_t dblk_size() const { return _dblk_size; }
+ inline u_int32_t wcache_pgsize_sblks() const { return _wcache_pgsize_sblks; }
+ inline u_int16_t wcache_num_pages() const { return _wcache_num_pages; }
+ inline u_int32_t rcache_pgsize_sblks() const { return _rcache_pgsize_sblks; }
+ inline u_int16_t rcache_num_pages() const { return _rcache_num_pages; }
+ u_int16_t get_first_pfid();
+ u_int16_t get_last_pfid();
+ pfid_list& get_pfid_list();
+ void get_normalized_pfid_list(pfid_list& pfid_list);
+ bool get_initial_owi();
+ bool get_frot();
+
+ std::string to_string() const;
+ std::string xml_str() const;
+
+ private:
+ void set_filename();
+ void read(const std::string& jinf_filename);
+ bool bool_value(char* line) const;
+ u_int16_t u_int16_value(char* line) const;
+ u_int32_t u_int32_value(char* line) const;
+ std::string& string_value(std::string& str, char* line) const;
+ char* find_value(char* line) const;
+ u_int32_t get_filesize(const std::string& file_name) const;
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_JINF_H
diff --git a/cpp/src/qpid/legacystore/jrnl/jrec.cpp b/cpp/src/qpid/legacystore/jrnl/jrec.cpp
new file mode 100644
index 0000000000..61b9b6cc9b
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/jrec.cpp
@@ -0,0 +1,119 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file jrec.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing source code for class mrg::journal::jrec (abstract journal
+ * jrecord). See comments in file jrec.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/jrec.h"
+
+#include <iomanip>
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+#include <sstream>
+
+namespace mrg
+{
+namespace journal
+{
+
+jrec::jrec() {}
+jrec::~jrec() {}
+
+void
+jrec::chk_hdr(const rec_hdr& hdr)
+{
+ if (hdr._magic == 0)
+ {
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0');
+ oss << "enq magic NULL: rid=0x" << hdr._rid;
+ throw jexception(jerrno::JERR_JREC_BADRECHDR, oss.str(), "jrec", "chk_hdr");
+ }
+ if (hdr._version != RHM_JDAT_VERSION)
+ {
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0');
+ oss << "version: rid=0x" << hdr._rid;
+ oss << ": expected=0x" << std::setw(2) << (int)RHM_JDAT_VERSION;
+ oss << " read=0x" << std::setw(2) << (int)hdr._version;
+ throw jexception(jerrno::JERR_JREC_BADRECHDR, oss.str(), "jrec", "chk_hdr");
+ }
+#if defined (JRNL_LITTLE_ENDIAN)
+ u_int8_t endian_flag = RHM_LENDIAN_FLAG;
+#else
+ u_int8_t endian_flag = RHM_BENDIAN_FLAG;
+#endif
+ if (hdr._eflag != endian_flag)
+ {
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0');
+ oss << "endian_flag: rid=" << hdr._rid;
+ oss << ": expected=0x" << std::setw(2) << (int)endian_flag;
+ oss << " read=0x" << std::setw(2) << (int)hdr._eflag;
+ throw jexception(jerrno::JERR_JREC_BADRECHDR, oss.str(), "jrec", "chk_hdr");
+ }
+}
+
+void
+jrec::chk_rid(const rec_hdr& hdr, const u_int64_t rid)
+{
+ if (hdr._rid != rid)
+ {
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0');
+ oss << "rid mismatch: expected=0x" << rid;
+ oss << " read=0x" << hdr._rid;
+ throw jexception(jerrno::JERR_JREC_BADRECHDR, oss.str(), "jrec", "chk_hdr");
+ }
+}
+
+void
+jrec::chk_tail(const rec_tail& tail, const rec_hdr& hdr)
+{
+ if (tail._xmagic != ~hdr._magic)
+ {
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0');
+ oss << "magic: rid=0x" << hdr._rid;
+ oss << ": expected=0x" << ~hdr._magic;
+ oss << " read=0x" << tail._xmagic;
+ throw jexception(jerrno::JERR_JREC_BADRECTAIL, oss.str(), "jrec", "chk_tail");
+ }
+ if (tail._rid != hdr._rid)
+ {
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0');
+ oss << "rid: rid=0x" << hdr._rid;
+ oss << ": read=0x" << tail._rid;
+ throw jexception(jerrno::JERR_JREC_BADRECTAIL, oss.str(), "jrec", "chk_tail");
+ }
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/jrec.h b/cpp/src/qpid/legacystore/jrnl/jrec.h
new file mode 100644
index 0000000000..9d0771cabd
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/jrec.h
@@ -0,0 +1,183 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file jrec.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing source code for class mrg::journal::jrec (abstract journal
+ * jrecord). See class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_JREC_H
+#define QPID_LEGACYSTORE_JRNL_JREC_H
+
+namespace mrg
+{
+namespace journal
+{
+class jrec;
+}
+}
+
+#include <cstddef>
+#include <fstream>
+#include "qpid/legacystore/jrnl/rec_hdr.h"
+#include "qpid/legacystore/jrnl/rec_tail.h"
+#include <string>
+#include <sys/types.h>
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \class jrec
+ * \brief Abstract class for all file jrecords, both data and log. This class establishes
+ * the common data format and structure for these jrecords.
+ */
+ class jrec
+ {
+ public:
+ jrec();
+ virtual ~jrec();
+
+ /**
+ * \brief Encode this instance of jrec into the write buffer at the disk-block-aligned
+ * pointer wptr starting at position rec_offs_dblks in the encoded record to a
+ * maximum size of max_size_dblks.
+ *
+ * This call encodes the content of the data contianed in this instance of jrec into a
+ * disk-softblock-aligned (defined by JRNL_SBLK_SIZE) buffer pointed to by parameter
+ * wptr. No more than paramter max_size_dblks data-blocks may be written to the buffer.
+ * The parameter rec_offs_dblks is the offset in data-blocks within the fully encoded
+ * data block this instance represents at which to start encoding.
+ *
+ * Encoding entails writing the record header (struct enq_hdr), the data and the record tail
+ * (struct enq_tail). The record must be data-block-aligned (defined by JRNL_DBLK_SIZE),
+ * thus any remaining space in the final data-block is ignored; the returned value is the
+ * number of data-blocks consumed from the page by the encode action. Provided the initial
+ * alignment requirements are met, records may be of arbitrary size and may span multiple
+ * data-blocks, disk-blocks and/or pages.
+ *
+ * Since the record size in data-blocks is known, the general usage pattern is to call
+ * encode() as many times as is needed to fully encode the data. Each call to encode()
+ * will encode as much of the record as it can to what remains of the current page cache,
+ * and will return the number of data-blocks actually encoded.
+ *
+ * <b>Example:</b> Assume that record r1 was previously written to page 0, and that this
+ * is an instance representing record r2. Being larger than the page size ps, r2 would span
+ * multiple pages as follows:
+ * <pre>
+ * |<---ps--->|
+ * +----------+----------+----------+----...
+ * | |r2a| r2b | r2c | |
+ * |<-r1-><----------r2----------> |
+ * +----------+----------+----------+----...
+ * page: p0 p1 p2
+ * </pre>
+ * Encoding record r2 will require multiple calls to encode; one for each page which
+ * is involved. Record r2 is divided logically into sections r2a, r2b and r2c at the
+ * points where the page boundaries intersect with the record. Assuming a page size
+ * of ps, the page boundary pointers are represented by their names p0, p1... and the
+ * sizes of the record segments are represented by their names r1, r2a, r2b..., the calls
+ * should be as follows:
+ * <pre>
+ * encode(p0+r1, 0, ps-r1); (returns r2a data-blocks)
+ * encode(p1, r2a, ps); (returns r2b data-blocks which equals ps)
+ * encode(p2, r2a+r2b, ps); (returns r2c data-blocks)
+ * </pre>
+ *
+ * \param wptr Data-block-aligned pointer to position in page buffer where encoding is to
+ * take place.
+ * \param rec_offs_dblks Offset in data-blocks within record from which to start encoding.
+ * \param max_size_dblks Maximum number of data-blocks to write to pointer wptr.
+ * \returns Number of data-blocks encoded.
+ */
+ virtual u_int32_t encode(void* wptr, u_int32_t rec_offs_dblks,
+ u_int32_t max_size_dblks) = 0;
+
+ /**
+ * \brief Decode into this instance of jrec from the read buffer at the disk-block-aligned
+ * pointer rptr starting at position jrec_offs_dblks in the encoded record to a
+ * maximum size of max_size_blks.
+ *
+ * This call decodes a record in the page buffer pointed to by the data-block-aligned
+ * (defined by JRNL_DBLK_SIZE) parameter rptr into this instance of jrec. No more than
+ * paramter max_size_dblks data-blocks may be read from the buffer. The parameter
+ * jrec_offs_dblks is the offset in data-blocks within the encoded record at which to start
+ * decoding.
+ *
+ * Decoding entails reading the record header, the data and the tail. The record is
+ * data-block-aligned (defined by JRNL_DBLK_SIZE); the returned value is the number of
+ * data-blocks read from the buffer by the decode action. As the record data size is only
+ * known once the header is read, the number of calls required to complete reading the
+ * record will depend on the vlaues within this instance which are set when the
+ * header is decoded.
+ *
+ * A non-zero value for jrec_offs_dblks implies that this is not the first call to
+ * decode and the record data will be appended at this offset.
+ *
+ * \param h Reference to instance of struct hdr, already read from page buffer and used
+ * to determine record type
+ * \param rptr Data-block-aligned pointer to position in page buffer where decoding is to
+ * begin.
+ * \param rec_offs_dblks Offset within record from which to start appending the decoded
+ * record.
+ * \param max_size_dblks Maximum number of data-blocks to read from pointer rptr.
+ * \returns Number of data-blocks read (consumed).
+ */
+ virtual u_int32_t decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks,
+ u_int32_t max_size_dblks) = 0;
+
+ virtual bool rcv_decode(rec_hdr h, std::ifstream* ifsp, std::size_t& rec_offs) = 0;
+
+ virtual std::string& str(std::string& str) const = 0;
+ virtual std::size_t data_size() const = 0;
+ virtual std::size_t xid_size() const = 0;
+ virtual std::size_t rec_size() const = 0;
+ inline virtual u_int32_t rec_size_dblks() const { return size_dblks(rec_size()); }
+ static inline u_int32_t size_dblks(const std::size_t size)
+ { return size_blks(size, JRNL_DBLK_SIZE); }
+ static inline u_int32_t size_sblks(const std::size_t size)
+ { return size_blks(size, JRNL_DBLK_SIZE * JRNL_SBLK_SIZE); }
+ static inline u_int32_t size_blks(const std::size_t size, const std::size_t blksize)
+ { return (size + blksize - 1)/blksize; }
+ virtual u_int64_t rid() const = 0;
+
+ protected:
+ virtual void chk_hdr() const = 0;
+ virtual void chk_hdr(u_int64_t rid) const = 0;
+ virtual void chk_tail() const = 0;
+ static void chk_hdr(const rec_hdr& hdr);
+ static void chk_rid(const rec_hdr& hdr, u_int64_t rid);
+ static void chk_tail(const rec_tail& tail, const rec_hdr& hdr);
+ virtual void clean() = 0;
+ }; // class jrec
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_JREC_H
diff --git a/cpp/src/qpid/legacystore/jrnl/lp_map.cpp b/cpp/src/qpid/legacystore/jrnl/lp_map.cpp
new file mode 100644
index 0000000000..8024ddadd2
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/lp_map.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.
+ *
+ */
+
+/**
+ * \file lp_map.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::lp_map (logical file map). See
+ * comments in file lp_map.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/lp_map.h"
+
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+#include <sstream>
+
+namespace mrg
+{
+namespace journal
+{
+lp_map::lp_map() : _map() {}
+lp_map::~lp_map() {}
+
+void
+lp_map::insert(u_int16_t lfid, u_int16_t pfid)
+{
+ lfpair ip = lfpair(lfid, pfid);
+ lfret ret = _map.insert(ip);
+ if (ret.second == false)
+ {
+ std::ostringstream oss;
+ oss << std::hex << "lfid=0x" << lfid << " pfid=0x" << pfid;
+ throw jexception(jerrno::JERR_MAP_DUPLICATE, oss.str(), "lp_map", "insert");
+ }
+}
+
+void
+lp_map::get_pfid_list(std::vector<u_int16_t>& pfid_list)
+{
+ for (lp_map_citr_t i = _map.begin(); i != _map.end(); i++)
+ pfid_list.push_back(i->second);
+}
+
+// debug aid
+std::string
+lp_map::to_string()
+{
+ std::ostringstream oss;
+ oss << "{lfid:pfid ";
+ for (lp_map_citr_t i=_map.begin(); i!=_map.end(); i++)
+ {
+ if (i != _map.begin()) oss << ", ";
+ oss << (*i).first << ":" << (*i).second;
+ }
+ oss << "}";
+ return oss.str();
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/lp_map.h b/cpp/src/qpid/legacystore/jrnl/lp_map.h
new file mode 100644
index 0000000000..c43cbc0173
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/lp_map.h
@@ -0,0 +1,83 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file lp_map.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::lp_map (logical file map).
+ * See class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_LP_MAP_H
+#define QPID_LEGACYSTORE_JRNL_LP_MAP_H
+
+#include <map>
+#include <string>
+#include <sys/types.h>
+#include <vector>
+
+namespace mrg
+{
+namespace journal
+{
+ /**
+ * \class lp_map
+ * \brief Maps the logical file id (lfid) to the physical file id (pfid) in the journal.
+ *
+ * NOTE: NOT THREAD SAFE
+ */
+ class lp_map
+ {
+ public:
+ typedef std::map<u_int16_t, u_int16_t> lp_map_t;
+ typedef lp_map_t::const_iterator lp_map_citr_t;
+ typedef lp_map_t::const_reverse_iterator lp_map_critr_t;
+
+ private:
+ typedef std::pair<u_int16_t, u_int16_t> lfpair;
+ typedef std::pair<lp_map_t::iterator, bool> lfret;
+ lp_map_t _map;
+
+ public:
+ lp_map();
+ virtual ~lp_map();
+
+ void insert(u_int16_t lfid, u_int16_t pfid);
+ inline u_int16_t size() const { return u_int16_t(_map.size()); }
+ inline bool empty() const { return _map.empty(); }
+ inline lp_map_citr_t begin() { return _map.begin(); }
+ inline lp_map_citr_t end() { return _map.end(); }
+ inline lp_map_critr_t rbegin() { return _map.rbegin(); }
+ inline lp_map_critr_t rend() { return _map.rend(); }
+ void get_pfid_list(std::vector<u_int16_t>& pfid_list);
+
+ // debug aid
+ std::string to_string();
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_LP_MAP_H
diff --git a/cpp/src/qpid/legacystore/jrnl/lpmgr.cpp b/cpp/src/qpid/legacystore/jrnl/lpmgr.cpp
new file mode 100644
index 0000000000..d7b0c9f516
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/lpmgr.cpp
@@ -0,0 +1,226 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file lpmgr.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::lpmgr (non-logging file
+ * handle), used for controlling journal log files. See comments in file
+ * lpmgr.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/lpmgr.h"
+
+#include <cassert>
+#include <qpid/legacystore/jrnl/jerrno.h>
+#include <qpid/legacystore/jrnl/jexception.h>
+
+namespace mrg
+{
+namespace journal
+{
+
+lpmgr::lpmgr() : _ae(false), _ae_max_jfiles(0)
+{}
+
+lpmgr::~lpmgr()
+{
+ finalize();
+}
+
+void
+lpmgr::initialize(const u_int16_t num_jfiles,
+ const bool ae,
+ const u_int16_t ae_max_jfiles,
+ jcntl* const jcp,
+ new_obj_fn_ptr fp)
+{
+ assert(jcp != 0);
+ finalize();
+
+ // Validate params
+ if (ae && ae_max_jfiles > 0 && ae_max_jfiles <= num_jfiles)
+ {
+ std::ostringstream oss;
+ oss << "ae_max_jfiles (" << ae_max_jfiles << ") <= num_jfiles (" << num_jfiles << ")";
+ throw jexception(jerrno::JERR_LFMGR_BADAEFNUMLIM, oss.str(), "lpmgr", "initialize");
+ }
+ _ae = ae;
+ _ae_max_jfiles = ae_max_jfiles;
+
+ const std::size_t num_res_files = ae
+ ? (ae_max_jfiles ? ae_max_jfiles : JRNL_MAX_NUM_FILES)
+ : num_jfiles;
+ _fcntl_arr.reserve(num_res_files);
+ append(jcp, fp, num_jfiles);
+}
+
+void
+lpmgr::recover(const rcvdat& rd,
+ jcntl* const jcp,
+ new_obj_fn_ptr fp)
+{
+ assert(jcp != 0);
+ finalize();
+
+ // Validate rd params
+ if (rd._aemjf > 0 && rd._aemjf <= rd._njf)
+ {
+ std::ostringstream oss;
+ oss << "ae_max_jfiles (" << rd._aemjf << ") <= num_jfiles (" << rd._njf << ")";
+ throw jexception(jerrno::JERR_LFMGR_BADAEFNUMLIM, oss.str(), "lpmgr", "recover");
+ }
+ _ae = rd._ae;
+ _ae_max_jfiles = rd._aemjf;
+
+ const std::size_t num_res_files = rd._ae
+ ? (rd._aemjf ? rd._aemjf : JRNL_MAX_NUM_FILES)
+ : rd._njf;
+ _fcntl_arr.reserve(num_res_files);
+ _fcntl_arr.assign(rd._njf, 0);
+ std::vector<u_int16_t> lfid_list(rd._fid_list.size(), 0);
+ for (std::size_t lid = 0; lid < rd._fid_list.size(); lid++)
+ lfid_list[rd._fid_list[lid]] = lid;
+ // NOTE: rd._fid_list may be smaller than rd._njf (journal may be empty or not yet file-cycled)
+ for (std::size_t pfid = 0; pfid < rd._njf; pfid++)
+ if (pfid < rd._fid_list.size())
+ _fcntl_arr[lfid_list[pfid]] = fp(jcp, lfid_list[pfid], pfid, &rd);
+ else
+ _fcntl_arr[pfid] = fp(jcp, pfid, pfid, &rd);
+}
+
+void
+lpmgr::insert(const u_int16_t after_lfid,
+ jcntl* const jcp,
+ new_obj_fn_ptr fp,
+ const u_int16_t num_jfiles)
+{
+ assert(jcp != 0);
+ assert(after_lfid < _fcntl_arr.size());
+ if (!_ae) throw jexception(jerrno::JERR_LFMGR_AEDISABLED, "lpmgr", "insert");
+ if (num_jfiles == 0) return;
+ std::size_t pfid = _fcntl_arr.size();
+ const u_int16_t eff_ae_max_jfiles = _ae_max_jfiles ? _ae_max_jfiles : JRNL_MAX_NUM_FILES;
+ if (pfid + num_jfiles > eff_ae_max_jfiles)
+ {
+ std::ostringstream oss;
+ oss << "num_files=" << pfid << " incr=" << num_jfiles << " limit=" << _ae_max_jfiles;
+ throw jexception(jerrno::JERR_LFMGR_AEFNUMLIMIT, oss.str(), "lpmgr", "insert");
+ }
+ for (std::size_t lid = after_lfid + 1; lid <= after_lfid + num_jfiles; lid++, pfid++)
+ _fcntl_arr.insert(_fcntl_arr.begin() + lid, fp(jcp, lid, pfid, 0));
+ for (std::size_t lid = after_lfid + num_jfiles + 1; lid < _fcntl_arr.size(); lid++)
+ {
+ fcntl* p = _fcntl_arr[lid];
+ assert(p != 0);
+ p->set_lfid(p->lfid() + num_jfiles);
+ }
+}
+
+void
+lpmgr::finalize()
+{
+ for (u_int32_t i = 0; i < _fcntl_arr.size(); i++)
+ delete _fcntl_arr[i];
+ _fcntl_arr.clear();
+ _ae = false;
+ _ae_max_jfiles = 0;
+}
+
+void
+lpmgr::set_ae(const bool ae)
+{
+ if (ae && _ae_max_jfiles > 0 && _ae_max_jfiles <= _fcntl_arr.size())
+ {
+ std::ostringstream oss;
+ oss << "ae_max_jfiles (" << _ae_max_jfiles << ") <= _fcntl_arr.size (" << _fcntl_arr.size() << ")";
+ throw jexception(jerrno::JERR_LFMGR_BADAEFNUMLIM, oss.str(), "lpmgr", "set_ae");
+ }
+ if (ae && _fcntl_arr.max_size() < _ae_max_jfiles)
+ _fcntl_arr.reserve(_ae_max_jfiles ? _ae_max_jfiles : JRNL_MAX_NUM_FILES);
+ _ae = ae;
+}
+
+void
+lpmgr::set_ae_max_jfiles(const u_int16_t ae_max_jfiles)
+{
+ if (_ae && ae_max_jfiles > 0 && ae_max_jfiles <= _fcntl_arr.size())
+ {
+ std::ostringstream oss;
+ oss << "ae_max_jfiles (" << _ae_max_jfiles << ") <= _fcntl_arr.size() (" << _fcntl_arr.size() << ")";
+ throw jexception(jerrno::JERR_LFMGR_BADAEFNUMLIM, oss.str(), "lpmgr", "set_ae_max_jfiles");
+ }
+ if (_ae && _fcntl_arr.max_size() < ae_max_jfiles)
+ _fcntl_arr.reserve(ae_max_jfiles ? ae_max_jfiles : JRNL_MAX_NUM_FILES);
+ _ae_max_jfiles = ae_max_jfiles;
+}
+
+u_int16_t
+lpmgr::ae_jfiles_rem() const
+{
+ if (_ae_max_jfiles > _fcntl_arr.size()) return _ae_max_jfiles - _fcntl_arr.size();
+ if (_ae_max_jfiles == 0) return JRNL_MAX_NUM_FILES - _fcntl_arr.size();
+ return 0;
+}
+
+// Testing functions
+
+void
+lpmgr::get_pfid_list(std::vector<u_int16_t>& pfid_list) const
+{
+ pfid_list.clear();
+ for (std::size_t i = 0; i < _fcntl_arr.size(); i++)
+ pfid_list.push_back(_fcntl_arr[i]->pfid());
+}
+
+void
+lpmgr::get_lfid_list(std::vector<u_int16_t>& lfid_list) const
+{
+ lfid_list.clear();
+ lfid_list.assign(_fcntl_arr.size(), 0);
+ for (std::size_t i = 0; i < _fcntl_arr.size(); i++)
+ lfid_list[_fcntl_arr[i]->pfid()] = i;
+}
+
+// === protected fns ===
+
+void
+lpmgr::append(jcntl* const jcp,
+ new_obj_fn_ptr fp,
+ const u_int16_t num_jfiles)
+{
+ std::size_t s = _fcntl_arr.size();
+ if (_ae_max_jfiles && s + num_jfiles > _ae_max_jfiles)
+ {
+ std::ostringstream oss;
+ oss << "num_files=" << s << " incr=" << num_jfiles << " limit=" << _ae_max_jfiles;
+ throw jexception(jerrno::JERR_LFMGR_AEFNUMLIMIT, oss.str(), "lpmgr", "append");
+ }
+ for (std::size_t i = s; i < s + num_jfiles; i++)
+ _fcntl_arr.push_back(fp(jcp, i, i, 0));
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/lpmgr.h b/cpp/src/qpid/legacystore/jrnl/lpmgr.h
new file mode 100644
index 0000000000..be5c4494cc
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/lpmgr.h
@@ -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.
+ *
+ */
+
+/**
+ * \file lpmgr.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * Class mrg::journal::lpmgr. See class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_LPMGR_H
+#define QPID_LEGACYSTORE_JRNL_LPMGR_H
+
+namespace mrg
+{
+namespace journal
+{
+ class jcntl;
+ class lpmgr;
+}
+}
+
+#include "qpid/legacystore/jrnl/fcntl.h"
+#include <vector>
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \brief LFID-PFID manager. This class maps the logical file id (lfid) to the physical file id (pfid) so that files
+ * may be inserted into the file ring buffer in (nearly) arbitrary logical locations while the physical ids continue
+ * to be appended. NOTE: NOT THREAD SAFE.
+ *
+ * The entire functionality of the LFID-PFID manager is to maintain an array of pointers to fcntl objects which have
+ * a one-to-one relationship to the physical %journal files. The logical file id (lfid) is used as an index to the
+ * array to read the mapped physical file id (pfid). By altering the order of these pointers within the array, the
+ * mapping of logical to physical files may be altered. This can be used to allow for the logical insertion of
+ * %journal files into a ring buffer, even though the physical file ids must be appended to those that preceded them.
+ *
+ * Since the insert() operation uses after-lfid as its position parameter, it is not possible to insert before lfid
+ * 0 - i.e. It is only possible to insert after an existing lfid. Consequently, lfid 0 and pfid 0 are always
+ * coincident in a %journal. Note, however, that inserting before lfid 0 is logically equivilent to inserting after
+ * the last lfid.
+ *
+ * When one or more files are inserted after a particular lfid, the lfids of the following files are incremented. The
+ * pfids of the inserted files follow those of all existing files, thus leading to a lfid-pfid discreppancy (ie no
+ * longer a one-to-one mapping):
+ *
+ * Example: Before insertion, %journal file headers would look as follows:
+ * <pre>
+ * Logical view (sorted by lfid): Physical view (sorted by pfid):
+ * +---+---+---+---+---+---+ +---+---+---+---+---+---+
+ * pfid --> | 0 | 1 | 2 | 3 | 4 | 5 | pfid --> | 0 | 1 | 2 | 3 | 4 | 5 |
+ * lfid --> | 0 | 1 | 2 | 3 | 4 | 5 | lfid --> | 0 | 1 | 2 | 3 | 4 | 5 |
+ * +---+---+---+---+---+---+ +---+---+---+---+---+---+
+ * </pre>
+ *
+ * After insertion of 2 files after lid 2 (marked with *s):
+ * <pre>
+ * Logical view (sorted by lfid): Physical view (sorted by pfid):
+ * +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+
+ * pfid --> | 0 | 1 | 2 |*6*|*7*| 3 | 4 | 5 | pfid --> | 0 | 1 | 2 | 3 | 4 | 5 |*6*|*7*|
+ * lfid --> | 0 | 1 | 2 |*3*|*4*| 5 | 6 | 7 | lfid --> | 0 | 1 | 2 | 5 | 6 | 7 |*3*|*4*|
+ * +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+
+ * </pre>
+ *
+ * The insert() function updates the internal map immediately, but the physical files (which have both the pfid and
+ * lfid written into the file header) are only updated as they are overwritten in the normal course of enqueueing
+ * and dequeueing messages. If the %journal should fail after insertion but before the files following those inserted
+ * are overwritten, then duplicate lfids will be present (though no duplicate pfids are possible). The overwrite
+ * indicator (owi) flag and the pfid numbers may be used to resolve the ambiguity and determine the logically earlier
+ * lfid in this case.
+ *
+ * Example: Before insertion, the current active write file being lfid/pfid 2 as determined by the owi flag, %journal
+ * file headers would look as follows:
+ * <pre>
+ * Logical view (sorted by lfid): Physical view (sorted by pfid):
+ * +---+---+---+---+---+---+ +---+---+---+---+---+---+
+ * pfid --> | 0 | 1 | 2 | 3 | 4 | 5 | pfid --> | 0 | 1 | 2 | 3 | 4 | 5 |
+ * lfid --> | 0 | 1 | 2 | 3 | 4 | 5 | lfid --> | 0 | 1 | 2 | 3 | 4 | 5 |
+ * owi --> | t | t | t | f | f | f | owi --> | t | t | t | f | f | f |
+ * +---+---+---+---+---+---+ +---+---+---+---+---+---+
+ * </pre>
+ *
+ * After inserting 2 files after lfid 2 and then 3 (the newly inserted file) - marked with *s:
+ * <pre>
+ * Logical view (sorted by lfid): Physical view (sorted by pfid):
+ * +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+
+ * pfid --> | 0 | 1 | 2 |*6*|*7*| 3 | 4 | 5 | pfid --> | 0 | 1 | 2 | 3 | 4 | 5 |*3*|*4*|
+ * lfid --> | 0 | 1 | 2 |*3*|*4*| 3 | 4 | 5 | lfid --> | 0 | 1 | 2 | 3 | 4 | 5 |*3*|*4*|
+ * owi --> | t | t | t | t | t | f | f | f | owi --> | t | t | t | f | f | f | t | t |
+ * +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+
+ * </pre>
+ *
+ * If a broker failure occurs at this point, then there are two independent tests that may be made to resolve
+ * duplicate lfids during recovery in such cases:
+ * <ol>
+ * <li>The correct lfid has owi flag that matches that of pfid/lfid 0</li>
+ * <li>The most recently inserted (hence correct) lfid has pfids that are higher than the duplicate that was not
+ * overwritten</li>
+ * </ol>
+ *
+ * NOTE: NOT THREAD SAFE. Provide external thread protection if used in multi-threaded environments.
+ */
+ class lpmgr
+ {
+ public:
+ /**
+ * \brief Function pointer to function that will create a new fcntl object and return its pointer.
+ *
+ * \param jcp Pointer to jcntl instance from which journal file details will be obtained.
+ * \param lfid Logical file ID for new fcntl instance.
+ * \param pfid Physical file ID for file associated with new fcntl instance.
+ * \param rdp Pointer to rcvdat instance which conatins recovery information for new fcntl instance when
+ * recovering an existing file, or null if a new file is to be created.
+ */
+ typedef fcntl* (new_obj_fn_ptr)(jcntl* const jcp,
+ const u_int16_t lfid,
+ const u_int16_t pfid,
+ const rcvdat* const rdp);
+
+ private:
+ bool _ae; ///< Auto-expand mode
+ u_int16_t _ae_max_jfiles; ///< Max file count for auto-expansion; 0 = no limit
+ std::vector<fcntl*> _fcntl_arr; ///< Array of pointers to fcntl objects
+
+ public:
+ lpmgr();
+ virtual ~lpmgr();
+
+ /**
+ * \brief Initialize from scratch for a known number of %journal files. All lfid values are identical to pfid
+ * values (which is normal before any inserts have occurred).
+ *
+ * \param num_jfiles Number of files to be created, and consequently the number of fcntl objects in array
+ * _fcntl_arr.
+ * \param ae If true, allows auto-expansion; if false, disables auto-expansion.
+ * \param ae_max_jfiles The maximum number of files allowed for auto-expansion. Cannot be lower than the current
+ * number of files. However, a zero value disables the limit checks, and allows unlimited
+ * expansion.
+ * \param jcp Pointer to jcntl instance. This is used to find the file path and base filename so that
+ * new files may be created.
+ * \param fp Pointer to function which creates and returns a pointer to a new fcntl object (and hence
+ * causes a new %journal file to be created).
+ */
+ void initialize(const u_int16_t num_jfiles,
+ const bool ae,
+ const u_int16_t ae_max_jfiles,
+ jcntl* const jcp,
+ new_obj_fn_ptr fp);
+
+ /**
+ * \brief Initialize from a known lfid-pfid map pfid_list (within rcvdat param rd), which is usually obtained
+ * from a recover. The index of pfid_list is the logical file id (lfid); the value contained in the vector is
+ * the physical file id (pfid).
+ *
+ * \param rd Ref to rcvdat struct which contains recovery data and the pfid_list.
+ * \param jcp Pointer to jcntl instance. This is used to find the file path and base filename so that
+ * new files may be created.
+ * \param fp Pointer to function which creates and returns a pointer to a new fcntl object (and hence
+ * causes a new %journal file to be created).
+ */
+ void recover(const rcvdat& rd,
+ jcntl* const jcp,
+ new_obj_fn_ptr fp);
+
+ /**
+ * \brief Insert num_jfiles files after lfid index after_lfid. This causes all lfids after after_lfid to be
+ * increased by num_jfiles.
+ *
+ * Note that it is not possible to insert <i>before</i> lfid 0, and thus lfid 0 should always point to pfid 0.
+ * Inserting before lfid 0 is logically equivilent to inserting after the last lfid in a circular buffer.
+ *
+ * \param after_lfid Lid index after which to insert file(s).
+ * \param jcp Pointer to jcntl instance. This is used to find the file path and base filename so that
+ * new files may be created.
+ * \param fp Pointer to function which creates and returns a pointer to a new fcntl object (and hence
+ * causes a new %journal file to be created).
+ * \param num_jfiles The number of files by which to increase.
+ */
+ void insert(const u_int16_t after_lfid,
+ jcntl* const jcp,
+ new_obj_fn_ptr fp,
+ const u_int16_t num_jfiles = 1);
+
+ /**
+ * \brief Clears _fcntl_arr and deletes all fcntl instances.
+ */
+ void finalize();
+
+ /**
+ * \brief Returns true if initialized; false otherwise. After construction, will return false until initialize()
+ * is called; thereafter true until finalize() is called, whereupon it will return false again.
+ *
+ * \return True if initialized; false otherwise.
+ */
+ inline bool is_init() const { return _fcntl_arr.size() > 0; }
+
+ /**
+ * \brief Returns true if auto-expand mode is enabled; false if not.
+ *
+ * \return True if auto-expand mode is enabled; false if not.
+ */
+ inline bool is_ae() const { return _ae; }
+
+ /**
+ * \brief Sets the auto-expand mode to enabled if ae is true, to disabled otherwise. The value of _ae_max_jfiles
+ * must be valid to succeed (i.e. _ae_max_jfiles must be greater than the current number of files or be zero).
+ *
+ * \param ae If true will enable auto-expand mode; if false will disable it.
+ */
+ void set_ae(const bool ae);
+
+ /**
+ * \brief Returns the number of %journal files, including any that were appended or inserted since
+ * initialization.
+ *
+ * \return Number of %journal files if initialized; 0 otherwise.
+ */
+ inline u_int16_t num_jfiles() const { return static_cast<u_int16_t>(_fcntl_arr.size()); }
+
+ /**
+ * \brief Returns the maximum number of files allowed for auto-expansion.
+ *
+ * \return Maximum number of files allowed for auto-expansion. A zero value represents a disabled limit
+ * - i.e. unlimited expansion.
+ */
+ inline u_int16_t ae_max_jfiles() const { return _ae_max_jfiles; }
+
+ /**
+ * \brief Sets the maximum number of files allowed for auto-expansion. A zero value disables the limit.
+ *
+ * \param ae_max_jfiles The maximum number of files allowed for auto-expansion. Cannot be lower than the current
+ * number of files. However, a zero value disables the limit checks, and allows unlimited
+ * expansion.
+ */
+ void set_ae_max_jfiles(const u_int16_t ae_max_jfiles);
+
+ /**
+ * \brief Calculates the number of future files available for auto-expansion.
+ *
+ * \return The number of future files available for auto-expansion.
+ */
+ u_int16_t ae_jfiles_rem() const;
+
+ /**
+ * \brief Get a pointer to fcntl instance for a given lfid.
+ *
+ * \return Pointer to fcntl object corresponding to logical file id lfid, or 0 if lfid is out of range
+ * (greater than number of files in use).
+ */
+ inline fcntl* get_fcntlp(const u_int16_t lfid) const
+ { if (lfid >= _fcntl_arr.size()) return 0; return _fcntl_arr[lfid]; }
+
+ // Testing functions
+ void get_pfid_list(std::vector<u_int16_t>& pfid_list) const;
+ void get_lfid_list(std::vector<u_int16_t>& lfid_list) const;
+
+ protected:
+
+ /**
+ * \brief Append num_jfiles files to the end of the logical and file id sequence. This is similar to extending
+ * the from-scratch initialization.
+ *
+ * \param jcp Pointer to jcntl instance. This is used to find the file path and base filename so that
+ * new files may be created.
+ * \param fp Pointer to function which creates and returns a pointer to a new fcntl object (and hence
+ * causes a new %journal file to be created).
+ * \param num_jfiles The number of files by which to increase.
+ */
+ void append(jcntl* const jcp,
+ new_obj_fn_ptr fp,
+ const u_int16_t num_jfiles = 1);
+
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_LPMGR_H
diff --git a/cpp/src/qpid/legacystore/jrnl/pmgr.cpp b/cpp/src/qpid/legacystore/jrnl/pmgr.cpp
new file mode 100644
index 0000000000..3dc61e2661
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/pmgr.cpp
@@ -0,0 +1,215 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file pmgr.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::pmgr (page manager). See
+ * comments in file pmgr.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/pmgr.h"
+
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include "qpid/legacystore/jrnl/jcfg.h"
+#include "qpid/legacystore/jrnl/jcntl.h"
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include <sstream>
+
+
+namespace mrg
+{
+namespace journal
+{
+
+pmgr::page_cb::page_cb(u_int16_t index):
+ _index(index),
+ _state(UNUSED),
+ _wdblks(0),
+ _rdblks(0),
+ _pdtokl(0),
+ _wfh(0),
+ _rfh(0),
+ _pbuff(0)
+{}
+
+const char*
+pmgr::page_cb::state_str() const
+{
+ switch(_state)
+ {
+ case UNUSED:
+ return "UNUSED";
+ case IN_USE:
+ return "IN_USE";
+ case AIO_PENDING:
+ return "AIO_PENDING";
+ case AIO_COMPLETE:
+ return "AIO_COMPLETE";
+ }
+ return "<unknown>";
+}
+
+const u_int32_t pmgr::_sblksize = JRNL_SBLK_SIZE * JRNL_DBLK_SIZE;
+
+pmgr::pmgr(jcntl* jc, enq_map& emap, txn_map& tmap):
+ _cache_pgsize_sblks(0),
+ _cache_num_pages(0),
+ _jc(jc),
+ _emap(emap),
+ _tmap(tmap),
+ _page_base_ptr(0),
+ _page_ptr_arr(0),
+ _page_cb_arr(0),
+ _aio_cb_arr(0),
+ _aio_event_arr(0),
+ _ioctx(0),
+ _pg_index(0),
+ _pg_cntr(0),
+ _pg_offset_dblks(0),
+ _aio_evt_rem(0),
+ _cbp(0),
+ _enq_rec(),
+ _deq_rec(),
+ _txn_rec()
+{}
+
+pmgr::~pmgr()
+{
+ pmgr::clean();
+}
+
+void
+pmgr::initialize(aio_callback* const cbp, const u_int32_t cache_pgsize_sblks, const u_int16_t cache_num_pages)
+{
+ // As static use of this class keeps old values around, clean up first...
+ pmgr::clean();
+ _pg_index = 0;
+ _pg_cntr = 0;
+ _pg_offset_dblks = 0;
+ _aio_evt_rem = 0;
+ _cache_pgsize_sblks = cache_pgsize_sblks;
+ _cache_num_pages = cache_num_pages;
+ _cbp = cbp;
+
+ // 1. Allocate page memory (as a single block)
+ std::size_t cache_pgsize = _cache_num_pages * _cache_pgsize_sblks * _sblksize;
+ if (::posix_memalign(&_page_base_ptr, _sblksize, cache_pgsize))
+ {
+ clean();
+ std::ostringstream oss;
+ oss << "posix_memalign(): blksize=" << _sblksize << " size=" << cache_pgsize;
+ oss << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR__MALLOC, oss.str(), "pmgr", "initialize");
+ }
+ // 2. Allocate array of page pointers
+ _page_ptr_arr = (void**)std::malloc(_cache_num_pages * sizeof(void*));
+ MALLOC_CHK(_page_ptr_arr, "_page_ptr_arr", "pmgr", "initialize");
+
+ // 3. Allocate and initilaize page control block (page_cb) array
+ _page_cb_arr = (page_cb*)std::malloc(_cache_num_pages * sizeof(page_cb));
+ MALLOC_CHK(_page_cb_arr, "_page_cb_arr", "pmgr", "initialize");
+ std::memset(_page_cb_arr, 0, _cache_num_pages * sizeof(page_cb));
+
+ // 5. Allocate IO control block (iocb) array
+ _aio_cb_arr = (aio_cb*)std::malloc(_cache_num_pages * sizeof(aio_cb));
+ MALLOC_CHK(_aio_cb_arr, "_aio_cb_arr", "pmgr", "initialize");
+
+ // 6. Set page pointers in _page_ptr_arr, _page_cb_arr and iocbs to pages within page block
+ for (u_int16_t i=0; i<_cache_num_pages; i++)
+ {
+ _page_ptr_arr[i] = (void*)((char*)_page_base_ptr + _cache_pgsize_sblks * _sblksize * i);
+ _page_cb_arr[i]._index = i;
+ _page_cb_arr[i]._state = UNUSED;
+ _page_cb_arr[i]._pbuff = _page_ptr_arr[i];
+ _page_cb_arr[i]._pdtokl = new std::deque<data_tok*>;
+ _page_cb_arr[i]._pdtokl->clear();
+ _aio_cb_arr[i].data = (void*)&_page_cb_arr[i];
+ }
+
+ // 7. Allocate io_event array, max one event per cache page plus one for each file
+ const u_int16_t max_aio_evts = _cache_num_pages + _jc->num_jfiles();
+ _aio_event_arr = (aio_event*)std::malloc(max_aio_evts * sizeof(aio_event));
+ MALLOC_CHK(_aio_event_arr, "_aio_event_arr", "pmgr", "initialize");
+
+ // 8. Initialize AIO context
+ if (int ret = aio::queue_init(max_aio_evts, &_ioctx))
+ {
+ std::ostringstream oss;
+ oss << "io_queue_init() failed: " << FORMAT_SYSERR(-ret);
+ throw jexception(jerrno::JERR__AIO, oss.str(), "pmgr", "initialize");
+ }
+}
+
+void
+pmgr::clean()
+{
+ // clean up allocated memory here
+
+ if (_ioctx)
+ aio::queue_release(_ioctx);
+
+ std::free(_page_base_ptr);
+ _page_base_ptr = 0;
+
+ if (_page_cb_arr)
+ {
+ for (int i=0; i<_cache_num_pages; i++)
+ delete _page_cb_arr[i]._pdtokl;
+ std::free(_page_ptr_arr);
+ _page_ptr_arr = 0;
+ }
+
+ std::free(_page_cb_arr);
+ _page_cb_arr = 0;
+
+ std::free(_aio_cb_arr);
+ _aio_cb_arr = 0;
+
+ std::free(_aio_event_arr);
+ _aio_event_arr = 0;
+}
+
+const char*
+pmgr::page_state_str(page_state ps)
+{
+ switch (ps)
+ {
+ case UNUSED:
+ return "UNUSED";
+ case IN_USE:
+ return "IN_USE";
+ case AIO_PENDING:
+ return "AIO_PENDING";
+ case AIO_COMPLETE:
+ return "AIO_COMPLETE";
+ }
+ return "<page_state unknown>";
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/pmgr.h b/cpp/src/qpid/legacystore/jrnl/pmgr.h
new file mode 100644
index 0000000000..64115e225e
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/pmgr.h
@@ -0,0 +1,142 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 pmgr.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::pmgr (page manager). See
+ * class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_PMGR_H
+#define QPID_LEGACYSTORE_JRNL_PMGR_H
+
+namespace mrg
+{
+namespace journal
+{
+ class pmgr;
+ class jcntl;
+}
+}
+
+#include <deque>
+#include "qpid/legacystore/jrnl/aio.h"
+#include "qpid/legacystore/jrnl/aio_callback.h"
+#include "qpid/legacystore/jrnl/data_tok.h"
+#include "qpid/legacystore/jrnl/deq_rec.h"
+#include "qpid/legacystore/jrnl/enq_map.h"
+#include "qpid/legacystore/jrnl/enq_rec.h"
+#include "qpid/legacystore/jrnl/fcntl.h"
+#include "qpid/legacystore/jrnl/txn_map.h"
+#include "qpid/legacystore/jrnl/txn_rec.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \brief Abstract class for managing either read or write page cache of arbitrary size and
+ * number of cache_num_pages.
+ */
+ class pmgr
+ {
+ public:
+ /**
+ * \brief Enumeration of possible stats of a page within a page cache.
+ */
+ enum page_state
+ {
+ UNUSED, ///< A page is uninitialized, contains no data.
+ IN_USE, ///< Page is in use.
+ AIO_PENDING, ///< An AIO request outstanding.
+ AIO_COMPLETE ///< An AIO request is complete.
+ };
+
+ protected:
+ /**
+ * \brief Page control block, carries control and state information for each page in the
+ * cache.
+ */
+ struct page_cb
+ {
+ u_int16_t _index; ///< Index of this page
+ page_state _state; ///< Status of page
+ u_int64_t _frid; ///< First rid in page (used for fhdr init)
+ u_int32_t _wdblks; ///< Total number of dblks in page so far
+ u_int32_t _rdblks; ///< Total number of dblks in page
+ std::deque<data_tok*>* _pdtokl; ///< Page message tokens list
+ fcntl* _wfh; ///< File handle for incrementing write compl counts
+ fcntl* _rfh; ///< File handle for incrementing read compl counts
+ void* _pbuff; ///< Page buffer
+
+ page_cb(u_int16_t index); ///< Convenience constructor
+ const char* state_str() const; ///< Return state as string for this pcb
+ };
+
+ static const u_int32_t _sblksize; ///< Disk softblock size
+ u_int32_t _cache_pgsize_sblks; ///< Size of page cache cache_num_pages
+ u_int16_t _cache_num_pages; ///< Number of page cache cache_num_pages
+ jcntl* _jc; ///< Pointer to journal controller
+ enq_map& _emap; ///< Ref to enqueue map
+ txn_map& _tmap; ///< Ref to transaction map
+ void* _page_base_ptr; ///< Base pointer to page memory
+ void** _page_ptr_arr; ///< Array of pointers to cache_num_pages in page memory
+ page_cb* _page_cb_arr; ///< Array of page_cb structs
+ aio_cb* _aio_cb_arr; ///< Array of iocb structs
+ aio_event* _aio_event_arr; ///< Array of io_events
+ io_context_t _ioctx; ///< AIO context for read/write operations
+ u_int16_t _pg_index; ///< Index of current page being used
+ u_int32_t _pg_cntr; ///< Page counter; determines if file rotation req'd
+ u_int32_t _pg_offset_dblks; ///< Page offset (used so far) in data blocks
+ u_int32_t _aio_evt_rem; ///< Remaining AIO events
+ aio_callback* _cbp; ///< Pointer to callback object
+
+ enq_rec _enq_rec; ///< Enqueue record used for encoding/decoding
+ deq_rec _deq_rec; ///< Dequeue record used for encoding/decoding
+ txn_rec _txn_rec; ///< Transaction record used for encoding/decoding
+
+ public:
+ pmgr(jcntl* jc, enq_map& emap, txn_map& tmap);
+ virtual ~pmgr();
+
+ virtual int32_t get_events(page_state state, timespec* const timeout, bool flush = false) = 0;
+ inline u_int32_t get_aio_evt_rem() const { return _aio_evt_rem; }
+ static const char* page_state_str(page_state ps);
+ inline u_int32_t cache_pgsize_sblks() const { return _cache_pgsize_sblks; }
+ inline u_int16_t cache_num_pages() const { return _cache_num_pages; }
+
+ protected:
+ virtual void initialize(aio_callback* const cbp, const u_int32_t cache_pgsize_sblks,
+ const u_int16_t cache_num_pages);
+ virtual void rotate_page() = 0;
+ virtual void clean();
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_PMGR_H
diff --git a/cpp/src/qpid/legacystore/jrnl/rcvdat.h b/cpp/src/qpid/legacystore/jrnl/rcvdat.h
new file mode 100644
index 0000000000..a7ef2341f0
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/rcvdat.h
@@ -0,0 +1,181 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file rcvdat.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * Contains structure for recovery status and offset data.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_RCVDAT_H
+#define QPID_LEGACYSTORE_JRNL_RCVDAT_H
+
+#include <cstddef>
+#include <iomanip>
+#include <map>
+#include "qpid/legacystore/jrnl/jcfg.h"
+#include <sstream>
+#include <sys/types.h>
+#include <vector>
+
+namespace mrg
+{
+namespace journal
+{
+
+ struct rcvdat
+ {
+ u_int16_t _njf; ///< Number of journal files
+ bool _ae; ///< Auto-expand mode
+ u_int16_t _aemjf; ///< Auto-expand mode max journal files
+ bool _owi; ///< Overwrite indicator
+ bool _frot; ///< First rotation flag
+ bool _jempty; ///< Journal data files empty
+ u_int16_t _ffid; ///< First file id
+ std::size_t _fro; ///< First record offset in ffid
+ u_int16_t _lfid; ///< Last file id
+ std::size_t _eo; ///< End offset (first byte past last record)
+ u_int64_t _h_rid; ///< Highest rid found
+ bool _lffull; ///< Last file is full
+ bool _jfull; ///< Journal is full
+ std::vector<u_int16_t> _fid_list; ///< Fid-lid mapping - list of fids in order of lid
+ std::vector<u_int32_t> _enq_cnt_list; ///< Number enqueued records found for each file
+
+ rcvdat():
+ _njf(0),
+ _ae(false),
+ _aemjf(0),
+ _owi(false),
+ _frot(false),
+ _jempty(true),
+ _ffid(0),
+ _fro(0),
+ _lfid(0),
+ _eo(0),
+ _h_rid(0),
+ _lffull(false),
+ _jfull(false),
+ _fid_list(),
+ _enq_cnt_list()
+ {}
+
+ void reset(const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles)
+ {
+ _njf = num_jfiles;
+ _ae = auto_expand;
+ _aemjf = ae_max_jfiles;
+ _owi = false;
+ _frot = false;
+ _jempty = true;
+ _ffid = 0;
+ _fro = 0;
+ _lfid = 0;
+ _eo = 0;
+ _h_rid = 0;
+ _lffull = false;
+ _jfull = false;
+ _fid_list.clear();
+ _enq_cnt_list.clear();
+ _enq_cnt_list.resize(num_jfiles, 0);
+ }
+
+ // Find first fid with enqueued records
+ u_int16_t ffid()
+ {
+ u_int16_t index = _ffid;
+ while (index != _lfid && _enq_cnt_list[index] == 0)
+ {
+ if (++index >= _njf)
+ index = 0;
+ }
+ return index;
+ }
+
+ std::string to_string(const std::string& jid)
+ {
+ std::ostringstream oss;
+ oss << "Recover file analysis (jid=\"" << jid << "\"):" << std::endl;
+ oss << " Number of journal files (_njf) = " << _njf << std::endl;
+ oss << " Auto-expand mode (_ae) = " << (_ae ? "TRUE" : "FALSE") << std::endl;
+ if (_ae) oss << " Auto-expand mode max journal files (_aemjf) = " << _aemjf << std::endl;
+ oss << " Overwrite indicator (_owi) = " << (_owi ? "TRUE" : "FALSE") << std::endl;
+ oss << " First rotation (_frot) = " << (_frot ? "TRUE" : "FALSE") << std::endl;
+ oss << " Journal empty (_jempty) = " << (_jempty ? "TRUE" : "FALSE") << std::endl;
+ oss << " First (earliest) fid (_ffid) = " << _ffid << std::endl;
+ oss << " First record offset in first fid (_fro) = 0x" << std::hex << _fro <<
+ std::dec << " (" << (_fro/JRNL_DBLK_SIZE) << " dblks)" << std::endl;
+ oss << " Last (most recent) fid (_lfid) = " << _lfid << std::endl;
+ oss << " End offset (_eo) = 0x" << std::hex << _eo << std::dec << " (" <<
+ (_eo/JRNL_DBLK_SIZE) << " dblks)" << std::endl;
+ oss << " Highest rid (_h_rid) = 0x" << std::hex << _h_rid << std::dec << std::endl;
+ oss << " Last file full (_lffull) = " << (_lffull ? "TRUE" : "FALSE") << std::endl;
+ oss << " Journal full (_jfull) = " << (_jfull ? "TRUE" : "FALSE") << std::endl;
+ oss << " Normalized fid list (_fid_list) = [";
+ for (std::vector<u_int16_t>::const_iterator i = _fid_list.begin(); i < _fid_list.end(); i++)
+ {
+ if (i != _fid_list.begin()) oss << ", ";
+ oss << *i;
+ }
+ oss << "]" << std::endl;
+ oss << " Enqueued records (txn & non-txn):" << std::endl;
+ for (unsigned i=0; i<_enq_cnt_list.size(); i++)
+ oss << " File " << std::setw(2) << i << ": " << _enq_cnt_list[i] <<
+ std::endl;
+ return oss.str();
+ }
+
+ std::string to_log(const std::string& jid)
+ {
+ std::ostringstream oss;
+ oss << "Recover file analysis (jid=\"" << jid << "\"):";
+ oss << " njf=" << _njf;
+ oss << " ae=" << (_owi ? "T" : "F");
+ oss << " aemjf=" << _aemjf;
+ oss << " owi=" << (_ae ? "T" : "F");
+ oss << " frot=" << (_frot ? "T" : "F");
+ oss << " jempty=" << (_jempty ? "T" : "F");
+ oss << " ffid=" << _ffid;
+ oss << " fro=0x" << std::hex << _fro << std::dec << " (" <<
+ (_fro/JRNL_DBLK_SIZE) << " dblks)";
+ oss << " lfid=" << _lfid;
+ oss << " eo=0x" << std::hex << _eo << std::dec << " (" <<
+ (_eo/JRNL_DBLK_SIZE) << " dblks)";
+ oss << " h_rid=0x" << std::hex << _h_rid << std::dec;
+ oss << " lffull=" << (_lffull ? "T" : "F");
+ oss << " jfull=" << (_jfull ? "T" : "F");
+ oss << " Enqueued records (txn & non-txn): [ ";
+ for (unsigned i=0; i<_enq_cnt_list.size(); i++)
+ {
+ if (i) oss << " ";
+ oss << "fid_" << std::setw(2) << std::setfill('0') << i << "=" << _enq_cnt_list[i];
+ }
+ oss << " ]";
+ return oss.str();
+ }
+ };
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_RCVDAT_H
diff --git a/cpp/src/qpid/legacystore/jrnl/rec_hdr.h b/cpp/src/qpid/legacystore/jrnl/rec_hdr.h
new file mode 100644
index 0000000000..ff6325a760
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/rec_hdr.h
@@ -0,0 +1,143 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file rec_hdr.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::rec_hdr (record header),
+ * which is a common initial header used for all journal record structures
+ * except the record tail (rec_tail).
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_REC_HDR_H
+#define QPID_LEGACYSTORE_JRNL_REC_HDR_H
+
+#include <cstddef>
+#include "qpid/legacystore/jrnl/jcfg.h"
+#include <sys/types.h>
+
+namespace mrg
+{
+namespace journal
+{
+
+#pragma pack(1)
+
+ /**
+ * \brief Struct for data common to the head of all journal files and records.
+ * This includes identification for the file type, the encoding version, endian
+ * indicator and a record ID.
+ *
+ * File header info in binary format (16 bytes):
+ * <pre>
+ * 0 7
+ * +---+---+---+---+---+---+---+---+
+ * | magic | v | e | flags |
+ * +---+---+---+---+---+---+---+---+
+ * | rid |
+ * +---+---+---+---+---+---+---+---+
+ * v = file version (If the format or encoding of this file changes, then this
+ * number should be incremented)
+ * e = endian flag, false (0x00) for little endian, true (0x01) for big endian
+ * </pre>
+ *
+ * Note that journal files should be transferable between 32- and 64-bit
+ * hardware of the same endianness, but not between hardware of opposite
+ * entianness without some sort of binary conversion utility. Thus buffering
+ * will be needed for types that change size between 32- and 64-bit compiles.
+ */
+ struct rec_hdr
+ {
+ u_int32_t _magic; ///< File type identifier (magic number)
+ u_int8_t _version; ///< File encoding version
+ u_int8_t _eflag; ///< Flag for determining endianness
+ u_int16_t _uflag; ///< User-defined flags
+ u_int64_t _rid; ///< Record ID (rotating 64-bit counter)
+
+ // Global flags
+ static const u_int16_t HDR_OVERWRITE_INDICATOR_MASK = 0x1;
+
+ // Convenience constructors and methods
+ /**
+ * \brief Default constructor, which sets all values to 0.
+ */
+ inline rec_hdr(): _magic(0), _version(0), _eflag(0), _uflag(0), _rid(0) {}
+
+ /**
+ * \brief Convenience constructor which initializes values during construction.
+ */
+ inline rec_hdr(const u_int32_t magic, const u_int8_t version, const u_int64_t rid,
+ const bool owi): _magic(magic), _version(version),
+#if defined(JRNL_BIG_ENDIAN)
+ _eflag(RHM_BENDIAN_FLAG),
+#else
+ _eflag(RHM_LENDIAN_FLAG),
+#endif
+ _uflag(owi ? HDR_OVERWRITE_INDICATOR_MASK : 0), _rid(rid) {}
+
+ /**
+ * \brief Convenience copy method.
+ */
+ inline void hdr_copy(const rec_hdr& h)
+ {
+ _magic = h._magic;
+ _version = h._version;
+ _eflag = h._eflag;
+ _uflag = h._uflag;
+ _rid =h._rid;
+ }
+
+ /**
+ * \brief Resets all fields to 0
+ */
+ inline void reset()
+ {
+ _magic = 0;
+ _version = 0;
+ _eflag = 0;
+ _uflag = 0;
+ _rid = 0;
+ }
+
+ inline bool get_owi() const { return _uflag & HDR_OVERWRITE_INDICATOR_MASK; }
+
+ inline void set_owi(const bool owi)
+ {
+ _uflag = owi ? _uflag | HDR_OVERWRITE_INDICATOR_MASK :
+ _uflag & (~HDR_OVERWRITE_INDICATOR_MASK);
+ }
+
+ /**
+ * \brief Returns the size of the header in bytes.
+ */
+ inline static std::size_t size() { return sizeof(rec_hdr); }
+ }; // struct rec_hdr
+
+#pragma pack()
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_REC_HDR_H
diff --git a/cpp/src/qpid/legacystore/jrnl/rec_tail.h b/cpp/src/qpid/legacystore/jrnl/rec_tail.h
new file mode 100644
index 0000000000..0c36151927
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/rec_tail.h
@@ -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.
+ *
+ */
+
+/**
+ * \file rec_tail.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::rec_tail (record tail), used to
+ * finalize a persistent record. The presence of a valid tail at the expected
+ * position in the journal file indicates that the record write was completed.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_REC_TAIL_H
+#define QPID_LEGACYSTORE_JRNL_REC_TAIL_H
+
+#include <cstddef>
+#include "qpid/legacystore/jrnl/jcfg.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+#pragma pack(1)
+
+ /**
+ * \brief Struct for data common to the tail of all records. The magic number
+ * used here is the binary inverse (1's complement) of the magic used in the
+ * record header; this minimizes possible confusion with other headers that may
+ * be present during recovery. The tail is used with all records that have either
+ * XIDs or data - ie any size-variable content. Currently the only records that
+ * do NOT use the tail are non-transactional dequeues and filler records.
+ *
+ * Record header info in binary format (12 bytes):
+ * <pre>
+ * 0 7
+ * +---+---+---+---+---+---+---+---+
+ * | ~(magic) | rid |
+ * +---+---+---+---+---+---+---+---+
+ * | rid (con't) |
+ * +---+---+---+---+
+ * </pre>
+ */
+ struct rec_tail
+ {
+ u_int32_t _xmagic; ///< Binary inverse (1's complement) of hdr magic number
+ u_int64_t _rid; ///< ID (rotating 64-bit counter)
+
+
+ /**
+ * \brief Default constructor, which sets all values to 0.
+ */
+ inline rec_tail(): _xmagic(0xffffffff), _rid(0) {}
+
+ /**
+ * \brief Convenience constructor which initializes values during construction from
+ * existing enq_hdr instance.
+ */
+ inline rec_tail(const rec_hdr& h): _xmagic(~h._magic), _rid(h._rid) {}
+
+ /**
+ * \brief Convenience constructor which initializes values during construction.
+ */
+ inline rec_tail(const u_int32_t xmagic, const u_int64_t rid): _xmagic(xmagic), _rid(rid) {}
+
+ /**
+ * \brief Returns the size of the header in bytes.
+ */
+ inline static std::size_t size() { return sizeof(rec_tail); }
+ };
+
+#pragma pack()
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_REC_TAIL_H
diff --git a/cpp/src/qpid/legacystore/jrnl/rfc.cpp b/cpp/src/qpid/legacystore/jrnl/rfc.cpp
new file mode 100644
index 0000000000..9b5ed95e81
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/rfc.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.
+ *
+ */
+
+/**
+ * \file rfc.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::rfc (rotating
+ * file controller). See comments in file rfc.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/rfc.h"
+
+#include <cassert>
+
+namespace mrg
+{
+namespace journal
+{
+
+rfc::rfc(const lpmgr* lpmp): _lpmp(lpmp), _fc_index(0), _curr_fc(0)
+{}
+
+rfc::~rfc()
+{}
+
+void
+rfc::finalize()
+{
+ unset_findex();
+}
+
+void
+rfc::set_findex(const u_int16_t fc_index)
+{
+ _fc_index = fc_index;
+ _curr_fc = _lpmp->get_fcntlp(fc_index);
+ _curr_fc->rd_reset();
+}
+
+void
+rfc::unset_findex()
+{
+ _fc_index = 0;
+ _curr_fc = 0;
+}
+
+std::string
+rfc::status_str() const
+{
+ if (!_lpmp->is_init())
+ return "state: Uninitialized";
+ if (_curr_fc == 0)
+ return "state: Inactive";
+ std::ostringstream oss;
+ oss << "state: Active";
+ return oss.str();
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/rfc.h b/cpp/src/qpid/legacystore/jrnl/rfc.h
new file mode 100644
index 0000000000..faa5d566ba
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/rfc.h
@@ -0,0 +1,193 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file rfc.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::rfc (rotating
+ * file controller). See class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_RFC_H
+#define QPID_LEGACYSTORE_JRNL_RFC_H
+
+namespace mrg
+{
+namespace journal
+{
+class rfc;
+}
+}
+
+#include "qpid/legacystore/jrnl/lpmgr.h"
+#include "qpid/legacystore/jrnl/enums.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \class rfc
+ * \brief Rotating File Controller (rfc) - Class to handle the manangement of an array of file controllers (fcntl)
+ * objects for use in a circular disk buffer (journal). Each fcntl object corresponds to a file in the journal.
+ *
+ * The following states exist in this class:
+ *
+ * <pre>
+ * is_init() is_active()
+ * +===+ _lpmp.is_init() == false
+ * +---------->| | Uninitialized: _curr_fc == 0 F F
+ * | +-->+===+ --+
+ * | | |
+ * | | |
+ * | finalize() initialize()
+ * | | |
+ * | | |
+ * | +-- +===+<--+ _lpmp.is_init() == true
+ * finalize() | | Inactive: _curr_fc == 0 T F
+ * | +-->+===+ --+
+ * | | |
+ * | | |
+ * | unset_findex() set_findex()
+ * | | |
+ * | | |
+ * | +-- +===+<--+ _lpmp.is_init() == true
+ * +---------- | | Active: _curr_fc != 0 T T
+ * +===+
+ * </pre>
+ *
+ * The Uninitialized state is where the class starts after construction. Once the number of files is known and
+ * the array of file controllers allocated, then initialize() is called to set these, causing the state to move
+ * to Inactive.
+ *
+ * The Inactive state has the file controllers allocated and pointing to their respective journal files, but no
+ * current file controller has been selected. The pointer to the current file controller _curr_fc is null. Once the
+ * index of the active file is known, then calling set_findex() will set the index and internal pointer
+ * to the currently active file controller. This moves the state to Active.
+ *
+ * Note TODO: Comment on sync issues between change in num files in _lpmp and _fc_index/_curr_fc.
+ */
+ class rfc
+ {
+ protected:
+ const lpmgr* _lpmp; ///< Pointer to jcntl's lpmgr instance containing lfid/pfid map and fcntl objects
+ u_int16_t _fc_index; ///< Index of current file controller
+ fcntl* _curr_fc; ///< Pointer to current file controller
+
+ public:
+ rfc(const lpmgr* lpmp);
+ virtual ~rfc();
+
+ /**
+ * \brief Initialize the controller, moving from state Uninitialized to Inactive. The main function of
+ * initialize() is to set the number of files and the pointer to the fcntl array.
+ */
+ virtual inline void initialize() {}
+
+ /**
+ * \brief Reset the controller to Uninitialized state, usually called when the journal is stopped. Once called,
+ * initialize() must be called to reuse an instance.
+ */
+ virtual void finalize();
+
+ /**
+ * \brief Check initialization state: true = Not Uninitialized, ie Initialized or Active; false = Uninitialized.
+ */
+ virtual inline bool is_init() const { return _lpmp->is_init(); }
+
+ /**
+ * \brief Check active state: true = Initialized and _curr_fc not null; false otherwise.
+ */
+ virtual inline bool is_active() const { return _lpmp->is_init() && _curr_fc != 0; }
+
+ /**
+ * \brief Sets the current file index and active fcntl object. Moves to state Active.
+ */
+ virtual void set_findex(const u_int16_t fc_index);
+
+ /**
+ * \brief Nulls the current file index and active fcntl pointer, moves to state Inactive.
+ */
+ virtual void unset_findex();
+
+ /**
+ * \brief Rotate active file controller to next file in rotating file group.
+ * \exception jerrno::JERR__NINIT if called before calling initialize().
+ */
+ virtual iores rotate() = 0;
+
+ /**
+ * \brief Returns the index of the currently active file within the rotating file group.
+ */
+ inline u_int16_t index() const { return _fc_index; }
+
+ /**
+ * \brief Returns the currently active journal file controller within the rotating file group.
+ */
+ inline fcntl* file_controller() const { return _curr_fc; }
+
+ /**
+ * \brief Returns the currently active physical file id (pfid)
+ */
+ inline u_int16_t pfid() const { return _curr_fc->pfid(); }
+
+ // Convenience access methods to current file controller
+ // Note: Do not call when not in active state
+
+ inline u_int32_t enqcnt() const { return _curr_fc->enqcnt(); }
+ inline u_int32_t incr_enqcnt() { return _curr_fc->incr_enqcnt(); }
+ inline u_int32_t incr_enqcnt(const u_int16_t fid) { return _lpmp->get_fcntlp(fid)->incr_enqcnt(); }
+ inline u_int32_t add_enqcnt(const u_int32_t a) { return _curr_fc->add_enqcnt(a); }
+ inline u_int32_t add_enqcnt(const u_int16_t fid, const u_int32_t a)
+ { return _lpmp->get_fcntlp(fid)->add_enqcnt(a); }
+ inline u_int32_t decr_enqcnt(const u_int16_t fid) { return _lpmp->get_fcntlp(fid)->decr_enqcnt(); }
+ inline u_int32_t subtr_enqcnt(const u_int16_t fid, const u_int32_t s)
+ { return _lpmp->get_fcntlp(fid)->subtr_enqcnt(s); }
+
+ virtual inline u_int32_t subm_cnt_dblks() const = 0;
+ virtual inline std::size_t subm_offs() const = 0;
+ virtual inline u_int32_t add_subm_cnt_dblks(u_int32_t a) = 0;
+
+ virtual inline u_int32_t cmpl_cnt_dblks() const = 0;
+ virtual inline std::size_t cmpl_offs() const = 0;
+ virtual inline u_int32_t add_cmpl_cnt_dblks(u_int32_t a) = 0;
+
+ virtual inline bool is_void() const = 0;
+ virtual inline bool is_empty() const = 0;
+ virtual inline u_int32_t remaining_dblks() const = 0;
+ virtual inline bool is_full() const = 0;
+ virtual inline bool is_compl() const = 0;
+ virtual inline u_int32_t aio_outstanding_dblks() const = 0;
+ virtual inline bool file_rotate() const = 0;
+
+ // Debug aid
+ virtual std::string status_str() const;
+ }; // class rfc
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_RFC_H
diff --git a/cpp/src/qpid/legacystore/jrnl/rmgr.cpp b/cpp/src/qpid/legacystore/jrnl/rmgr.cpp
new file mode 100644
index 0000000000..3a11817d1e
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/rmgr.cpp
@@ -0,0 +1,698 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 rmgr.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::rmgr (read manager). See
+ * comments in file rmgr.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/rmgr.h"
+
+#include <cassert>
+#include <cerrno>
+#include <cstdlib>
+#include "qpid/legacystore/jrnl/jcntl.h"
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include <sstream>
+
+namespace mrg
+{
+namespace journal
+{
+
+rmgr::rmgr(jcntl* jc, enq_map& emap, txn_map& tmap, rrfc& rrfc):
+ pmgr(jc, emap, tmap),
+ _rrfc(rrfc),
+ _hdr(),
+ _fhdr_buffer(0),
+ _fhdr_aio_cb_ptr(0),
+ _fhdr_rd_outstanding(false)
+{}
+
+rmgr::~rmgr()
+{
+ rmgr::clean();
+}
+
+void
+rmgr::initialize(aio_callback* const cbp)
+{
+ pmgr::initialize(cbp, JRNL_RMGR_PAGE_SIZE, JRNL_RMGR_PAGES);
+ clean();
+ // Allocate memory for reading file header
+ if (::posix_memalign(&_fhdr_buffer, _sblksize, _sblksize))
+ {
+ std::ostringstream oss;
+ oss << "posix_memalign(): blksize=" << _sblksize << " size=" << _sblksize;
+ oss << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR__MALLOC, oss.str(), "rmgr", "initialize");
+ }
+ _fhdr_aio_cb_ptr = new aio_cb;
+ std::memset(_fhdr_aio_cb_ptr, 0, sizeof(aio_cb*));
+}
+
+void
+rmgr::clean()
+{
+ std::free(_fhdr_buffer);
+ _fhdr_buffer = 0;
+
+ if (_fhdr_aio_cb_ptr)
+ {
+ delete _fhdr_aio_cb_ptr;
+ _fhdr_aio_cb_ptr = 0;
+ }
+}
+
+iores
+rmgr::read(void** const datapp, std::size_t& dsize, void** const xidpp, std::size_t& xidsize,
+ bool& transient, bool& external, data_tok* dtokp, bool ignore_pending_txns)
+{
+ iores res = pre_read_check(dtokp);
+ if (res != RHM_IORES_SUCCESS)
+ {
+ set_params_null(datapp, dsize, xidpp, xidsize);
+ return res;
+ }
+
+ if (dtokp->rstate() == data_tok::SKIP_PART)
+ {
+ if (_page_cb_arr[_pg_index]._state != AIO_COMPLETE)
+ {
+ aio_cycle(); // check if rd AIOs returned; initiate new reads if possible
+ return RHM_IORES_PAGE_AIOWAIT;
+ }
+ const iores res = skip(dtokp);
+ if (res != RHM_IORES_SUCCESS)
+ {
+ set_params_null(datapp, dsize, xidpp, xidsize);
+ return res;
+ }
+ }
+ if (dtokp->rstate() == data_tok::READ_PART)
+ {
+ assert(dtokp->rid() == _hdr._rid);
+ void* rptr = (void*)((char*)_page_ptr_arr[_pg_index] + (_pg_offset_dblks * JRNL_DBLK_SIZE));
+ const iores res = read_enq(_hdr, rptr, dtokp);
+ dsize = _enq_rec.get_data(datapp);
+ xidsize = _enq_rec.get_xid(xidpp);
+ transient = _enq_rec.is_transient();
+ external = _enq_rec.is_external();
+ return res;
+ }
+
+ set_params_null(datapp, dsize, xidpp, xidsize);
+ _hdr.reset();
+ // Read header, determine next record type
+ while (true)
+ {
+ if(dblks_rem() == 0 && _rrfc.is_compl() && !_rrfc.is_wr_aio_outstanding())
+ {
+ aio_cycle(); // check if rd AIOs returned; initiate new reads if possible
+ if(dblks_rem() == 0 && _rrfc.is_compl() && !_rrfc.is_wr_aio_outstanding())
+ {
+ if (_jc->unflushed_dblks() > 0)
+ _jc->flush();
+ else if (!_aio_evt_rem)
+ return RHM_IORES_EMPTY;
+ }
+ }
+ if (_page_cb_arr[_pg_index]._state != AIO_COMPLETE)
+ {
+ aio_cycle();
+ return RHM_IORES_PAGE_AIOWAIT;
+ }
+ void* rptr = (void*)((char*)_page_ptr_arr[_pg_index] + (_pg_offset_dblks * JRNL_DBLK_SIZE));
+ std::memcpy(&_hdr, rptr, sizeof(rec_hdr));
+ switch (_hdr._magic)
+ {
+ case RHM_JDAT_ENQ_MAGIC:
+ {
+ _enq_rec.reset(); // sets enqueue rec size
+ // Check if RID of this rec is still enqueued, if so read it, else skip
+ bool is_enq = false;
+ int16_t fid = _emap.get_pfid(_hdr._rid);
+ if (fid < enq_map::EMAP_OK)
+ {
+ bool enforce_txns = !_jc->is_read_only() && !ignore_pending_txns;
+ // Block read for transactionally locked record (only when not recovering)
+ if (fid == enq_map::EMAP_LOCKED && enforce_txns)
+ return RHM_IORES_TXPENDING;
+
+ // (Recover mode only) Ok, not in emap - now search tmap, if present then read
+ is_enq = _tmap.is_enq(_hdr._rid);
+ if (enforce_txns && is_enq)
+ return RHM_IORES_TXPENDING;
+ }
+ else
+ is_enq = true;
+
+ if (is_enq) // ok, this record is enqueued, check it, then read it...
+ {
+ if (dtokp->rid())
+ {
+ if (_hdr._rid != dtokp->rid())
+ {
+ std::ostringstream oss;
+ oss << std::hex << "rid=0x" << _hdr._rid << "; dtok_rid=0x" << dtokp->rid()
+ << "; dtok_id=0x" << dtokp->id();
+ throw jexception(jerrno::JERR_RMGR_RIDMISMATCH, oss.str(), "rmgr", "read");
+ }
+ }
+ else
+ dtokp->set_rid(_hdr._rid);
+
+// TODO: Add member _fid to pmgr::page_cb which indicates the fid from which this page was
+// populated. When this value is set in wmgr::flush() somewehere, then uncomment the following
+// check:
+// if (fid != _page_cb_arr[_pg_index]._fid)
+// {
+// std::ostringstream oss;
+// oss << std::hex << std::setfill('0');
+// oss << "rid=0x" << std::setw(16) << _hdr._rid;
+// oss << "; emap_fid=0x" << std::setw(4) << fid;
+// oss << "; current_fid=" << _rrfc.fid();
+// throw jexception(jerrno::JERR_RMGR_FIDMISMATCH, oss.str(), "rmgr",
+// "read");
+// }
+
+ const iores res = read_enq(_hdr, rptr, dtokp);
+ dsize = _enq_rec.get_data(datapp);
+ xidsize = _enq_rec.get_xid(xidpp);
+ transient = _enq_rec.is_transient();
+ external = _enq_rec.is_external();
+ return res;
+ }
+ else // skip this record, it is already dequeued
+ consume_xid_rec(_hdr, rptr, dtokp);
+ break;
+ }
+ case RHM_JDAT_DEQ_MAGIC:
+ consume_xid_rec(_hdr, rptr, dtokp);
+ break;
+ case RHM_JDAT_TXA_MAGIC:
+ consume_xid_rec(_hdr, rptr, dtokp);
+ break;
+ case RHM_JDAT_TXC_MAGIC:
+ consume_xid_rec(_hdr, rptr, dtokp);
+ break;
+ case RHM_JDAT_EMPTY_MAGIC:
+ consume_filler();
+ break;
+ default:
+ return RHM_IORES_EMPTY;
+ }
+ }
+}
+
+int32_t
+rmgr::get_events(page_state state, timespec* const timeout, bool flush)
+{
+ if (_aio_evt_rem == 0) // no events to get
+ return 0;
+
+ int32_t ret;
+ if ((ret = aio::getevents(_ioctx, flush ? _aio_evt_rem : 1, _aio_evt_rem/*_cache_num_pages + _jc->num_jfiles()*/, _aio_event_arr, timeout)) < 0)
+ {
+ if (ret == -EINTR) // Interrupted by signal
+ return 0;
+ std::ostringstream oss;
+ oss << "io_getevents() failed: " << std::strerror(-ret) << " (" << ret << ")";
+ throw jexception(jerrno::JERR__AIO, oss.str(), "rmgr", "get_events");
+ }
+ if (ret == 0 && timeout)
+ return jerrno::AIO_TIMEOUT;
+
+ std::vector<u_int16_t> pil;
+ pil.reserve(ret);
+ for (int i=0; i<ret; i++) // Index of returned AIOs
+ {
+ if (_aio_evt_rem == 0)
+ {
+ std::ostringstream oss;
+ oss << "_aio_evt_rem; evt " << (i + 1) << " of " << ret;
+ throw jexception(jerrno::JERR__UNDERFLOW, oss.str(), "rmgr", "get_events");
+ }
+ _aio_evt_rem--;
+ aio_cb* aiocbp = _aio_event_arr[i].obj; // This I/O control block (iocb)
+ page_cb* pcbp = (page_cb*)(aiocbp->data); // This page control block (pcb)
+ long aioret = (long)_aio_event_arr[i].res;
+ if (aioret < 0)
+ {
+ std::ostringstream oss;
+ oss << "AIO read operation failed: " << std::strerror(-aioret) << " (" << aioret << ")";
+ oss << " [pg=" << pcbp->_index << " buf=" << aiocbp->u.c.buf;
+ oss << " rsize=0x" << std::hex << aiocbp->u.c.nbytes;
+ oss << " offset=0x" << aiocbp->u.c.offset << std::dec;
+ oss << " fh=" << aiocbp->aio_fildes << "]";
+ throw jexception(jerrno::JERR__AIO, oss.str(), "rmgr", "get_events");
+ }
+
+ if (pcbp) // Page reads have pcb
+ {
+ if (pcbp->_rfh->rd_subm_cnt_dblks() >= JRNL_SBLK_SIZE) // Detects if write reset of this fcntl obj has occurred.
+ {
+ // Increment the completed read offset
+ // NOTE: We cannot use _rrfc here, as it may have rotated since submitting count.
+ // Use stored pointer to fcntl in the pcb instead.
+ pcbp->_rdblks = aiocbp->u.c.nbytes / JRNL_DBLK_SIZE;
+ pcbp->_rfh->add_rd_cmpl_cnt_dblks(pcbp->_rdblks);
+ pcbp->_state = state;
+ pil[i] = pcbp->_index;
+ }
+ }
+ else // File header reads have no pcb
+ {
+ std::memcpy(&_fhdr, _fhdr_buffer, sizeof(file_hdr));
+ _rrfc.add_cmpl_cnt_dblks(JRNL_SBLK_SIZE);
+
+ u_int32_t fro_dblks = (_fhdr._fro / JRNL_DBLK_SIZE) - JRNL_SBLK_SIZE;
+ // Check fro_dblks does not exceed the write pointers which can happen in some corrupted journal recoveries
+ if (fro_dblks > _jc->wr_subm_cnt_dblks(_fhdr._pfid) - JRNL_SBLK_SIZE)
+ fro_dblks = _jc->wr_subm_cnt_dblks(_fhdr._pfid) - JRNL_SBLK_SIZE;
+ _pg_cntr = fro_dblks / (JRNL_RMGR_PAGE_SIZE * JRNL_SBLK_SIZE);
+ u_int32_t tot_pg_offs_dblks = _pg_cntr * JRNL_RMGR_PAGE_SIZE * JRNL_SBLK_SIZE;
+ _pg_index = _pg_cntr % JRNL_RMGR_PAGES;
+ _pg_offset_dblks = fro_dblks - tot_pg_offs_dblks;
+ _rrfc.add_subm_cnt_dblks(tot_pg_offs_dblks);
+ _rrfc.add_cmpl_cnt_dblks(tot_pg_offs_dblks);
+
+ _fhdr_rd_outstanding = false;
+ _rrfc.set_valid();
+ }
+ }
+
+ // Perform AIO return callback
+ if (_cbp && ret)
+ _cbp->rd_aio_cb(pil);
+ return ret;
+}
+
+void
+rmgr::recover_complete()
+{}
+
+void
+rmgr::invalidate()
+{
+ if (_rrfc.is_valid())
+ _rrfc.set_invalid();
+}
+
+void
+rmgr::flush(timespec* timeout)
+{
+ // Wait for any outstanding AIO read operations to complete before synchronizing
+ while (_aio_evt_rem)
+ {
+ if (get_events(AIO_COMPLETE, timeout) == jerrno::AIO_TIMEOUT) // timed out, nothing returned
+ {
+ throw jexception(jerrno::JERR__TIMEOUT,
+ "Timed out waiting for outstanding read aio to return", "rmgr", "init_validation");
+ }
+ }
+
+ // Reset all read states and pointers
+ for (int i=0; i<_cache_num_pages; i++)
+ _page_cb_arr[i]._state = UNUSED;
+ _rrfc.unset_findex();
+ _pg_index = 0;
+ _pg_offset_dblks = 0;
+}
+
+bool
+rmgr::wait_for_validity(timespec* timeout, const bool throw_on_timeout)
+{
+ bool timed_out = false;
+ while (!_rrfc.is_valid() && !timed_out)
+ {
+ timed_out = get_events(AIO_COMPLETE, timeout) == jerrno::AIO_TIMEOUT;
+ if (timed_out && throw_on_timeout)
+ throw jexception(jerrno::JERR__TIMEOUT, "Timed out waiting for read validity", "rmgr", "wait_for_validity");
+ }
+ return _rrfc.is_valid();
+}
+
+iores
+rmgr::pre_read_check(data_tok* dtokp)
+{
+ if (_aio_evt_rem)
+ get_events(AIO_COMPLETE, 0);
+
+ if (!_rrfc.is_valid())
+ return RHM_IORES_RCINVALID;
+
+ // block reads until outstanding file header read completes as fro is needed to read
+ if (_fhdr_rd_outstanding)
+ return RHM_IORES_PAGE_AIOWAIT;
+
+ if(dblks_rem() == 0 && _rrfc.is_compl() && !_rrfc.is_wr_aio_outstanding())
+ {
+ aio_cycle(); // check if any AIOs have returned
+ if(dblks_rem() == 0 && _rrfc.is_compl() && !_rrfc.is_wr_aio_outstanding())
+ {
+ if (_jc->unflushed_dblks() > 0)
+ _jc->flush();
+ else if (!_aio_evt_rem)
+ return RHM_IORES_EMPTY;
+ }
+ }
+
+ // Check write state of this token is ENQ - required for read
+ if (dtokp)
+ {
+ if (!dtokp->is_readable())
+ {
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0');
+ oss << "dtok_id=0x" << std::setw(8) << dtokp->id();
+ oss << "; dtok_rid=0x" << std::setw(16) << dtokp->rid();
+ oss << "; dtok_wstate=" << dtokp->wstate_str();
+ throw jexception(jerrno::JERR_RMGR_ENQSTATE, oss.str(), "rmgr", "pre_read_check");
+ }
+ }
+
+ return RHM_IORES_SUCCESS;
+}
+
+iores
+rmgr::read_enq(rec_hdr& h, void* rptr, data_tok* dtokp)
+{
+ if (_page_cb_arr[_pg_index]._state != AIO_COMPLETE)
+ {
+ aio_cycle(); // check if any AIOs have returned
+ return RHM_IORES_PAGE_AIOWAIT;
+ }
+
+ // Read data from this page, first block will have header and data size.
+ u_int32_t dblks_rd = _enq_rec.decode(h, rptr, dtokp->dblocks_read(), dblks_rem());
+ dtokp->incr_dblocks_read(dblks_rd);
+
+ _pg_offset_dblks += dblks_rd;
+
+ // If data still incomplete, move to next page and decode again
+ while (dtokp->dblocks_read() < _enq_rec.rec_size_dblks())
+ {
+ rotate_page();
+ if (_page_cb_arr[_pg_index]._state != AIO_COMPLETE)
+ {
+ dtokp->set_rstate(data_tok::READ_PART);
+ dtokp->set_dsize(_enq_rec.data_size());
+ return RHM_IORES_PAGE_AIOWAIT;
+ }
+
+ rptr = (void*)((char*)_page_ptr_arr[_pg_index]);
+ dblks_rd = _enq_rec.decode(h, rptr, dtokp->dblocks_read(), dblks_rem());
+ dtokp->incr_dblocks_read(dblks_rd);
+ _pg_offset_dblks += dblks_rd;
+ }
+
+ // If we have finished with this page, rotate it
+ if (dblks_rem() == 0)
+ rotate_page();
+
+ // Set the record size in dtokp
+ dtokp->set_rstate(data_tok::READ);
+ dtokp->set_dsize(_enq_rec.data_size());
+ return RHM_IORES_SUCCESS;
+}
+
+void
+rmgr::consume_xid_rec(rec_hdr& h, void* rptr, data_tok* dtokp)
+{
+ if (h._magic == RHM_JDAT_ENQ_MAGIC)
+ {
+ enq_hdr ehdr;
+ std::memcpy(&ehdr, rptr, sizeof(enq_hdr));
+ if (ehdr.is_external())
+ dtokp->set_dsize(ehdr._xidsize + sizeof(enq_hdr) + sizeof(rec_tail));
+ else
+ dtokp->set_dsize(ehdr._xidsize + ehdr._dsize + sizeof(enq_hdr) + sizeof(rec_tail));
+ }
+ else if (h._magic == RHM_JDAT_DEQ_MAGIC)
+ {
+ deq_hdr dhdr;
+ std::memcpy(&dhdr, rptr, sizeof(deq_hdr));
+ if (dhdr._xidsize)
+ dtokp->set_dsize(dhdr._xidsize + sizeof(deq_hdr) + sizeof(rec_tail));
+ else
+ dtokp->set_dsize(sizeof(deq_hdr));
+ }
+ else if (h._magic == RHM_JDAT_TXA_MAGIC || h._magic == RHM_JDAT_TXC_MAGIC)
+ {
+ txn_hdr thdr;
+ std::memcpy(&thdr, rptr, sizeof(txn_hdr));
+ dtokp->set_dsize(thdr._xidsize + sizeof(txn_hdr) + sizeof(rec_tail));
+ }
+ else
+ {
+ std::ostringstream oss;
+ oss << "Record type found = \"" << (char*)&h._magic << "\"";
+ throw jexception(jerrno::JERR_RMGR_BADRECTYPE, oss.str(), "rmgr", "consume_xid_rec");
+ }
+ dtokp->set_dblocks_read(0);
+ skip(dtokp);
+}
+
+void
+rmgr::consume_filler()
+{
+ // Filler (Magic "RHMx") is one dblk by definition
+ _pg_offset_dblks++;
+ if (dblks_rem() == 0)
+ rotate_page();
+}
+
+iores
+rmgr::skip(data_tok* dtokp)
+{
+ u_int32_t dsize_dblks = jrec::size_dblks(dtokp->dsize());
+ u_int32_t tot_dblk_cnt = dtokp->dblocks_read();
+ while (true)
+ {
+ u_int32_t this_dblk_cnt = 0;
+ if (dsize_dblks - tot_dblk_cnt > dblks_rem())
+ this_dblk_cnt = dblks_rem();
+ else
+ this_dblk_cnt = dsize_dblks - tot_dblk_cnt;
+ if (this_dblk_cnt)
+ {
+ dtokp->incr_dblocks_read(this_dblk_cnt);
+ _pg_offset_dblks += this_dblk_cnt;
+ tot_dblk_cnt += this_dblk_cnt;
+ }
+ // If skip still incomplete, move to next page and decode again
+ if (tot_dblk_cnt < dsize_dblks)
+ {
+ if (dblks_rem() == 0)
+ rotate_page();
+ if (_page_cb_arr[_pg_index]._state != AIO_COMPLETE)
+ {
+ dtokp->set_rstate(data_tok::SKIP_PART);
+ return RHM_IORES_PAGE_AIOWAIT;
+ }
+ }
+ else
+ {
+ // Skip complete, put state back to unread
+ dtokp->set_rstate(data_tok::UNREAD);
+ dtokp->set_dsize(0);
+ dtokp->set_dblocks_read(0);
+
+ // If we have finished with this page, rotate it
+ if (dblks_rem() == 0)
+ rotate_page();
+ return RHM_IORES_SUCCESS;
+ }
+ }
+}
+
+iores
+rmgr::aio_cycle()
+{
+ // Perform validity checks
+ if (_fhdr_rd_outstanding) // read of file header still outstanding in aio
+ return RHM_IORES_SUCCESS;
+ if (!_rrfc.is_valid())
+ {
+ // Flush and reset all read states and pointers
+ flush(&jcntl::_aio_cmpl_timeout);
+
+ _jc->get_earliest_fid(); // determine initial file to read; calls _rrfc.set_findex() to set value
+ // If this file has not yet been written to, return RHM_IORES_EMPTY
+ if (_rrfc.is_void() && !_rrfc.is_wr_aio_outstanding())
+ return RHM_IORES_EMPTY;
+ init_file_header_read(); // send off AIO read request for file header
+ return RHM_IORES_SUCCESS;
+ }
+
+ int16_t first_uninit = -1;
+ u_int16_t num_uninit = 0;
+ u_int16_t num_compl = 0;
+ bool outstanding = false;
+ // Index must start with current buffer and cycle around so that first
+ // uninitialized buffer is initialized first
+ for (u_int16_t i=_pg_index; i<_pg_index+_cache_num_pages; i++)
+ {
+ int16_t ci = i % _cache_num_pages;
+ switch (_page_cb_arr[ci]._state)
+ {
+ case UNUSED:
+ if (first_uninit < 0)
+ first_uninit = ci;
+ num_uninit++;
+ break;
+ case IN_USE:
+ break;
+ case AIO_PENDING:
+ outstanding = true;
+ break;
+ case AIO_COMPLETE:
+ num_compl++;
+ break;
+ default:;
+ }
+ }
+ iores res = RHM_IORES_SUCCESS;
+ if (num_uninit)
+ res = init_aio_reads(first_uninit, num_uninit);
+ else if (num_compl == _cache_num_pages) // This condition exists after invalidation
+ res = init_aio_reads(0, _cache_num_pages);
+ if (outstanding)
+ get_events(AIO_COMPLETE, 0);
+ return res;
+}
+
+iores
+rmgr::init_aio_reads(const int16_t first_uninit, const u_int16_t num_uninit)
+{
+ for (int16_t i=0; i<num_uninit; i++)
+ {
+ if (_rrfc.is_void()) // Nothing to do; this file not yet written to
+ break;
+
+ if (_rrfc.subm_offs() == 0)
+ {
+ _rrfc.add_subm_cnt_dblks(JRNL_SBLK_SIZE);
+ _rrfc.add_cmpl_cnt_dblks(JRNL_SBLK_SIZE);
+ }
+
+ // TODO: Future perf improvement: Do a single AIO read for all available file
+ // space into all contiguous empty pages in one AIO operation.
+
+ u_int32_t file_rem_dblks = _rrfc.remaining_dblks();
+ file_rem_dblks -= file_rem_dblks % JRNL_SBLK_SIZE; // round down to closest sblk boundary
+ u_int32_t pg_size_dblks = JRNL_RMGR_PAGE_SIZE * JRNL_SBLK_SIZE;
+ u_int32_t rd_size = file_rem_dblks > pg_size_dblks ? pg_size_dblks : file_rem_dblks;
+ if (rd_size)
+ {
+ int16_t pi = (i + first_uninit) % _cache_num_pages;
+ // TODO: For perf, combine contiguous pages into single read
+ // 1 or 2 AIOs needed depending on whether read block folds
+ aio_cb* aiocbp = &_aio_cb_arr[pi];
+ aio::prep_pread_2(aiocbp, _rrfc.fh(), _page_ptr_arr[pi], rd_size * JRNL_DBLK_SIZE, _rrfc.subm_offs());
+ if (aio::submit(_ioctx, 1, &aiocbp) < 0)
+ throw jexception(jerrno::JERR__AIO, "rmgr", "init_aio_reads");
+ _rrfc.add_subm_cnt_dblks(rd_size);
+ _aio_evt_rem++;
+ _page_cb_arr[pi]._state = AIO_PENDING;
+ _page_cb_arr[pi]._rfh = _rrfc.file_controller();
+ }
+ else // If there is nothing to read for this page, neither will there be for the others...
+ break;
+ if (_rrfc.file_rotate())
+ _rrfc.rotate();
+ }
+ return RHM_IORES_SUCCESS;
+}
+
+void
+rmgr::rotate_page()
+{
+ _page_cb_arr[_pg_index]._rdblks = 0;
+ _page_cb_arr[_pg_index]._state = UNUSED;
+ if (_pg_offset_dblks >= JRNL_RMGR_PAGE_SIZE * JRNL_SBLK_SIZE)
+ {
+ _pg_offset_dblks = 0;
+ _pg_cntr++;
+ }
+ if (++_pg_index >= _cache_num_pages)
+ _pg_index = 0;
+ aio_cycle();
+ _pg_offset_dblks = 0;
+ // This counter is for bookkeeping only, page rotates are handled directly in init_aio_reads()
+ // FIXME: _pg_cntr should be sync'd with aio ops, not use of page as it is now...
+ // Need to move reset into if (_rrfc.file_rotate()) above.
+ if (_pg_cntr >= (_jc->jfsize_sblks() / JRNL_RMGR_PAGE_SIZE))
+ _pg_cntr = 0;
+}
+
+u_int32_t
+rmgr::dblks_rem() const
+{
+ return _page_cb_arr[_pg_index]._rdblks - _pg_offset_dblks;
+}
+
+void
+rmgr::set_params_null(void** const datapp, std::size_t& dsize, void** const xidpp, std::size_t& xidsize)
+{
+ *datapp = 0;
+ dsize = 0;
+ *xidpp = 0;
+ xidsize = 0;
+}
+
+void
+rmgr::init_file_header_read()
+{
+ _jc->fhdr_wr_sync(_rrfc.index()); // wait if the file header write is outstanding
+ int rfh = _rrfc.fh();
+ aio::prep_pread_2(_fhdr_aio_cb_ptr, rfh, _fhdr_buffer, _sblksize, 0);
+ if (aio::submit(_ioctx, 1, &_fhdr_aio_cb_ptr) < 0)
+ throw jexception(jerrno::JERR__AIO, "rmgr", "init_file_header_read");
+ _aio_evt_rem++;
+ _rrfc.add_subm_cnt_dblks(JRNL_SBLK_SIZE);
+ _fhdr_rd_outstanding = true;
+}
+
+/* TODO (sometime in the future)
+const iores
+rmgr::get(const u_int64_t& rid, const std::size_t& dsize, const std::size_t& dsize_avail,
+ const void** const data, bool auto_discard)
+{
+ return RHM_IORES_SUCCESS;
+}
+
+const iores
+rmgr::discard(data_tok* dtokp)
+{
+ return RHM_IORES_SUCCESS;
+}
+*/
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/rmgr.h b/cpp/src/qpid/legacystore/jrnl/rmgr.h
new file mode 100644
index 0000000000..ae4b5f56c8
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/rmgr.h
@@ -0,0 +1,114 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file rmgr.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::rmgr (read manager). See
+ * class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_RMGR_H
+#define QPID_LEGACYSTORE_JRNL_RMGR_H
+
+namespace mrg
+{
+namespace journal
+{
+class rmgr;
+}
+}
+
+#include <cstring>
+#include "jrnl/enums.h"
+#include "jrnl/file_hdr.h"
+#include "jrnl/pmgr.h"
+#include "jrnl/rec_hdr.h"
+#include "jrnl/rrfc.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \brief Class for managing a read page cache of arbitrary size and number of pages.
+ *
+ * The read page cache works on the principle of filling as many pages as possilbe in advance of
+ * reading the data. This ensures that delays caused by AIO operations are minimized.
+ */
+ class rmgr : public pmgr
+ {
+ private:
+ rrfc& _rrfc; ///< Ref to read rotating file controller
+ rec_hdr _hdr; ///< Header used to determind record type
+
+ void* _fhdr_buffer; ///< Buffer used for fhdr reads
+ aio_cb* _fhdr_aio_cb_ptr; ///< iocb pointer for fhdr reads
+ file_hdr _fhdr; ///< file header instance for reading file headers
+ bool _fhdr_rd_outstanding; ///< true if a fhdr read is outstanding
+
+ public:
+ rmgr(jcntl* jc, enq_map& emap, txn_map& tmap, rrfc& rrfc);
+ virtual ~rmgr();
+
+ using pmgr::initialize;
+ void initialize(aio_callback* const cbp);
+ iores read(void** const datapp, std::size_t& dsize, void** const xidpp,
+ std::size_t& xidsize, bool& transient, bool& external, data_tok* dtokp,
+ bool ignore_pending_txns);
+ int32_t get_events(page_state state, timespec* const timeout, bool flush = false);
+ void recover_complete();
+ inline iores synchronize() { if (_rrfc.is_valid()) return RHM_IORES_SUCCESS; return aio_cycle(); }
+ void invalidate();
+ bool wait_for_validity(timespec* const timeout, const bool throw_on_timeout = false);
+
+ /* TODO (if required)
+ const iores get(const u_int64_t& rid, const std::size_t& dsize, const std::size_t& dsize_avail,
+ const void** const data, bool auto_discard);
+ const iores discard(data_tok* dtok);
+ */
+
+ private:
+ void clean();
+ void flush(timespec* timeout);
+ iores pre_read_check(data_tok* dtokp);
+ iores read_enq(rec_hdr& h, void* rptr, data_tok* dtokp);
+ void consume_xid_rec(rec_hdr& h, void* rptr, data_tok* dtokp);
+ void consume_filler();
+ iores skip(data_tok* dtokp);
+ iores aio_cycle();
+ iores init_aio_reads(const int16_t first_uninit, const u_int16_t num_uninit);
+ void rotate_page();
+ u_int32_t dblks_rem() const;
+ void set_params_null(void** const datapp, std::size_t& dsize, void** const xidpp,
+ std::size_t& xidsize);
+ void init_file_header_read();
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_RMGR_H
diff --git a/cpp/src/qpid/legacystore/jrnl/rrfc.cpp b/cpp/src/qpid/legacystore/jrnl/rrfc.cpp
new file mode 100644
index 0000000000..fc6f5d427f
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/rrfc.cpp
@@ -0,0 +1,125 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file rrfc.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::rrfc (rotating
+ * file controller). See comments in file rrfc.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+
+#include "qpid/legacystore/jrnl/rrfc.h"
+
+#include <cerrno>
+#include <fcntl.h>
+#include <unistd.h>
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+rrfc::rrfc(const lpmgr* lpmp): rfc(lpmp), _fh(-1), _valid(false)
+{}
+
+rrfc::~rrfc()
+{
+ close_fh();
+}
+
+void
+rrfc::finalize()
+{
+ unset_findex();
+ rfc::finalize();
+}
+
+void
+rrfc::set_findex(const u_int16_t fc_index)
+{
+ rfc::set_findex(fc_index);
+ open_fh(_curr_fc->fname());
+}
+
+void
+rrfc::unset_findex()
+{
+ set_invalid();
+ close_fh();
+ rfc::unset_findex();
+}
+
+iores
+rrfc::rotate()
+{
+ if (!_lpmp->num_jfiles())
+ throw jexception(jerrno::JERR__NINIT, "rrfc", "rotate");
+ u_int16_t next_fc_index = _fc_index + 1;
+ if (next_fc_index == _lpmp->num_jfiles())
+ next_fc_index = 0;
+ set_findex(next_fc_index);
+ return RHM_IORES_SUCCESS;
+}
+
+std::string
+rrfc::status_str() const
+{
+ std::ostringstream oss;
+ oss << "rrfc: " << rfc::status_str();
+ if (is_active())
+ oss << " fcntl[" << _fc_index << "]: " << _curr_fc->status_str();
+ return oss.str();
+}
+
+// === protected functions ===
+
+void
+rrfc::open_fh(const std::string& fn)
+{
+ close_fh();
+ _fh = ::open(fn.c_str(), O_RDONLY | O_DIRECT);
+ if (_fh < 0)
+ {
+ std::ostringstream oss;
+ oss << "file=\"" << fn << "\"" << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR_RRFC_OPENRD, oss.str(), "rrfc", "open_fh");
+ }
+}
+
+void
+rrfc::close_fh()
+{
+ if (_fh >= 0)
+ {
+ ::close(_fh);
+ _fh = -1;
+ }
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/rrfc.h b/cpp/src/qpid/legacystore/jrnl/rrfc.h
new file mode 100644
index 0000000000..5066d6048a
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/rrfc.h
@@ -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.
+ *
+ */
+
+/**
+ * \file rrfc.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::rrfc (rotating
+ * file controller). See class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_RRFC_H
+#define QPID_LEGACYSTORE_JRNL_RRFC_H
+
+namespace mrg
+{
+namespace journal
+{
+class rrfc;
+}
+}
+
+#include "qpid/legacystore/jrnl/fcntl.h"
+#include "qpid/legacystore/jrnl/rfc.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \class rrfc
+ * \brief Read Rotating File Controller (rrfc) - Subclassed from pure virtual class rfc. Used to control the read
+ * pipeline in a rotating file buffer or journal. See class rfc for further details.
+ *
+ * The states that exist in this class are identical to class rfc from which it inherits, but in addition, the value
+ * of the read file handle _fh is also considered. The calls to set_findex also opens the file handle _fh to the
+ * active file for reading. Similarly, unset_findex() closes this file handle.
+ *
+ * <pre>
+ * is_init() is_active()
+ * +===+ _lpmp.is_init() == false
+ * +---------->| | Uninitialized: _curr_fc == 0 F F
+ * | +-->+===+ --+ _fh == -1
+ * | | |
+ * | | |
+ * | finalize() initialize()
+ * | | |
+ * | | |
+ * | +-- +===+<--+ _lpmp.is_init() == true
+ * finalize() | | Inactive: _curr_fc == 0 T F
+ * | +-->+===+ --+ _fh == -1
+ * | | |
+ * | | |
+ * | unset_findex() set_findex()
+ * | | |
+ * | | |
+ * | +-- +===+<--+ _lpmp.is_init() == true
+ * +---------- | | Active: _curr_fc != 0 T T
+ * +===+ _fh >= 0
+ * </pre>
+ *
+ * In adition to the states above, class rrfc contains a validity flag. This is operated indepenedently of the state
+ * machine. This flag (_valid) indicates when the read buffers are valid for reading. This is not strictly a state,
+ * but simply a flag used to keep track of the status, and is set/unset with calls to set_valid() and set_invalid()
+ * respectively.
+ */
+ class rrfc : public rfc
+ {
+ protected:
+ int _fh; ///< Read file handle
+ bool _valid; ///< Flag is true when read pages contain vailid data
+
+ public:
+ rrfc(const lpmgr* lpmp);
+ virtual ~rrfc();
+
+ /**
+ * \brief Initialize the controller, moving from state Uninitialized to Initialized. The main function of
+ * initialize() is to set the number of files and the pointer to the fcntl array.
+ */
+ inline void initialize() { rfc::initialize(); _valid = false; }
+
+ /**
+ * \brief Reset the controller to Uninitialized state, usually called when the journal is stopped. Once called,
+ * initialize() must be called to reuse an instance.
+ */
+ void finalize();
+
+ /**
+ * \brief Opens the file handle for reading a particular fid. Moves to state open.
+ */
+ void set_findex(const u_int16_t fc_index);
+
+ /**
+ * \brief Closes the read file handle and nulls the active fcntl pointer. Moves to state closed.
+ */
+ void unset_findex();
+
+ /**
+ * \brief Check the state: true = open; false = closed.
+ */
+ inline bool is_active() const { return _curr_fc != 0 && _fh >= 0; }
+
+ /**
+ * \brief Sets the validity flag which indicates that the read buffers contain valid data for reading.
+ */
+ inline void set_invalid() { _valid = false; }
+
+ /**
+ * \brief Resets the validity flag wich indicates that the read buffers are no longer synchronized and cannot
+ * be read whithout resynchronization.
+ */
+ inline void set_valid() { _valid = true; }
+
+ /**
+ * \brief Checks the read buffer validity status: true = valid, can be read; false = invalid, synchronization
+ * required.
+ */
+ inline bool is_valid() const { return _valid; }
+
+ /**
+ * \brief Rotate active file controller to next file in rotating file group.
+ * \exception jerrno::JERR__NINIT if called before calling initialize().
+ */
+ iores rotate();
+
+ inline int fh() const { return _fh; }
+
+ inline u_int32_t subm_cnt_dblks() const { return _curr_fc->rd_subm_cnt_dblks(); }
+ inline std::size_t subm_offs() const { return _curr_fc->rd_subm_offs(); }
+ inline u_int32_t add_subm_cnt_dblks(u_int32_t a) { return _curr_fc->add_rd_subm_cnt_dblks(a); }
+
+ inline u_int32_t cmpl_cnt_dblks() const { return _curr_fc->rd_cmpl_cnt_dblks(); }
+ inline std::size_t cmpl_offs() const { return _curr_fc->rd_cmpl_offs(); }
+ inline u_int32_t add_cmpl_cnt_dblks(u_int32_t a) { return _curr_fc->add_rd_cmpl_cnt_dblks(a); }
+
+ inline bool is_void() const { return _curr_fc->rd_void(); }
+ inline bool is_empty() const { return _curr_fc->rd_empty(); }
+ inline u_int32_t remaining_dblks() const { return _curr_fc->rd_remaining_dblks(); }
+ inline bool is_full() const { return _curr_fc->is_rd_full(); }
+ inline bool is_compl() const { return _curr_fc->is_rd_compl(); }
+ inline u_int32_t aio_outstanding_dblks() const { return _curr_fc->rd_aio_outstanding_dblks(); }
+ inline bool file_rotate() const { return _curr_fc->rd_file_rotate(); }
+ inline bool is_wr_aio_outstanding() const { return _curr_fc->wr_aio_outstanding_dblks() > 0; }
+
+ // Debug aid
+ std::string status_str() const;
+
+ protected:
+ void open_fh(const std::string& fn);
+ void close_fh();
+ }; // class rrfc
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_RRFC_H
diff --git a/cpp/src/qpid/legacystore/jrnl/slock.cpp b/cpp/src/qpid/legacystore/jrnl/slock.cpp
new file mode 100644
index 0000000000..8f26d349ef
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/slock.cpp
@@ -0,0 +1,33 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file slock.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::slock (scoped lock). See
+ * comments in file slock.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/slock.h"
diff --git a/cpp/src/qpid/legacystore/jrnl/slock.h b/cpp/src/qpid/legacystore/jrnl/slock.h
new file mode 100644
index 0000000000..c05b5cf336
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/slock.h
@@ -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.
+ *
+ */
+
+/**
+ * \file slock.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * Messaging journal scoped lock class mrg::journal::slock and scoped try-lock
+ * class mrg::journal::stlock.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_SLOCK_H
+#define QPID_LEGACYSTORE_JRNL_SLOCK_H
+
+#include "qpid/legacystore/jrnl/jexception.h"
+#include "qpid/legacystore/jrnl/smutex.h"
+#include <pthread.h>
+
+namespace mrg
+{
+namespace journal
+{
+
+ // Ultra-simple scoped lock class, auto-releases mutex when it goes out-of-scope
+ class slock
+ {
+ protected:
+ const smutex& _sm;
+ public:
+ inline slock(const smutex& sm) : _sm(sm)
+ {
+ PTHREAD_CHK(::pthread_mutex_lock(_sm.get()), "::pthread_mutex_lock", "slock", "slock");
+ }
+ inline ~slock()
+ {
+ PTHREAD_CHK(::pthread_mutex_unlock(_sm.get()), "::pthread_mutex_unlock", "slock", "~slock");
+ }
+ };
+
+ // Ultra-simple scoped try-lock class, auto-releases mutex when it goes out-of-scope
+ class stlock
+ {
+ protected:
+ const smutex& _sm;
+ bool _locked;
+ public:
+ inline stlock(const smutex& sm) : _sm(sm), _locked(false)
+ {
+ int ret = ::pthread_mutex_trylock(_sm.get());
+ _locked = (ret == 0); // check if lock obtained
+ if (!_locked && ret != EBUSY) PTHREAD_CHK(ret, "::pthread_mutex_trylock", "stlock", "stlock");
+ }
+ inline ~stlock()
+ {
+ if (_locked)
+ PTHREAD_CHK(::pthread_mutex_unlock(_sm.get()), "::pthread_mutex_unlock", "stlock", "~stlock");
+ }
+ inline bool locked() const { return _locked; }
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_SLOCK_H
diff --git a/cpp/src/qpid/legacystore/jrnl/smutex.cpp b/cpp/src/qpid/legacystore/jrnl/smutex.cpp
new file mode 100644
index 0000000000..6f8991ca5b
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/smutex.cpp
@@ -0,0 +1,33 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file smutex.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::smutex (scoped mutex). See
+ * comments in file smutex.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/smutex.h"
diff --git a/cpp/src/qpid/legacystore/jrnl/smutex.h b/cpp/src/qpid/legacystore/jrnl/smutex.h
new file mode 100644
index 0000000000..def0fb70f6
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/smutex.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file smutex.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * Messaging journal scoped mutex class mrg::journal::smutex.
+ *
+ * \author Kim van der Riet
+ */
+
+
+#ifndef QPID_LEGACYSTORE_JRNL_SMUTEX_H
+#define QPID_LEGACYSTORE_JRNL_SMUTEX_H
+
+#include "qpid/legacystore/jrnl/jexception.h"
+#include <pthread.h>
+
+namespace mrg
+{
+namespace journal
+{
+
+ // Ultra-simple scoped mutex class that allows a posix mutex to be initialized and destroyed with error checks
+ class smutex
+ {
+ protected:
+ mutable pthread_mutex_t _m;
+ public:
+ inline smutex()
+ {
+ PTHREAD_CHK(::pthread_mutex_init(&_m, 0), "::pthread_mutex_init", "smutex", "smutex");
+ }
+ inline virtual ~smutex()
+ {
+ PTHREAD_CHK(::pthread_mutex_destroy(&_m), "::pthread_mutex_destroy", "smutex", "~smutex");
+ }
+ inline pthread_mutex_t* get() const { return &_m; }
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_SMUTEX_H
diff --git a/cpp/src/qpid/legacystore/jrnl/time_ns.cpp b/cpp/src/qpid/legacystore/jrnl/time_ns.cpp
new file mode 100644
index 0000000000..976068ef68
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/time_ns.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.
+ *
+ */
+
+/**
+ * \file time_ns.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * Messaging journal time struct mrg::journal::time_ns, derived from
+ * the timespec struct and provided with helper functions.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/time_ns.h"
+
+#include <sstream>
+
+namespace mrg
+{
+namespace journal
+{
+
+const std::string
+time_ns::str(int precision) const
+{
+ const double t = tv_sec + (tv_nsec/1e9);
+ std::ostringstream oss;
+ oss.setf(std::ios::fixed, std::ios::floatfield);
+ oss.precision(precision);
+ oss << t;
+ return oss.str();
+}
+
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/time_ns.h b/cpp/src/qpid/legacystore/jrnl/time_ns.h
new file mode 100644
index 0000000000..a9f69e2631
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/time_ns.h
@@ -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.
+ *
+ */
+
+/**
+ * \file time_ns.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * Messaging journal time struct mrg::journal::time_ns, derived from
+ * the timespec struct and provided with helper functions.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_TIME_NS_H
+#define QPID_LEGACYSTORE_JRNL_TIME_NS_H
+
+#include <cerrno>
+#include <ctime>
+#include <string>
+
+namespace mrg
+{
+namespace journal
+{
+
+struct time_ns : public timespec
+{
+ inline time_ns() { tv_sec = 0; tv_nsec = 0; }
+ inline time_ns(const std::time_t sec, const long nsec = 0) { tv_sec = sec; tv_nsec = nsec; }
+ inline time_ns(const time_ns& t) { tv_sec = t.tv_sec; tv_nsec = t.tv_nsec; }
+
+ inline void set_zero() { tv_sec = 0; tv_nsec = 0; }
+ inline bool is_zero() const { return tv_sec == 0 && tv_nsec == 0; }
+ inline int now() { if(::clock_gettime(CLOCK_REALTIME, this)) return errno; return 0; }
+ const std::string str(int precision = 6) const;
+
+ inline time_ns& operator=(const time_ns& rhs)
+ { tv_sec = rhs.tv_sec; tv_nsec = rhs.tv_nsec; return *this; }
+ inline time_ns& operator+=(const time_ns& rhs)
+ {
+ tv_nsec += rhs.tv_nsec;
+ if (tv_nsec >= 1000000000L) { tv_sec++; tv_nsec -= 1000000000L; }
+ tv_sec += rhs.tv_sec;
+ return *this;
+ }
+ inline time_ns& operator+=(const long ns)
+ {
+ tv_nsec += ns;
+ if (tv_nsec >= 1000000000L) { tv_sec++; tv_nsec -= 1000000000L; }
+ return *this;
+ }
+ inline time_ns& operator-=(const long ns)
+ {
+ tv_nsec -= ns;
+ if (tv_nsec < 0) { tv_sec--; tv_nsec += 1000000000L; }
+ return *this;
+ }
+ inline time_ns& operator-=(const time_ns& rhs)
+ {
+ tv_nsec -= rhs.tv_nsec;
+ if (tv_nsec < 0) { tv_sec--; tv_nsec += 1000000000L; }
+ tv_sec -= rhs.tv_sec;
+ return *this;
+ }
+ inline const time_ns operator+(const time_ns& rhs)
+ { time_ns t(*this); t += rhs; return t; }
+ inline const time_ns operator-(const time_ns& rhs)
+ { time_ns t(*this); t -= rhs; return t; }
+ inline bool operator==(const time_ns& rhs)
+ { return tv_sec == rhs.tv_sec && tv_nsec == rhs.tv_nsec; }
+ inline bool operator!=(const time_ns& rhs)
+ { return tv_sec != rhs.tv_sec || tv_nsec != rhs.tv_nsec; }
+ inline bool operator>(const time_ns& rhs)
+ { if(tv_sec == rhs.tv_sec) return tv_nsec > rhs.tv_nsec; return tv_sec > rhs.tv_sec; }
+ inline bool operator>=(const time_ns& rhs)
+ { if(tv_sec == rhs.tv_sec) return tv_nsec >= rhs.tv_nsec; return tv_sec >= rhs.tv_sec; }
+ inline bool operator<(const time_ns& rhs)
+ { if(tv_sec == rhs.tv_sec) return tv_nsec < rhs.tv_nsec; return tv_sec < rhs.tv_sec; }
+ inline bool operator<=(const time_ns& rhs)
+ { if(tv_sec == rhs.tv_sec) return tv_nsec <= rhs.tv_nsec; return tv_sec <= rhs.tv_sec; }
+};
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_TIME_NS_H
diff --git a/cpp/src/qpid/legacystore/jrnl/txn_hdr.h b/cpp/src/qpid/legacystore/jrnl/txn_hdr.h
new file mode 100644
index 0000000000..94b812ccec
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/txn_hdr.h
@@ -0,0 +1,125 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file txn_hdr.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::txn_hdr (transaction
+ * record header), used to start a transaction (commit or abort) record.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_TXN_HDR_H
+#define QPID_LEGACYSTORE_JRNL_TXN_HDR_H
+
+#include <cstddef>
+#include "qpid/legacystore/jrnl/rec_hdr.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+#pragma pack(1)
+
+ /**
+ * \brief Struct for transaction commit and abort records.
+ *
+ * Struct for DTX commit and abort records. Only the magic distinguishes between them. Since
+ * this record must be used in the context of a valid XID, the xidsize field must not be zero.
+ * Immediately following this record is the XID itself which is xidsize bytes long, followed by
+ * a rec_tail.
+ *
+ * Note that this record had its own rid distinct from the rids of the record(s) making up the
+ * transaction it is committing or aborting.
+ *
+ * Record header info in binary format (24 bytes):
+ * <pre>
+ * 0 7
+ * +---+---+---+---+---+---+---+---+ -+
+ * | magic | v | e | flags | |
+ * +---+---+---+---+---+---+---+---+ | struct hdr
+ * | rid | |
+ * +---+---+---+---+---+---+---+---+ -+
+ * | xidsize |
+ * +---+---+---+---+---+---+---+---+
+ * v = file version (If the format or encoding of this file changes, then this
+ * number should be incremented)
+ * e = endian flag, false (0x00) for little endian, true (0x01) for big endian
+ * </pre>
+ *
+ * Note that journal files should be transferable between 32- and 64-bit
+ * hardware of the same endianness, but not between hardware of opposite
+ * entianness without some sort of binary conversion utility. Thus buffering
+ * will be needed for types that change size between 32- and 64-bit compiles.
+ */
+ struct txn_hdr : rec_hdr
+ {
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ u_int32_t _filler0; ///< Big-endian filler for 32-bit size_t
+#endif
+ std::size_t _xidsize; ///< XID size
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ u_int32_t _filler0; ///< Little-endian filler for 32-bit size_t
+#endif
+
+ /**
+ * \brief Default constructor, which sets all values to 0.
+ */
+ txn_hdr(): rec_hdr(),
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ _filler0(0),
+#endif
+ _xidsize(0)
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ , _filler0(0)
+#endif
+ {}
+
+ /**
+ * \brief Convenience constructor which initializes values during construction.
+ */
+ txn_hdr(const u_int32_t magic, const u_int8_t version, const u_int64_t rid,
+ const std::size_t xidsize, const bool owi): rec_hdr(magic, version, rid, owi),
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ _filler0(0),
+#endif
+ _xidsize(xidsize)
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ , _filler0(0)
+#endif
+ {}
+
+ /**
+ * \brief Returns the size of the header in bytes.
+ */
+ inline static std::size_t size() { return sizeof(txn_hdr); }
+ };
+
+#pragma pack()
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_TXN_HDR_H
diff --git a/cpp/src/qpid/legacystore/jrnl/txn_map.cpp b/cpp/src/qpid/legacystore/jrnl/txn_map.cpp
new file mode 100644
index 0000000000..c514670601
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/txn_map.cpp
@@ -0,0 +1,256 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file txn_map.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::txn_map (transaction map). See
+ * comments in file txn_map.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/txn_map.h"
+
+#include <iomanip>
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+#include "qpid/legacystore/jrnl/slock.h"
+#include <sstream>
+
+namespace mrg
+{
+namespace journal
+{
+
+// return/error codes
+int16_t txn_map::TMAP_RID_NOT_FOUND = -2;
+int16_t txn_map::TMAP_XID_NOT_FOUND = -1;
+int16_t txn_map::TMAP_OK = 0;
+int16_t txn_map::TMAP_NOT_SYNCED = 0;
+int16_t txn_map::TMAP_SYNCED = 1;
+
+txn_data_struct::txn_data_struct(const u_int64_t rid, const u_int64_t drid, const u_int16_t pfid,
+ const bool enq_flag, const bool commit_flag):
+ _rid(rid),
+ _drid(drid),
+ _pfid(pfid),
+ _enq_flag(enq_flag),
+ _commit_flag(commit_flag),
+ _aio_compl(false)
+{}
+
+txn_map::txn_map():
+ _map(),
+ _pfid_txn_cnt()
+{}
+
+txn_map::~txn_map() {}
+
+void
+txn_map::set_num_jfiles(const u_int16_t num_jfiles)
+{
+ _pfid_txn_cnt.resize(num_jfiles, 0);
+}
+
+u_int32_t
+txn_map::get_txn_pfid_cnt(const u_int16_t pfid) const
+{
+ return _pfid_txn_cnt.at(pfid);
+}
+
+bool
+txn_map::insert_txn_data(const std::string& xid, const txn_data& td)
+{
+ bool ok = true;
+ slock s(_mutex);
+ xmap_itr itr = _map.find(xid);
+ if (itr == _map.end()) // not found in map
+ {
+ txn_data_list list;
+ list.push_back(td);
+ std::pair<xmap_itr, bool> ret = _map.insert(xmap_param(xid, list));
+ if (!ret.second) // duplicate
+ ok = false;
+ }
+ else
+ itr->second.push_back(td);
+ _pfid_txn_cnt.at(td._pfid)++;
+ return ok;
+}
+
+const txn_data_list
+txn_map::get_tdata_list(const std::string& xid)
+{
+ slock s(_mutex);
+ return get_tdata_list_nolock(xid);
+}
+
+const txn_data_list
+txn_map::get_tdata_list_nolock(const std::string& xid)
+{
+ xmap_itr itr = _map.find(xid);
+ if (itr == _map.end()) // not found in map
+ return _empty_data_list;
+ return itr->second;
+}
+
+const txn_data_list
+txn_map::get_remove_tdata_list(const std::string& xid)
+{
+ slock s(_mutex);
+ xmap_itr itr = _map.find(xid);
+ if (itr == _map.end()) // not found in map
+ return _empty_data_list;
+ txn_data_list list = itr->second;
+ _map.erase(itr);
+ for (tdl_itr i=list.begin(); i!=list.end(); i++)
+ _pfid_txn_cnt.at(i->_pfid)--;
+ return list;
+}
+
+bool
+txn_map::in_map(const std::string& xid)
+{
+ slock s(_mutex);
+ xmap_itr itr= _map.find(xid);
+ return itr != _map.end();
+}
+
+u_int32_t
+txn_map::enq_cnt()
+{
+ return cnt(true);
+}
+
+u_int32_t
+txn_map::deq_cnt()
+{
+ return cnt(true);
+}
+
+u_int32_t
+txn_map::cnt(const bool enq_flag)
+{
+ slock s(_mutex);
+ u_int32_t c = 0;
+ for (xmap_itr i = _map.begin(); i != _map.end(); i++)
+ {
+ for (tdl_itr j = i->second.begin(); j < i->second.end(); j++)
+ {
+ if (j->_enq_flag == enq_flag)
+ c++;
+ }
+ }
+ return c;
+}
+
+int16_t
+txn_map::is_txn_synced(const std::string& xid)
+{
+ slock s(_mutex);
+ xmap_itr itr = _map.find(xid);
+ if (itr == _map.end()) // not found in map
+ return TMAP_XID_NOT_FOUND;
+ bool is_synced = true;
+ for (tdl_itr litr = itr->second.begin(); litr < itr->second.end(); litr++)
+ {
+ if (!litr->_aio_compl)
+ {
+ is_synced = false;
+ break;
+ }
+ }
+ return is_synced ? TMAP_SYNCED : TMAP_NOT_SYNCED;
+}
+
+int16_t
+txn_map::set_aio_compl(const std::string& xid, const u_int64_t rid)
+{
+ slock s(_mutex);
+ xmap_itr itr = _map.find(xid);
+ if (itr == _map.end()) // xid not found in map
+ return TMAP_XID_NOT_FOUND;
+ for (tdl_itr litr = itr->second.begin(); litr < itr->second.end(); litr++)
+ {
+ if (litr->_rid == rid)
+ {
+ litr->_aio_compl = true;
+ return TMAP_OK; // rid found
+ }
+ }
+ // xid present, but rid not found
+ return TMAP_RID_NOT_FOUND;
+}
+
+bool
+txn_map::data_exists(const std::string& xid, const u_int64_t rid)
+{
+ bool found = false;
+ {
+ slock s(_mutex);
+ txn_data_list tdl = get_tdata_list_nolock(xid);
+ tdl_itr itr = tdl.begin();
+ while (itr != tdl.end() && !found)
+ {
+ found = itr->_rid == rid;
+ itr++;
+ }
+ }
+ return found;
+}
+
+bool
+txn_map::is_enq(const u_int64_t rid)
+{
+ bool found = false;
+ {
+ slock s(_mutex);
+ for (xmap_itr i = _map.begin(); i != _map.end() && !found; i++)
+ {
+ txn_data_list list = i->second;
+ for (tdl_itr j = list.begin(); j < list.end() && !found; j++)
+ {
+ if (j->_enq_flag)
+ found = j->_rid == rid;
+ else
+ found = j->_drid == rid;
+ }
+ }
+ }
+ return found;
+}
+
+void
+txn_map::xid_list(std::vector<std::string>& xv)
+{
+ xv.clear();
+ {
+ slock s(_mutex);
+ for (xmap_itr itr = _map.begin(); itr != _map.end(); itr++)
+ xv.push_back(itr->first);
+ }
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/txn_map.h b/cpp/src/qpid/legacystore/jrnl/txn_map.h
new file mode 100644
index 0000000000..6b38564e53
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/txn_map.h
@@ -0,0 +1,159 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 txn_map.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::txn_map (transaction map).
+ * See class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_TXN_MAP_H
+#define QPID_LEGACYSTORE_JRNL_TXN_MAP_H
+
+namespace mrg
+{
+namespace journal
+{
+ class txn_map;
+}
+}
+
+#include "qpid/legacystore/jrnl/smutex.h"
+#include <map>
+#include <pthread.h>
+#include <string>
+#include <sys/types.h>
+#include <vector>
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \struct txn_data_struct
+ * \brief Struct encapsulating transaction data necessary for processing a transaction
+ * in the journal once it is closed with either a commit or abort.
+ */
+ struct txn_data_struct
+ {
+ u_int64_t _rid; ///< Record id for this operation
+ u_int64_t _drid; ///< Dequeue record id for this operation
+ u_int16_t _pfid; ///< Physical file id, to be used when transferring to emap on commit
+ bool _enq_flag; ///< If true, enq op, otherwise deq op
+ bool _commit_flag; ///< (2PC transactions) Records 2PC complete c/a mode
+ bool _aio_compl; ///< Initially false, set to true when record AIO returns
+ txn_data_struct(const u_int64_t rid, const u_int64_t drid, const u_int16_t pfid,
+ const bool enq_flag, const bool commit_flag = false);
+ };
+ typedef txn_data_struct txn_data;
+ typedef std::vector<txn_data> txn_data_list;
+ typedef txn_data_list::iterator tdl_itr;
+
+ /**
+ * \class txn_map
+ * \brief Class for storing transaction data for each open (ie not committed or aborted)
+ * xid in the store. If aborted, records are discarded; if committed, they are
+ * transferred to the enqueue map.
+ *
+ * The data is encapsulated by struct txn_data_struct. A vector containing the information
+ * for each operation included as part of the same transaction is mapped against the
+ * xid.
+ *
+ * The aio_compl flag is set true as each AIO write operation for the enqueue or dequeue
+ * returns. Checking that all of these flags are true for a given xid is the mechanism
+ * used to determine if the transaction is syncronized (through method is_txn_synced()).
+ *
+ * On transaction commit, then each operation is handled as follows:
+ *
+ * If an enqueue (_enq_flag is true), then the rid and pfid are transferred to the enq_map.
+ * If a dequeue (_enq_flag is false), then the rid stored in the drid field is used to
+ * remove the corresponding record from the enq_map.
+ *
+ * On transaction abort, then each operation is handled as follows:
+ *
+ * If an enqueue (_enq_flag is true), then the data is simply discarded.
+ * If a dequeue (_enq_flag is false), then the lock for the corresponding enqueue in enq_map
+ * (if not a part of the same transaction) is removed, and the data discarded.
+ *
+ * <pre>
+ * key data
+ *
+ * xid1 --- vector< [ rid, drid, pfid, enq_flag, commit_flag, aio_compl ] >
+ * xid2 --- vector< [ rid, drid, pfid, enq_flag, commit_flag, aio_compl ] >
+ * xid3 --- vector< [ rid, drid, pfid, enq_flag, commit_flag, aio_compl ] >
+ * ...
+ * </pre>
+ */
+ class txn_map
+ {
+ public:
+ // return/error codes
+ static int16_t TMAP_RID_NOT_FOUND;
+ static int16_t TMAP_XID_NOT_FOUND;
+ static int16_t TMAP_OK;
+ static int16_t TMAP_NOT_SYNCED;
+ static int16_t TMAP_SYNCED;
+
+ private:
+ typedef std::pair<std::string, txn_data_list> xmap_param;
+ typedef std::map<std::string, txn_data_list> xmap;
+ typedef xmap::iterator xmap_itr;
+
+ xmap _map;
+ smutex _mutex;
+ std::vector<u_int32_t> _pfid_txn_cnt;
+ const txn_data_list _empty_data_list;
+
+ public:
+ txn_map();
+ virtual ~txn_map();
+
+ void set_num_jfiles(const u_int16_t num_jfiles);
+ u_int32_t get_txn_pfid_cnt(const u_int16_t pfid) const;
+ bool insert_txn_data(const std::string& xid, const txn_data& td);
+ const txn_data_list get_tdata_list(const std::string& xid);
+ const txn_data_list get_remove_tdata_list(const std::string& xid);
+ bool in_map(const std::string& xid);
+ u_int32_t enq_cnt();
+ u_int32_t deq_cnt();
+ int16_t is_txn_synced(const std::string& xid); // -1=xid not found; 0=not synced; 1=synced
+ int16_t set_aio_compl(const std::string& xid, const u_int64_t rid); // -2=rid not found; -1=xid not found; 0=done
+ bool data_exists(const std::string& xid, const u_int64_t rid);
+ bool is_enq(const u_int64_t rid);
+ inline void clear() { _map.clear(); }
+ inline bool empty() const { return _map.empty(); }
+ inline size_t size() const { return _map.size(); }
+ void xid_list(std::vector<std::string>& xv);
+ private:
+ u_int32_t cnt(const bool enq_flag);
+ const txn_data_list get_tdata_list_nolock(const std::string& xid);
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_TXN_MAP_H
diff --git a/cpp/src/qpid/legacystore/jrnl/txn_rec.cpp b/cpp/src/qpid/legacystore/jrnl/txn_rec.cpp
new file mode 100644
index 0000000000..918a6ce902
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/txn_rec.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.
+ *
+ */
+
+/**
+ * \file txn_rec.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * This file contains the code for the mrg::journal::txn_rec (journal dequeue
+ * record) class. See comments in file txn_rec.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/txn_rec.h"
+
+#include <cassert>
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <iomanip>
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+#include <sstream>
+
+namespace mrg
+{
+namespace journal
+{
+
+txn_rec::txn_rec():
+ _txn_hdr(),
+ _xidp(0),
+ _buff(0),
+ _txn_tail()
+{
+ _txn_hdr._version = RHM_JDAT_VERSION;
+}
+
+txn_rec::txn_rec(const u_int32_t magic, const u_int64_t rid, const void* const xidp,
+ const std::size_t xidlen, const bool owi):
+ _txn_hdr(magic, RHM_JDAT_VERSION, rid, xidlen, owi),
+ _xidp(xidp),
+ _buff(0),
+ _txn_tail(_txn_hdr)
+{}
+
+txn_rec::~txn_rec()
+{
+ clean();
+}
+
+void
+txn_rec::reset(const u_int32_t magic)
+{
+ _txn_hdr._magic = magic;
+ _txn_hdr._rid = 0;
+ _txn_hdr._xidsize = 0;
+ _xidp = 0;
+ _buff = 0;
+ _txn_tail._xmagic = ~magic;
+ _txn_tail._rid = 0;
+}
+
+void
+txn_rec::reset(const u_int32_t magic, const u_int64_t rid, const void* const xidp,
+ const std::size_t xidlen, const bool owi)
+{
+ _txn_hdr._magic = magic;
+ _txn_hdr._rid = rid;
+ _txn_hdr.set_owi(owi);
+ _txn_hdr._xidsize = xidlen;
+ _xidp = xidp;
+ _buff = 0;
+ _txn_tail._xmagic = ~magic;
+ _txn_tail._rid = rid;
+}
+
+u_int32_t
+txn_rec::encode(void* wptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks)
+{
+ assert(wptr != 0);
+ assert(max_size_dblks > 0);
+ assert(_xidp != 0 && _txn_hdr._xidsize > 0);
+
+ std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE;
+ std::size_t rem = max_size_dblks * JRNL_DBLK_SIZE;
+ std::size_t wr_cnt = 0;
+ if (rec_offs_dblks) // Continuation of split dequeue record (over 2 or more pages)
+ {
+ if (size_dblks(rec_size()) - rec_offs_dblks > max_size_dblks) // Further split required
+ {
+ rec_offs -= sizeof(_txn_hdr);
+ std::size_t wsize = _txn_hdr._xidsize > rec_offs ? _txn_hdr._xidsize - rec_offs : 0;
+ std::size_t wsize2 = wsize;
+ if (wsize)
+ {
+ if (wsize > rem)
+ wsize = rem;
+ std::memcpy(wptr, (const char*)_xidp + rec_offs, wsize);
+ wr_cnt += wsize;
+ rem -= wsize;
+ }
+ rec_offs -= _txn_hdr._xidsize - wsize2;
+ if (rem)
+ {
+ wsize = sizeof(_txn_tail) > rec_offs ? sizeof(_txn_tail) - rec_offs : 0;
+ wsize2 = wsize;
+ if (wsize)
+ {
+ if (wsize > rem)
+ wsize = rem;
+ std::memcpy((char*)wptr + wr_cnt, (char*)&_txn_tail + rec_offs, wsize);
+ wr_cnt += wsize;
+ rem -= wsize;
+ }
+ rec_offs -= sizeof(_txn_tail) - wsize2;
+ }
+ assert(rem == 0);
+ assert(rec_offs == 0);
+ }
+ else // No further split required
+ {
+ rec_offs -= sizeof(_txn_hdr);
+ std::size_t wsize = _txn_hdr._xidsize > rec_offs ? _txn_hdr._xidsize - rec_offs : 0;
+ if (wsize)
+ {
+ std::memcpy(wptr, (const char*)_xidp + rec_offs, wsize);
+ wr_cnt += wsize;
+ }
+ rec_offs -= _txn_hdr._xidsize - wsize;
+ wsize = sizeof(_txn_tail) > rec_offs ? sizeof(_txn_tail) - rec_offs : 0;
+ if (wsize)
+ {
+ std::memcpy((char*)wptr + wr_cnt, (char*)&_txn_tail + rec_offs, wsize);
+ wr_cnt += wsize;
+#ifdef RHM_CLEAN
+ std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE;
+ std::size_t dblk_rec_size = size_dblks(rec_size() - rec_offs) * JRNL_DBLK_SIZE;
+ std::memset((char*)wptr + wr_cnt, RHM_CLEAN_CHAR, dblk_rec_size - wr_cnt);
+#endif
+ }
+ rec_offs -= sizeof(_txn_tail) - wsize;
+ assert(rec_offs == 0);
+ }
+ }
+ else // Start at beginning of data record
+ {
+ // Assumption: the header will always fit into the first dblk
+ std::memcpy(wptr, (void*)&_txn_hdr, sizeof(_txn_hdr));
+ wr_cnt = sizeof(_txn_hdr);
+ if (size_dblks(rec_size()) > max_size_dblks) // Split required
+ {
+ std::size_t wsize;
+ rem -= sizeof(_txn_hdr);
+ if (rem)
+ {
+ wsize = rem >= _txn_hdr._xidsize ? _txn_hdr._xidsize : rem;
+ std::memcpy((char*)wptr + wr_cnt, _xidp, wsize);
+ wr_cnt += wsize;
+ rem -= wsize;
+ }
+ if (rem)
+ {
+ wsize = rem >= sizeof(_txn_tail) ? sizeof(_txn_tail) : rem;
+ std::memcpy((char*)wptr + wr_cnt, (void*)&_txn_tail, wsize);
+ wr_cnt += wsize;
+ rem -= wsize;
+ }
+ assert(rem == 0);
+ }
+ else // No split required
+ {
+ std::memcpy((char*)wptr + wr_cnt, _xidp, _txn_hdr._xidsize);
+ wr_cnt += _txn_hdr._xidsize;
+ std::memcpy((char*)wptr + wr_cnt, (void*)&_txn_tail, sizeof(_txn_tail));
+ wr_cnt += sizeof(_txn_tail);
+#ifdef RHM_CLEAN
+ std::size_t dblk_rec_size = size_dblks(rec_size()) * JRNL_DBLK_SIZE;
+ std::memset((char*)wptr + wr_cnt, RHM_CLEAN_CHAR, dblk_rec_size - wr_cnt);
+#endif
+ }
+ }
+ return size_dblks(wr_cnt);
+}
+
+u_int32_t
+txn_rec::decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks)
+{
+ assert(rptr != 0);
+ assert(max_size_dblks > 0);
+
+ std::size_t rd_cnt = 0;
+ if (rec_offs_dblks) // Continuation of record on new page
+ {
+ const u_int32_t hdr_xid_dblks = size_dblks(txn_hdr::size() + _txn_hdr._xidsize);
+ const u_int32_t hdr_xid_tail_dblks = size_dblks(txn_hdr::size() + _txn_hdr._xidsize +
+ rec_tail::size());
+ const std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE;
+
+ if (hdr_xid_tail_dblks - rec_offs_dblks <= max_size_dblks)
+ {
+ // Remainder of xid fits within this page
+ if (rec_offs - txn_hdr::size() < _txn_hdr._xidsize)
+ {
+ // Part of xid still outstanding, copy remainder of xid and tail
+ const std::size_t xid_offs = rec_offs - txn_hdr::size();
+ const std::size_t xid_rem = _txn_hdr._xidsize - xid_offs;
+ std::memcpy((char*)_buff + xid_offs, rptr, xid_rem);
+ rd_cnt = xid_rem;
+ std::memcpy((void*)&_txn_tail, ((char*)rptr + rd_cnt), sizeof(_txn_tail));
+ chk_tail();
+ rd_cnt += sizeof(_txn_tail);
+ }
+ else
+ {
+ // Tail or part of tail only outstanding, complete tail
+ const std::size_t tail_offs = rec_offs - txn_hdr::size() - _txn_hdr._xidsize;
+ const std::size_t tail_rem = rec_tail::size() - tail_offs;
+ std::memcpy((char*)&_txn_tail + tail_offs, rptr, tail_rem);
+ chk_tail();
+ rd_cnt = tail_rem;
+ }
+ }
+ else if (hdr_xid_dblks - rec_offs_dblks <= max_size_dblks)
+ {
+ // Remainder of xid fits within this page, tail split
+ const std::size_t xid_offs = rec_offs - txn_hdr::size();
+ const std::size_t xid_rem = _txn_hdr._xidsize - xid_offs;
+ std::memcpy((char*)_buff + xid_offs, rptr, xid_rem);
+ rd_cnt += xid_rem;
+ const std::size_t tail_rem = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt;
+ if (tail_rem)
+ {
+ std::memcpy((void*)&_txn_tail, ((char*)rptr + xid_rem), tail_rem);
+ rd_cnt += tail_rem;
+ }
+ }
+ else
+ {
+ // Remainder of xid split
+ const std::size_t xid_cp_size = (max_size_dblks * JRNL_DBLK_SIZE);
+ std::memcpy((char*)_buff + rec_offs - txn_hdr::size(), rptr, xid_cp_size);
+ rd_cnt += xid_cp_size;
+ }
+ }
+ else // Start of record
+ {
+ // Get and check header
+ _txn_hdr.hdr_copy(h);
+ rd_cnt = sizeof(rec_hdr);
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ rd_cnt += sizeof(u_int32_t); // Filler 0
+#endif
+ _txn_hdr._xidsize = *(std::size_t*)((char*)rptr + rd_cnt);
+ rd_cnt = _txn_hdr.size();
+ chk_hdr();
+ _buff = std::malloc(_txn_hdr._xidsize);
+ MALLOC_CHK(_buff, "_buff", "txn_rec", "decode");
+ const u_int32_t hdr_xid_dblks = size_dblks(txn_hdr::size() + _txn_hdr._xidsize);
+ const u_int32_t hdr_xid_tail_dblks = size_dblks(txn_hdr::size() + _txn_hdr._xidsize +
+ rec_tail::size());
+
+ // Check if record (header + xid + tail) fits within this page, we can check the
+ // tail before the expense of copying data to memory
+ if (hdr_xid_tail_dblks <= max_size_dblks)
+ {
+ // Entire header, xid and tail fits within this page
+ std::memcpy(_buff, (char*)rptr + rd_cnt, _txn_hdr._xidsize);
+ rd_cnt += _txn_hdr._xidsize;
+ std::memcpy((void*)&_txn_tail, (char*)rptr + rd_cnt, sizeof(_txn_tail));
+ rd_cnt += sizeof(_txn_tail);
+ chk_tail();
+ }
+ else if (hdr_xid_dblks <= max_size_dblks)
+ {
+ // Entire header and xid fit within this page, tail split
+ std::memcpy(_buff, (char*)rptr + rd_cnt, _txn_hdr._xidsize);
+ rd_cnt += _txn_hdr._xidsize;
+ const std::size_t tail_rem = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt;
+ if (tail_rem)
+ {
+ std::memcpy((void*)&_txn_tail, (char*)rptr + rd_cnt, tail_rem);
+ rd_cnt += tail_rem;
+ }
+ }
+ else
+ {
+ // Header fits within this page, xid split
+ const std::size_t xid_cp_size = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt;
+ std::memcpy(_buff, (char*)rptr + rd_cnt, xid_cp_size);
+ rd_cnt += xid_cp_size;
+ }
+ }
+ return size_dblks(rd_cnt);
+}
+
+bool
+txn_rec::rcv_decode(rec_hdr h, std::ifstream* ifsp, std::size_t& rec_offs)
+{
+ if (rec_offs == 0)
+ {
+ // Read header, allocate for xid
+ _txn_hdr.hdr_copy(h);
+#if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT)
+ ifsp->ignore(sizeof(u_int32_t)); // _filler0
+#endif
+ ifsp->read((char*)&_txn_hdr._xidsize, sizeof(std::size_t));
+#if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT)
+ ifsp->ignore(sizeof(u_int32_t)); // _filler0
+#endif
+ rec_offs = sizeof(_txn_hdr);
+ _buff = std::malloc(_txn_hdr._xidsize);
+ MALLOC_CHK(_buff, "_buff", "txn_rec", "rcv_decode");
+ }
+ if (rec_offs < sizeof(_txn_hdr) + _txn_hdr._xidsize)
+ {
+ // Read xid (or continue reading xid)
+ std::size_t offs = rec_offs - sizeof(_txn_hdr);
+ ifsp->read((char*)_buff + offs, _txn_hdr._xidsize - offs);
+ std::size_t size_read = ifsp->gcount();
+ rec_offs += size_read;
+ if (size_read < _txn_hdr._xidsize - offs)
+ {
+ assert(ifsp->eof());
+ // As we may have read past eof, turn off fail bit
+ ifsp->clear(ifsp->rdstate()&(~std::ifstream::failbit));
+ assert(!ifsp->fail() && !ifsp->bad());
+ return false;
+ }
+ }
+ if (rec_offs < sizeof(_txn_hdr) + _txn_hdr._xidsize + sizeof(rec_tail))
+ {
+ // Read tail (or continue reading tail)
+ std::size_t offs = rec_offs - sizeof(_txn_hdr) - _txn_hdr._xidsize;
+ ifsp->read((char*)&_txn_tail + offs, sizeof(rec_tail) - offs);
+ std::size_t size_read = ifsp->gcount();
+ rec_offs += size_read;
+ if (size_read < sizeof(rec_tail) - offs)
+ {
+ assert(ifsp->eof());
+ // As we may have read past eof, turn off fail bit
+ ifsp->clear(ifsp->rdstate()&(~std::ifstream::failbit));
+ assert(!ifsp->fail() && !ifsp->bad());
+ return false;
+ }
+ }
+ ifsp->ignore(rec_size_dblks() * JRNL_DBLK_SIZE - rec_size());
+ chk_tail(); // Throws if tail invalid or record incomplete
+ assert(!ifsp->fail() && !ifsp->bad());
+ return true;
+}
+
+std::size_t
+txn_rec::get_xid(void** const xidpp)
+{
+ if (!_buff)
+ {
+ *xidpp = 0;
+ return 0;
+ }
+ *xidpp = _buff;
+ return _txn_hdr._xidsize;
+}
+
+std::string&
+txn_rec::str(std::string& str) const
+{
+ std::ostringstream oss;
+ if (_txn_hdr._magic == RHM_JDAT_TXA_MAGIC)
+ oss << "dtxa_rec: m=" << _txn_hdr._magic;
+ else
+ oss << "dtxc_rec: m=" << _txn_hdr._magic;
+ oss << " v=" << (int)_txn_hdr._version;
+ oss << " rid=" << _txn_hdr._rid;
+ oss << " xid=\"" << _xidp << "\"";
+ str.append(oss.str());
+ return str;
+}
+
+std::size_t
+txn_rec::xid_size() const
+{
+ return _txn_hdr._xidsize;
+}
+
+std::size_t
+txn_rec::rec_size() const
+{
+ return txn_hdr::size() + _txn_hdr._xidsize + rec_tail::size();
+}
+
+void
+txn_rec::chk_hdr() const
+{
+ jrec::chk_hdr(_txn_hdr);
+ if (_txn_hdr._magic != RHM_JDAT_TXA_MAGIC && _txn_hdr._magic != RHM_JDAT_TXC_MAGIC)
+ {
+ std::ostringstream oss;
+ oss << std::hex << std::setfill('0');
+ oss << "dtx magic: rid=0x" << std::setw(16) << _txn_hdr._rid;
+ oss << ": expected=(0x" << std::setw(8) << RHM_JDAT_TXA_MAGIC;
+ oss << " or 0x" << RHM_JDAT_TXC_MAGIC;
+ oss << ") read=0x" << std::setw(2) << (int)_txn_hdr._magic;
+ throw jexception(jerrno::JERR_JREC_BADRECHDR, oss.str(), "txn_rec", "chk_hdr");
+ }
+}
+
+void
+txn_rec::chk_hdr(u_int64_t rid) const
+{
+ chk_hdr();
+ jrec::chk_rid(_txn_hdr, rid);
+}
+
+void
+txn_rec::chk_tail() const
+{
+ jrec::chk_tail(_txn_tail, _txn_hdr);
+}
+
+void
+txn_rec::clean()
+{
+ // clean up allocated memory here
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/txn_rec.h b/cpp/src/qpid/legacystore/jrnl/txn_rec.h
new file mode 100644
index 0000000000..1a49df1c96
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/txn_rec.h
@@ -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.
+ *
+ */
+
+/**
+ * \file txn_rec.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * This file contains the code for the mrg::journal::txn_rec (journal data
+ * record) class. See class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_TXN_REC_H
+#define QPID_LEGACYSTORE_JRNL_TXN_REC_H
+
+namespace mrg
+{
+namespace journal
+{
+class txn_rec;
+}
+}
+
+#include <cstddef>
+#include "qpid/legacystore/jrnl/jrec.h"
+#include "qpid/legacystore/jrnl/txn_hdr.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \class txn_rec
+ * \brief Class to handle a single journal DTX commit or abort record.
+ */
+ class txn_rec : public jrec
+ {
+ private:
+ txn_hdr _txn_hdr; ///< transaction header
+ const void* _xidp; ///< xid pointer for encoding (writing to disk)
+ void* _buff; ///< Pointer to buffer to receive data read from disk
+ rec_tail _txn_tail; ///< Record tail
+
+ public:
+ // constructor used for read operations and xid must have memory allocated
+ txn_rec();
+ // constructor used for write operations, where xid already exists
+ txn_rec(const u_int32_t magic, const u_int64_t rid, const void* const xidp,
+ const std::size_t xidlen, const bool owi);
+ virtual ~txn_rec();
+
+ // Prepare instance for use in reading data from journal
+ void reset(const u_int32_t magic);
+ // Prepare instance for use in writing data to journal
+ void reset(const u_int32_t magic, const u_int64_t rid, const void* const xidp,
+ const std::size_t xidlen, const bool owi);
+ u_int32_t encode(void* wptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks);
+ u_int32_t decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks,
+ u_int32_t max_size_dblks);
+ // Decode used for recover
+ bool rcv_decode(rec_hdr h, std::ifstream* ifsp, std::size_t& rec_offs);
+
+ std::size_t get_xid(void** const xidpp);
+ std::string& str(std::string& str) const;
+ inline std::size_t data_size() const { return 0; } // This record never carries data
+ std::size_t xid_size() const;
+ std::size_t rec_size() const;
+ inline u_int64_t rid() const { return _txn_hdr._rid; }
+
+ private:
+ void chk_hdr() const;
+ void chk_hdr(u_int64_t rid) const;
+ void chk_tail() const;
+ virtual void clean();
+ }; // class txn_rec
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_TXN_REC_H
diff --git a/cpp/src/qpid/legacystore/jrnl/wmgr.cpp b/cpp/src/qpid/legacystore/jrnl/wmgr.cpp
new file mode 100644
index 0000000000..4353fcfbca
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/wmgr.cpp
@@ -0,0 +1,1051 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 wmgr.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::wmgr (write manager). See
+ * comments in file wmgr.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/wmgr.h"
+
+#include <cassert>
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include "qpid/legacystore/jrnl/file_hdr.h"
+#include "qpid/legacystore/jrnl/jcntl.h"
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include <sstream>
+
+namespace mrg
+{
+namespace journal
+{
+
+wmgr::wmgr(jcntl* jc, enq_map& emap, txn_map& tmap, wrfc& wrfc):
+ pmgr(jc, emap, tmap),
+ _wrfc(wrfc),
+ _max_dtokpp(0),
+ _max_io_wait_us(0),
+ _fhdr_base_ptr(0),
+ _fhdr_ptr_arr(0),
+ _fhdr_aio_cb_arr(0),
+ _cached_offset_dblks(0),
+ _jfsize_dblks(0),
+ _jfsize_pgs(0),
+ _num_jfiles(0),
+ _enq_busy(false),
+ _deq_busy(false),
+ _abort_busy(false),
+ _commit_busy(false),
+ _txn_pending_set()
+{}
+
+wmgr::wmgr(jcntl* jc, enq_map& emap, txn_map& tmap, wrfc& wrfc,
+ const u_int32_t max_dtokpp, const u_int32_t max_iowait_us):
+ pmgr(jc, emap, tmap /* , dtoklp */),
+ _wrfc(wrfc),
+ _max_dtokpp(max_dtokpp),
+ _max_io_wait_us(max_iowait_us),
+ _fhdr_base_ptr(0),
+ _fhdr_ptr_arr(0),
+ _fhdr_aio_cb_arr(0),
+ _cached_offset_dblks(0),
+ _jfsize_dblks(0),
+ _jfsize_pgs(0),
+ _num_jfiles(0),
+ _enq_busy(false),
+ _deq_busy(false),
+ _abort_busy(false),
+ _commit_busy(false),
+ _txn_pending_set()
+{}
+
+wmgr::~wmgr()
+{
+ wmgr::clean();
+}
+
+void
+wmgr::initialize(aio_callback* const cbp, const u_int32_t wcache_pgsize_sblks,
+ const u_int16_t wcache_num_pages, const u_int32_t max_dtokpp, const u_int32_t max_iowait_us,
+ std::size_t eo)
+{
+ _enq_busy = false;
+ _deq_busy = false;
+ _abort_busy = false;
+ _commit_busy = false;
+ _max_dtokpp = max_dtokpp;
+ _max_io_wait_us = max_iowait_us;
+
+ initialize(cbp, wcache_pgsize_sblks, wcache_num_pages);
+
+ _jfsize_dblks = _jc->jfsize_sblks() * JRNL_SBLK_SIZE;
+ _jfsize_pgs = _jc->jfsize_sblks() / _cache_pgsize_sblks;
+ assert(_jc->jfsize_sblks() % JRNL_RMGR_PAGE_SIZE == 0);
+
+ if (eo)
+ {
+ const u_int32_t wr_pg_size_dblks = _cache_pgsize_sblks * JRNL_SBLK_SIZE;
+ u_int32_t data_dblks = (eo / JRNL_DBLK_SIZE) - 4; // 4 dblks for file hdr
+ _pg_cntr = data_dblks / wr_pg_size_dblks;
+ _pg_offset_dblks = data_dblks - (_pg_cntr * wr_pg_size_dblks);
+ }
+}
+
+iores
+wmgr::enqueue(const void* const data_buff, const std::size_t tot_data_len,
+ const std::size_t this_data_len, data_tok* dtokp, const void* const xid_ptr,
+ const std::size_t xid_len, const bool transient, const bool external)
+{
+ if (xid_len)
+ assert(xid_ptr != 0);
+
+ if (_deq_busy || _abort_busy || _commit_busy)
+ return RHM_IORES_BUSY;
+
+ if (this_data_len != tot_data_len && !external)
+ return RHM_IORES_NOTIMPL;
+
+ iores res = pre_write_check(WMGR_ENQUEUE, dtokp, xid_len, tot_data_len, external);
+ if (res != RHM_IORES_SUCCESS)
+ return res;
+
+ bool cont = false;
+ if (_enq_busy) // If enqueue() exited last time with RHM_IORES_FULL or RHM_IORES_PAGE_AIOWAIT
+ {
+ if (dtokp->wstate() == data_tok::ENQ_PART)
+ cont = true;
+ else
+ {
+ std::ostringstream oss;
+ oss << "This data_tok: id=" << dtokp->id() << " state=" << dtokp->wstate_str();
+ throw jexception(jerrno::JERR_WMGR_ENQDISCONT, oss.str(), "wmgr", "enqueue");
+ }
+ }
+
+ u_int64_t rid = (dtokp->external_rid() | cont) ? dtokp->rid() : _wrfc.get_incr_rid();
+ _enq_rec.reset(rid, data_buff, tot_data_len, xid_ptr, xid_len, _wrfc.owi(), transient,
+ external);
+ if (!cont)
+ {
+ dtokp->set_rid(rid);
+ dtokp->set_dequeue_rid(0);
+ if (xid_len)
+ dtokp->set_xid(xid_ptr, xid_len);
+ else
+ dtokp->clear_xid();
+ _enq_busy = true;
+ }
+ bool done = false;
+ while (!done)
+ {
+ assert(_pg_offset_dblks < _cache_pgsize_sblks * JRNL_SBLK_SIZE);
+ void* wptr = (void*)((char*)_page_ptr_arr[_pg_index] + _pg_offset_dblks * JRNL_DBLK_SIZE);
+ u_int32_t data_offs_dblks = dtokp->dblocks_written();
+ u_int32_t ret = _enq_rec.encode(wptr, data_offs_dblks,
+ (_cache_pgsize_sblks * JRNL_SBLK_SIZE) - _pg_offset_dblks);
+
+ // Remember fid which contains the record header in case record is split over several files
+ if (data_offs_dblks == 0)
+ dtokp->set_fid(_wrfc.index());
+ _pg_offset_dblks += ret;
+ _cached_offset_dblks += ret;
+ dtokp->incr_dblocks_written(ret);
+ dtokp->incr_pg_cnt();
+ _page_cb_arr[_pg_index]._pdtokl->push_back(dtokp);
+
+ // Is the encoding of this record complete?
+ if (dtokp->dblocks_written() >= _enq_rec.rec_size_dblks())
+ {
+ // TODO: Incorrect - must set state to ENQ_CACHED; ENQ_SUBM is set when AIO returns.
+ dtokp->set_wstate(data_tok::ENQ_SUBM);
+ dtokp->set_dsize(tot_data_len);
+ // Only add this data token to page token list when submit is complete, this way
+ // long multi-page messages have their token on the page containing the END of the
+ // message. AIO callbacks will then only process this token when entire message is
+ // enqueued.
+ _wrfc.incr_enqcnt(dtokp->fid());
+
+ if (xid_len) // If part of transaction, add to transaction map
+ {
+ std::string xid((const char*)xid_ptr, xid_len);
+ _tmap.insert_txn_data(xid, txn_data(rid, 0, dtokp->fid(), true));
+ }
+ else
+ {
+ if (_emap.insert_pfid(rid, dtokp->fid()) < enq_map::EMAP_OK) // fail
+ {
+ // The only error code emap::insert_pfid() returns is enq_map::EMAP_DUP_RID.
+ std::ostringstream oss;
+ oss << std::hex << "rid=0x" << rid << " _pfid=0x" << dtokp->fid();
+ throw jexception(jerrno::JERR_MAP_DUPLICATE, oss.str(), "wmgr", "enqueue");
+ }
+ }
+
+ done = true;
+ }
+ else
+ dtokp->set_wstate(data_tok::ENQ_PART);
+
+ file_header_check(rid, cont, _enq_rec.rec_size_dblks() - data_offs_dblks);
+ flush_check(res, cont, done);
+ }
+ if (dtokp->wstate() >= data_tok::ENQ_SUBM)
+ _enq_busy = false;
+ return res;
+}
+
+iores
+wmgr::dequeue(data_tok* dtokp, const void* const xid_ptr, const std::size_t xid_len, const bool txn_coml_commit)
+{
+ if (xid_len)
+ assert(xid_ptr != 0);
+
+ if (_enq_busy || _abort_busy || _commit_busy)
+ return RHM_IORES_BUSY;
+
+ iores res = pre_write_check(WMGR_DEQUEUE, dtokp);
+ if (res != RHM_IORES_SUCCESS)
+ return res;
+
+ bool cont = false;
+ if (_deq_busy) // If dequeue() exited last time with RHM_IORES_FULL or RHM_IORES_PAGE_AIOWAIT
+ {
+ if (dtokp->wstate() == data_tok::DEQ_PART)
+ cont = true;
+ else
+ {
+ std::ostringstream oss;
+ oss << "This data_tok: id=" << dtokp->id() << " state=" << dtokp->wstate_str();
+ throw jexception(jerrno::JERR_WMGR_DEQDISCONT, oss.str(), "wmgr", "dequeue");
+ }
+ }
+
+ const bool ext_rid = dtokp->external_rid();
+ u_int64_t rid = (ext_rid | cont) ? dtokp->rid() : _wrfc.get_incr_rid();
+ u_int64_t dequeue_rid = (ext_rid | cont) ? dtokp->dequeue_rid() : dtokp->rid();
+ _deq_rec.reset(rid, dequeue_rid, xid_ptr, xid_len, _wrfc.owi(), txn_coml_commit);
+ if (!cont)
+ {
+ if (!ext_rid)
+ {
+ dtokp->set_rid(rid);
+ dtokp->set_dequeue_rid(dequeue_rid);
+ }
+ if (xid_len)
+ dtokp->set_xid(xid_ptr, xid_len);
+ else
+ dtokp->clear_xid();
+ dequeue_check(dtokp->xid(), dequeue_rid);
+ dtokp->set_dblocks_written(0); // Reset dblks_written from previous op
+ _deq_busy = true;
+ }
+ bool done = false;
+ while (!done)
+ {
+ assert(_pg_offset_dblks < _cache_pgsize_sblks * JRNL_SBLK_SIZE);
+ void* wptr = (void*)((char*)_page_ptr_arr[_pg_index] + _pg_offset_dblks * JRNL_DBLK_SIZE);
+ u_int32_t data_offs_dblks = dtokp->dblocks_written();
+ u_int32_t ret = _deq_rec.encode(wptr, data_offs_dblks,
+ (_cache_pgsize_sblks * JRNL_SBLK_SIZE) - _pg_offset_dblks);
+
+ // Remember fid which contains the record header in case record is split over several files
+ if (data_offs_dblks == 0)
+ dtokp->set_fid(_wrfc.index());
+ _pg_offset_dblks += ret;
+ _cached_offset_dblks += ret;
+ dtokp->incr_dblocks_written(ret);
+ dtokp->incr_pg_cnt();
+ _page_cb_arr[_pg_index]._pdtokl->push_back(dtokp);
+
+ // Is the encoding of this record complete?
+ if (dtokp->dblocks_written() >= _deq_rec.rec_size_dblks())
+ {
+ // TODO: Incorrect - must set state to ENQ_CACHED; ENQ_SUBM is set when AIO returns.
+ dtokp->set_wstate(data_tok::DEQ_SUBM);
+
+ if (xid_len) // If part of transaction, add to transaction map
+ {
+ // If the enqueue is part of a pending txn, it will not yet be in emap
+ _emap.lock(dequeue_rid); // ignore rid not found error
+ std::string xid((const char*)xid_ptr, xid_len);
+ _tmap.insert_txn_data(xid, txn_data(rid, dequeue_rid, dtokp->fid(), false));
+ }
+ else
+ {
+ int16_t fid = _emap.get_remove_pfid(dtokp->dequeue_rid());
+ if (fid < enq_map::EMAP_OK) // fail
+ {
+ if (fid == enq_map::EMAP_RID_NOT_FOUND)
+ {
+ std::ostringstream oss;
+ oss << std::hex << "rid=0x" << rid;
+ throw jexception(jerrno::JERR_MAP_NOTFOUND, oss.str(), "wmgr", "dequeue");
+ }
+ if (fid == enq_map::EMAP_LOCKED)
+ {
+ std::ostringstream oss;
+ oss << std::hex << "rid=0x" << rid;
+ throw jexception(jerrno::JERR_MAP_LOCKED, oss.str(), "wmgr", "dequeue");
+ }
+ }
+ _wrfc.decr_enqcnt(fid);
+ }
+
+ done = true;
+ }
+ else
+ dtokp->set_wstate(data_tok::DEQ_PART);
+
+ file_header_check(rid, cont, _deq_rec.rec_size_dblks() - data_offs_dblks);
+ flush_check(res, cont, done);
+ }
+ if (dtokp->wstate() >= data_tok::DEQ_SUBM)
+ _deq_busy = false;
+ return res;
+}
+
+iores
+wmgr::abort(data_tok* dtokp, const void* const xid_ptr, const std::size_t xid_len)
+{
+ // commit and abort MUST have a valid xid
+ assert(xid_ptr != 0 && xid_len > 0);
+
+ if (_enq_busy || _deq_busy || _commit_busy)
+ return RHM_IORES_BUSY;
+
+ iores res = pre_write_check(WMGR_ABORT, dtokp);
+ if (res != RHM_IORES_SUCCESS)
+ return res;
+
+ bool cont = false;
+ if (_abort_busy) // If abort() exited last time with RHM_IORES_FULL or RHM_IORES_PAGE_AIOWAIT
+ {
+ if (dtokp->wstate() == data_tok::ABORT_PART)
+ cont = true;
+ else
+ {
+ std::ostringstream oss;
+ oss << "This data_tok: id=" << dtokp->id() << " state=" << dtokp->wstate_str();
+ throw jexception(jerrno::JERR_WMGR_DEQDISCONT, oss.str(), "wmgr", "abort");
+ }
+ }
+
+ u_int64_t rid = (dtokp->external_rid() | cont) ? dtokp->rid() : _wrfc.get_incr_rid();
+ _txn_rec.reset(RHM_JDAT_TXA_MAGIC, rid, xid_ptr, xid_len, _wrfc.owi());
+ if (!cont)
+ {
+ dtokp->set_rid(rid);
+ dtokp->set_dequeue_rid(0);
+ dtokp->set_xid(xid_ptr, xid_len);
+ dtokp->set_dblocks_written(0); // Reset dblks_written from previous op
+ _abort_busy = true;
+ }
+ bool done = false;
+ while (!done)
+ {
+ assert(_pg_offset_dblks < _cache_pgsize_sblks * JRNL_SBLK_SIZE);
+ void* wptr = (void*)((char*)_page_ptr_arr[_pg_index] + _pg_offset_dblks * JRNL_DBLK_SIZE);
+ u_int32_t data_offs_dblks = dtokp->dblocks_written();
+ u_int32_t ret = _txn_rec.encode(wptr, data_offs_dblks,
+ (_cache_pgsize_sblks * JRNL_SBLK_SIZE) - _pg_offset_dblks);
+
+ // Remember fid which contains the record header in case record is split over several files
+ if (data_offs_dblks == 0)
+ dtokp->set_fid(_wrfc.index());
+ _pg_offset_dblks += ret;
+ _cached_offset_dblks += ret;
+ dtokp->incr_dblocks_written(ret);
+ dtokp->incr_pg_cnt();
+ _page_cb_arr[_pg_index]._pdtokl->push_back(dtokp);
+
+ // Is the encoding of this record complete?
+ if (dtokp->dblocks_written() >= _txn_rec.rec_size_dblks())
+ {
+ dtokp->set_wstate(data_tok::ABORT_SUBM);
+
+ // Delete this txn from tmap, unlock any locked records in emap
+ std::string xid((const char*)xid_ptr, xid_len);
+ txn_data_list tdl = _tmap.get_remove_tdata_list(xid); // tdl will be empty if xid not found
+ for (tdl_itr itr = tdl.begin(); itr != tdl.end(); itr++)
+ {
+ if (!itr->_enq_flag)
+ _emap.unlock(itr->_drid); // ignore rid not found error
+ if (itr->_enq_flag)
+ _wrfc.decr_enqcnt(itr->_pfid);
+ }
+ std::pair<std::set<std::string>::iterator, bool> res = _txn_pending_set.insert(xid);
+ if (!res.second)
+ {
+ std::ostringstream oss;
+ oss << std::hex << "_txn_pending_set: xid=\"" << xid << "\"";
+ throw jexception(jerrno::JERR_MAP_DUPLICATE, oss.str(), "wmgr", "abort");
+ }
+
+ done = true;
+ }
+ else
+ dtokp->set_wstate(data_tok::ABORT_PART);
+
+ file_header_check(rid, cont, _txn_rec.rec_size_dblks() - data_offs_dblks);
+ flush_check(res, cont, done);
+ }
+ if (dtokp->wstate() >= data_tok::ABORT_SUBM)
+ _abort_busy = false;
+ return res;
+}
+
+iores
+wmgr::commit(data_tok* dtokp, const void* const xid_ptr, const std::size_t xid_len)
+{
+ // commit and abort MUST have a valid xid
+ assert(xid_ptr != 0 && xid_len > 0);
+
+ if (_enq_busy || _deq_busy || _abort_busy)
+ return RHM_IORES_BUSY;
+
+ iores res = pre_write_check(WMGR_COMMIT, dtokp);
+ if (res != RHM_IORES_SUCCESS)
+ return res;
+
+ bool cont = false;
+ if (_commit_busy) // If commit() exited last time with RHM_IORES_FULL or RHM_IORES_PAGE_AIOWAIT
+ {
+ if (dtokp->wstate() == data_tok::COMMIT_PART)
+ cont = true;
+ else
+ {
+ std::ostringstream oss;
+ oss << "This data_tok: id=" << dtokp->id() << " state=" << dtokp->wstate_str();
+ throw jexception(jerrno::JERR_WMGR_DEQDISCONT, oss.str(), "wmgr", "commit");
+ }
+ }
+
+ u_int64_t rid = (dtokp->external_rid() | cont) ? dtokp->rid() : _wrfc.get_incr_rid();
+ _txn_rec.reset(RHM_JDAT_TXC_MAGIC, rid, xid_ptr, xid_len, _wrfc.owi());
+ if (!cont)
+ {
+ dtokp->set_rid(rid);
+ dtokp->set_dequeue_rid(0);
+ dtokp->set_xid(xid_ptr, xid_len);
+ dtokp->set_dblocks_written(0); // Reset dblks_written from previous op
+ _commit_busy = true;
+ }
+ bool done = false;
+ while (!done)
+ {
+ assert(_pg_offset_dblks < _cache_pgsize_sblks * JRNL_SBLK_SIZE);
+ void* wptr = (void*)((char*)_page_ptr_arr[_pg_index] + _pg_offset_dblks * JRNL_DBLK_SIZE);
+ u_int32_t data_offs_dblks = dtokp->dblocks_written();
+ u_int32_t ret = _txn_rec.encode(wptr, data_offs_dblks,
+ (_cache_pgsize_sblks * JRNL_SBLK_SIZE) - _pg_offset_dblks);
+
+ // Remember fid which contains the record header in case record is split over several files
+ if (data_offs_dblks == 0)
+ dtokp->set_fid(_wrfc.index());
+ _pg_offset_dblks += ret;
+ _cached_offset_dblks += ret;
+ dtokp->incr_dblocks_written(ret);
+ dtokp->incr_pg_cnt();
+ _page_cb_arr[_pg_index]._pdtokl->push_back(dtokp);
+
+ // Is the encoding of this record complete?
+ if (dtokp->dblocks_written() >= _txn_rec.rec_size_dblks())
+ {
+ dtokp->set_wstate(data_tok::COMMIT_SUBM);
+
+ // Delete this txn from tmap, process records into emap
+ std::string xid((const char*)xid_ptr, xid_len);
+ txn_data_list tdl = _tmap.get_remove_tdata_list(xid); // tdl will be empty if xid not found
+ for (tdl_itr itr = tdl.begin(); itr != tdl.end(); itr++)
+ {
+ if (itr->_enq_flag) // txn enqueue
+ {
+ if (_emap.insert_pfid(itr->_rid, itr->_pfid) < enq_map::EMAP_OK) // fail
+ {
+ // The only error code emap::insert_pfid() returns is enq_map::EMAP_DUP_RID.
+ std::ostringstream oss;
+ oss << std::hex << "rid=0x" << itr->_rid << " _pfid=0x" << itr->_pfid;
+ throw jexception(jerrno::JERR_MAP_DUPLICATE, oss.str(), "wmgr", "commit");
+ }
+ }
+ else // txn dequeue
+ {
+ int16_t fid = _emap.get_remove_pfid(itr->_drid, true);
+ if (fid < enq_map::EMAP_OK) // fail
+ {
+ if (fid == enq_map::EMAP_RID_NOT_FOUND)
+ {
+ std::ostringstream oss;
+ oss << std::hex << "rid=0x" << rid;
+ throw jexception(jerrno::JERR_MAP_NOTFOUND, oss.str(), "wmgr", "dequeue");
+ }
+ if (fid == enq_map::EMAP_LOCKED)
+ {
+ std::ostringstream oss;
+ oss << std::hex << "rid=0x" << rid;
+ throw jexception(jerrno::JERR_MAP_LOCKED, oss.str(), "wmgr", "dequeue");
+ }
+ }
+ _wrfc.decr_enqcnt(fid);
+ }
+ }
+ std::pair<std::set<std::string>::iterator, bool> res = _txn_pending_set.insert(xid);
+ if (!res.second)
+ {
+ std::ostringstream oss;
+ oss << std::hex << "_txn_pending_set: xid=\"" << xid << "\"";
+ throw jexception(jerrno::JERR_MAP_DUPLICATE, oss.str(), "wmgr", "commit");
+ }
+
+ done = true;
+ }
+ else
+ dtokp->set_wstate(data_tok::COMMIT_PART);
+
+ file_header_check(rid, cont, _txn_rec.rec_size_dblks() - data_offs_dblks);
+ flush_check(res, cont, done);
+ }
+ if (dtokp->wstate() >= data_tok::COMMIT_SUBM)
+ _commit_busy = false;
+ return res;
+}
+
+void
+wmgr::file_header_check(const u_int64_t rid, const bool cont, const u_int32_t rec_dblks_rem)
+{
+ // Has the file header been written (i.e. write pointers still at 0)?
+ if (_wrfc.is_void())
+ {
+ bool file_fit = rec_dblks_rem <= _jfsize_dblks;
+ bool file_full = rec_dblks_rem == _jfsize_dblks;
+ std::size_t fro = 0;
+ if (cont)
+ {
+ if (file_fit && !file_full)
+ fro = (rec_dblks_rem + JRNL_SBLK_SIZE) * JRNL_DBLK_SIZE;
+ }
+ else
+ fro = JRNL_SBLK_SIZE * JRNL_DBLK_SIZE;
+ write_fhdr(rid, _wrfc.index(), _wrfc.index(), fro);
+ }
+}
+
+void
+wmgr::flush_check(iores& res, bool& cont, bool& done)
+{
+ // Is page is full, flush
+ if (_pg_offset_dblks >= _cache_pgsize_sblks * JRNL_SBLK_SIZE)
+ {
+ res = write_flush();
+ assert(res == RHM_IORES_SUCCESS);
+
+ if (_page_cb_arr[_pg_index]._state == AIO_PENDING && !done)
+ {
+ res = RHM_IORES_PAGE_AIOWAIT;
+ done = true;
+ }
+
+ // If file is full, rotate to next file
+ if (_pg_cntr >= _jfsize_pgs)
+ {
+ iores rfres = rotate_file();
+ if (rfres != RHM_IORES_SUCCESS)
+ res = rfres;
+ if (!done)
+ {
+ if (rfres == RHM_IORES_SUCCESS)
+ cont = true;
+ else
+ done = true;
+ }
+ }
+ }
+}
+
+iores
+wmgr::flush()
+{
+ iores res = write_flush();
+ if (_pg_cntr >= _jfsize_pgs)
+ {
+ iores rfres = rotate_file();
+ if (rfres != RHM_IORES_SUCCESS)
+ res = rfres;
+ }
+ return res;
+}
+
+iores
+wmgr::write_flush()
+{
+ iores res = RHM_IORES_SUCCESS;
+ // Don't bother flushing an empty page or one that is still in state AIO_PENDING
+ if (_cached_offset_dblks)
+ {
+ if (_page_cb_arr[_pg_index]._state == AIO_PENDING)
+ res = RHM_IORES_PAGE_AIOWAIT;
+ else
+ {
+ if (_page_cb_arr[_pg_index]._state != IN_USE)
+ {
+ std::ostringstream oss;
+ oss << "pg_index=" << _pg_index << " state=" << _page_cb_arr[_pg_index].state_str();
+ throw jexception(jerrno::JERR_WMGR_BADPGSTATE, oss.str(), "wmgr",
+ "write_flush");
+ }
+
+ // Send current page using AIO
+
+ // In manual flushes, dblks may not coincide with sblks, add filler records ("RHMx")
+ // if necessary.
+ dblk_roundup();
+
+ std::size_t pg_offs = (_pg_offset_dblks - _cached_offset_dblks) * JRNL_DBLK_SIZE;
+ aio_cb* aiocbp = &_aio_cb_arr[_pg_index];
+ aio::prep_pwrite_2(aiocbp, _wrfc.fh(),
+ (char*)_page_ptr_arr[_pg_index] + pg_offs, _cached_offset_dblks * JRNL_DBLK_SIZE,
+ _wrfc.subm_offs());
+ page_cb* pcbp = (page_cb*)(aiocbp->data); // This page control block (pcb)
+ pcbp->_wdblks = _cached_offset_dblks;
+ pcbp->_wfh = _wrfc.file_controller();
+ if (aio::submit(_ioctx, 1, &aiocbp) < 0)
+ throw jexception(jerrno::JERR__AIO, "wmgr", "write_flush");
+ _wrfc.add_subm_cnt_dblks(_cached_offset_dblks);
+ _wrfc.incr_aio_cnt();
+ _aio_evt_rem++;
+ _cached_offset_dblks = 0;
+ _jc->instr_incr_outstanding_aio_cnt();
+
+ rotate_page(); // increments _pg_index, resets _pg_offset_dblks if req'd
+ if (_page_cb_arr[_pg_index]._state == UNUSED)
+ _page_cb_arr[_pg_index]._state = IN_USE;
+ }
+ }
+ get_events(UNUSED, 0);
+ if (_page_cb_arr[_pg_index]._state == UNUSED)
+ _page_cb_arr[_pg_index]._state = IN_USE;
+ return res;
+}
+
+iores
+wmgr::rotate_file()
+{
+ _pg_cntr = 0;
+ iores res = _wrfc.rotate();
+ _jc->chk_wr_frot();
+ return res;
+}
+
+int32_t
+wmgr::get_events(page_state state, timespec* const timeout, bool flush)
+{
+ if (_aio_evt_rem == 0) // no events to get
+ return 0;
+
+ int ret = 0;
+ if ((ret = aio::getevents(_ioctx, flush ? _aio_evt_rem : 1, _aio_evt_rem/*_cache_num_pages + _jc->num_jfiles()*/, _aio_event_arr, timeout)) < 0)
+ {
+ if (ret == -EINTR) // Interrupted by signal
+ return 0;
+ std::ostringstream oss;
+ oss << "io_getevents() failed: " << std::strerror(-ret) << " (" << ret << ")";
+ throw jexception(jerrno::JERR__AIO, oss.str(), "wmgr", "get_events");
+ }
+
+ if (ret == 0 && timeout)
+ return jerrno::AIO_TIMEOUT;
+
+ int32_t tot_data_toks = 0;
+ for (int i=0; i<ret; i++) // Index of returned AIOs
+ {
+ if (_aio_evt_rem == 0)
+ {
+ std::ostringstream oss;
+ oss << "_aio_evt_rem; evt " << (i + 1) << " of " << ret;
+ throw jexception(jerrno::JERR__UNDERFLOW, oss.str(), "wmgr", "get_events");
+ }
+ _aio_evt_rem--;
+ aio_cb* aiocbp = _aio_event_arr[i].obj; // This I/O control block (iocb)
+ page_cb* pcbp = (page_cb*)(aiocbp->data); // This page control block (pcb)
+ long aioret = (long)_aio_event_arr[i].res;
+ if (aioret < 0)
+ {
+ std::ostringstream oss;
+ oss << "AIO write operation failed: " << std::strerror(-aioret) << " (" << aioret << ") [";
+ if (pcbp)
+ oss << "pg=" << pcbp->_index;
+ else
+ {
+ file_hdr* fhp = (file_hdr*)aiocbp->u.c.buf;
+ oss << "fid=" << fhp->_pfid;
+ }
+ oss << " size=" << aiocbp->u.c.nbytes;
+ oss << " offset=" << aiocbp->u.c.offset << " fh=" << aiocbp->aio_fildes << "]";
+ throw jexception(jerrno::JERR__AIO, oss.str(), "wmgr", "get_events");
+ }
+ if (pcbp) // Page writes have pcb
+ {
+ u_int32_t s = pcbp->_pdtokl->size();
+ std::vector<data_tok*> dtokl;
+ dtokl.reserve(s);
+ for (u_int32_t k=0; k<s; k++)
+ {
+ data_tok* dtokp = pcbp->_pdtokl->at(k);
+ if (dtokp->decr_pg_cnt() == 0)
+ {
+ std::set<std::string>::iterator it;
+ switch (dtokp->wstate())
+ {
+ case data_tok::ENQ_SUBM:
+ dtokl.push_back(dtokp);
+ tot_data_toks++;
+ dtokp->set_wstate(data_tok::ENQ);
+ if (dtokp->has_xid())
+ // Ignoring return value here. A non-zero return can signify that the transaction
+ // has committed or aborted, and which was completed prior to the aio returning.
+ _tmap.set_aio_compl(dtokp->xid(), dtokp->rid());
+ break;
+ case data_tok::DEQ_SUBM:
+ dtokl.push_back(dtokp);
+ tot_data_toks++;
+ dtokp->set_wstate(data_tok::DEQ);
+ if (dtokp->has_xid())
+ // Ignoring return value - see note above.
+ _tmap.set_aio_compl(dtokp->xid(), dtokp->rid());
+ break;
+ case data_tok::ABORT_SUBM:
+ dtokl.push_back(dtokp);
+ tot_data_toks++;
+ dtokp->set_wstate(data_tok::ABORTED);
+ it = _txn_pending_set.find(dtokp->xid());
+ if (it == _txn_pending_set.end())
+ {
+ std::ostringstream oss;
+ oss << std::hex << "_txn_pending_set: abort xid=\"";
+ oss << dtokp->xid() << "\"";
+ throw jexception(jerrno::JERR_MAP_NOTFOUND, oss.str(), "wmgr",
+ "get_events");
+ }
+ _txn_pending_set.erase(it);
+ break;
+ case data_tok::COMMIT_SUBM:
+ dtokl.push_back(dtokp);
+ tot_data_toks++;
+ dtokp->set_wstate(data_tok::COMMITTED);
+ it = _txn_pending_set.find(dtokp->xid());
+ if (it == _txn_pending_set.end())
+ {
+ std::ostringstream oss;
+ oss << std::hex << "_txn_pending_set: commit xid=\"";
+ oss << dtokp->xid() << "\"";
+ throw jexception(jerrno::JERR_MAP_NOTFOUND, oss.str(), "wmgr",
+ "get_events");
+ }
+ _txn_pending_set.erase(it);
+ break;
+ case data_tok::ENQ_PART:
+ case data_tok::DEQ_PART:
+ case data_tok::ABORT_PART:
+ case data_tok::COMMIT_PART:
+ // ignore these
+ break;
+ default:
+ // throw for anything else
+ std::ostringstream oss;
+ oss << "dtok_id=" << dtokp->id() << " dtok_state=" << dtokp->wstate_str();
+ throw jexception(jerrno::JERR_WMGR_BADDTOKSTATE, oss.str(), "wmgr",
+ "get_events");
+ } // switch
+ } // if
+ } // for
+
+ // Increment the completed write offset
+ // NOTE: We cannot use _wrfc here, as it may have rotated since submitting count.
+ // Use stored pointer to fcntl in the pcb instead.
+ pcbp->_wfh->add_wr_cmpl_cnt_dblks(pcbp->_wdblks);
+ pcbp->_wfh->decr_aio_cnt();
+ _jc->instr_decr_outstanding_aio_cnt();
+
+ // Clean up this pcb's data_tok list
+ pcbp->_pdtokl->clear();
+ pcbp->_state = state;
+
+ // Perform AIO return callback
+ if (_cbp && tot_data_toks)
+ _cbp->wr_aio_cb(dtokl);
+ }
+ else // File header writes have no pcb
+ {
+ // get lfid from original file header record, update info for that lfid
+ file_hdr* fhp = (file_hdr*)aiocbp->u.c.buf;
+ u_int32_t lfid = fhp->_lfid;
+ fcntl* fcntlp = _jc->get_fcntlp(lfid);
+ fcntlp->add_wr_cmpl_cnt_dblks(JRNL_SBLK_SIZE);
+ fcntlp->decr_aio_cnt();
+ fcntlp->set_wr_fhdr_aio_outstanding(false);
+ }
+ }
+
+ return tot_data_toks;
+}
+
+bool
+wmgr::is_txn_synced(const std::string& xid)
+{
+ // Ignore xid not found error here
+ if (_tmap.is_txn_synced(xid) == txn_map::TMAP_NOT_SYNCED)
+ return false;
+ // Check for outstanding commit/aborts
+ std::set<std::string>::iterator it = _txn_pending_set.find(xid);
+ return it == _txn_pending_set.end();
+}
+
+void
+wmgr::initialize(aio_callback* const cbp, const u_int32_t wcache_pgsize_sblks, const u_int16_t wcache_num_pages)
+{
+ pmgr::initialize(cbp, wcache_pgsize_sblks, wcache_num_pages);
+ wmgr::clean();
+ _num_jfiles = _jc->num_jfiles();
+ if (::posix_memalign(&_fhdr_base_ptr, _sblksize, _sblksize * _num_jfiles))
+ {
+ wmgr::clean();
+ std::ostringstream oss;
+ oss << "posix_memalign(): blksize=" << _sblksize << " size=" << _sblksize;
+ oss << FORMAT_SYSERR(errno);
+ throw jexception(jerrno::JERR__MALLOC, oss.str(), "wmgr", "initialize");
+ }
+ _fhdr_ptr_arr = (void**)std::malloc(_num_jfiles * sizeof(void*));
+ MALLOC_CHK(_fhdr_ptr_arr, "_fhdr_ptr_arr", "wmgr", "initialize");
+ _fhdr_aio_cb_arr = (aio_cb**)std::malloc(sizeof(aio_cb*) * _num_jfiles);
+ MALLOC_CHK(_fhdr_aio_cb_arr, "_fhdr_aio_cb_arr", "wmgr", "initialize");
+ std::memset(_fhdr_aio_cb_arr, 0, sizeof(aio_cb*) * _num_jfiles);
+ for (u_int16_t i=0; i<_num_jfiles; i++)
+ {
+ _fhdr_ptr_arr[i] = (void*)((char*)_fhdr_base_ptr + _sblksize * i);
+ _fhdr_aio_cb_arr[i] = new aio_cb;
+ }
+ _page_cb_arr[0]._state = IN_USE;
+ _ddtokl.clear();
+ _cached_offset_dblks = 0;
+ _enq_busy = false;
+}
+
+iores
+wmgr::pre_write_check(const _op_type op, const data_tok* const dtokp,
+ const std::size_t xidsize, const std::size_t dsize, const bool external
+ ) const
+{
+ // Check status of current file
+ if (!_wrfc.is_wr_reset())
+ {
+ if (!_wrfc.wr_reset())
+ return RHM_IORES_FULL;
+ }
+
+ // Check status of current page is ok for writing
+ if (_page_cb_arr[_pg_index]._state != IN_USE)
+ {
+ if (_page_cb_arr[_pg_index]._state == UNUSED)
+ _page_cb_arr[_pg_index]._state = IN_USE;
+ else if (_page_cb_arr[_pg_index]._state == AIO_PENDING)
+ return RHM_IORES_PAGE_AIOWAIT;
+ else
+ {
+ std::ostringstream oss;
+ oss << "jrnl=" << _jc->id() << " op=" << _op_str[op];
+ oss << " index=" << _pg_index << " pg_state=" << _page_cb_arr[_pg_index].state_str();
+ throw jexception(jerrno::JERR_WMGR_BADPGSTATE, oss.str(), "wmgr", "pre_write_check");
+ }
+ }
+
+ // operation-specific checks
+ switch (op)
+ {
+ case WMGR_ENQUEUE:
+ {
+ // Check for enqueue reaching cutoff threshold
+ u_int32_t size_dblks = jrec::size_dblks(enq_rec::rec_size(xidsize, dsize,
+ external));
+ if (!_enq_busy && _wrfc.enq_threshold(_cached_offset_dblks + size_dblks))
+ return RHM_IORES_ENQCAPTHRESH;
+ if (!dtokp->is_writable())
+ {
+ std::ostringstream oss;
+ oss << "jrnl=" << _jc->id() << " op=" << _op_str[op];
+ oss << " dtok_id=" << dtokp->id() << " dtok_state=" << dtokp->wstate_str();
+ throw jexception(jerrno::JERR_WMGR_BADDTOKSTATE, oss.str(), "wmgr",
+ "pre_write_check");
+ }
+ }
+ break;
+ case WMGR_DEQUEUE:
+ if (!dtokp->is_dequeueable())
+ {
+ std::ostringstream oss;
+ oss << "jrnl=" << _jc->id() << " op=" << _op_str[op];
+ oss << " dtok_id=" << dtokp->id() << " dtok_state=" << dtokp->wstate_str();
+ throw jexception(jerrno::JERR_WMGR_BADDTOKSTATE, oss.str(), "wmgr",
+ "pre_write_check");
+ }
+ break;
+ case WMGR_ABORT:
+ break;
+ case WMGR_COMMIT:
+ break;
+ }
+
+ return RHM_IORES_SUCCESS;
+}
+
+void
+wmgr::dequeue_check(const std::string& xid, const u_int64_t drid)
+{
+ // First check emap
+ bool found = false;
+ int16_t fid = _emap.get_pfid(drid);
+ if (fid < enq_map::EMAP_OK) // fail
+ {
+ if (fid == enq_map::EMAP_RID_NOT_FOUND)
+ {
+ if (xid.size())
+ found = _tmap.data_exists(xid, drid);
+ }
+ else if (fid == enq_map::EMAP_LOCKED)
+ {
+ std::ostringstream oss;
+ oss << std::hex << "drid=0x" << drid;
+ throw jexception(jerrno::JERR_MAP_LOCKED, oss.str(), "wmgr", "dequeue_check");
+ }
+ }
+ else
+ found = true;
+ if (!found)
+ {
+ std::ostringstream oss;
+ oss << "jrnl=" << _jc->id() << " drid=0x" << std::hex << drid;
+ throw jexception(jerrno::JERR_WMGR_DEQRIDNOTENQ, oss.str(), "wmgr", "dequeue_check");
+ }
+}
+
+void
+wmgr::dblk_roundup()
+{
+ const u_int32_t xmagic = RHM_JDAT_EMPTY_MAGIC;
+ u_int32_t wdblks = jrec::size_blks(_cached_offset_dblks, JRNL_SBLK_SIZE) * JRNL_SBLK_SIZE;
+ while (_cached_offset_dblks < wdblks)
+ {
+ void* wptr = (void*)((char*)_page_ptr_arr[_pg_index] + _pg_offset_dblks * JRNL_DBLK_SIZE);
+ std::memcpy(wptr, (const void*)&xmagic, sizeof(xmagic));
+#ifdef RHM_CLEAN
+ std::memset((char*)wptr + sizeof(xmagic), RHM_CLEAN_CHAR, JRNL_DBLK_SIZE - sizeof(xmagic));
+#endif
+ _pg_offset_dblks++;
+ _cached_offset_dblks++;
+ }
+}
+
+void
+wmgr::write_fhdr(u_int64_t rid, u_int16_t fid, u_int16_t lid, std::size_t fro)
+{
+ file_hdr fhdr(RHM_JDAT_FILE_MAGIC, RHM_JDAT_VERSION, rid, fid, lid, fro, _wrfc.owi(), true);
+ std::memcpy(_fhdr_ptr_arr[fid], &fhdr, sizeof(fhdr));
+#ifdef RHM_CLEAN
+ std::memset((char*)_fhdr_ptr_arr[fid] + sizeof(fhdr), RHM_CLEAN_CHAR, _sblksize - sizeof(fhdr));
+#endif
+ aio_cb* aiocbp = _fhdr_aio_cb_arr[fid];
+ aio::prep_pwrite(aiocbp, _wrfc.fh(), _fhdr_ptr_arr[fid], _sblksize, 0);
+ if (aio::submit(_ioctx, 1, &aiocbp) < 0)
+ throw jexception(jerrno::JERR__AIO, "wmgr", "write_fhdr");
+ _aio_evt_rem++;
+ _wrfc.add_subm_cnt_dblks(JRNL_SBLK_SIZE);
+ _wrfc.incr_aio_cnt();
+ _wrfc.file_controller()->set_wr_fhdr_aio_outstanding(true);
+}
+
+void
+wmgr::rotate_page()
+{
+ _page_cb_arr[_pg_index]._state = AIO_PENDING;
+ if (_pg_offset_dblks >= _cache_pgsize_sblks * JRNL_SBLK_SIZE)
+ {
+ _pg_offset_dblks = 0;
+ _pg_cntr++;
+ }
+ if (++_pg_index >= _cache_num_pages)
+ _pg_index = 0;
+}
+
+void
+wmgr::clean()
+{
+ std::free(_fhdr_base_ptr);
+ _fhdr_base_ptr = 0;
+
+ std::free(_fhdr_ptr_arr);
+ _fhdr_ptr_arr = 0;
+
+ if (_fhdr_aio_cb_arr)
+ {
+ for (u_int32_t i=0; i<_num_jfiles; i++)
+ delete _fhdr_aio_cb_arr[i];
+ std::free(_fhdr_aio_cb_arr);
+ _fhdr_aio_cb_arr = 0;
+ }
+}
+
+const std::string
+wmgr::status_str() const
+{
+ std::ostringstream oss;
+ oss << "wmgr: pi=" << _pg_index << " pc=" << _pg_cntr;
+ oss << " po=" << _pg_offset_dblks << " aer=" << _aio_evt_rem;
+ oss << " edac:" << (_enq_busy?"T":"F") << (_deq_busy?"T":"F");
+ oss << (_abort_busy?"T":"F") << (_commit_busy?"T":"F");
+ oss << " ps=[";
+ for (int i=0; i<_cache_num_pages; i++)
+ {
+ switch (_page_cb_arr[i]._state)
+ {
+ case UNUSED: oss << "-"; break;
+ case IN_USE: oss << "U"; break;
+ case AIO_PENDING: oss << "A"; break;
+ case AIO_COMPLETE: oss << "*"; break;
+ default: oss << _page_cb_arr[i]._state;
+ }
+ }
+ oss << "] " << _wrfc.status_str();
+ return oss.str();
+}
+
+// static
+
+const char* wmgr::_op_str[] = {"enqueue", "dequeue", "abort", "commit"};
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/wmgr.h b/cpp/src/qpid/legacystore/jrnl/wmgr.h
new file mode 100644
index 0000000000..8347221b1d
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/wmgr.h
@@ -0,0 +1,147 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * \file wmgr.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::wmgr (write manager). See
+ * class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_WMGR_H
+#define QPID_LEGACYSTORE_JRNL_WMGR_H
+
+namespace mrg
+{
+namespace journal
+{
+class wmgr;
+}
+}
+
+#include <cstring>
+#include "qpid/legacystore/jrnl/enums.h"
+#include "qpid/legacystore/jrnl/pmgr.h"
+#include "qpid/legacystore/jrnl/wrfc.h"
+#include <set>
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \brief Class for managing a write page cache of arbitrary size and number of pages.
+ *
+ * The write page cache works on the principle of caching the write data within a page until
+ * that page is either full or flushed; this initiates a single AIO write operation to store
+ * the data on disk.
+ *
+ * The maximum disk throughput is achieved by keeping the write operations of uniform size.
+ * Waiting for a page cache to fill achieves this; and in high data volume/throughput situations
+ * achieves the optimal disk throughput. Calling flush() forces a write of the current page cache
+ * no matter how full it is, and disrupts the uniformity of the write operations. This should
+ * normally only be done if throughput drops and there is a danger of a page of unwritten data
+ * waiting around for excessive time.
+ *
+ * The usual tradeoff between data storage latency and throughput performance applies.
+ */
+ class wmgr : public pmgr
+ {
+ private:
+ wrfc& _wrfc; ///< Ref to write rotating file controller
+ u_int32_t _max_dtokpp; ///< Max data writes per page
+ u_int32_t _max_io_wait_us; ///< Max wait in microseconds till submit
+ void* _fhdr_base_ptr; ///< Base pointer to file header memory
+ void** _fhdr_ptr_arr; ///< Array of pointers to file headers memory
+ aio_cb** _fhdr_aio_cb_arr; ///< Array of iocb pointers for file header writes
+ u_int32_t _cached_offset_dblks; ///< Amount of unwritten data in page (dblocks)
+ std::deque<data_tok*> _ddtokl; ///< Deferred dequeue data_tok list
+ u_int32_t _jfsize_dblks; ///< Journal file size in dblks (NOT sblks!)
+ u_int32_t _jfsize_pgs; ///< Journal file size in cache pages
+ u_int16_t _num_jfiles; ///< Number of files used in iocb mallocs
+
+ // TODO: Convert _enq_busy etc into a proper threadsafe lock
+ // TODO: Convert to enum? Are these encodes mutually exclusive?
+ bool _enq_busy; ///< Flag true if enqueue is in progress
+ bool _deq_busy; ///< Flag true if dequeue is in progress
+ bool _abort_busy; ///< Flag true if abort is in progress
+ bool _commit_busy; ///< Flag true if commit is in progress
+
+ enum _op_type { WMGR_ENQUEUE = 0, WMGR_DEQUEUE, WMGR_ABORT, WMGR_COMMIT };
+ static const char* _op_str[];
+
+ enq_rec _enq_rec; ///< Enqueue record used for encoding/decoding
+ deq_rec _deq_rec; ///< Dequeue record used for encoding/decoding
+ txn_rec _txn_rec; ///< Transaction record used for encoding/decoding
+ std::set<std::string> _txn_pending_set; ///< Set containing xids of pending commits/aborts
+
+ public:
+ wmgr(jcntl* jc, enq_map& emap, txn_map& tmap, wrfc& wrfc);
+ wmgr(jcntl* jc, enq_map& emap, txn_map& tmap, wrfc& wrfc, const u_int32_t max_dtokpp,
+ const u_int32_t max_iowait_us);
+ virtual ~wmgr();
+
+ void initialize(aio_callback* const cbp, const u_int32_t wcache_pgsize_sblks,
+ const u_int16_t wcache_num_pages, const u_int32_t max_dtokpp,
+ const u_int32_t max_iowait_us, std::size_t eo = 0);
+ iores enqueue(const void* const data_buff, const std::size_t tot_data_len,
+ const std::size_t this_data_len, data_tok* dtokp, const void* const xid_ptr,
+ const std::size_t xid_len, const bool transient, const bool external);
+ iores dequeue(data_tok* dtokp, const void* const xid_ptr, const std::size_t xid_len,
+ const bool txn_coml_commit);
+ iores abort(data_tok* dtokp, const void* const xid_ptr, const std::size_t xid_len);
+ iores commit(data_tok* dtokp, const void* const xid_ptr, const std::size_t xid_len);
+ iores flush();
+ int32_t get_events(page_state state, timespec* const timeout, bool flush = false);
+ bool is_txn_synced(const std::string& xid);
+ inline bool curr_pg_blocked() const { return _page_cb_arr[_pg_index]._state != UNUSED; }
+ inline bool curr_file_blocked() const { return _wrfc.aio_cnt() > 0; }
+ inline u_int32_t unflushed_dblks() { return _cached_offset_dblks; }
+
+ // Debug aid
+ const std::string status_str() const;
+
+ private:
+ void initialize(aio_callback* const cbp, const u_int32_t wcache_pgsize_sblks,
+ const u_int16_t wcache_num_pages);
+ iores pre_write_check(const _op_type op, const data_tok* const dtokp,
+ const std::size_t xidsize = 0, const std::size_t dsize = 0, const bool external = false)
+ const;
+ void dequeue_check(const std::string& xid, const u_int64_t drid);
+ void file_header_check(const u_int64_t rid, const bool cont, const u_int32_t rec_dblks_rem);
+ void flush_check(iores& res, bool& cont, bool& done);
+ iores write_flush();
+ iores rotate_file();
+ void dblk_roundup();
+ void write_fhdr(u_int64_t rid, u_int16_t fid, u_int16_t lid, std::size_t fro);
+ void rotate_page();
+ void clean();
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_WMGR_H
diff --git a/cpp/src/qpid/legacystore/jrnl/wrfc.cpp b/cpp/src/qpid/legacystore/jrnl/wrfc.cpp
new file mode 100644
index 0000000000..43461b66a3
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/wrfc.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.
+ *
+ */
+
+/**
+ * \file wrfc.cpp
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::wrfc (rotating
+ * file controller). See comments in file wrfc.h for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#include "qpid/legacystore/jrnl/wrfc.h"
+
+#include <cmath>
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+wrfc::wrfc(const lpmgr* lpmp):
+ rfc(lpmp),
+ _fsize_sblks(0),
+ _fsize_dblks(0),
+ _enq_cap_offs_dblks(0),
+ _rid(0),
+ _reset_ok(false),
+ _owi(false),
+ _frot(true)
+{}
+
+wrfc::~wrfc()
+{}
+
+void
+wrfc::initialize(const u_int32_t fsize_sblks, rcvdat* rdp)
+{
+ if (rdp)
+ {
+ _fc_index = rdp->_lfid;
+ _curr_fc = _lpmp->get_fcntlp(_fc_index);
+ _curr_fc->wr_reset(rdp);
+ _rid = rdp->_h_rid + 1;
+ _reset_ok = true;
+ _owi = rdp->_owi;
+ _frot = rdp->_frot;
+ if (rdp->_lffull)
+ rotate();
+ }
+ else
+ {
+ rfc::initialize();
+ rfc::set_findex(0);
+ _rid = 0ULL;
+ _reset_ok = false;
+ }
+ _fsize_sblks = fsize_sblks;
+ _fsize_dblks = fsize_sblks * JRNL_SBLK_SIZE;
+ _enq_cap_offs_dblks = (u_int32_t)std::ceil(_fsize_dblks * _lpmp->num_jfiles() * (100.0 - JRNL_ENQ_THRESHOLD) / 100);
+ // Check the offset is at least one file; if not, make it so
+ if (_enq_cap_offs_dblks < _fsize_dblks)
+ _enq_cap_offs_dblks = _fsize_dblks;
+}
+
+iores wrfc::rotate()
+{
+ if (!_lpmp->num_jfiles())
+ throw jexception(jerrno::JERR__NINIT, "wrfc", "rotate");
+ _fc_index++;
+ if (_fc_index == _lpmp->num_jfiles())
+ {
+ _fc_index = 0;
+ _owi = !_owi;
+ _frot = false;
+ }
+ _curr_fc = _lpmp->get_fcntlp(_fc_index);
+ if (_curr_fc->aio_cnt())
+ return RHM_IORES_FILE_AIOWAIT;
+ if (!wr_reset()) //Checks if file is still in use (ie not fully dequeued yet)
+ return RHM_IORES_FULL;
+ return RHM_IORES_SUCCESS;
+}
+
+u_int16_t wrfc::earliest_index() const
+{
+ if (_frot)
+ return 0;
+ u_int16_t next_index = _fc_index + 1;
+ if (next_index >= _lpmp->num_jfiles())
+ next_index = 0;
+ return next_index;
+}
+
+bool
+wrfc::enq_threshold(const u_int32_t enq_dsize_dblks) const
+{
+ u_int32_t subm_dblks = subm_cnt_dblks(); // includes file hdr if > 0
+ // This compensates for new files which don't have their file headers written yet,
+ // as file header space cannot be included in this calculation.
+ if (subm_dblks != 0)
+ subm_dblks -= 4;
+ u_int32_t fwd_dblks = subm_dblks + enq_dsize_dblks + _enq_cap_offs_dblks;
+ u_int16_t findex = _fc_index;
+ fcntl* fcp = _curr_fc;
+ bool in_use = false;
+ while (fwd_dblks && !(findex != _fc_index && fcp->enqcnt()))
+ {
+ fwd_dblks -= fwd_dblks > _fsize_dblks ? _fsize_dblks : fwd_dblks;
+ if (fwd_dblks)
+ {
+ if (++findex == _lpmp->num_jfiles())
+ findex = 0;
+ fcp = _lpmp->get_fcntlp(findex);
+ }
+ in_use |= fcp->enqcnt() > 0;
+ }
+ // Return true if threshold exceeded
+ return findex != _fc_index && in_use;
+}
+
+bool wrfc::wr_reset()
+{
+ _reset_ok = _curr_fc->reset(); // returns false if full (ie file still contains enqueued recs)
+ return _reset_ok;
+}
+
+// TODO: update this to reflect all status data
+std::string
+wrfc::status_str() const
+{
+ std::ostringstream oss;
+ oss << "wrfc: " << rfc::status_str();
+ if (is_active())
+ oss << " fcntl[" << _fc_index << "]: " << _curr_fc->status_str();
+ return oss.str();
+}
+
+} // namespace journal
+} // namespace mrg
diff --git a/cpp/src/qpid/legacystore/jrnl/wrfc.h b/cpp/src/qpid/legacystore/jrnl/wrfc.h
new file mode 100644
index 0000000000..f0e4e73151
--- /dev/null
+++ b/cpp/src/qpid/legacystore/jrnl/wrfc.h
@@ -0,0 +1,154 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 wrfc.h
+ *
+ * Qpid asynchronous store plugin library
+ *
+ * File containing code for class mrg::journal::wrfc (write rotating
+ * file controller). See class documentation for details.
+ *
+ * \author Kim van der Riet
+ */
+
+#ifndef QPID_LEGACYSTORE_JRNL_WRFC_H
+#define QPID_LEGACYSTORE_JRNL_WRFC_H
+
+namespace mrg
+{
+namespace journal
+{
+class wrfc;
+}
+}
+
+#include <cstddef>
+#include "qpid/legacystore/jrnl/enums.h"
+#include "qpid/legacystore/jrnl/rrfc.h"
+
+namespace mrg
+{
+namespace journal
+{
+
+ /**
+ * \class wrfc
+ * \brief Class to handle write management of a journal rotating file controller.
+ */
+ class wrfc : public rfc
+ {
+ private:
+ u_int32_t _fsize_sblks; ///< Size of journal files in sblks
+ u_int32_t _fsize_dblks; ///< Size of journal files in dblks
+ u_int32_t _enq_cap_offs_dblks; ///< Enqueue capacity offset
+ u_int64_t _rid; ///< Master counter for record ID (rid)
+ bool _reset_ok; ///< Flag set when reset succeeds
+ bool _owi; ///< Overwrite indicator
+ bool _frot; ///< Flag is true for first rotation, false otherwise
+
+ public:
+ wrfc(const lpmgr* lpmp);
+ virtual ~wrfc();
+
+ /**
+ * \brief Initialize the controller.
+ * \param fsize_sblks Size of each journal file in sblks.
+ * \param rdp Struct carrying restore information. Optional for non-restore use, defaults to 0 (NULL).
+ */
+ using rfc::initialize;
+ void initialize(const u_int32_t fsize_sblks, rcvdat* rdp = 0);
+
+ /**
+ * \brief Rotate active file controller to next file in rotating file group.
+ * \exception jerrno::JERR__NINIT if called before calling initialize().
+ */
+ iores rotate();
+
+ /**
+ * \brief Returns the index of the earliest complete file within the rotating
+ * file group. Unwritten files are excluded. The currently active file is
+ * excluded unless it is the only written file.
+ */
+ u_int16_t earliest_index() const;
+
+ /**
+ * \brief Determines if a proposed write would cause the enqueue threshold to be exceeded.
+ *
+ * The following routine finds whether the next write will take the write pointer to beyond the
+ * enqueue limit threshold. The following illustrates how this is achieved.
+ * <pre>
+ * Current file index: 4 +---+----------+
+ * X's mark still-enqueued records |msg| 1-thresh |
+ * msg = current msg size + unwritten cache +---+----------+
+ * thresh = JRNL_ENQ_THRESHOLD as a fraction ^ V
+ * +-------+-------+-------+-------+--+----+-------+-+-----+-------+
+ * file num ->| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+ * enq recs ->| X XX |XX XXX |XX XXXX|XXXXXXX|XX | | | X |
+ * +-------+-------+-------+-------+--+----+-------+-+-----+-------+
+ * ^ ^ ^
+ * subm_dblks --+ | |
+ * These files must be free of enqueues
+ * If not, return true.
+ * </pre>
+ * \param enq_dsize_dblks Proposed size of write in dblocks
+ */
+ bool enq_threshold(const u_int32_t enq_dsize_dblks) const;
+
+ inline u_int64_t rid() const { return _rid; }
+ inline u_int64_t get_incr_rid() { return _rid++; }
+ bool wr_reset();
+ inline bool is_wr_reset() const { return _reset_ok; }
+ inline bool owi() const { return _owi; }
+ inline bool frot() const { return _frot; }
+
+ // Convenience access methods to current file controller
+
+ inline int fh() const { return _curr_fc->wr_fh(); }
+
+ inline u_int32_t subm_cnt_dblks() const { return _curr_fc->wr_subm_cnt_dblks(); }
+ inline std::size_t subm_offs() const { return _curr_fc->wr_subm_offs(); }
+ inline u_int32_t add_subm_cnt_dblks(u_int32_t a) { return _curr_fc->add_wr_subm_cnt_dblks(a); }
+
+ inline u_int32_t cmpl_cnt_dblks() const { return _curr_fc->wr_cmpl_cnt_dblks(); }
+ inline std::size_t cmpl_offs() const { return _curr_fc->wr_cmpl_offs(); }
+ inline u_int32_t add_cmpl_cnt_dblks(u_int32_t a) { return _curr_fc->add_wr_cmpl_cnt_dblks(a); }
+
+ inline u_int16_t aio_cnt() const { return _curr_fc->aio_cnt(); }
+ inline u_int16_t incr_aio_cnt() { return _curr_fc->incr_aio_cnt(); }
+ inline u_int16_t decr_aio_cnt() { return _curr_fc->decr_aio_cnt(); }
+
+ inline bool is_void() const { return _curr_fc->wr_void(); }
+ inline bool is_empty() const { return _curr_fc->wr_empty(); }
+ inline u_int32_t remaining_dblks() const { return _curr_fc->wr_remaining_dblks(); }
+ inline bool is_full() const { return _curr_fc->is_wr_full(); };
+ inline bool is_compl() const { return _curr_fc->is_wr_compl(); };
+ inline u_int32_t aio_outstanding_dblks() const { return _curr_fc->wr_aio_outstanding_dblks(); }
+ inline bool file_rotate() const { return _curr_fc->wr_file_rotate(); }
+
+ // Debug aid
+ std::string status_str() const;
+ };
+
+} // namespace journal
+} // namespace mrg
+
+#endif // ifndef QPID_LEGACYSTORE_JRNL_WRFC_H
diff --git a/cpp/src/qpid/legacystore/management-schema.xml b/cpp/src/qpid/legacystore/management-schema.xml
new file mode 100644
index 0000000000..65969f0fb2
--- /dev/null
+++ b/cpp/src/qpid/legacystore/management-schema.xml
@@ -0,0 +1,99 @@
+<schema package="org.apache.qpid.legacystore">
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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="Store">
+ <property name="brokerRef" type="objId" access="RO" references="qpid.Broker" index="y" parentRef="y"/>
+ <property name="location" type="sstr" access="RO" desc="Logical directory on disk"/>
+ <property name="defaultInitialFileCount" type="uint16" access="RO" unit="file" desc="Default number of files initially allocated to each journal"/>
+ <property name="defaultDataFileSize" type="uint32" access="RO" unit="RdPg" desc="Default size of each journal data file"/>
+ <property name="tplIsInitialized" type="bool" access="RO" desc="Transaction prepared list has been initialized by a transactional prepare"/>
+ <property name="tplDirectory" type="sstr" access="RO" desc="Transaction prepared list directory"/>
+ <property name="tplWritePageSize" type="uint32" access="RO" unit="byte" desc="Page size in transaction prepared list write-page-cache"/>
+ <property name="tplWritePages" type="uint32" access="RO" unit="wpage" desc="Number of pages in transaction prepared list write-page-cache"/>
+ <property name="tplInitialFileCount" type="uint16" access="RO" unit="file" desc="Number of files initially allocated to transaction prepared list journal"/>
+ <property name="tplDataFileSize" type="uint32" access="RO" unit="byte" desc="Size of each journal data file in transaction prepared list journal"/>
+ <property name="tplCurrentFileCount" type="uint32" access="RO" unit="file" desc="Number of files currently allocated to transaction prepared list journal"/>
+
+ <statistic name="tplTransactionDepth" type="hilo32" unit="txn" desc="Number of currently enqueued prepared transactions"/>
+ <statistic name="tplTxnPrepares" type="count64" unit="record" desc="Total transaction prepares on transaction prepared list"/>
+ <statistic name="tplTxnCommits" type="count64" unit="record" desc="Total transaction commits on transaction prepared list"/>
+ <statistic name="tplTxnAborts" type="count64" unit="record" desc="Total transaction aborts on transaction prepared list"/>
+ <statistic name="tplOutstandingAIOs" type="hilo32" unit="aio_op" desc="Number of currently outstanding AIO requests in Async IO system"/>
+ </class>
+
+ <class name="Journal">
+ <property name="queueRef" type="objId" access="RO" references="qpid.Queue" isGeneralReference="y"/>
+ <property name="name" type="sstr" access="RO" index="y"/>
+ <property name="directory" type="sstr" access="RO" desc="Directory containing journal files"/>
+ <property name="baseFileName" type="sstr" access="RO" desc="Base filename prefix for journal"/>
+ <property name="writePageSize" type="uint32" access="RO" unit="byte" desc="Page size in write-page-cache"/>
+ <property name="writePages" type="uint32" access="RO" unit="wpage" desc="Number of pages in write-page-cache"/>
+ <property name="readPageSize" type="uint32" access="RO" unit="byte" desc="Page size in read-page-cache"/>
+ <property name="readPages" type="uint32" access="RO" unit="rpage" desc="Number of pages in read-page-cache"/>
+ <property name="initialFileCount" type="uint16" access="RO" unit="file" desc="Number of files initially allocated to this journal"/>
+ <property name="autoExpand" type="bool" access="RO" desc="Auto-expand enabled"/>
+ <property name="currentFileCount" type="uint16" access="RO" unit="file" desc="Number of files currently allocated to this journal"/>
+ <property name="maxFileCount" type="uint16" access="RO" unit="file" desc="Max number of files allowed for this journal"/>
+ <property name="dataFileSize" type="uint32" access="RO" unit="byte" desc="Size of each journal data file"/>
+
+ <statistic name="recordDepth" type="hilo32" unit="record" desc="Number of currently enqueued records (durable messages)"/>
+ <statistic name="enqueues" type="count64" unit="record" desc="Total enqueued records on journal"/>
+ <statistic name="dequeues" type="count64" unit="record" desc="Total dequeued records on journal"/>
+ <statistic name="txn" type="count32" unit="record" desc="Total open transactions (xids) on journal"/>
+ <statistic name="txnEnqueues" type="count64" unit="record" desc="Total transactional enqueued records on journal"/>
+ <statistic name="txnDequeues" type="count64" unit="record" desc="Total transactional dequeued records on journal"/>
+ <statistic name="txnCommits" type="count64" unit="record" desc="Total transactional commit records on journal"/>
+ <statistic name="txnAborts" type="count64" unit="record" desc="Total transactional abort records on journal"/>
+ <statistic name="outstandingAIOs" type="hilo32" unit="aio_op" desc="Number of currently outstanding AIO requests in Async IO system"/>
+
+<!--
+ The following are not yet "wired up" in JournalImpl.cpp
+-->
+ <statistic name="freeFileCount" type="hilo32" unit="file" desc="Number of files free on this journal. Includes free files trapped in holes."/>
+ <statistic name="availableFileCount" type="hilo32" unit="file" desc="Number of files available to be written. Excluding holes"/>
+ <statistic name="writeWaitFailures" type="count64" unit="record" desc="AIO Wait failures on write"/>
+ <statistic name="writeBusyFailures" type="count64" unit="record" desc="AIO Busy failures on write"/>
+ <statistic name="readRecordCount" type="count64" unit="record" desc="Records read from the journal"/>
+ <statistic name="readBusyFailures" type="count64" unit="record" desc="AIO Busy failures on read"/>
+ <statistic name="writePageCacheDepth" type="hilo32" unit="wpage" desc="Current depth of write-page-cache"/>
+ <statistic name="readPageCacheDepth" type="hilo32" unit="rpage" desc="Current depth of read-page-cache"/>
+
+ <method name="expand" desc="Increase number of files allocated for this journal">
+ <arg name="by" type="uint32" dir="I" desc="Number of files to increase journal size by"/>
+ </method>
+ </class>
+
+ <eventArguments>
+ <arg name="autoExpand" type="bool" desc="Journal auto-expand enabled"/>
+ <arg name="fileSize" type="uint32" desc="Journal file size in bytes"/>
+ <arg name="jrnlId" type="sstr" desc="Journal Id"/>
+ <arg name="numEnq" type="uint32" desc="Number of recovered enqueues"/>
+ <arg name="numFiles" type="uint16" desc="Number of journal files"/>
+ <arg name="numTxn" type="uint32" desc="Number of recovered transactions"/>
+ <arg name="numTxnDeq" type="uint32" desc="Number of recovered transactional dequeues"/>
+ <arg name="numTxnEnq" type="uint32" desc="Number of recovered transactional enqueues"/>
+ <arg name="what" type="sstr" desc="Description of event"/>
+ </eventArguments>
+ <event name="enqThresholdExceeded" sev="warn" args="jrnlId, what"/>
+ <event name="created" sev="notice" args="jrnlId, fileSize, numFiles"/>
+ <event name="full" sev="error" args="jrnlId, what"/>
+ <event name="recovered" sev="notice" args="jrnlId, fileSize, numFiles, numEnq, numTxn, numTxnEnq, numTxnDeq"/>
+</schema>
diff --git a/cpp/src/qpid/log/Logger.cpp b/cpp/src/qpid/log/Logger.cpp
index 7c2e807ca9..7e7e1e94c1 100644
--- a/cpp/src/qpid/log/Logger.cpp
+++ b/cpp/src/qpid/log/Logger.cpp
@@ -177,4 +177,20 @@ void Logger::reconfigure(const std::vector<std::string>& selectors) {
void Logger::setPrefix(const std::string& p) { prefix = p; }
+
+bool Logger::getHiresTimestamp()
+{
+ return flags & HIRES;
+}
+
+
+void Logger::setHiresTimestamp(bool setting)
+{
+ ScopedLock l(lock);
+ if (setting)
+ flags |= HIRES;
+ else
+ flags &= ~HIRES;
+}
+
}} // namespace qpid::log
diff --git a/cpp/src/qpid/management/ManagementAgent.cpp b/cpp/src/qpid/management/ManagementAgent.cpp
index 474c86ed48..86e9d0be8d 100644
--- a/cpp/src/qpid/management/ManagementAgent.cpp
+++ b/cpp/src/qpid/management/ManagementAgent.cpp
@@ -28,12 +28,15 @@
#include "qpid/management/ManagementObject.h"
#include "qpid/broker/DeliverableMessage.h"
#include "qpid/log/Statement.h"
-#include <qpid/broker/Message.h>
+#include "qpid/broker/Message.h"
+#include "qpid/broker/Broker.h"
#include "qpid/framing/MessageTransferBody.h"
#include "qpid/framing/FieldValue.h"
#include "qpid/broker/amqp_0_10/MessageTransfer.h"
#include "qpid/sys/Time.h"
+#include "qpid/sys/Timer.h"
#include "qpid/sys/Thread.h"
+#include "qpid/sys/PollableQueue.h"
#include "qpid/broker/ConnectionState.h"
#include "qpid/broker/AclModule.h"
#include "qpid/types/Variant.h"
@@ -46,6 +49,9 @@
#include <sstream>
#include <typeinfo>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
namespace qpid {
namespace management {
@@ -62,22 +68,23 @@ namespace _qmf = qmf::org::apache::qpid::broker;
namespace {
- const string defaultVendorName("vendor");
- const string defaultProductName("product");
+const size_t qmfV1BufferSize(65536);
+const string defaultVendorName("vendor");
+const string defaultProductName("product");
- // Create a valid binding key substring by
- // replacing all '.' chars with '_'
- const string keyifyNameStr(const string& name)
- {
- string n2 = name;
+// Create a valid binding key substring by
+// replacing all '.' chars with '_'
+const string keyifyNameStr(const string& name)
+{
+ string n2 = name;
- size_t pos = n2.find('.');
- while (pos != n2.npos) {
- n2.replace(pos, 1, "_");
- pos = n2.find('.', pos);
- }
- return n2;
+ size_t pos = n2.find('.');
+ while (pos != n2.npos) {
+ n2.replace(pos, 1, "_");
+ pos = n2.find('.', pos);
}
+ return n2;
+}
struct ScopedManagementContext
{
@@ -90,6 +97,32 @@ struct ScopedManagementContext
setManagementExecutionContext(0);
}
};
+
+typedef boost::function0<void> FireFunction;
+struct Periodic : public qpid::sys::TimerTask
+{
+ FireFunction fireFunction;
+ qpid::sys::Timer* timer;
+
+ Periodic (FireFunction f, qpid::sys::Timer* t, uint32_t seconds);
+ virtual ~Periodic ();
+ void fire ();
+};
+
+Periodic::Periodic (FireFunction f, qpid::sys::Timer* t, uint32_t _seconds)
+ : TimerTask(sys::Duration((_seconds ? _seconds : 1) * sys::TIME_SEC),
+ "ManagementAgent::periodicProcessing"),
+ fireFunction(f), timer(t) {}
+
+Periodic::~Periodic() {}
+
+void Periodic::fire()
+{
+ setupNextFire();
+ timer->add(this);
+ fireFunction();
+}
+
}
@@ -113,9 +146,8 @@ ManagementAgent::RemoteAgent::~RemoteAgent ()
QPID_LOG(debug, "Remote Agent removed bank=[" << brokerBank << "." << agentBank << "]");
if (mgmtObject != 0) {
mgmtObject->resourceDestroy();
- agent.deleteObjectNowLH(mgmtObject->getObjectId());
- delete mgmtObject;
- mgmtObject = 0;
+ agent.deleteObjectNow(mgmtObject->getObjectId());
+ mgmtObject.reset();
}
}
@@ -124,8 +156,7 @@ ManagementAgent::ManagementAgent (const bool qmfV1, const bool qmfV2) :
startTime(sys::now()),
suppressed(false), disallowAllV1Methods(false),
vendorNameKey(defaultVendorName), productNameKey(defaultProductName),
- qmf1Support(qmfV1), qmf2Support(qmfV2), maxReplyObjs(100),
- msgBuffer(MA_BUFFER_SIZE), memstat(0)
+ qmf1Support(qmfV1), qmf2Support(qmfV2), maxReplyObjs(100)
{
nextObjectId = 1;
brokerBank = 1;
@@ -136,7 +167,7 @@ ManagementAgent::ManagementAgent (const bool qmfV1, const bool qmfV2) :
attrMap["_vendor"] = defaultVendorName;
attrMap["_product"] = defaultProductName;
- memstat = new qmf::org::apache::qpid::broker::Memory(this, 0, "amqp-broker");
+ memstat = _qmf::Memory::shared_ptr(new qmf::org::apache::qpid::broker::Memory(this, 0, "amqp-broker"));
addObject(memstat, "amqp-broker");
}
@@ -155,15 +186,6 @@ ManagementAgent::~ManagementAgent ()
v2Direct.reset();
remoteAgents.clear();
-
- moveNewObjectsLH();
- for (ManagementObjectMap::iterator iter = managementObjects.begin ();
- iter != managementObjects.end ();
- iter++) {
- ManagementObject* object = iter->second;
- delete object;
- }
- managementObjects.clear();
}
}
@@ -176,6 +198,11 @@ void ManagementAgent::configure(const string& _dataDir, bool _publish, uint16_t
broker = _broker;
threadPoolSize = _threads;
ManagementObject::maxThreads = threadPoolSize;
+ sendQueue.reset(
+ new EventQueue(boost::bind(&ManagementAgent::sendEvents, this, _1), broker->getPoller()));
+ sendQueue->start();
+ timer = &broker->getTimer();
+ timer->add(new Periodic(boost::bind(&ManagementAgent::periodicProcessing, this), timer, interval));
// Get from file or generate and save to file.
if (dataDir.empty())
@@ -218,13 +245,6 @@ void ManagementAgent::configure(const string& _dataDir, bool _publish, uint16_t
}
}
-void ManagementAgent::pluginsInitialized() {
- // Do this here so cluster plugin has the chance to set up the timer.
- timer = &broker->getClusterTimer();
- timer->add(new Periodic(*this, interval));
-}
-
-
void ManagementAgent::setName(const string& vendor, const string& product, const string& instance)
{
if (vendor.find(':') != vendor.npos) {
@@ -245,13 +265,13 @@ void ManagementAgent::setName(const string& vendor, const string& product, const
} else
inst = instance;
- name_address = vendor + ":" + product + ":" + inst;
- attrMap["_instance"] = inst;
- attrMap["_name"] = name_address;
+ name_address = vendor + ":" + product + ":" + inst;
+ attrMap["_instance"] = inst;
+ attrMap["_name"] = name_address;
- vendorNameKey = keyifyNameStr(vendor);
- productNameKey = keyifyNameStr(product);
- instanceNameKey = keyifyNameStr(inst);
+ vendorNameKey = keyifyNameStr(vendor);
+ productNameKey = keyifyNameStr(product);
+ instanceNameKey = keyifyNameStr(inst);
}
@@ -316,11 +336,12 @@ void ManagementAgent::registerEvent (const string& packageName,
}
// Deprecated: V1 objects
-ObjectId ManagementAgent::addObject(ManagementObject* object, uint64_t persistId, bool persistent)
+ObjectId ManagementAgent::addObject(ManagementObject::shared_ptr object, uint64_t persistId, bool persistent)
{
uint16_t sequence;
uint64_t objectNum;
+ sys::Mutex::ScopedLock lock(addLock);
sequence = persistent ? 0 : bootSequence;
objectNum = persistId ? persistId : nextObjectId++;
@@ -329,17 +350,14 @@ ObjectId ManagementAgent::addObject(ManagementObject* object, uint64_t persistId
object->setObjectId(objId);
- {
- sys::Mutex::ScopedLock lock(addLock);
- newManagementObjects.push_back(object);
- }
+ newManagementObjects.push_back(object);
QPID_LOG(debug, "Management object (V1) added: " << objId.getV2Key());
return objId;
}
-ObjectId ManagementAgent::addObject(ManagementObject* object,
+ObjectId ManagementAgent::addObject(ManagementObject::shared_ptr object,
const string& key,
bool persistent)
{
@@ -369,12 +387,11 @@ void ManagementAgent::raiseEvent(const ManagementEvent& event, severity_t severi
"emerg", "alert", "crit", "error", "warn",
"note", "info", "debug"
};
- sys::Mutex::ScopedLock lock (userLock);
uint8_t sev = (severity == SEV_DEFAULT) ? event.getSeverity() : (uint8_t) severity;
if (qmf1Support) {
- Buffer outBuffer(eventBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
+ char buffer[qmfV1BufferSize];
+ Buffer outBuffer(buffer, qmfV1BufferSize);
encodeHeader(outBuffer, 'e');
outBuffer.putShortString(event.getPackageName());
@@ -385,9 +402,7 @@ void ManagementAgent::raiseEvent(const ManagementEvent& event, severity_t severi
string sBuf;
event.encode(sBuf);
outBuffer.putRawData(sBuf);
- outLen = MA_BUFFER_SIZE - outBuffer.available();
- outBuffer.reset();
- sendBufferLH(outBuffer, outLen, mExchange,
+ sendBuffer(outBuffer, mExchange,
"console.event.1.0." + event.getPackageName() + "." + event.getEventName());
QPID_LOG(debug, "SEND raiseEvent (v1) class=" << event.getPackageName() << "." << event.getEventName());
}
@@ -426,25 +441,11 @@ void ManagementAgent::raiseEvent(const ManagementEvent& event, severity_t severi
Variant::List list_;
list_.push_back(map_);
ListCodec::encode(list_, content);
- sendBufferLH(content, "", headers, "amqp/list", v2Topic, key.str());
+ sendBuffer(content, "", headers, "amqp/list", v2Topic, key.str());
QPID_LOG(debug, "SEND raiseEvent (v2) class=" << event.getPackageName() << "." << event.getEventName());
}
}
-ManagementAgent::Periodic::Periodic (ManagementAgent& _agent, uint32_t _seconds)
- : TimerTask(sys::Duration((_seconds ? _seconds : 1) * sys::TIME_SEC),
- "ManagementAgent::periodicProcessing"),
- agent(_agent) {}
-
-ManagementAgent::Periodic::~Periodic() {}
-
-void ManagementAgent::Periodic::fire()
-{
- setupNextFire();
- agent.timer->add(this);
- agent.periodicProcessing();
-}
-
void ManagementAgent::clientAdded (const string& routingKey)
{
sys::Mutex::ScopedLock lock(userLock);
@@ -480,28 +481,14 @@ void ManagementAgent::clientAdded (const string& routingKey)
while (rkeys.size()) {
char localBuffer[16];
Buffer outBuffer(localBuffer, 16);
- uint32_t outLen;
encodeHeader(outBuffer, 'x');
- outLen = outBuffer.getPosition();
- outBuffer.reset();
- sendBufferLH(outBuffer, outLen, dExchange, rkeys.front());
+ sendBuffer(outBuffer, dExchange, rkeys.front());
QPID_LOG(debug, "SEND ConsoleAddedIndication to=" << rkeys.front());
rkeys.pop_front();
}
}
-void ManagementAgent::clusterUpdate() {
- // Called on all cluster memebers when a new member joins a cluster.
- // Set clientWasAdded so that on the next periodicProcessing we will do
- // a full update on all cluster members.
- sys::Mutex::ScopedLock l(userLock);
- moveNewObjectsLH(); // keep lists consistent with updater/updatee.
- moveDeletedObjectsLH();
- clientWasAdded = true;
- debugSnapshot("Cluster member joined");
-}
-
void ManagementAgent::encodeHeader (Buffer& buf, uint8_t opcode, uint32_t seq)
{
buf.putOctet ('A');
@@ -523,12 +510,9 @@ bool ManagementAgent::checkHeader (Buffer& buf, uint8_t *opcode, uint32_t *seq)
return h1 == 'A' && h2 == 'M' && h3 == '2';
}
-// NOTE WELL: assumes userLock is held by caller (LH)
-// NOTE EVEN WELLER: drops this lock when delivering the message!!!
-void ManagementAgent::sendBufferLH(Buffer& buf,
- uint32_t length,
- qpid::broker::Exchange::shared_ptr exchange,
- const string& routingKey)
+void ManagementAgent::sendBuffer(Buffer& buf,
+ qpid::broker::Exchange::shared_ptr exchange,
+ const string& routingKey)
{
if (suppressed) {
QPID_LOG(debug, "Suppressing management message to " << routingKey);
@@ -541,6 +525,8 @@ void ManagementAgent::sendBufferLH(Buffer& buf,
AMQFrame header((AMQHeaderBody()));
AMQFrame content((AMQContentBody()));
+ size_t length = buf.getPosition();
+ buf.reset();
content.castBody<AMQContentBody>()->decode(buf, length);
method.setEof(false);
@@ -560,42 +546,30 @@ void ManagementAgent::sendBufferLH(Buffer& buf,
dp->setRoutingKey(routingKey);
transfer->getFrames().append(content);
-
Message msg(transfer, transfer);
msg.setIsManagementMessage(true);
-
- {
- sys::Mutex::ScopedUnlock u(userLock);
-
- DeliverableMessage deliverable (msg, 0);
- try {
- exchange->route(deliverable);
- } catch(exception&) {}
- }
+ sendQueue->push(make_pair(exchange, msg));
buf.reset();
}
-void ManagementAgent::sendBufferLH(Buffer& buf,
- uint32_t length,
- const string& exchange,
- const string& routingKey)
+void ManagementAgent::sendBuffer(Buffer& buf,
+ const string& exchange,
+ const string& routingKey)
{
qpid::broker::Exchange::shared_ptr ex(broker->getExchanges().get(exchange));
if (ex.get() != 0)
- sendBufferLH(buf, length, ex, routingKey);
+ sendBuffer(buf, ex, routingKey);
}
-// NOTE WELL: assumes userLock is held by caller (LH)
-// NOTE EVEN WELLER: drops this lock when delivering the message!!!
-void ManagementAgent::sendBufferLH(const string& data,
- const string& cid,
- const Variant::Map& headers,
- const string& content_type,
- qpid::broker::Exchange::shared_ptr exchange,
- const string& routingKey,
- uint64_t ttl_msec)
+void ManagementAgent::sendBuffer(const string& data,
+ const string& cid,
+ const Variant::Map& headers,
+ const string& content_type,
+ qpid::broker::Exchange::shared_ptr exchange,
+ const string& routingKey,
+ uint64_t ttl_msec)
{
Variant::Map::const_iterator i;
@@ -643,34 +617,27 @@ void ManagementAgent::sendBufferLH(const string& data,
msg.setIsManagementMessage(true);
msg.computeExpiration(broker->getExpiryPolicy());
- {
- sys::Mutex::ScopedUnlock u(userLock);
-
- DeliverableMessage deliverable (msg, 0);
- try {
- exchange->route(deliverable);
- } catch(exception&) {}
- }
+ sendQueue->push(make_pair(exchange, msg));
}
-void ManagementAgent::sendBufferLH(const string& data,
- const string& cid,
- const Variant::Map& headers,
- const string& content_type,
- const string& exchange,
- const string& routingKey,
- uint64_t ttl_msec)
+void ManagementAgent::sendBuffer(const string& data,
+ const string& cid,
+ const Variant::Map& headers,
+ const string& content_type,
+ const string& exchange,
+ const string& routingKey,
+ uint64_t ttl_msec)
{
qpid::broker::Exchange::shared_ptr ex(broker->getExchanges().get(exchange));
if (ex.get() != 0)
- sendBufferLH(data, cid, headers, content_type, ex, routingKey, ttl_msec);
+ sendBuffer(data, cid, headers, content_type, ex, routingKey, ttl_msec);
}
/** Objects that have been added since the last periodic poll are temporarily
* saved in the newManagementObjects list. This allows objects to be
- * added without needing to block on the userLock (addLock is used instead).
+ * added without needing to block on the userLock (objectLock is used instead).
* These new objects need to be integrated into the object database
* (managementObjects) *before* they can be properly managed. This routine
* performs the integration.
@@ -680,34 +647,33 @@ void ManagementAgent::sendBufferLH(const string& data,
* duplicate object ids. To avoid clashes, don't put deleted objects
* into the active object database.
*/
-void ManagementAgent::moveNewObjectsLH()
+void ManagementAgent::moveNewObjects()
{
- sys::Mutex::ScopedLock lock (addLock);
+ sys::Mutex::ScopedLock lock(addLock);
+ sys::Mutex::ScopedLock objLock (objectLock);
while (!newManagementObjects.empty()) {
- ManagementObject *object = newManagementObjects.back();
+ ManagementObject::shared_ptr object = newManagementObjects.back();
newManagementObjects.pop_back();
if (object->isDeleted()) {
DeletedObject::shared_ptr dptr(new DeletedObject(object, qmf1Support, qmf2Support));
pendingDeletedObjs[dptr->getKey()].push_back(dptr);
- delete object;
} else { // add to active object list, check for duplicates.
ObjectId oid = object->getObjectId();
ManagementObjectMap::iterator destIter = managementObjects.find(oid);
if (destIter != managementObjects.end()) {
// duplicate found. It is OK if the old object has been marked
// deleted, just replace the old with the new.
- ManagementObject *oldObj = destIter->second;
- if (oldObj->isDeleted()) {
- DeletedObject::shared_ptr dptr(new DeletedObject(oldObj, qmf1Support, qmf2Support));
- pendingDeletedObjs[dptr->getKey()].push_back(dptr);
- delete oldObj;
- } else {
+ ManagementObject::shared_ptr oldObj = destIter->second;
+ if (!oldObj->isDeleted()) {
// Duplicate non-deleted objects? This is a user error - oids must be unique.
// for now, leak the old object (safer than deleting - may still be referenced)
// and complain loudly...
QPID_LOG(error, "Detected two management objects with the same identifier: " << oid);
+ oldObj->resourceDestroy();
}
+ DeletedObject::shared_ptr dptr(new DeletedObject(oldObj, qmf1Support, qmf2Support));
+ pendingDeletedObjs[dptr->getKey()].push_back(dptr);
// QPID-3666: be sure to replace the -index- also, as non-key members of
// the index object may be different for the new object! So erase the
// entry, rather than []= assign here:
@@ -720,32 +686,41 @@ void ManagementAgent::moveNewObjectsLH()
void ManagementAgent::periodicProcessing (void)
{
-#define BUFSIZE 65536
#define HEADROOM 4096
debugSnapshot("Management agent periodic processing");
sys::Mutex::ScopedLock lock (userLock);
- uint32_t contentSize;
string routingKey;
string sBuf;
- moveNewObjectsLH();
+ moveNewObjects();
//
// If we're publishing updates, get the latest memory statistics and uptime now
//
if (publish) {
uint64_t uptime = sys::Duration(startTime, sys::now());
- static_cast<_qmf::Broker*>(broker->GetManagementObject())->set_uptime(uptime);
- qpid::sys::MemStat::loadMemInfo(memstat);
+ boost::dynamic_pointer_cast<_qmf::Broker>(broker->GetManagementObject())->set_uptime(uptime);
+ qpid::sys::MemStat::loadMemInfo(memstat.get());
+ }
+
+ //
+ // Use a copy of the management object map to avoid holding the objectLock
+ //
+ ManagementObjectVector localManagementObjects;
+ {
+ sys::Mutex::ScopedLock objLock(objectLock);
+ std::transform(managementObjects.begin(), managementObjects.end(),
+ std::back_inserter(localManagementObjects),
+ boost::bind(&ManagementObjectMap::value_type::second, _1));
}
//
// Clear the been-here flag on all objects in the map.
//
- for (ManagementObjectMap::iterator iter = managementObjects.begin();
- iter != managementObjects.end();
+ for (ManagementObjectVector::iterator iter = localManagementObjects.begin();
+ iter != localManagementObjects.end();
iter++) {
- ManagementObject* object = iter->second;
+ ManagementObject::shared_ptr object = *iter;
object->setFlags(0);
if (clientWasAdded) {
object->setForcePublish(true);
@@ -760,22 +735,25 @@ void ManagementAgent::periodicProcessing (void)
// if we sent the active update first, _then_ the delete update, clients
// would incorrectly think the object was deleted. See QPID-2997
//
- bool objectsDeleted = moveDeletedObjectsLH();
+ bool objectsDeleted = moveDeletedObjects();
+ PendingDeletedObjsMap localPendingDeletedObjs;
+ {
+ sys::Mutex::ScopedLock objLock(objectLock);
+ localPendingDeletedObjs.swap(pendingDeletedObjs);
+ }
//
// If we are not publishing updates, just clear the pending deletes. There's no
// need to tell anybody.
//
if (!publish)
- pendingDeletedObjs.clear();
-
- if (!pendingDeletedObjs.empty()) {
- // use a temporary copy of the pending deletes so dropping the lock when
- // the buffer is sent is safe.
- PendingDeletedObjsMap tmp(pendingDeletedObjs);
- pendingDeletedObjs.clear();
+ localPendingDeletedObjs.clear();
- for (PendingDeletedObjsMap::iterator mIter = tmp.begin(); mIter != tmp.end(); mIter++) {
+ ResizableBuffer msgBuffer(qmfV1BufferSize);
+ if (!localPendingDeletedObjs.empty()) {
+ for (PendingDeletedObjsMap::iterator mIter = localPendingDeletedObjs.begin();
+ mIter != localPendingDeletedObjs.end();
+ mIter++) {
std::string packageName;
std::string className;
msgBuffer.reset();
@@ -807,11 +785,10 @@ void ManagementAgent::periodicProcessing (void)
}
if (v1Objs >= maxReplyObjs) {
v1Objs = 0;
- contentSize = msgBuffer.getSize();
stringstream key;
key << "console.obj.1.0." << packageName << "." << className;
- msgBuffer.reset();
- sendBufferLH(msgBuffer, contentSize, mExchange, key.str()); // UNLOCKS USERLOCK
+ size_t contentSize = msgBuffer.getPosition();
+ sendBuffer(msgBuffer, mExchange, key.str());
QPID_LOG(debug, "SEND V1 Multicast ContentInd V1 (delete) to="
<< key.str() << " len=" << contentSize);
}
@@ -840,7 +817,7 @@ void ManagementAgent::periodicProcessing (void)
headers["qmf.content"] = "_data";
headers["qmf.agent"] = name_address;
- sendBufferLH(content, "", headers, "amqp/list", v2Topic, key.str()); // UNLOCKS USERLOCK
+ sendBuffer(content, "", headers, "amqp/list", v2Topic, key.str(), 0);
QPID_LOG(debug, "SEND Multicast ContentInd V2 (delete) to=" << key.str() << " len=" << content.length());
}
}
@@ -850,11 +827,10 @@ void ManagementAgent::periodicProcessing (void)
// send any remaining objects...
if (v1Objs) {
- contentSize = BUFSIZE - msgBuffer.available();
stringstream key;
key << "console.obj.1.0." << packageName << "." << className;
- msgBuffer.reset();
- sendBufferLH(msgBuffer, contentSize, mExchange, key.str()); // UNLOCKS USERLOCK
+ size_t contentSize = msgBuffer.getPosition();
+ sendBuffer(msgBuffer, mExchange, key.str());
QPID_LOG(debug, "SEND V1 Multicast ContentInd V1 (delete) to=" << key.str() << " len=" << contentSize);
}
@@ -877,7 +853,7 @@ void ManagementAgent::periodicProcessing (void)
headers["qmf.content"] = "_data";
headers["qmf.agent"] = name_address;
- sendBufferLH(content, "", headers, "amqp/list", v2Topic, key.str()); // UNLOCKS USERLOCK
+ sendBuffer(content, "", headers, "amqp/list", v2Topic, key.str(), 0);
QPID_LOG(debug, "SEND Multicast ContentInd V2 (delete) to=" << key.str() << " len=" << content.length());
}
}
@@ -885,9 +861,7 @@ void ManagementAgent::periodicProcessing (void)
}
//
- // Process the entire object map. Remember: we drop the userLock each time we call
- // sendBuffer(). This allows the managementObjects map to be altered during the
- // sendBuffer() call, so always restart the search after a sendBuffer() call
+ // Process the entire object map.
//
// If publish is disabled, don't send any updates.
//
@@ -897,14 +871,14 @@ void ManagementAgent::periodicProcessing (void)
uint32_t pcount;
uint32_t scount;
uint32_t v1Objs, v2Objs;
- ManagementObjectMap::iterator baseIter;
+ ManagementObjectVector::iterator baseIter;
std::string packageName;
std::string className;
- for (baseIter = managementObjects.begin();
- baseIter != managementObjects.end();
+ for (baseIter = localManagementObjects.begin();
+ baseIter != localManagementObjects.end();
baseIter++) {
- ManagementObject* baseObject = baseIter->second;
+ ManagementObject::shared_ptr baseObject = *baseIter;
//
// Skip until we find a base object requiring processing...
//
@@ -915,7 +889,7 @@ void ManagementAgent::periodicProcessing (void)
}
}
- if (baseIter == managementObjects.end())
+ if (baseIter == localManagementObjects.end())
break; // done - all objects processed
pcount = scount = 0;
@@ -924,12 +898,12 @@ void ManagementAgent::periodicProcessing (void)
list_.clear();
msgBuffer.reset();
- for (ManagementObjectMap::iterator iter = baseIter;
- iter != managementObjects.end();
+ for (ManagementObjectVector::iterator iter = baseIter;
+ iter != localManagementObjects.end();
iter++) {
msgBuffer.makeAvailable(HEADROOM); // Make sure there's buffer space
- ManagementObject* baseObject = baseIter->second;
- ManagementObject* object = iter->second;
+ ManagementObject::shared_ptr baseObject = *baseIter;
+ ManagementObject::shared_ptr object = *iter;
bool send_stats, send_props;
if (baseObject->isSameClass(*object) && object->getFlags() == 0) {
object->setFlags(1);
@@ -1004,12 +978,11 @@ void ManagementAgent::periodicProcessing (void)
if (pcount || scount) {
if (qmf1Support) {
- contentSize = BUFSIZE - msgBuffer.available();
- if (contentSize > 0) {
+ if (msgBuffer.getPosition() > 0) {
stringstream key;
key << "console.obj.1.0." << packageName << "." << className;
- msgBuffer.reset();
- sendBufferLH(msgBuffer, contentSize, mExchange, key.str()); // UNLOCKS USERLOCK
+ size_t contentSize = msgBuffer.getPosition();
+ sendBuffer(msgBuffer, mExchange, key.str());
QPID_LOG(debug, "SEND V1 Multicast ContentInd to=" << key.str()
<< " props=" << pcount
<< " stats=" << scount
@@ -1035,7 +1008,7 @@ void ManagementAgent::periodicProcessing (void)
headers["qmf.content"] = "_data";
headers["qmf.agent"] = name_address;
- sendBufferLH(content, "", headers, "amqp/list", v2Topic, key.str()); // UNLOCKS USERLOCK
+ sendBuffer(content, "", headers, "amqp/list", v2Topic, key.str(), 0);
QPID_LOG(debug, "SEND Multicast ContentInd to=" << key.str()
<< " props=" << pcount
<< " stats=" << scount
@@ -1045,21 +1018,21 @@ void ManagementAgent::periodicProcessing (void)
}
} // end processing updates for all objects
- if (objectsDeleted) deleteOrphanedAgentsLH();
+ if (objectsDeleted) {
+ sys::Mutex::ScopedLock lock (userLock);
+ deleteOrphanedAgentsLH();
+ }
// heartbeat generation. Note that heartbeats need to be sent even if publish is disabled.
if (qmf1Support) {
- uint32_t contentSize;
- char msgChars[BUFSIZE];
- Buffer msgBuffer(msgChars, BUFSIZE);
+ char msgChars[qmfV1BufferSize];
+ Buffer msgBuffer(msgChars, qmfV1BufferSize);
encodeHeader(msgBuffer, 'h');
msgBuffer.putLongLong(uint64_t(sys::Duration(sys::EPOCH, sys::now())));
- contentSize = BUFSIZE - msgBuffer.available ();
- msgBuffer.reset ();
routingKey = "console.heartbeat.1.0";
- sendBufferLH(msgBuffer, contentSize, mExchange, routingKey);
+ sendBuffer(msgBuffer, mExchange, routingKey);
QPID_LOG(debug, "SEND HeartbeatInd to=" << routingKey);
}
@@ -1087,23 +1060,26 @@ void ManagementAgent::periodicProcessing (void)
// Set TTL (in msecs) on outgoing heartbeat indications based on the interval
// time to prevent stale heartbeats from getting to the consoles.
- sendBufferLH(content, "", headers, "amqp/map", v2Topic, addr_key.str(), interval * 2 * 1000);
+ sendBuffer(content, "", headers, "amqp/map", v2Topic, addr_key.str(), interval * 2 * 1000);
QPID_LOG(debug, "SENT AgentHeartbeat name=" << name_address);
}
}
-void ManagementAgent::deleteObjectNowLH(const ObjectId& oid)
+void ManagementAgent::deleteObjectNow(const ObjectId& oid)
{
- ManagementObjectMap::iterator iter = managementObjects.find(oid);
- if (iter == managementObjects.end())
- return;
- ManagementObject* object = iter->second;
- if (!object->isDeleted())
- return;
+ ManagementObject::shared_ptr object;
+ {
+ sys::Mutex::ScopedLock lock(objectLock);
+ ManagementObjectMap::iterator iter = managementObjects.find(oid);
+ if (iter == managementObjects.end())
+ return;
+ object = iter->second;
+ if (!object->isDeleted())
+ return;
+ managementObjects.erase(oid);
+ }
- // since sendBufferLH drops the userLock, don't call it until we
- // are done manipulating the object.
#define DNOW_BUFSIZE 2048
char msgChars[DNOW_BUFSIZE];
Buffer msgBuffer(msgChars, DNOW_BUFSIZE);
@@ -1139,15 +1115,12 @@ void ManagementAgent::deleteObjectNowLH(const ObjectId& oid)
v2key << "." << instanceNameKey;
}
- object = 0;
- managementObjects.erase(oid);
+ object.reset();
// object deleted, ok to drop lock now.
if (publish && qmf1Support) {
- uint32_t contentSize = msgBuffer.getPosition();
- msgBuffer.reset();
- sendBufferLH(msgBuffer, contentSize, mExchange, v1key.str());
+ sendBuffer(msgBuffer, mExchange, v1key.str());
QPID_LOG(debug, "SEND Immediate(delete) ContentInd to=" << v1key.str());
}
@@ -1160,29 +1133,26 @@ void ManagementAgent::deleteObjectNowLH(const ObjectId& oid)
string content;
ListCodec::encode(list_, content);
- sendBufferLH(content, "", headers, "amqp/list", v2Topic, v2key.str());
+ sendBuffer(content, "", headers, "amqp/list", v2Topic, v2key.str(), 0);
QPID_LOG(debug, "SEND Immediate(delete) ContentInd to=" << v2key.str());
}
}
-void ManagementAgent::sendCommandCompleteLH(const string& replyToKey, uint32_t sequence,
- uint32_t code, const string& text)
+void ManagementAgent::sendCommandComplete(const string& replyToKey, uint32_t sequence,
+ uint32_t code, const string& text)
{
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
+ ResizableBuffer outBuffer (qmfV1BufferSize);
encodeHeader (outBuffer, 'z', sequence);
outBuffer.putLong (code);
outBuffer.putShortString (text);
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset ();
- sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ sendBuffer(outBuffer, dExchange, replyToKey);
QPID_LOG(debug, "SEND CommandCompleteInd code=" << code << " text=" << text << " to=" <<
replyToKey << " seq=" << sequence);
}
-void ManagementAgent::sendExceptionLH(const string& rte, const string& rtk, const string& cid,
- const string& text, uint32_t code, bool viaLocal)
+void ManagementAgent::sendException(const string& rte, const string& rtk, const string& cid,
+ const string& text, uint32_t code, bool viaLocal)
{
static const string addr_exchange("qmf.default.direct");
@@ -1200,7 +1170,7 @@ void ManagementAgent::sendExceptionLH(const string& rte, const string& rtk, cons
map["_values"] = values;
MapCodec::encode(map, content);
- sendBufferLH(content, cid, headers, "amqp/map", rte, rtk);
+ sendBuffer(content, cid, headers, "amqp/map", rte, rtk);
QPID_LOG(debug, "SENT Exception code=" << code <<" text=" << text);
}
@@ -1211,7 +1181,6 @@ bool ManagementAgent::dispatchCommand (Deliverable& deliverable,
const bool topic,
int qmfVersion)
{
- sys::Mutex::ScopedLock lock (userLock);
Message& msg = ((DeliverableMessage&) deliverable).getMessage ();
if (topic && qmfVersion == 1) {
@@ -1225,23 +1194,23 @@ bool ManagementAgent::dispatchCommand (Deliverable& deliverable,
// schema.#
if (routingKey == "broker") {
- dispatchAgentCommandLH(msg);
+ dispatchAgentCommand(msg);
return false;
}
if (routingKey.length() > 6) {
if (routingKey.compare(0, 9, "agent.1.0") == 0) {
- dispatchAgentCommandLH(msg);
+ dispatchAgentCommand(msg);
return false;
}
if (routingKey.compare(0, 8, "agent.1.") == 0) {
- return authorizeAgentMessageLH(msg);
+ return authorizeAgentMessage(msg);
}
if (routingKey.compare(0, 7, "schema.") == 0) {
- dispatchAgentCommandLH(msg);
+ dispatchAgentCommand(msg);
return true;
}
}
@@ -1253,7 +1222,7 @@ bool ManagementAgent::dispatchCommand (Deliverable& deliverable,
// Intercept messages bound to:
// "console.ind.locate.# - process these messages, and also allow them to be forwarded.
if (routingKey == "console.request.agent_locate") {
- dispatchAgentCommandLH(msg);
+ dispatchAgentCommand(msg);
return true;
}
@@ -1264,7 +1233,7 @@ bool ManagementAgent::dispatchCommand (Deliverable& deliverable,
// "<name_address>" - the broker agent's proper name
// and do not forward them futher
if (routingKey == "broker" || routingKey == name_address) {
- dispatchAgentCommandLH(msg, routingKey == "broker");
+ dispatchAgentCommand(msg, routingKey == "broker");
return false;
}
}
@@ -1273,16 +1242,15 @@ bool ManagementAgent::dispatchCommand (Deliverable& deliverable,
return true;
}
-void ManagementAgent::handleMethodRequestLH(Buffer& inBuffer, const string& replyToKey, uint32_t sequence, const ConnectionToken* connToken)
+void ManagementAgent::handleMethodRequest(Buffer& inBuffer, const string& replyToKey, uint32_t sequence, const ConnectionToken* connToken)
{
- moveNewObjectsLH();
+ moveNewObjects();
string methodName;
string packageName;
string className;
uint8_t hash[16];
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
+ ResizableBuffer outBuffer (qmfV1BufferSize);
AclModule* acl = broker->getAcl();
string inArgs;
@@ -1304,9 +1272,7 @@ void ManagementAgent::handleMethodRequestLH(Buffer& inBuffer, const string& repl
if (disallowAllV1Methods) {
outBuffer.putLong(Manageable::STATUS_FORBIDDEN);
outBuffer.putMediumString("QMFv1 methods forbidden on this broker, use QMFv2");
- outLen = MA_BUFFER_SIZE - outBuffer.available();
- outBuffer.reset();
- sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ sendBuffer(outBuffer, dExchange, replyToKey);
QPID_LOG(debug, "SEND MethodResponse status=FORBIDDEN reason='All QMFv1 Methods Forbidden' seq=" << sequence);
return;
}
@@ -1315,9 +1281,7 @@ void ManagementAgent::handleMethodRequestLH(Buffer& inBuffer, const string& repl
if (i != disallowed.end()) {
outBuffer.putLong(Manageable::STATUS_FORBIDDEN);
outBuffer.putMediumString(i->second);
- outLen = MA_BUFFER_SIZE - outBuffer.available();
- outBuffer.reset();
- sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ sendBuffer(outBuffer, dExchange, replyToKey);
QPID_LOG(debug, "SEND MethodResponse status=FORBIDDEN text=" << i->second << " seq=" << sequence);
return;
}
@@ -1331,30 +1295,34 @@ void ManagementAgent::handleMethodRequestLH(Buffer& inBuffer, const string& repl
if (!acl->authorise(userId, acl::ACT_ACCESS, acl::OBJ_METHOD, methodName, &params)) {
outBuffer.putLong(Manageable::STATUS_FORBIDDEN);
outBuffer.putMediumString(Manageable::StatusText(Manageable::STATUS_FORBIDDEN));
- outLen = MA_BUFFER_SIZE - outBuffer.available();
- outBuffer.reset();
- sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ sendBuffer(outBuffer, dExchange, replyToKey);
QPID_LOG(debug, "SEND MethodResponse status=FORBIDDEN" << " seq=" << sequence);
return;
}
}
- ManagementObjectMap::iterator iter = numericFind(objId);
- if (iter == managementObjects.end() || iter->second->isDeleted()) {
+ ManagementObject::shared_ptr object;
+ {
+ sys::Mutex::ScopedLock lock(objectLock);
+ ManagementObjectMap::iterator iter = numericFind(objId);
+ if (iter != managementObjects.end())
+ object = iter->second;
+ }
+
+ if (!object || object->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)) {
+ if ((object->getPackageName() != packageName) ||
+ (object->getClassName() != className)) {
outBuffer.putLong (Manageable::STATUS_PARAMETER_INVALID);
outBuffer.putMediumString(Manageable::StatusText (Manageable::STATUS_PARAMETER_INVALID));
}
else {
uint32_t pos = outBuffer.getPosition();
try {
- sys::Mutex::ScopedUnlock u(userLock);
string outBuf;
- iter->second->doMethod(methodName, inArgs, outBuf, userId);
+ object->doMethod(methodName, inArgs, outBuf, userId);
outBuffer.putRawData(outBuf);
} catch(exception& e) {
outBuffer.setPosition(pos);;
@@ -1364,17 +1332,15 @@ void ManagementAgent::handleMethodRequestLH(Buffer& inBuffer, const string& repl
}
}
- outLen = MA_BUFFER_SIZE - outBuffer.available();
- outBuffer.reset();
- sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ sendBuffer(outBuffer, dExchange, replyToKey);
QPID_LOG(debug, "SEND MethodResponse (v1) to=" << replyToKey << " seq=" << sequence);
}
-void ManagementAgent::handleMethodRequestLH (const string& body, const string& rte, const string& rtk,
- const string& cid, const ConnectionToken* connToken, bool viaLocal)
+void ManagementAgent::handleMethodRequest (const string& body, const string& rte, const string& rtk,
+ const string& cid, const ConnectionToken* connToken, bool viaLocal)
{
- moveNewObjectsLH();
+ moveNewObjects();
string methodName;
Variant::Map inMap;
@@ -1393,8 +1359,8 @@ void ManagementAgent::handleMethodRequestLH (const string& body, const string& r
if ((oid = inMap.find("_object_id")) == inMap.end() ||
(mid = inMap.find("_method_name")) == inMap.end()) {
- sendExceptionLH(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_PARAMETER_INVALID),
- Manageable::STATUS_PARAMETER_INVALID, viaLocal);
+ sendException(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_PARAMETER_INVALID),
+ Manageable::STATUS_PARAMETER_INVALID, viaLocal);
return;
}
@@ -1412,16 +1378,22 @@ void ManagementAgent::handleMethodRequestLH (const string& body, const string& r
inArgs = (mid->second).asMap();
}
} catch(exception& e) {
- sendExceptionLH(rte, rtk, cid, e.what(), Manageable::STATUS_EXCEPTION, viaLocal);
+ sendException(rte, rtk, cid, e.what(), Manageable::STATUS_EXCEPTION, viaLocal);
return;
}
- ManagementObjectMap::iterator iter = managementObjects.find(objId);
+ ManagementObject::shared_ptr object;
+ {
+ sys::Mutex::ScopedLock lock(objectLock);
+ ManagementObjectMap::iterator iter = managementObjects.find(objId);
+ if (iter != managementObjects.end())
+ object = iter->second;
+ }
- if (iter == managementObjects.end() || iter->second->isDeleted()) {
+ if (!object || object->isDeleted()) {
stringstream estr;
estr << "No object found with ID=" << objId;
- sendExceptionLH(rte, rtk, cid, estr.str(), 1, viaLocal);
+ sendException(rte, rtk, cid, estr.str(), 1, viaLocal);
return;
}
@@ -1429,34 +1401,33 @@ void ManagementAgent::handleMethodRequestLH (const string& body, const string& r
AclModule* acl = broker->getAcl();
DisallowedMethods::const_iterator i;
- i = disallowed.find(make_pair(iter->second->getClassName(), methodName));
+ i = disallowed.find(make_pair(object->getClassName(), methodName));
if (i != disallowed.end()) {
- sendExceptionLH(rte, rtk, cid, i->second, Manageable::STATUS_FORBIDDEN, viaLocal);
+ sendException(rte, rtk, cid, i->second, Manageable::STATUS_FORBIDDEN, viaLocal);
return;
}
string userId = ((const qpid::broker::ConnectionState*) connToken)->getUserId();
if (acl != 0) {
map<acl::Property, string> params;
- params[acl::PROP_SCHEMAPACKAGE] = iter->second->getPackageName();
- params[acl::PROP_SCHEMACLASS] = iter->second->getClassName();
+ params[acl::PROP_SCHEMAPACKAGE] = object->getPackageName();
+ params[acl::PROP_SCHEMACLASS] = object->getClassName();
if (!acl->authorise(userId, acl::ACT_ACCESS, acl::OBJ_METHOD, methodName, &params)) {
- sendExceptionLH(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_FORBIDDEN),
- Manageable::STATUS_FORBIDDEN, viaLocal);
+ sendException(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_FORBIDDEN),
+ Manageable::STATUS_FORBIDDEN, viaLocal);
return;
}
}
// invoke the method
- QPID_LOG(debug, "RECV MethodRequest (v2) class=" << iter->second->getPackageName()
- << ":" << iter->second->getClassName() << " method=" <<
+ QPID_LOG(debug, "RECV MethodRequest (v2) class=" << object->getPackageName()
+ << ":" << object->getClassName() << " method=" <<
methodName << " replyTo=" << rte << "/" << rtk << " objId=" << objId << " inArgs=" << inArgs);
try {
- sys::Mutex::ScopedUnlock u(userLock);
- iter->second->doMethod(methodName, inArgs, callMap, userId);
+ object->doMethod(methodName, inArgs, callMap, userId);
errorCode = callMap["_status_code"].asUint32();
if (errorCode == 0) {
outMap["_arguments"] = Variant::Map();
@@ -1467,62 +1438,59 @@ void ManagementAgent::handleMethodRequestLH (const string& body, const string& r
} else
error = callMap["_status_text"].asString();
} catch(exception& e) {
- sendExceptionLH(rte, rtk, cid, e.what(), Manageable::STATUS_EXCEPTION, viaLocal);
+ sendException(rte, rtk, cid, e.what(), Manageable::STATUS_EXCEPTION, viaLocal);
return;
}
if (errorCode != 0) {
- sendExceptionLH(rte, rtk, cid, error, errorCode, viaLocal);
+ sendException(rte, rtk, cid, error, errorCode, viaLocal);
return;
}
MapCodec::encode(outMap, content);
- sendBufferLH(content, cid, headers, "amqp/map", rte, rtk);
+ sendBuffer(content, cid, headers, "amqp/map", rte, rtk);
QPID_LOG(debug, "SEND MethodResponse (v2) to=" << rte << "/" << rtk << " seq=" << cid << " map=" << outMap);
}
-void ManagementAgent::handleBrokerRequestLH (Buffer&, const string& replyToKey, uint32_t sequence)
+void ManagementAgent::handleBrokerRequest (Buffer&, const string& replyToKey, uint32_t sequence)
{
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
+ ResizableBuffer outBuffer (qmfV1BufferSize);
QPID_LOG(debug, "RECV BrokerRequest replyTo=" << replyToKey);
encodeHeader (outBuffer, 'b', sequence);
uuid.encode (outBuffer);
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset ();
- sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ sendBuffer(outBuffer, dExchange, replyToKey);
QPID_LOG(debug, "SEND BrokerResponse to=" << replyToKey);
}
-void ManagementAgent::handlePackageQueryLH (Buffer&, const string& replyToKey, uint32_t sequence)
+void ManagementAgent::handlePackageQuery (Buffer&, const string& replyToKey, uint32_t sequence)
{
QPID_LOG(debug, "RECV PackageQuery replyTo=" << replyToKey);
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
+ ResizableBuffer outBuffer (qmfV1BufferSize);
- for (PackageMap::iterator pIter = packages.begin ();
- pIter != packages.end ();
- pIter++)
{
- encodeHeader (outBuffer, 'p', sequence);
- encodePackageIndication (outBuffer, pIter);
+ sys::Mutex::ScopedLock lock(userLock);
+ for (PackageMap::iterator pIter = packages.begin ();
+ pIter != packages.end ();
+ pIter++)
+ {
+ encodeHeader (outBuffer, 'p', sequence);
+ encodePackageIndication (outBuffer, pIter);
+ }
}
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- if (outLen) {
- outBuffer.reset ();
- sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ if (outBuffer.getPosition() > 0) {
+ sendBuffer(outBuffer, dExchange, replyToKey);
QPID_LOG(debug, "SEND PackageInd to=" << replyToKey << " seq=" << sequence);
}
- sendCommandCompleteLH(replyToKey, sequence);
+ sendCommandComplete(replyToKey, sequence);
}
-void ManagementAgent::handlePackageIndLH (Buffer& inBuffer, const string& replyToKey, uint32_t sequence)
+void ManagementAgent::handlePackageInd (Buffer& inBuffer, const string& replyToKey, uint32_t sequence)
{
string packageName;
@@ -1530,10 +1498,11 @@ void ManagementAgent::handlePackageIndLH (Buffer& inBuffer, const string& replyT
QPID_LOG(debug, "RECV PackageInd package=" << packageName << " replyTo=" << replyToKey << " seq=" << sequence);
+ sys::Mutex::ScopedLock lock(userLock);
findOrAddPackageLH(packageName);
}
-void ManagementAgent::handleClassQueryLH(Buffer& inBuffer, const string& replyToKey, uint32_t sequence)
+void ManagementAgent::handleClassQuery(Buffer& inBuffer, const string& replyToKey, uint32_t sequence)
{
string packageName;
@@ -1541,40 +1510,39 @@ void ManagementAgent::handleClassQueryLH(Buffer& inBuffer, const string& replyTo
QPID_LOG(debug, "RECV ClassQuery package=" << packageName << " replyTo=" << replyToKey << " seq=" << sequence);
- PackageMap::iterator pIter = packages.find(packageName);
- if (pIter != packages.end())
+ typedef std::pair<SchemaClassKey, uint8_t> _ckeyType;
+ std::list<_ckeyType> classes;
{
- typedef std::pair<SchemaClassKey, uint8_t> _ckeyType;
- std::list<_ckeyType> classes;
- ClassMap &cMap = pIter->second;
- for (ClassMap::iterator cIter = cMap.begin();
- cIter != cMap.end();
- cIter++) {
- if (cIter->second.hasSchema()) {
- classes.push_back(make_pair(cIter->first, cIter->second.kind));
+ sys::Mutex::ScopedLock lock(userLock);
+ 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()) {
+ classes.push_back(make_pair(cIter->first, cIter->second.kind));
+ }
}
}
+ }
- while (classes.size()) {
- Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
+ while (classes.size()) {
+ ResizableBuffer outBuffer(qmfV1BufferSize);
- encodeHeader(outBuffer, 'q', sequence);
- encodeClassIndication(outBuffer, packageName, classes.front().first, classes.front().second);
-
- outLen = MA_BUFFER_SIZE - outBuffer.available();
- outBuffer.reset();
- sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
- QPID_LOG(debug, "SEND ClassInd class=" << packageName << ":" << classes.front().first.name <<
- "(" << Uuid(classes.front().first.hash) << ") to=" << replyToKey << " seq=" << sequence);
- classes.pop_front();
- }
+ encodeHeader(outBuffer, 'q', sequence);
+ encodeClassIndication(outBuffer, packageName, classes.front().first, classes.front().second);
+ sendBuffer(outBuffer, dExchange, replyToKey);
+ QPID_LOG(debug, "SEND ClassInd class=" << packageName << ":" << classes.front().first.name <<
+ "(" << Uuid(classes.front().first.hash) << ") to=" << replyToKey << " seq=" << sequence);
+ classes.pop_front();
}
- sendCommandCompleteLH(replyToKey, sequence);
+ sendCommandComplete(replyToKey, sequence);
}
-void ManagementAgent::handleClassIndLH (Buffer& inBuffer, const string& replyToKey, uint32_t)
+void ManagementAgent::handleClassInd (Buffer& inBuffer, const string& replyToKey, uint32_t)
{
string packageName;
SchemaClassKey key;
@@ -1587,20 +1555,18 @@ void ManagementAgent::handleClassIndLH (Buffer& inBuffer, const string& replyToK
QPID_LOG(debug, "RECV ClassInd class=" << packageName << ":" << key.name << "(" << Uuid(key.hash) <<
"), replyTo=" << replyToKey);
+ sys::Mutex::ScopedLock lock(userLock);
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;
+ ResizableBuffer outBuffer (qmfV1BufferSize);
uint32_t sequence = nextRequestSequence++;
// Schema Request
encodeHeader (outBuffer, 'S', sequence);
outBuffer.putShortString(packageName);
key.encode(outBuffer);
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset ();
- sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ sendBuffer(outBuffer, dExchange, replyToKey);
QPID_LOG(debug, "SEND SchemaRequest class=" << packageName << ":" << key.name << "(" << Uuid(key.hash) <<
"), to=" << replyToKey << " seq=" << sequence);
@@ -1625,7 +1591,7 @@ void ManagementAgent::SchemaClass::appendSchema(Buffer& buf)
buf.putRawData(reinterpret_cast<uint8_t*>(&data[0]), data.size());
}
-void ManagementAgent::handleSchemaRequestLH(Buffer& inBuffer, const string& rte, const string& rtk, uint32_t sequence)
+void ManagementAgent::handleSchemaRequest(Buffer& inBuffer, const string& rte, const string& rtk, uint32_t sequence)
{
string packageName;
SchemaClassKey key;
@@ -1636,34 +1602,32 @@ void ManagementAgent::handleSchemaRequestLH(Buffer& inBuffer, const string& rte,
QPID_LOG(debug, "RECV SchemaRequest class=" << packageName << ":" << key.name << "(" << Uuid(key.hash) <<
"), replyTo=" << rte << "/" << rtk << " seq=" << sequence);
+ sys::Mutex::ScopedLock lock(userLock);
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;
+ ResizableBuffer outBuffer(qmfV1BufferSize);
SchemaClass& classInfo = cIter->second;
if (classInfo.hasSchema()) {
encodeHeader(outBuffer, 's', sequence);
classInfo.appendSchema(outBuffer);
- outLen = MA_BUFFER_SIZE - outBuffer.available();
- outBuffer.reset();
- sendBufferLH(outBuffer, outLen, rte, rtk);
+ sendBuffer(outBuffer, rte, rtk);
QPID_LOG(debug, "SEND SchemaResponse to=" << rte << "/" << rtk << " seq=" << sequence);
}
else
- sendCommandCompleteLH(rtk, sequence, 1, "Schema not available");
+ sendCommandComplete(rtk, sequence, 1, "Schema not available");
}
else
- sendCommandCompleteLH(rtk, sequence, 1, "Class key not found");
+ sendCommandComplete(rtk, sequence, 1, "Class key not found");
}
else
- sendCommandCompleteLH(rtk, sequence, 1, "Package not found");
+ sendCommandComplete(rtk, sequence, 1, "Package not found");
}
-void ManagementAgent::handleSchemaResponseLH(Buffer& inBuffer, const string& /*replyToKey*/, uint32_t sequence)
+void ManagementAgent::handleSchemaResponse(Buffer& inBuffer, const string& /*replyToKey*/, uint32_t sequence)
{
string packageName;
SchemaClassKey key;
@@ -1676,6 +1640,7 @@ void ManagementAgent::handleSchemaResponseLH(Buffer& inBuffer, const string& /*r
QPID_LOG(debug, "RECV SchemaResponse class=" << packageName << ":" << key.name << "(" << Uuid(key.hash) << ")" << " seq=" << sequence);
+ sys::Mutex::ScopedLock lock(userLock);
PackageMap::iterator pIter = packages.find(packageName);
if (pIter != packages.end()) {
ClassMap& cMap = pIter->second;
@@ -1690,14 +1655,11 @@ void ManagementAgent::handleSchemaResponseLH(Buffer& inBuffer, const string& /*r
inBuffer.getRawData(reinterpret_cast<uint8_t*>(&cIter->second.data[0]), length);
// Publish a class-indication message
- Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
+ ResizableBuffer outBuffer(qmfV1BufferSize);
encodeHeader(outBuffer, 'q');
encodeClassIndication(outBuffer, pIter->first, cIter->first, cIter->second.kind);
- outLen = MA_BUFFER_SIZE - outBuffer.available();
- outBuffer.reset();
- sendBufferLH(outBuffer, outLen, mExchange, "schema.class");
+ sendBuffer(outBuffer, mExchange, "schema.class");
QPID_LOG(debug, "SEND ClassInd class=" << packageName << ":" << key.name << "(" << Uuid(key.hash) << ")" <<
" to=schema.class");
}
@@ -1756,7 +1718,7 @@ void ManagementAgent::deleteOrphanedAgentsLH()
remoteAgents.erase(*dIter);
}
-void ManagementAgent::handleAttachRequestLH (Buffer& inBuffer, const string& replyToKey, uint32_t sequence, const ConnectionToken* connToken)
+void ManagementAgent::handleAttachRequest (Buffer& inBuffer, const string& replyToKey, uint32_t sequence, const ConnectionToken* connToken)
{
string label;
uint32_t requestedBrokerBank, requestedAgentBank;
@@ -1764,12 +1726,14 @@ void ManagementAgent::handleAttachRequestLH (Buffer& inBuffer, const string& rep
ObjectId connectionRef = ((const ConnectionState*) connToken)->GetManagementObject()->getObjectId();
Uuid systemId;
- moveNewObjectsLH();
+ moveNewObjects();
+
+ sys::Mutex::ScopedLock lock(userLock);
deleteOrphanedAgentsLH();
RemoteAgentMap::iterator aIter = remoteAgents.find(connectionRef);
if (aIter != remoteAgents.end()) {
// There already exists an agent on this session. Reject the request.
- sendCommandCompleteLH(replyToKey, sequence, 1, "Connection already has remote agent");
+ sendCommandComplete(replyToKey, sequence, 1, "Connection already has remote agent");
return;
}
@@ -1788,7 +1752,7 @@ void ManagementAgent::handleAttachRequestLH (Buffer& inBuffer, const string& rep
agent->agentBank = assignedBank;
agent->routingKey = replyToKey;
agent->connectionRef = connectionRef;
- agent->mgmtObject = new _qmf::Agent (this, agent.get());
+ agent->mgmtObject = _qmf::Agent::shared_ptr(new _qmf::Agent (this, agent.get()));
agent->mgmtObject->set_connectionRef(agent->connectionRef);
agent->mgmtObject->set_label (label);
agent->mgmtObject->set_registeredTo (broker->GetManagementObject()->getObjectId());
@@ -1801,25 +1765,22 @@ void ManagementAgent::handleAttachRequestLH (Buffer& inBuffer, const string& rep
QPID_LOG(debug, "Remote Agent registered bank=[" << brokerBank << "." << assignedBank << "] replyTo=" << replyToKey);
// Send an Attach Response
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
+ ResizableBuffer outBuffer (qmfV1BufferSize);
encodeHeader (outBuffer, 'a', sequence);
outBuffer.putLong (brokerBank);
outBuffer.putLong (assignedBank);
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset ();
- sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ sendBuffer(outBuffer, dExchange, replyToKey);
QPID_LOG(debug, "SEND AttachResponse brokerBank=" << brokerBank << " agentBank=" << assignedBank <<
" to=" << replyToKey << " seq=" << sequence);
}
-void ManagementAgent::handleGetQueryLH(Buffer& inBuffer, const string& replyToKey, uint32_t sequence)
+void ManagementAgent::handleGetQuery(Buffer& inBuffer, const string& replyToKey, uint32_t sequence)
{
FieldTable ft;
FieldTable::ValuePtr value;
- moveNewObjectsLH();
+ moveNewObjects();
ft.decode(inBuffer);
@@ -1832,11 +1793,17 @@ void ManagementAgent::handleGetQueryLH(Buffer& inBuffer, const string& replyToKe
return;
ObjectId selector(value->get<string>());
- ManagementObjectMap::iterator iter = numericFind(selector);
- if (iter != managementObjects.end()) {
- ManagementObject* object = iter->second;
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
+
+ ManagementObject::shared_ptr object;
+ {
+ sys::Mutex::ScopedLock lock(objectLock);
+ ManagementObjectMap::iterator iter = numericFind(selector);
+ if (iter != managementObjects.end())
+ object = iter->second;
+ }
+
+ if (object) {
+ ResizableBuffer outBuffer (qmfV1BufferSize);
if (object->getConfigChanged() || object->getInstChanged())
object->setUpdateTime();
@@ -1849,89 +1816,80 @@ void ManagementAgent::handleGetQueryLH(Buffer& inBuffer, const string& replyToKe
sBuf.clear();
object->writeStatistics(sBuf, true);
outBuffer.putRawData(sBuf);
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset ();
- sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ sendBuffer(outBuffer, dExchange, replyToKey);
QPID_LOG(debug, "SEND GetResponse (v1) to=" << replyToKey << " seq=" << sequence);
}
}
- sendCommandCompleteLH(replyToKey, sequence);
+ sendCommandComplete(replyToKey, sequence);
return;
}
string className (value->get<string>());
- std::list<ObjectId>matches;
+ std::list<ManagementObject::shared_ptr> matches;
if (className == "memory")
- qpid::sys::MemStat::loadMemInfo(memstat);
+ qpid::sys::MemStat::loadMemInfo(memstat.get());
if (className == "broker") {
uint64_t uptime = sys::Duration(startTime, sys::now());
- static_cast<_qmf::Broker*>(broker->GetManagementObject())->set_uptime(uptime);
+ boost::dynamic_pointer_cast<_qmf::Broker>(broker->GetManagementObject())->set_uptime(uptime);
}
// build up a set of all objects to be dumped
- for (ManagementObjectMap::iterator iter = managementObjects.begin();
- iter != managementObjects.end();
- iter++) {
- ManagementObject* object = iter->second;
- if (object->getClassName () == className) {
- matches.push_back(object->getObjectId());
+ {
+ sys::Mutex::ScopedLock lock(objectLock);
+ for (ManagementObjectMap::iterator iter = managementObjects.begin();
+ iter != managementObjects.end();
+ iter++) {
+ ManagementObject::shared_ptr object = iter->second;
+ if (object->getClassName () == className) {
+ matches.push_back(object);
+ }
}
}
- // send them (as sendBufferLH drops the userLock)
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
+ // send them
+ ResizableBuffer outBuffer (qmfV1BufferSize);
while (matches.size()) {
- ObjectId objId = matches.front();
- ManagementObjectMap::iterator oIter = managementObjects.find( objId );
- if (oIter != managementObjects.end()) {
- ManagementObject* object = oIter->second;
-
- if (object->getConfigChanged() || object->getInstChanged())
- object->setUpdateTime();
-
- if (!object->isDeleted()) {
- string sProps, sStats;
- object->writeProperties(sProps);
- object->writeStatistics(sStats, true);
-
- size_t len = 8 + sProps.length() + sStats.length(); // 8 == size of header in bytes.
- if (len > MA_BUFFER_SIZE) {
- QPID_LOG(error, "Object " << objId << " too large for output buffer - discarded!");
- } else {
- if (outBuffer.available() < len) { // not enough room in current buffer, send it.
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset ();
- sendBufferLH(outBuffer, outLen, dExchange, replyToKey); // drops lock
- QPID_LOG(debug, "SEND GetResponse (v1) to=" << replyToKey << " seq=" << sequence);
- continue; // lock dropped, need to re-find _SAME_ objid as it may have been deleted.
- }
- encodeHeader(outBuffer, 'g', sequence);
- outBuffer.putRawData(sProps);
- outBuffer.putRawData(sStats);
+ ManagementObject::shared_ptr object = matches.front();
+ if (object->getConfigChanged() || object->getInstChanged())
+ object->setUpdateTime();
+
+ if (!object->isDeleted()) {
+ string sProps, sStats;
+ object->writeProperties(sProps);
+ object->writeStatistics(sStats, true);
+
+ size_t len = 8 + sProps.length() + sStats.length(); // 8 == size of header in bytes.
+ if (len > qmfV1BufferSize) {
+ QPID_LOG(error, "Object " << object->getObjectId() << " too large for output buffer - discarded!");
+ } else {
+ if (outBuffer.available() < len) { // not enough room in current buffer, send it.
+ sendBuffer(outBuffer, dExchange, replyToKey);
+ QPID_LOG(debug, "SEND GetResponse (v1) to=" << replyToKey << " seq=" << sequence);
+ continue; // lock dropped, need to re-find _SAME_ objid as it may have been deleted.
}
+ encodeHeader(outBuffer, 'g', sequence);
+ outBuffer.putRawData(sProps);
+ outBuffer.putRawData(sStats);
}
}
matches.pop_front();
}
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- if (outLen) {
- outBuffer.reset ();
- sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ if (outBuffer.getPosition() > 0) {
+ sendBuffer(outBuffer, dExchange, replyToKey);
QPID_LOG(debug, "SEND GetResponse (v1) to=" << replyToKey << " seq=" << sequence);
}
- sendCommandCompleteLH(replyToKey, sequence);
+ sendCommandComplete(replyToKey, sequence);
}
-void ManagementAgent::handleGetQueryLH(const string& body, const string& rte, const string& rtk, const string& cid, bool viaLocal)
+void ManagementAgent::handleGetQuery(const string& body, const string& rte, const string& rtk, const string& cid, bool viaLocal)
{
- moveNewObjectsLH();
+ moveNewObjects();
Variant::Map inMap;
Variant::Map::const_iterator i;
@@ -1950,17 +1908,17 @@ void ManagementAgent::handleGetQueryLH(const string& body, const string& rte, co
*/
i = inMap.find("_what");
if (i == inMap.end()) {
- sendExceptionLH(rte, rtk, cid, "_what element missing in Query");
+ sendException(rte, rtk, cid, "_what element missing in Query");
return;
}
if (i->second.getType() != qpid::types::VAR_STRING) {
- sendExceptionLH(rte, rtk, cid, "_what element is not a string");
+ sendException(rte, rtk, cid, "_what element is not a string");
return;
}
if (i->second.asString() != "OBJECT") {
- sendExceptionLH(rte, rtk, cid, "Query for _what => '" + i->second.asString() + "' not supported");
+ sendException(rte, rtk, cid, "Query for _what => '" + i->second.asString() + "' not supported");
return;
}
@@ -1984,11 +1942,11 @@ void ManagementAgent::handleGetQueryLH(const string& body, const string& rte, co
}
if (className == "memory")
- qpid::sys::MemStat::loadMemInfo(memstat);
+ qpid::sys::MemStat::loadMemInfo(memstat.get());
if (className == "broker") {
uint64_t uptime = sys::Duration(startTime, sys::now());
- static_cast<_qmf::Broker*>(broker->GetManagementObject())->set_uptime(uptime);
+ boost::dynamic_pointer_cast<_qmf::Broker>(broker->GetManagementObject())->set_uptime(uptime);
}
/*
@@ -2000,10 +1958,14 @@ void ManagementAgent::handleGetQueryLH(const string& body, const string& rte, co
Variant::List list_;
ObjectId objId(i->second.asMap());
- ManagementObjectMap::iterator iter = managementObjects.find(objId);
- if (iter != managementObjects.end()) {
- ManagementObject* object = iter->second;
-
+ ManagementObject::shared_ptr object;
+ {
+ sys::Mutex::ScopedLock lock (objectLock);
+ ManagementObjectMap::iterator iter = managementObjects.find(objId);
+ if (iter != managementObjects.end())
+ object = iter->second;
+ }
+ if (object) {
if (object->getConfigChanged() || object->getInstChanged())
object->setUpdateTime();
@@ -2027,7 +1989,7 @@ void ManagementAgent::handleGetQueryLH(const string& body, const string& rte, co
string content;
ListCodec::encode(list_, content);
- sendBufferLH(content, cid, headers, "amqp/list", rte, rtk);
+ sendBuffer(content, cid, headers, "amqp/list", rte, rtk);
QPID_LOG(debug, "SENT QueryResponse (query by object_id) to=" << rte << "/" << rtk);
return;
}
@@ -2037,10 +1999,18 @@ void ManagementAgent::handleGetQueryLH(const string& body, const string& rte, co
Variant::List _subList;
unsigned int objCount = 0;
- for (ManagementObjectMap::iterator iter = managementObjects.begin();
- iter != managementObjects.end();
+ ManagementObjectVector localManagementObjects;
+ {
+ sys::Mutex::ScopedLock objLock(objectLock);
+ std::transform(managementObjects.begin(), managementObjects.end(),
+ std::back_inserter(localManagementObjects),
+ boost::bind(&ManagementObjectMap::value_type::second, _1));
+ }
+
+ for (ManagementObjectVector::iterator iter = localManagementObjects.begin();
+ iter != localManagementObjects.end();
iter++) {
- ManagementObject* object = iter->second;
+ ManagementObject::shared_ptr object = *iter;
if (object->getClassName() == className &&
(packageName.empty() || object->getPackageName() == packageName)) {
@@ -2055,7 +2025,7 @@ void ManagementAgent::handleGetQueryLH(const string& body, const string& rte, co
object->writeTimestamps(map_);
object->mapEncodeValues(values, true, true); // write both stats and properties
- iter->first.mapEncode(oidMap);
+ object->getObjectId().mapEncode(oidMap);
map_["_values"] = values;
map_["_object_id"] = oidMap;
@@ -2080,13 +2050,13 @@ void ManagementAgent::handleGetQueryLH(const string& body, const string& rte, co
string content;
while (_list.size() > 1) {
ListCodec::encode(_list.front().asList(), content);
- sendBufferLH(content, cid, headers, "amqp/list", rte, rtk);
+ sendBuffer(content, cid, headers, "amqp/list", rte, rtk);
_list.pop_front();
QPID_LOG(debug, "SENT QueryResponse (partial, query by schema_id) to=" << rte << "/" << rtk << " len=" << content.length());
}
headers.erase("partial");
ListCodec::encode(_list.size() ? _list.front().asList() : Variant::List(), content);
- sendBufferLH(content, cid, headers, "amqp/list", rte, rtk);
+ sendBuffer(content, cid, headers, "amqp/list", rte, rtk);
QPID_LOG(debug, "SENT QueryResponse (query by schema_id) to=" << rte << "/" << rtk << " len=" << content.length());
return;
}
@@ -2094,12 +2064,12 @@ void ManagementAgent::handleGetQueryLH(const string& body, const string& rte, co
// Unrecognized query - Send empty message to indicate CommandComplete
string content;
ListCodec::encode(Variant::List(), content);
- sendBufferLH(content, cid, headers, "amqp/list", rte, rtk);
+ sendBuffer(content, cid, headers, "amqp/list", rte, rtk);
QPID_LOG(debug, "SENT QueryResponse (empty) to=" << rte << "/" << rtk);
}
-void ManagementAgent::handleLocateRequestLH(const string&, const string& rte, const string& rtk, const string& cid)
+void ManagementAgent::handleLocateRequest(const string&, const string& rte, const string& rtk, const string& cid)
{
QPID_LOG(debug, "RCVD AgentLocateRequest");
@@ -2117,16 +2087,17 @@ void ManagementAgent::handleLocateRequestLH(const string&, const string& rte, co
string content;
MapCodec::encode(map, content);
- sendBufferLH(content, cid, headers, "amqp/map", rte, rtk);
+ sendBuffer(content, cid, headers, "amqp/map", rte, rtk);
clientWasAdded = true;
QPID_LOG(debug, "SENT AgentLocateResponse replyTo=" << rte << "/" << rtk);
}
-bool ManagementAgent::authorizeAgentMessageLH(Message& msg)
+bool ManagementAgent::authorizeAgentMessage(Message& msg)
{
- Buffer inBuffer (inputBuffer, MA_BUFFER_SIZE);
+ sys::Mutex::ScopedLock lock(userLock);
+ ResizableBuffer inBuffer (qmfV1BufferSize);
uint32_t sequence = 0;
bool methodReq = false;
bool mapMsg = false;
@@ -2140,7 +2111,7 @@ bool ManagementAgent::authorizeAgentMessageLH(Message& msg)
// authorized or not. In this case, return true (authorized) if there is no ACL in place,
// otherwise return false;
//
- if (msg.getContentSize() > MA_BUFFER_SIZE)
+ if (msg.getContentSize() > qmfV1BufferSize)
return broker->getAcl() == 0;
inBuffer.putRawData(msg.getContent());
@@ -2149,7 +2120,7 @@ bool ManagementAgent::authorizeAgentMessageLH(Message& msg)
qpid::broker::amqp_0_10::MessageTransfer& transfer(qpid::broker::amqp_0_10::MessageTransfer::get(msg));
const framing::MessageProperties* p =
- transfer.getFrames().getHeaders()->get<framing::MessageProperties>();
+ transfer.getFrames().getHeaders()->get<framing::MessageProperties>();
const framing::FieldTable *headers = p ? &p->getApplicationHeaders() : 0;
@@ -2193,11 +2164,11 @@ bool ManagementAgent::authorizeAgentMessageLH(Message& msg)
}
// look up schema for object to get package and class name
-
+ sys::Mutex::ScopedLock lock(objectLock);
ManagementObjectMap::iterator iter = managementObjects.find(objId);
if (iter == managementObjects.end() || iter->second->isDeleted()) {
- QPID_LOG(debug, "ManagementAgent::authorizeAgentMessageLH: stale object id " <<
+ QPID_LOG(debug, "ManagementAgent::authorizeAgentMessage: stale object id " <<
objId);
return false;
}
@@ -2256,19 +2227,16 @@ bool ManagementAgent::authorizeAgentMessageLH(Message& msg)
cid = p->getCorrelationId();
if (mapMsg) {
- sendExceptionLH(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_FORBIDDEN),
- Manageable::STATUS_FORBIDDEN, false);
+ sendException(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_FORBIDDEN),
+ Manageable::STATUS_FORBIDDEN, false);
} else {
- Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
+ ResizableBuffer outBuffer(qmfV1BufferSize);
encodeHeader(outBuffer, 'm', sequence);
outBuffer.putLong(Manageable::STATUS_FORBIDDEN);
outBuffer.putMediumString(Manageable::StatusText(Manageable::STATUS_FORBIDDEN));
- outLen = MA_BUFFER_SIZE - outBuffer.available();
- outBuffer.reset();
- sendBufferLH(outBuffer, outLen, rte, rtk);
+ sendBuffer(outBuffer, rte, rtk);
}
QPID_LOG(debug, "SEND MethodResponse status=FORBIDDEN" << " seq=" << sequence);
@@ -2280,7 +2248,7 @@ bool ManagementAgent::authorizeAgentMessageLH(Message& msg)
return true;
}
-void ManagementAgent::dispatchAgentCommandLH(Message& msg, bool viaLocal)
+void ManagementAgent::dispatchAgentCommand(Message& msg, bool viaLocal)
{
string rte;
string rtk;
@@ -2295,10 +2263,10 @@ void ManagementAgent::dispatchAgentCommandLH(Message& msg, bool viaLocal)
else
return;
- Buffer inBuffer(inputBuffer, MA_BUFFER_SIZE);
+ ResizableBuffer inBuffer(qmfV1BufferSize);
uint8_t opcode;
- if (msg.getContentSize() > MA_BUFFER_SIZE) {
+ if (msg.getContentSize() > qmfV1BufferSize) {
QPID_LOG(debug, "ManagementAgent::dispatchAgentCommandLH: Message too large: " <<
msg.getContentSize());
return;
@@ -2317,39 +2285,38 @@ void ManagementAgent::dispatchAgentCommandLH(Message& msg, bool viaLocal)
string body;
string cid;
inBuffer.getRawData(body, bufferLen);
+ {
+ if (p && p->hasCorrelationId()) {
+ cid = p->getCorrelationId();
+ }
- if (p && p->hasCorrelationId()) {
- cid = p->getCorrelationId();
+ if (opcode == "_method_request")
+ return handleMethodRequest(body, rte, rtk, cid, msg.getPublisher(), viaLocal);
+ else if (opcode == "_query_request")
+ return handleGetQuery(body, rte, rtk, cid, viaLocal);
+ else if (opcode == "_agent_locate_request")
+ return handleLocateRequest(body, rte, rtk, cid);
}
-
- if (opcode == "_method_request")
- return handleMethodRequestLH(body, rte, rtk, cid, msg.getPublisher(), viaLocal);
- else if (opcode == "_query_request")
- return handleGetQueryLH(body, rte, rtk, cid, viaLocal);
- else if (opcode == "_agent_locate_request")
- return handleLocateRequestLH(body, rte, rtk, cid);
-
QPID_LOG(warning, "Support for QMF Opcode [" << opcode << "] TBD!!!");
return;
}
// old preV2 binary messages
-
while (inBuffer.getPosition() < bufferLen) {
uint32_t sequence;
if (!checkHeader(inBuffer, &opcode, &sequence))
return;
- if (opcode == 'B') handleBrokerRequestLH (inBuffer, rtk, sequence);
- else if (opcode == 'P') handlePackageQueryLH (inBuffer, rtk, sequence);
- else if (opcode == 'p') handlePackageIndLH (inBuffer, rtk, sequence);
- else if (opcode == 'Q') handleClassQueryLH (inBuffer, rtk, sequence);
- else if (opcode == 'q') handleClassIndLH (inBuffer, rtk, sequence);
- else if (opcode == 'S') handleSchemaRequestLH (inBuffer, rte, rtk, sequence);
- else if (opcode == 's') handleSchemaResponseLH (inBuffer, rtk, sequence);
- else if (opcode == 'A') handleAttachRequestLH (inBuffer, rtk, sequence, msg.getPublisher());
- else if (opcode == 'G') handleGetQueryLH (inBuffer, rtk, sequence);
- else if (opcode == 'M') handleMethodRequestLH (inBuffer, rtk, sequence, msg.getPublisher());
+ if (opcode == 'B') handleBrokerRequest (inBuffer, rtk, sequence);
+ else if (opcode == 'P') handlePackageQuery (inBuffer, rtk, sequence);
+ else if (opcode == 'p') handlePackageInd (inBuffer, rtk, sequence);
+ else if (opcode == 'Q') handleClassQuery (inBuffer, rtk, sequence);
+ else if (opcode == 'q') handleClassInd (inBuffer, rtk, sequence);
+ else if (opcode == 'S') handleSchemaRequest (inBuffer, rte, rtk, sequence);
+ else if (opcode == 's') handleSchemaResponse (inBuffer, rtk, sequence);
+ else if (opcode == 'A') handleAttachRequest (inBuffer, rtk, sequence, msg.getPublisher());
+ else if (opcode == 'G') handleGetQuery (inBuffer, rtk, sequence);
+ else if (opcode == 'M') handleMethodRequest (inBuffer, rtk, sequence, msg.getPublisher());
}
}
@@ -2365,14 +2332,11 @@ ManagementAgent::PackageMap::iterator ManagementAgent::findOrAddPackageLH(string
QPID_LOG (debug, "ManagementAgent added package " << name);
// Publish a package-indication message
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
+ ResizableBuffer outBuffer (qmfV1BufferSize);
encodeHeader (outBuffer, 'p');
encodePackageIndication (outBuffer, result.first);
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset ();
- sendBufferLH(outBuffer, outLen, mExchange, "schema.package");
+ sendBuffer(outBuffer, mExchange, "schema.package");
QPID_LOG(debug, "SEND PackageInd package=" << name << " to=schema.package");
return result.first;
@@ -2587,70 +2551,6 @@ void ManagementAgent::SchemaClass::mapDecode(const Variant::Map& _map) {
}
}
-void ManagementAgent::exportSchemas(string& out) {
- Variant::List list_;
- Variant::Map map_, kmap, cmap;
-
- for (PackageMap::const_iterator i = packages.begin(); i != packages.end(); ++i) {
- string name = i->first;
- const ClassMap& classes = i ->second;
- for (ClassMap::const_iterator j = classes.begin(); j != classes.end(); ++j) {
- const SchemaClassKey& key = j->first;
- const SchemaClass& klass = j->second;
- if (klass.writeSchemaCall == 0) { // Ignore built-in schemas.
- // Encode name, schema-key, schema-class
-
- map_.clear();
- kmap.clear();
- cmap.clear();
-
- key.mapEncode(kmap);
- klass.mapEncode(cmap);
-
- map_["_pname"] = name;
- map_["_key"] = kmap;
- map_["_class"] = cmap;
- list_.push_back(map_);
- }
- }
- }
-
- ListCodec::encode(list_, out);
-}
-
-void ManagementAgent::importSchemas(qpid::framing::Buffer& inBuf) {
-
- string buf(inBuf.getPointer(), inBuf.available());
- Variant::List content;
- ListCodec::decode(buf, content);
- Variant::List::const_iterator l;
-
-
- for (l = content.begin(); l != content.end(); l++) {
- string package;
- SchemaClassKey key;
- SchemaClass klass;
- Variant::Map map_, kmap, cmap;
- Variant::Map::const_iterator i;
-
- map_ = l->asMap();
-
- if ((i = map_.find("_pname")) != map_.end()) {
- package = i->second.asString();
-
- if ((i = map_.find("_key")) != map_.end()) {
- key.mapDecode(i->second.asMap());
-
- if ((i = map_.find("_class")) != map_.end()) {
- klass.mapDecode(i->second.asMap());
-
- packages[package][key] = klass;
- }
- }
- }
- }
-}
-
void ManagementAgent::RemoteAgent::mapEncode(Variant::Map& map_) const {
Variant::Map _objId, _values;
@@ -2684,7 +2584,7 @@ void ManagementAgent::RemoteAgent::mapDecode(const Variant::Map& map_) {
connectionRef.mapDecode(i->second.asMap());
}
- mgmtObject = new _qmf::Agent(&agent, this);
+ mgmtObject = _qmf::Agent::shared_ptr(new _qmf::Agent(&agent, this));
if ((i = map_.find("_values")) != map_.end()) {
mgmtObject->mapDecodeValues(i->second.asMap());
@@ -2694,52 +2594,6 @@ void ManagementAgent::RemoteAgent::mapDecode(const Variant::Map& map_) {
mgmtObject->set_connectionRef(connectionRef);
}
-void ManagementAgent::exportAgents(string& out) {
- Variant::List list_;
- Variant::Map map_, omap, amap;
-
- for (RemoteAgentMap::const_iterator i = remoteAgents.begin();
- i != remoteAgents.end();
- ++i)
- {
- // TODO aconway 2010-03-04: see comment in ManagementAgent::RemoteAgent::encode
- boost::shared_ptr<RemoteAgent> agent(i->second);
-
- map_.clear();
- amap.clear();
-
- agent->mapEncode(amap);
- map_["_remote_agent"] = amap;
- list_.push_back(map_);
- }
-
- ListCodec::encode(list_, out);
-}
-
-void ManagementAgent::importAgents(qpid::framing::Buffer& inBuf) {
- string buf(inBuf.getPointer(), inBuf.available());
- Variant::List content;
- ListCodec::decode(buf, content);
- Variant::List::const_iterator l;
- sys::Mutex::ScopedLock lock(userLock);
-
- for (l = content.begin(); l != content.end(); l++) {
- boost::shared_ptr<RemoteAgent> agent(new RemoteAgent(*this));
- Variant::Map map_;
- Variant::Map::const_iterator i;
-
- map_ = l->asMap();
-
- if ((i = map_.find("_remote_agent")) != map_.end()) {
-
- agent->mapDecode(i->second.asMap());
-
- addObject (agent->mgmtObject, 0, false);
- remoteAgents[agent->connectionRef] = agent;
- }
- }
-}
-
namespace {
bool isDeletedMap(const ManagementObjectMap::value_type& value) {
return value.second->isDeleted();
@@ -2818,56 +2672,8 @@ Variant::Map ManagementAgent::toMap(const FieldTable& from)
return map;
}
-
-// Build up a list of the current set of deleted objects that are pending their
-// next (last) publish-ment.
-void ManagementAgent::exportDeletedObjects(DeletedObjectList& outList)
-{
- outList.clear();
-
- sys::Mutex::ScopedLock lock (userLock);
-
- moveNewObjectsLH();
- moveDeletedObjectsLH();
-
- // now copy the pending deletes into the outList
- for (PendingDeletedObjsMap::iterator mIter = pendingDeletedObjs.begin();
- mIter != pendingDeletedObjs.end(); mIter++) {
- for (DeletedObjectList::iterator lIter = mIter->second.begin();
- lIter != mIter->second.end(); lIter++) {
- outList.push_back(*lIter);
- }
- }
-}
-
-// Called by cluster to reset the management agent's list of deleted
-// objects to match the rest of the cluster.
-void ManagementAgent::importDeletedObjects(const DeletedObjectList& inList)
-{
- sys::Mutex::ScopedLock lock (userLock);
- // Clear out any existing deleted objects
- moveNewObjectsLH();
- pendingDeletedObjs.clear();
- ManagementObjectMap::iterator i = managementObjects.begin();
- // Silently drop any deleted objects left over from receiving the update.
- while (i != managementObjects.end()) {
- ManagementObject* object = i->second;
- if (object->isDeleted()) {
- delete object;
- managementObjects.erase(i++);
- }
- else ++i;
- }
- for (DeletedObjectList::const_iterator lIter = inList.begin(); lIter != inList.end(); lIter++) {
-
- std::string classkey((*lIter)->packageName + std::string(":") + (*lIter)->className);
- pendingDeletedObjs[classkey].push_back(*lIter);
- }
-}
-
-
// construct a DeletedObject from a management object.
-ManagementAgent::DeletedObject::DeletedObject(ManagementObject *src, bool v1, bool v2)
+ManagementAgent::DeletedObject::DeletedObject(ManagementObject::shared_ptr src, bool v1, bool v2)
: packageName(src->getPackageName()),
className(src->getClassName())
{
@@ -2903,54 +2709,18 @@ ManagementAgent::DeletedObject::DeletedObject(ManagementObject *src, bool v1, bo
}
}
+// Remove Deleted objects, and save for later publishing...
+bool ManagementAgent::moveDeletedObjects() {
+ typedef vector<pair<ObjectId, ManagementObject::shared_ptr> > DeleteList;
+ sys::Mutex::ScopedLock lock (objectLock);
-// construct a DeletedObject from an encoded representation. Used by
-// clustering to move deleted objects between clustered brokers. See
-// DeletedObject::encode() for the reverse.
-ManagementAgent::DeletedObject::DeletedObject(const std::string& encoded)
-{
- qpid::types::Variant::Map map_;
- MapCodec::decode(encoded, map_);
-
- packageName = map_["_package_name"].getString();
- className = map_["_class_name"].getString();
- objectId = map_["_object_id"].getString();
-
- encodedV1Config = map_["_v1_config"].getString();
- encodedV1Inst = map_["_v1_inst"].getString();
- encodedV2 = map_["_v2_data"].asMap();
-}
-
-
-// encode a DeletedObject to a string buffer. Used by
-// clustering to move deleted objects between clustered brokers. See
-// DeletedObject(const std::string&) for the reverse.
-void ManagementAgent::DeletedObject::encode(std::string& toBuffer)
-{
- qpid::types::Variant::Map map_;
-
-
- map_["_package_name"] = packageName;
- map_["_class_name"] = className;
- map_["_object_id"] = objectId;
-
- map_["_v1_config"] = encodedV1Config;
- map_["_v1_inst"] = encodedV1Inst;
- map_["_v2_data"] = encodedV2;
-
- MapCodec::encode(map_, toBuffer);
-}
-
-// Remove Deleted objects, and save for later publishing...
-bool ManagementAgent::moveDeletedObjectsLH() {
- typedef vector<pair<ObjectId, ManagementObject*> > DeleteList;
DeleteList deleteList;
for (ManagementObjectMap::iterator iter = managementObjects.begin();
iter != managementObjects.end();
++iter)
{
- ManagementObject* object = iter->second;
+ ManagementObject::shared_ptr object = iter->second;
if (object->isDeleted()) deleteList.push_back(*iter);
}
@@ -2959,17 +2729,31 @@ bool ManagementAgent::moveDeletedObjectsLH() {
iter != deleteList.rend();
iter++)
{
- ManagementObject* delObj = iter->second;
+ ManagementObject::shared_ptr delObj = iter->second;
assert(delObj->isDeleted());
DeletedObject::shared_ptr dptr(new DeletedObject(delObj, qmf1Support, qmf2Support));
pendingDeletedObjs[dptr->getKey()].push_back(dptr);
managementObjects.erase(iter->first);
- delete iter->second;
}
return !deleteList.empty();
}
+ManagementAgent::EventQueue::Batch::const_iterator ManagementAgent::sendEvents(
+ const EventQueue::Batch& batch)
+{
+ EventQueue::Batch::const_iterator i;
+ for (i = batch.begin(); i != batch.end(); ++i) {
+ DeliverableMessage deliverable (i->second, 0);
+ try {
+ i->first->route(deliverable);
+ } catch(exception& e) {
+ QPID_LOG(warning, "ManagementAgent failed to route event: " << e.what());
+ }
+ }
+ return i;
+}
+
namespace {
QPID_TSS const qpid::broker::ConnectionState* executionContext = 0;
}
diff --git a/cpp/src/qpid/management/ManagementAgent.h b/cpp/src/qpid/management/ManagementAgent.h
index c7e830dcf5..6de5d1d719 100644
--- a/cpp/src/qpid/management/ManagementAgent.h
+++ b/cpp/src/qpid/management/ManagementAgent.h
@@ -26,7 +26,6 @@
#include "qpid/broker/Exchange.h"
#include "qpid/framing/Uuid.h"
#include "qpid/sys/Mutex.h"
-#include "qpid/sys/Timer.h"
#include "qpid/broker/ConnectionToken.h"
#include "qpid/management/ManagementObject.h"
#include "qpid/management/ManagementEvent.h"
@@ -34,9 +33,11 @@
#include "qmf/org/apache/qpid/broker/Agent.h"
#include "qmf/org/apache/qpid/broker/Memory.h"
#include "qpid/sys/MemStat.h"
+#include "qpid/sys/PollableQueue.h"
#include "qpid/types/Variant.h"
#include <qpid/framing/AMQFrame.h>
#include <qpid/framing/ResizableBuffer.h>
+#include <boost/shared_ptr.hpp>
#include <memory>
#include <string>
#include <map>
@@ -45,6 +46,9 @@ namespace qpid {
namespace broker {
class ConnectionState;
}
+namespace sys {
+class Timer;
+}
namespace management {
class ManagementAgent
@@ -73,11 +77,6 @@ public:
/** Called before plugins are initialized */
void configure (const std::string& dataDir, bool publish, uint16_t interval,
qpid::broker::Broker* broker, int threadPoolSize);
- /** Called after plugins are initialized. */
- void pluginsInitialized();
-
- /** Called by cluster to suppress management output during update. */
- void suppress(bool s) { suppressed = s; }
void setName(const std::string& vendor,
const std::string& product,
@@ -100,18 +99,16 @@ public:
const std::string& eventName,
uint8_t* md5Sum,
ManagementObject::writeSchemaCall_t schemaCall);
- QPID_BROKER_EXTERN ObjectId addObject (ManagementObject* object,
- uint64_t persistId = 0,
- bool persistent = false);
- QPID_BROKER_EXTERN ObjectId addObject (ManagementObject* object,
- const std::string& key,
- bool persistent = false);
+ QPID_BROKER_EXTERN ObjectId addObject (ManagementObject::shared_ptr object,
+ uint64_t persistId = 0,
+ bool persistent = false);
+ QPID_BROKER_EXTERN ObjectId addObject (ManagementObject::shared_ptr object,
+ const std::string& key,
+ bool persistent = false);
QPID_BROKER_EXTERN void raiseEvent(const ManagementEvent& event,
severity_t severity = SEV_DEFAULT);
QPID_BROKER_EXTERN void clientAdded (const std::string& routingKey);
- QPID_BROKER_EXTERN void clusterUpdate();
-
bool dispatchCommand (qpid::broker::Deliverable& msg,
const std::string& routingKey,
const framing::FieldTable* args,
@@ -121,25 +118,6 @@ public:
/** Disallow a method. Attempts to call it will receive an exception with message. */
void disallow(const std::string& className, const std::string& methodName, const std::string& message);
- /** Disallow all QMFv1 methods (used in clustered brokers). */
- void disallowV1Methods() { disallowAllV1Methods = true; }
-
- /** Serialize my schemas as a binary blob into schemaOut */
- void exportSchemas(std::string& schemaOut);
-
- /** Serialize my remote-agent map as a binary blob into agentsOut */
- void exportAgents(std::string& agentsOut);
-
- /** Decode a serialized schemas and add to my schema cache */
- void importSchemas(framing::Buffer& inBuf);
-
- /** Decode a serialized agent map */
- void importAgents(framing::Buffer& inBuf);
-
- // these are in support of the managementSetup-state stuff, for synch'ing clustered brokers
- uint64_t getNextObjectId(void) { return nextObjectId; }
- void setNextObjectId(uint64_t o) { nextObjectId = o; }
-
uint16_t getBootSequence(void) { return bootSequence; }
void setBootSequence(uint16_t b) { bootSequence = b; writeData(); }
@@ -148,20 +126,11 @@ public:
static types::Variant::Map toMap(const framing::FieldTable& from);
- // For Clustering: management objects that have been marked as
- // "deleted", but are waiting for their last published object
- // update are not visible to the cluster replication code. These
- // interfaces allow clustering to gather up all the management
- // objects that are deleted in order to allow all clustered
- // brokers to publish the same set of deleted objects.
-
class DeletedObject {
public:
typedef boost::shared_ptr<DeletedObject> shared_ptr;
- DeletedObject(ManagementObject *, bool v1, bool v2);
- DeletedObject( const std::string &encoded );
+ DeletedObject(ManagementObject::shared_ptr, bool v1, bool v2);
~DeletedObject() {};
- void encode( std::string& toBuffer );
const std::string getKey() const {
// used to batch up objects of the same class type
return std::string(packageName + std::string(":") + className);
@@ -181,22 +150,7 @@ public:
typedef std::vector<DeletedObject::shared_ptr> DeletedObjectList;
- /** returns a snapshot of all currently deleted management objects. */
- void exportDeletedObjects( DeletedObjectList& outList );
-
- /** Import a list of deleted objects to send on next publish interval. */
- void importDeletedObjects( const DeletedObjectList& inList );
-
private:
- struct Periodic : public qpid::sys::TimerTask
- {
- ManagementAgent& agent;
-
- Periodic (ManagementAgent& agent, uint32_t seconds);
- virtual ~Periodic ();
- void fire ();
- };
-
// Storage for tracking remote management agents, attached via the client
// management agent API.
//
@@ -207,9 +161,9 @@ private:
uint32_t agentBank;
std::string routingKey;
ObjectId connectionRef;
- qmf::org::apache::qpid::broker::Agent* mgmtObject;
- RemoteAgent(ManagementAgent& _agent) : agent(_agent), mgmtObject(0) {}
- ManagementObject* GetManagementObject (void) const { return mgmtObject; }
+ qmf::org::apache::qpid::broker::Agent::shared_ptr mgmtObject;
+ RemoteAgent(ManagementAgent& _agent) : agent(_agent) {}
+ ManagementObject::shared_ptr GetManagementObject (void) const { return mgmtObject; }
virtual ~RemoteAgent ();
void mapEncode(qpid::types::Variant::Map& _map) const;
@@ -276,7 +230,7 @@ private:
PackageMap packages;
//
- // Protected by userLock
+ // Protected by objectLock
//
ManagementObjectMap managementObjects;
@@ -288,11 +242,11 @@ private:
framing::Uuid uuid;
//
- // Lock hierarchy: If a thread needs to take both addLock and userLock,
- // it MUST take userLock first, then addLock.
+ // Lock ordering: userLock -> addLock -> objectLock
//
sys::Mutex userLock;
sys::Mutex addLock;
+ sys::Mutex objectLock;
qpid::broker::Exchange::shared_ptr mExchange;
qpid::broker::Exchange::shared_ptr dExchange;
@@ -335,53 +289,51 @@ private:
// list of objects that have been deleted, but have yet to be published
// one final time.
// Indexed by a string composed of the object's package and class name.
- // Protected by userLock.
+ // Protected by objectLock.
typedef std::map<std::string, DeletedObjectList> PendingDeletedObjsMap;
PendingDeletedObjsMap pendingDeletedObjs;
-# define MA_BUFFER_SIZE 65536
- char inputBuffer[MA_BUFFER_SIZE];
- char outputBuffer[MA_BUFFER_SIZE];
- char eventBuffer[MA_BUFFER_SIZE];
- framing::ResizableBuffer msgBuffer;
+ // Pollable queue to serialize event messages
+ typedef std::pair<boost::shared_ptr<broker::Exchange>,
+ broker::Message> ExchangeAndMessage;
+ typedef sys::PollableQueue<ExchangeAndMessage> EventQueue;
//
// Memory statistics object
//
- qmf::org::apache::qpid::broker::Memory *memstat;
+ qmf::org::apache::qpid::broker::Memory::shared_ptr memstat;
void writeData ();
void periodicProcessing (void);
- void deleteObjectNowLH(const ObjectId& oid);
+ void deleteObjectNow(const ObjectId& oid);
void encodeHeader (framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0);
bool checkHeader (framing::Buffer& buf, uint8_t *opcode, uint32_t *seq);
- void sendBufferLH(framing::Buffer& buf,
- uint32_t length,
- qpid::broker::Exchange::shared_ptr exchange,
- const std::string& routingKey);
- void sendBufferLH(framing::Buffer& buf,
- uint32_t length,
- const std::string& exchange,
- const std::string& routingKey);
- void sendBufferLH(const std::string& data,
- const std::string& cid,
- const qpid::types::Variant::Map& headers,
- const std::string& content_type,
- qpid::broker::Exchange::shared_ptr exchange,
- const std::string& routingKey,
- uint64_t ttl_msec = 0);
- void sendBufferLH(const std::string& data,
- const std::string& cid,
- const qpid::types::Variant::Map& headers,
- const std::string& content_type,
- const std::string& exchange,
- const std::string& routingKey,
- uint64_t ttl_msec = 0);
- void moveNewObjectsLH();
- bool moveDeletedObjectsLH();
-
- bool authorizeAgentMessageLH(qpid::broker::Message& msg);
- void dispatchAgentCommandLH(qpid::broker::Message& msg, bool viaLocal=false);
+ EventQueue::Batch::const_iterator sendEvents(const EventQueue::Batch& batch);
+ void sendBuffer(framing::Buffer& buf,
+ qpid::broker::Exchange::shared_ptr exchange,
+ const std::string& routingKey);
+ void sendBuffer(framing::Buffer& buf,
+ const std::string& exchange,
+ const std::string& routingKey);
+ void sendBuffer(const std::string& data,
+ const std::string& cid,
+ const qpid::types::Variant::Map& headers,
+ const std::string& content_type,
+ qpid::broker::Exchange::shared_ptr exchange,
+ const std::string& routingKey,
+ uint64_t ttl_msec = 0);
+ void sendBuffer(const std::string& data,
+ const std::string& cid,
+ const qpid::types::Variant::Map& headers,
+ const std::string& content_type,
+ const std::string& exchange,
+ const std::string& routingKey,
+ uint64_t ttl_msec = 0);
+ void moveNewObjects();
+ bool moveDeletedObjects();
+
+ bool authorizeAgentMessage(qpid::broker::Message& msg);
+ void dispatchAgentCommand(qpid::broker::Message& msg, bool viaLocal=false);
PackageMap::iterator findOrAddPackageLH(std::string name);
void addClassLH(uint8_t kind,
@@ -399,22 +351,22 @@ private:
uint32_t allocateNewBank ();
uint32_t assignBankLH (uint32_t requestedPrefix);
void deleteOrphanedAgentsLH();
- void sendCommandCompleteLH(const std::string& replyToKey, uint32_t sequence,
- uint32_t code = 0, const std::string& text = "OK");
- void sendExceptionLH(const std::string& rte, const std::string& rtk, const std::string& cid, const std::string& text, uint32_t code=1, bool viaLocal=false);
- void handleBrokerRequestLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
- void handlePackageQueryLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
- void handlePackageIndLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
- void handleClassQueryLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
- void handleClassIndLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
- void handleSchemaRequestLH (framing::Buffer& inBuffer, const std::string& replyToEx, const std::string& replyToKey, uint32_t sequence);
- void handleSchemaResponseLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
- void handleAttachRequestLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence, const qpid::broker::ConnectionToken* connToken);
- void handleGetQueryLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
- void handleMethodRequestLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence, const qpid::broker::ConnectionToken* connToken);
- void handleGetQueryLH (const std::string& body, const std::string& replyToEx, const std::string& replyToKey, const std::string& cid, bool viaLocal);
- void handleMethodRequestLH (const std::string& body, const std::string& replyToEx, const std::string& replyToKey, const std::string& cid, const qpid::broker::ConnectionToken* connToken, bool viaLocal);
- void handleLocateRequestLH (const std::string& body, const std::string& replyToEx, const std::string &replyToKey, const std::string& cid);
+ void sendCommandComplete(const std::string& replyToKey, uint32_t sequence,
+ uint32_t code = 0, const std::string& text = "OK");
+ void sendException(const std::string& rte, const std::string& rtk, const std::string& cid, const std::string& text, uint32_t code=1, bool viaLocal=false);
+ void handleBrokerRequest (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
+ void handlePackageQuery (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
+ void handlePackageInd (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
+ void handleClassQuery (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
+ void handleClassInd (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
+ void handleSchemaRequest (framing::Buffer& inBuffer, const std::string& replyToEx, const std::string& replyToKey, uint32_t sequence);
+ void handleSchemaResponse (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
+ void handleAttachRequest (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence, const qpid::broker::ConnectionToken* connToken);
+ void handleGetQuery (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
+ void handleMethodRequest (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence, const qpid::broker::ConnectionToken* connToken);
+ void handleGetQuery (const std::string& body, const std::string& replyToEx, const std::string& replyToKey, const std::string& cid, bool viaLocal);
+ void handleMethodRequest (const std::string& body, const std::string& replyToEx, const std::string& replyToKey, const std::string& cid, const qpid::broker::ConnectionToken* connToken, bool viaLocal);
+ void handleLocateRequest (const std::string& body, const std::string& replyToEx, const std::string &replyToKey, const std::string& cid);
size_t validateSchema(framing::Buffer&, uint8_t kind);
@@ -424,6 +376,7 @@ private:
std::string summarizeAgents();
void debugSnapshot(const char* title);
+ std::auto_ptr<EventQueue> sendQueue;
};
void setManagementExecutionContext(const qpid::broker::ConnectionState*);
diff --git a/cpp/src/qpid/messaging/Connection.cpp b/cpp/src/qpid/messaging/Connection.cpp
index bd90aa54a7..fde931038b 100644
--- a/cpp/src/qpid/messaging/Connection.cpp
+++ b/cpp/src/qpid/messaging/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
@@ -24,6 +24,7 @@
#include "qpid/messaging/Session.h"
#include "qpid/messaging/SessionImpl.h"
#include "qpid/messaging/PrivateImplRef.h"
+#include "qpid/messaging/ProtocolRegistry.h"
#include "qpid/client/amqp0_10/ConnectionImpl.h"
#include "qpid/log/Statement.h"
@@ -40,22 +41,32 @@ Connection& Connection::operator=(const Connection& c) { return PI::assign(*this
Connection::~Connection() { PI::dtor(*this); }
Connection::Connection(const std::string& url, const std::string& o)
-{
+{
Variant::Map options;
AddressParser parser(o);
if (o.empty() || parser.parseMap(options)) {
- PI::ctor(*this, new qpid::client::amqp0_10::ConnectionImpl(url, options));
+ ConnectionImpl* impl = ProtocolRegistry::create(url, options);
+ if (impl) {
+ PI::ctor(*this, impl);
+ } else {
+ PI::ctor(*this, new qpid::client::amqp0_10::ConnectionImpl(url, options));
+ }
} else {
throw InvalidOptionString("Invalid option string: " + o);
}
}
Connection::Connection(const std::string& url, const Variant::Map& options)
{
- PI::ctor(*this, new qpid::client::amqp0_10::ConnectionImpl(url, options));
+ ConnectionImpl* impl = ProtocolRegistry::create(url, options);
+ if (impl) {
+ PI::ctor(*this, impl);
+ } else {
+ PI::ctor(*this, new qpid::client::amqp0_10::ConnectionImpl(url, options));
+ }
}
Connection::Connection()
-{
+{
Variant::Map options;
std::string url = "amqp:tcp:127.0.0.1:5672";
PI::ctor(*this, new qpid::client::amqp0_10::ConnectionImpl(url, options));
@@ -67,12 +78,12 @@ bool Connection::isOpen() const { return impl->isOpen(); }
void Connection::close() { impl->close(); }
Session Connection::createSession(const std::string& name) { return impl->newSession(false, name); }
Session Connection::createTransactionalSession(const std::string& name)
-{
+{
return impl->newSession(true, name);
}
Session Connection::getSession(const std::string& name) const { return impl->getSession(name); }
void Connection::setOption(const std::string& name, const Variant& value)
-{
+{
impl->setOption(name, value);
}
std::string Connection::getAuthenticatedUsername()
diff --git a/cpp/src/qpid/messaging/ConnectionOptions.cpp b/cpp/src/qpid/messaging/ConnectionOptions.cpp
new file mode 100644
index 0000000000..ecd5ba9693
--- /dev/null
+++ b/cpp/src/qpid/messaging/ConnectionOptions.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/messaging/ConnectionOptions.h"
+#include "qpid/messaging/exceptions.h"
+#include "qpid/types/Variant.h"
+#include "qpid/log/Statement.h"
+#include <algorithm>
+#include <limits>
+
+namespace qpid {
+namespace messaging {
+
+namespace {
+double FOREVER(std::numeric_limits<double>::max());
+
+double timeValue(const qpid::types::Variant& value) {
+ if (types::isIntegerType(value.getType()))
+ return double(value.asInt64());
+ return value.asDouble();
+}
+
+void merge(const std::string& value, std::vector<std::string>& list) {
+ if (std::find(list.begin(), list.end(), value) == list.end())
+ list.push_back(value);
+}
+
+void merge(const qpid::types::Variant::List& from, std::vector<std::string>& to)
+{
+ for (qpid::types::Variant::List::const_iterator i = from.begin(); i != from.end(); ++i)
+ merge(i->asString(), to);
+}
+
+}
+
+ConnectionOptions::ConnectionOptions(const std::map<std::string, qpid::types::Variant>& options)
+ : replaceUrls(false), reconnect(false), timeout(FOREVER), limit(-1), minReconnectInterval(0.001), maxReconnectInterval(2),
+ retries(0), reconnectOnLimitExceeded(true)
+{
+ for (qpid::types::Variant::Map::const_iterator i = options.begin(); i != options.end(); ++i) {
+ set(i->first, i->second);
+ }
+}
+
+void ConnectionOptions::set(const std::string& name, const qpid::types::Variant& value)
+{
+ if (name == "reconnect") {
+ reconnect = value;
+ } else if (name == "reconnect-timeout" || name == "reconnect_timeout") {
+ timeout = timeValue(value);
+ } else if (name == "reconnect-limit" || name == "reconnect_limit") {
+ limit = value;
+ } else if (name == "reconnect-interval" || name == "reconnect_interval") {
+ maxReconnectInterval = minReconnectInterval = timeValue(value);
+ } else if (name == "reconnect-interval-min" || name == "reconnect_interval_min") {
+ minReconnectInterval = timeValue(value);
+ } else if (name == "reconnect-interval-max" || name == "reconnect_interval_max") {
+ maxReconnectInterval = timeValue(value);
+ } else if (name == "reconnect-urls-replace" || name == "reconnect_urls_replace") {
+ replaceUrls = value.asBool();
+ } else if (name == "reconnect-urls" || name == "reconnect_urls") {
+ if (replaceUrls) urls.clear();
+ if (value.getType() == qpid::types::VAR_LIST) {
+ merge(value.asList(), urls);
+ } else {
+ merge(value.asString(), urls);
+ }
+ } else if (name == "username") {
+ username = value.asString();
+ } else if (name == "password") {
+ password = value.asString();
+ } else if (name == "sasl-mechanism" || name == "sasl_mechanism" ||
+ name == "sasl-mechanisms" || name == "sasl_mechanisms") {
+ mechanism = value.asString();
+ } else if (name == "sasl-service" || name == "sasl_service") {
+ service = value.asString();
+ } else if (name == "sasl-min-ssf" || name == "sasl_min_ssf") {
+ minSsf = value;
+ } else if (name == "sasl-max-ssf" || name == "sasl_max_ssf") {
+ maxSsf = value;
+ } else if (name == "heartbeat") {
+ heartbeat = value;
+ } else if (name == "tcp-nodelay" || name == "tcp_nodelay") {
+ tcpNoDelay = value;
+ } else if (name == "locale") {
+ locale = value.asString();
+ } else if (name == "max-channels" || name == "max_channels") {
+ maxChannels = value;
+ } else if (name == "max-frame-size" || name == "max_frame_size") {
+ maxFrameSize = value;
+ } else if (name == "bounds") {
+ bounds = value;
+ } else if (name == "transport") {
+ protocol = value.asString();
+ } else if (name == "ssl-cert-name" || name == "ssl_cert_name") {
+ sslCertName = value.asString();
+ } else if (name == "x-reconnect-on-limit-exceeded" || name == "x_reconnect_on_limit_exceeded") {
+ reconnectOnLimitExceeded = value;
+ } else {
+ throw qpid::messaging::MessagingException(QPID_MSG("Invalid option: " << name << " not recognised"));
+ }
+}
+
+}} // namespace qpid::messaging
diff --git a/cpp/src/qpid/messaging/ConnectionOptions.h b/cpp/src/qpid/messaging/ConnectionOptions.h
new file mode 100644
index 0000000000..6786fd4a64
--- /dev/null
+++ b/cpp/src/qpid/messaging/ConnectionOptions.h
@@ -0,0 +1,51 @@
+#ifndef QPID_MESSAGING_CONNECTIONOPTIONS_H
+#define QPID_MESSAGING_CONNECTIONOPTIONS_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/ConnectionSettings.h"
+#include <map>
+#include <vector>
+
+namespace qpid {
+namespace types {
+class Variant;
+}
+namespace messaging {
+
+struct ConnectionOptions : qpid::client::ConnectionSettings
+{
+ std::vector<std::string> urls;
+ bool replaceUrls;
+ bool reconnect;
+ double timeout;
+ int32_t limit;
+ double minReconnectInterval;
+ double maxReconnectInterval;
+ int32_t retries;
+ bool reconnectOnLimitExceeded;
+
+ ConnectionOptions(const std::map<std::string, qpid::types::Variant>&);
+ void set(const std::string& name, const qpid::types::Variant& value);
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_CONNECTIONOPTIONS_H*/
diff --git a/cpp/src/qpid/messaging/Message.cpp b/cpp/src/qpid/messaging/Message.cpp
index ef70c103e9..0f03bc8ca3 100644
--- a/cpp/src/qpid/messaging/Message.cpp
+++ b/cpp/src/qpid/messaging/Message.cpp
@@ -46,26 +46,26 @@ const std::string& Message::getSubject() const { return impl->getSubject(); }
void Message::setContentType(const std::string& s) { impl->setContentType(s); }
const std::string& Message::getContentType() const { return impl->getContentType(); }
-void Message::setMessageId(const std::string& id) { impl->messageId = id; }
-const std::string& Message::getMessageId() const { return impl->messageId; }
+void Message::setMessageId(const std::string& id) { impl->setMessageId(id); }
+const std::string& Message::getMessageId() const { return impl->getMessageId(); }
-void Message::setUserId(const std::string& id) { impl->userId = id; }
-const std::string& Message::getUserId() const { return impl->userId; }
+void Message::setUserId(const std::string& id) { impl->setUserId(id); }
+const std::string& Message::getUserId() const { return impl->getUserId(); }
-void Message::setCorrelationId(const std::string& id) { impl->correlationId = id; }
-const std::string& Message::getCorrelationId() const { return impl->correlationId; }
+void Message::setCorrelationId(const std::string& id) { impl->setCorrelationId(id); }
+const std::string& Message::getCorrelationId() const { return impl->getCorrelationId(); }
-uint8_t Message::getPriority() const { return impl->priority; }
-void Message::setPriority(uint8_t priority) { impl->priority = priority; }
+uint8_t Message::getPriority() const { return impl->getPriority(); }
+void Message::setPriority(uint8_t priority) { impl->setPriority(priority); }
-void Message::setTtl(Duration ttl) { impl->ttl = ttl.getMilliseconds(); }
-Duration Message::getTtl() const { return Duration(impl->ttl); }
+void Message::setTtl(Duration ttl) { impl->setTtl(ttl.getMilliseconds()); }
+Duration Message::getTtl() const { return Duration(impl->getTtl()); }
-void Message::setDurable(bool durable) { impl->durable = durable; }
-bool Message::getDurable() const { return impl->durable; }
+void Message::setDurable(bool durable) { impl->setDurable(durable); }
+bool Message::getDurable() const { return impl->isDurable(); }
-bool Message::getRedelivered() const { return impl->redelivered; }
-void Message::setRedelivered(bool redelivered) { impl->redelivered = redelivered; }
+bool Message::getRedelivered() const { return impl->isRedelivered(); }
+void Message::setRedelivered(bool redelivered) { impl->setRedelivered(redelivered); }
const Variant::Map& Message::getProperties() const { return impl->getHeaders(); }
Variant::Map& Message::getProperties() { return impl->getHeaders(); }
diff --git a/cpp/src/qpid/messaging/MessageImpl.cpp b/cpp/src/qpid/messaging/MessageImpl.cpp
index 0601800e46..fc9bc5dfa1 100644
--- a/cpp/src/qpid/messaging/MessageImpl.cpp
+++ b/cpp/src/qpid/messaging/MessageImpl.cpp
@@ -45,28 +45,163 @@ 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::setReplyTo(const Address& d)
+{
+ replyTo = d;
+ updated();
+}
+const Address& MessageImpl::getReplyTo() const
+{
+ if (!replyTo && encoded) encoded->getReplyTo(replyTo);
+ return replyTo;
+}
-void MessageImpl::setSubject(const std::string& s) { subject = s; }
-const std::string& MessageImpl::getSubject() const { return subject; }
+void MessageImpl::setSubject(const std::string& s)
+{
+ subject = s;
+ updated();
+}
+const std::string& MessageImpl::getSubject() const
+{
+ if (!subject.size() && encoded) encoded->getSubject(subject);
+ return subject;
+}
-void MessageImpl::setContentType(const std::string& s) { contentType = s; }
-const std::string& MessageImpl::getContentType() const { return contentType; }
+void MessageImpl::setContentType(const std::string& s)
+{
+ contentType = s;
+ updated();
+}
+const std::string& MessageImpl::getContentType() const
+{
+ if (!contentType.size() && encoded) encoded->getContentType(contentType);
+ return contentType;
+}
-const Variant::Map& MessageImpl::getHeaders() const { return headers; }
-Variant::Map& MessageImpl::getHeaders() { return headers; }
-void MessageImpl::setHeader(const std::string& key, const qpid::types::Variant& val) { headers[key] = val; }
+void MessageImpl::setMessageId(const std::string& s)
+{
+ messageId = s;
+ updated();
+}
+const std::string& MessageImpl::getMessageId() const
+{
+ if (!messageId.size() && encoded) encoded->getMessageId(messageId);
+ return messageId;
+}
+void MessageImpl::setUserId(const std::string& s)
+{
+ userId = s;
+ updated();
+}
+const std::string& MessageImpl::getUserId() const
+{
+ if (!userId.size() && encoded) encoded->getUserId(userId);
+ return userId;
+}
+void MessageImpl::setCorrelationId(const std::string& s)
+{
+ correlationId = s;
+ updated();
+}
+const std::string& MessageImpl::getCorrelationId() const
+{
+ if (!correlationId.size() && encoded) encoded->getCorrelationId(correlationId);
+ return correlationId;
+}
+void MessageImpl::setPriority(uint8_t p)
+{
+ priority = p;
+}
+uint8_t MessageImpl::getPriority() const
+{
+ return priority;
+}
+void MessageImpl::setTtl(uint64_t t)
+{
+ ttl = t;
+}
+uint64_t MessageImpl::getTtl() const
+{
+ return ttl;
+}
+void MessageImpl::setDurable(bool d)
+{
+ durable = d;
+}
+bool MessageImpl::isDurable() const
+{
+ return durable;
+}
+void MessageImpl::setRedelivered(bool b)
+{
+ redelivered = b;
+}
+bool MessageImpl::isRedelivered() const
+{
+ return redelivered;
+}
+
+const Variant::Map& MessageImpl::getHeaders() const
+{
+ if (!headers.size() && encoded) encoded->populate(headers);
+ return headers;
+}
+Variant::Map& MessageImpl::getHeaders() {
+ if (!headers.size() && encoded) encoded->populate(headers);
+ updated();
+ return headers;
+}
+void MessageImpl::setHeader(const std::string& key, const qpid::types::Variant& val)
+{
+ headers[key] = val; updated();
+}
//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::setBytes(const std::string& c)
+{
+ bytes = c;
+ updated();
+}
+void MessageImpl::setBytes(const char* chars, size_t count)
+{
+ bytes.assign(chars, count);
+ updated();
+}
+void MessageImpl::appendBytes(const char* chars, size_t count)
+{
+ bytes.append(chars, count);
+ updated();
+}
+const std::string& MessageImpl::getBytes() const
+{
+ if (!bytes.size() && encoded) encoded->getBody(bytes);
+ return bytes;
+}
+std::string& MessageImpl::getBytes()
+{
+ if (!bytes.size() && encoded) encoded->getBody(bytes);
+ updated();//have to assume body may be edited, invalidating our message
+ return bytes;
+}
void MessageImpl::setInternalId(qpid::framing::SequenceNumber i) { internalId = i; }
qpid::framing::SequenceNumber MessageImpl::getInternalId() { return internalId; }
+void MessageImpl::updated()
+{
+
+ if (!replyTo && encoded) encoded->getReplyTo(replyTo);
+ if (!subject.size() && encoded) encoded->getSubject(subject);
+ if (!contentType.size() && encoded) encoded->getContentType(contentType);
+ if (!messageId.size() && encoded) encoded->getMessageId(messageId);
+ if (!userId.size() && encoded) encoded->getUserId(userId);
+ if (!correlationId.size() && encoded) encoded->getCorrelationId(correlationId);
+ if (!headers.size() && encoded) encoded->populate(headers);
+ if (!bytes.size() && encoded) encoded->getBody(bytes);
+
+ encoded.reset();
+}
+
MessageImpl& MessageImplAccess::get(Message& msg)
{
return *msg.impl;
diff --git a/cpp/src/qpid/messaging/MessageImpl.h b/cpp/src/qpid/messaging/MessageImpl.h
index 57df6b3fda..915c790153 100644
--- a/cpp/src/qpid/messaging/MessageImpl.h
+++ b/cpp/src/qpid/messaging/MessageImpl.h
@@ -24,52 +24,77 @@
#include "qpid/messaging/Address.h"
#include "qpid/types/Variant.h"
#include "qpid/framing/SequenceNumber.h"
+#include "qpid/messaging/amqp/EncodedMessage.h"
+#include <vector>
+#include <boost/shared_ptr.hpp>
namespace qpid {
namespace messaging {
-struct MessageImpl
+class MessageImpl
{
- Address replyTo;
- std::string subject;
- std::string contentType;
- std::string messageId;
- std::string userId;
- std::string correlationId;
+ private:
+ mutable Address replyTo;
+ mutable std::string subject;
+ mutable std::string contentType;
+ mutable std::string messageId;
+ mutable std::string userId;
+ mutable std::string correlationId;
uint8_t priority;
uint64_t ttl;
bool durable;
bool redelivered;
- qpid::types::Variant::Map headers;
+ mutable qpid::types::Variant::Map headers;
- std::string bytes;
+ mutable std::string bytes;
+ boost::shared_ptr<const qpid::messaging::amqp::EncodedMessage> encoded;
qpid::framing::SequenceNumber internalId;
+ void updated();
+ public:
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;
-
+
+ void setMessageId(const std::string&);
+ const std::string& getMessageId() const;
+ void setUserId(const std::string& );
+ const std::string& getUserId() const;
+ void setCorrelationId(const std::string& );
+ const std::string& getCorrelationId() const;
+ void setPriority(uint8_t);
+ uint8_t getPriority() const;
+ void setTtl(uint64_t);
+ uint64_t getTtl() const;
+ void setDurable(bool);
+ bool isDurable() const;
+ void setRedelivered(bool);
+ bool isRedelivered() const;
+
+
const qpid::types::Variant::Map& getHeaders() const;
qpid::types::Variant::Map& getHeaders();
void setHeader(const std::string& key, const qpid::types::Variant& val);
-
+
void setBytes(const std::string& bytes);
void setBytes(const char* chars, size_t count);
+ void appendBytes(const char* chars, size_t count);
const std::string& getBytes() const;
std::string& getBytes();
void setInternalId(qpid::framing::SequenceNumber id);
qpid::framing::SequenceNumber getInternalId();
-
+ void setEncoded(boost::shared_ptr<const qpid::messaging::amqp::EncodedMessage> e) { encoded = e; }
+ boost::shared_ptr<const qpid::messaging::amqp::EncodedMessage> getEncoded() const { return encoded; }
};
class Message;
diff --git a/cpp/src/qpid/messaging/ProtocolRegistry.cpp b/cpp/src/qpid/messaging/ProtocolRegistry.cpp
new file mode 100644
index 0000000000..0232da8ae1
--- /dev/null
+++ b/cpp/src/qpid/messaging/ProtocolRegistry.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 "ProtocolRegistry.h"
+#include "qpid/Exception.h"
+#include "qpid/client/amqp0_10/ConnectionImpl.h"
+#include "qpid/client/LoadPlugins.h"
+#include <map>
+
+using qpid::types::Variant;
+
+namespace qpid {
+namespace messaging {
+namespace {
+typedef std::map<std::string, ProtocolRegistry::Factory*> Registry;
+
+Registry& theRegistry()
+{
+ static Registry factories;
+ return factories;
+}
+
+bool extract(const std::string& key, Variant& value, const Variant::Map& in, Variant::Map& out)
+{
+ bool matched = false;
+ for (Variant::Map::const_iterator i = in.begin(); i != in.end(); ++i) {
+ if (i->first == key) {
+ value = i->second;
+ matched = true;
+ } else {
+ out.insert(*i);
+ }
+ }
+ return matched;
+}
+}
+
+ConnectionImpl* ProtocolRegistry::create(const std::string& url, const Variant::Map& options)
+{
+ qpid::client::theModuleLoader();//ensure modules are loaded
+ Variant name;
+ Variant::Map stripped;
+ if (extract("protocol", name, options, stripped)) {
+ Registry::const_iterator i = theRegistry().find(name.asString());
+ if (i != theRegistry().end()) return (i->second)(url, stripped);
+ else if (name.asString() == "amqp0-10") return new qpid::client::amqp0_10::ConnectionImpl(url, stripped);
+ else throw qpid::Exception("Unsupported protocol: " + name.asString());
+ }
+ return 0;
+}
+void ProtocolRegistry::add(const std::string& name, Factory* factory)
+{
+ theRegistry()[name] = factory;
+}
+
+}} // namespace qpid::messaging
diff --git a/cpp/src/qpid/messaging/ProtocolRegistry.h b/cpp/src/qpid/messaging/ProtocolRegistry.h
new file mode 100644
index 0000000000..bcb62248a5
--- /dev/null
+++ b/cpp/src/qpid/messaging/ProtocolRegistry.h
@@ -0,0 +1,42 @@
+#ifndef QPID_MESSAGING_PROTOCOLREGISTRY_H
+#define QPID_MESSAGING_PROTOCOLREGISTRY_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/types/Variant.h"
+
+namespace qpid {
+namespace messaging {
+class ConnectionImpl;
+/**
+ * Registry for different implementations of the messaging API e.g AMQP 1.0
+ */
+class ProtocolRegistry
+{
+ public:
+ typedef ConnectionImpl* Factory(const std::string& url, const qpid::types::Variant::Map& options);
+ static ConnectionImpl* create(const std::string& url, const qpid::types::Variant::Map& options);
+ static void add(const std::string& name, Factory* factory);
+ private:
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_PROTOCOLREGISTRY_H*/
diff --git a/cpp/src/qpid/messaging/ReceiverImpl.h b/cpp/src/qpid/messaging/ReceiverImpl.h
index 57059bfd28..e450693d2c 100644
--- a/cpp/src/qpid/messaging/ReceiverImpl.h
+++ b/cpp/src/qpid/messaging/ReceiverImpl.h
@@ -22,10 +22,12 @@
*
*/
#include "qpid/RefCounted.h"
+#include "qpid/sys/IntegerTypes.h"
namespace qpid {
namespace messaging {
+class Duration;
class Message;
class MessageListener;
class Session;
diff --git a/cpp/src/qpid/messaging/SenderImpl.h b/cpp/src/qpid/messaging/SenderImpl.h
index a1ca02c72c..d978463fdb 100644
--- a/cpp/src/qpid/messaging/SenderImpl.h
+++ b/cpp/src/qpid/messaging/SenderImpl.h
@@ -22,6 +22,7 @@
*
*/
#include "qpid/RefCounted.h"
+#include "qpid/sys/IntegerTypes.h"
namespace qpid {
namespace messaging {
diff --git a/cpp/src/qpid/messaging/amqp/AddressHelper.cpp b/cpp/src/qpid/messaging/amqp/AddressHelper.cpp
new file mode 100644
index 0000000000..359660dce5
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/AddressHelper.cpp
@@ -0,0 +1,182 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/amqp/AddressHelper.h"
+#include "qpid/messaging/Address.h"
+#include <vector>
+#include <boost/assign.hpp>
+extern "C" {
+#include <proton/engine.h>
+}
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+
+using qpid::types::Variant;
+
+namespace {
+//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 NODE("node");
+const std::string LINK("link");
+
+const std::string TYPE("type");
+const std::string TOPIC("topic");
+const std::string QUEUE("queue");
+
+//distribution modes:
+const std::string MOVE("move");
+const std::string COPY("copy");
+
+const std::string SUPPORTED_DIST_MODES("supported-dist-modes");
+
+
+const std::vector<std::string> RECEIVER_MODES = boost::assign::list_of<std::string>(ALWAYS) (RECEIVER);
+const std::vector<std::string> SENDER_MODES = boost::assign::list_of<std::string>(ALWAYS) (SENDER);
+
+pn_bytes_t convert(const std::string& s)
+{
+ pn_bytes_t result;
+ result.start = const_cast<char*>(s.data());
+ result.size = s.size();
+ return result;
+}
+
+bool bind(const Variant::Map& options, const std::string& name, std::string& variable)
+{
+ Variant::Map::const_iterator j = options.find(name);
+ if (j == options.end()) {
+ return false;
+ } else {
+ variable = j->second.asString();
+ return true;
+ }
+}
+
+bool bind(const Variant::Map& options, const std::string& name, Variant::Map& variable)
+{
+ Variant::Map::const_iterator j = options.find(name);
+ if (j == options.end()) {
+ return false;
+ } else {
+ variable = j->second.asMap();
+ return true;
+ }
+}
+
+bool bind(const Address& address, const std::string& name, std::string& variable)
+{
+ return bind(address.getOptions(), name, variable);
+}
+
+bool bind(const Address& address, const std::string& name, Variant::Map& variable)
+{
+ return bind(address.getOptions(), name, variable);
+}
+
+bool in(const std::string& value, const std::vector<std::string>& choices)
+{
+ for (std::vector<std::string>::const_iterator i = choices.begin(); i != choices.end(); ++i) {
+ if (value == *i) return true;
+ }
+ return false;
+}
+}
+
+AddressHelper::AddressHelper(const Address& address)
+{
+ bind(address, CREATE, createPolicy);
+ bind(address, DELETE, deletePolicy);
+ bind(address, ASSERT, assertPolicy);
+
+ bind(address, NODE, node);
+ bind(address, LINK, link);
+}
+
+bool AddressHelper::createEnabled(CheckMode mode) const
+{
+ return enabled(createPolicy, mode);
+}
+bool AddressHelper::deleteEnabled(CheckMode mode) const
+{
+ return enabled(deletePolicy, mode);
+}
+bool AddressHelper::assertEnabled(CheckMode mode) const
+{
+ return enabled(assertPolicy, mode);
+}
+bool AddressHelper::enabled(const std::string& policy, CheckMode mode) const
+{
+ 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;
+}
+
+const qpid::types::Variant::Map& AddressHelper::getNodeProperties() const
+{
+ return node;
+}
+const qpid::types::Variant::Map& AddressHelper::getLinkProperties() const
+{
+ return link;
+}
+
+void AddressHelper::setNodeProperties(pn_terminus_t* terminus)
+{
+ pn_terminus_set_dynamic(terminus, true);
+
+ //properties for dynamically created node:
+ pn_data_t* data = pn_terminus_properties(terminus);
+ if (node.size()) {
+ pn_data_put_map(data);
+ pn_data_enter(data);
+ }
+ for (qpid::types::Variant::Map::const_iterator i = node.begin(); i != node.end(); ++i) {
+ if (i->first == TYPE) {
+ pn_data_put_symbol(data, convert(SUPPORTED_DIST_MODES));
+ pn_data_put_string(data, convert(i->second == TOPIC ? COPY : MOVE));
+ } else {
+ pn_data_put_symbol(data, convert(i->first));
+ pn_data_put_string(data, convert(i->second.asString()));
+ }
+ }
+ if (node.size()) {
+ pn_data_exit(data);
+ }
+}
+
+}}} // namespace qpid::messaging::amqp
diff --git a/cpp/src/qpid/messaging/amqp/AddressHelper.h b/cpp/src/qpid/messaging/amqp/AddressHelper.h
new file mode 100644
index 0000000000..cd0aa1be9e
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/AddressHelper.h
@@ -0,0 +1,57 @@
+#ifndef QPID_MESSAGING_AMQP_ADDRESSHELPER_H
+#define QPID_MESSAGING_AMQP_ADDRESSHELPER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/types/Variant.h"
+
+struct pn_terminus_t;
+
+namespace qpid {
+namespace messaging {
+class Address;
+namespace amqp {
+
+class AddressHelper
+{
+ public:
+ enum CheckMode {FOR_RECEIVER, FOR_SENDER};
+
+ AddressHelper(const Address& address);
+ bool createEnabled(CheckMode mode) const;
+ bool deleteEnabled(CheckMode mode) const;
+ bool assertEnabled(CheckMode mode) const;
+
+ void setNodeProperties(pn_terminus_t*);
+ const qpid::types::Variant::Map& getNodeProperties() const;
+ const qpid::types::Variant::Map& getLinkProperties() const;
+ private:
+ std::string createPolicy;
+ std::string assertPolicy;
+ std::string deletePolicy;
+ qpid::types::Variant::Map node;
+ qpid::types::Variant::Map link;
+
+ bool enabled(const std::string& policy, CheckMode mode) const;
+};
+}}} // namespace qpid::messaging::amqp
+
+#endif /*!QPID_MESSAGING_AMQP_ADDRESSHELPER_H*/
diff --git a/cpp/src/qpid/messaging/amqp/ConnectionContext.cpp b/cpp/src/qpid/messaging/amqp/ConnectionContext.cpp
new file mode 100644
index 0000000000..b2a9b979b6
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/ConnectionContext.cpp
@@ -0,0 +1,612 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "ConnectionContext.h"
+#include "DriverImpl.h"
+#include "ReceiverContext.h"
+#include "Sasl.h"
+#include "SenderContext.h"
+#include "SessionContext.h"
+#include "Transport.h"
+#include "qpid/messaging/exceptions.h"
+#include "qpid/messaging/Duration.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/MessageImpl.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/ProtocolInitiation.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Time.h"
+#include <vector>
+extern "C" {
+#include <proton/engine.h>
+}
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+
+
+ConnectionContext::ConnectionContext(const std::string& u, const qpid::types::Variant::Map& o)
+ : qpid::messaging::ConnectionOptions(o),
+ url(u),
+ engine(pn_transport()),
+ connection(pn_connection()),
+ //note: disabled read/write of header as now handled by engine
+ writeHeader(false),
+ readHeader(false),
+ haveOutput(false),
+ state(DISCONNECTED),
+ codecSwitch(*this)
+{
+ if (pn_transport_bind(engine, connection)) {
+ //error
+ }
+ pn_connection_set_container(connection, "qpid::messaging");//TODO: take this from a connection option
+ bool enableTrace(false);
+ QPID_LOG_TEST_CAT(trace, protocol, enableTrace);
+ if (enableTrace) pn_transport_trace(engine, PN_TRACE_FRM);
+}
+
+ConnectionContext::~ConnectionContext()
+{
+ close();
+ sessions.clear();
+ pn_transport_free(engine);
+ pn_connection_free(connection);
+}
+
+namespace {
+const std::string COLON(":");
+}
+void ConnectionContext::open()
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ if (state != DISCONNECTED) throw qpid::messaging::ConnectionError("Connection was already opened!");
+ if (!driver) driver = DriverImpl::getDefault();
+
+ for (Url::const_iterator i = url.begin(); state != CONNECTED && i != url.end(); ++i) {
+ transport = driver->getTransport(i->protocol, *this);
+ std::stringstream port;
+ port << i->port;
+ id = i->host + COLON + port.str();
+ if (useSasl()) {
+ sasl = std::auto_ptr<Sasl>(new Sasl(id, *this, i->host));
+ }
+ state = CONNECTING;
+ try {
+ QPID_LOG(debug, id << " Connecting ...");
+ transport->connect(i->host, port.str());
+ } catch (const std::exception& e) {
+ QPID_LOG(info, id << " Error while connecting: " << e.what());
+ }
+ while (state == CONNECTING) {
+ lock.wait();
+ }
+ if (state == DISCONNECTED) {
+ QPID_LOG(debug, id << " Failed to connect");
+ transport = boost::shared_ptr<Transport>();
+ } else {
+ QPID_LOG(debug, id << " Connected");
+ }
+ }
+
+ if (state != CONNECTED) throw qpid::messaging::TransportFailure(QPID_MSG("Could not connect to " << url));
+
+ if (sasl.get()) {
+ wakeupDriver();
+ while (!sasl->authenticated()) {
+ QPID_LOG(debug, id << " Waiting to be authenticated...");
+ wait();
+ }
+ QPID_LOG(debug, id << " Authenticated");
+ }
+
+ QPID_LOG(debug, id << " Opening...");
+ pn_connection_open(connection);
+ wakeupDriver(); //want to write
+ while (pn_connection_state(connection) & PN_REMOTE_UNINIT) {
+ wait();
+ }
+ if (!(pn_connection_state(connection) & PN_REMOTE_ACTIVE)) {
+ throw qpid::messaging::ConnectionError("Failed to open connection");
+ }
+ QPID_LOG(debug, id << " Opened");
+}
+
+bool ConnectionContext::isOpen() const
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ return pn_connection_state(connection) & (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE);
+}
+
+void ConnectionContext::endSession(boost::shared_ptr<SessionContext> ssn)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ pn_session_close(ssn->session);
+ //TODO: need to destroy session and remove context from map
+ wakeupDriver();
+}
+
+void ConnectionContext::close()
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ if (state != CONNECTED) return;
+ if (!(pn_connection_state(connection) & PN_LOCAL_CLOSED)) {
+ for (SessionMap::iterator i = sessions.begin(); i != sessions.end(); ++i) {
+ //wait for outstanding sends to settle
+ while (!i->second->settled()) {
+ QPID_LOG(debug, "Waiting for sends to settle before closing");
+ wait();//wait until message has been confirmed
+ }
+
+
+ if (!(pn_session_state(i->second->session) & PN_LOCAL_CLOSED)) {
+ pn_session_close(i->second->session);
+ }
+ }
+ pn_connection_close(connection);
+ wakeupDriver();
+ //wait for close to be confirmed by peer?
+ while (!(pn_connection_state(connection) & PN_REMOTE_CLOSED)) {
+ wait();
+ }
+ sessions.clear();
+ }
+ transport->close();
+ while (state != DISCONNECTED) {
+ lock.wait();
+ }
+}
+
+bool ConnectionContext::fetch(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<ReceiverContext> lnk, qpid::messaging::Message& message, qpid::messaging::Duration timeout)
+{
+ {
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ if (!lnk->capacity) {
+ pn_link_flow(lnk->receiver, 1);
+ wakeupDriver();
+ }
+ }
+ if (get(ssn, lnk, message, timeout)) {
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ if (lnk->capacity) {
+ pn_link_flow(lnk->receiver, 1);//TODO: is this the right approach?
+ wakeupDriver();
+ }
+ return true;
+ } else {
+ {
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ pn_link_drain(lnk->receiver, 0);
+ wakeupDriver();
+ while (pn_link_credit(lnk->receiver) && !pn_link_queued(lnk->receiver)) {
+ QPID_LOG(debug, "Waiting for message or for credit to be drained: credit=" << pn_link_credit(lnk->receiver) << ", queued=" << pn_link_queued(lnk->receiver));
+ wait();
+ }
+ if (lnk->capacity && pn_link_queued(lnk->receiver) == 0) {
+ pn_link_flow(lnk->receiver, lnk->capacity);
+ }
+ }
+ if (get(ssn, lnk, message, qpid::messaging::Duration::IMMEDIATE)) {
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ if (lnk->capacity) {
+ pn_link_flow(lnk->receiver, 1);
+ wakeupDriver();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
+qpid::sys::AbsTime convert(qpid::messaging::Duration timeout)
+{
+ qpid::sys::AbsTime until;
+ uint64_t ms = timeout.getMilliseconds();
+ if (ms < (uint64_t) (qpid::sys::TIME_INFINITE/qpid::sys::TIME_MSEC)) {
+ return qpid::sys::AbsTime(qpid::sys::now(), ms * qpid::sys::TIME_MSEC);
+ } else {
+ return qpid::sys::FAR_FUTURE;
+ }
+}
+
+bool ConnectionContext::get(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<ReceiverContext> lnk, qpid::messaging::Message& message, qpid::messaging::Duration timeout)
+{
+ qpid::sys::AbsTime until(convert(timeout));
+ while (true) {
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ pn_delivery_t* current = pn_link_current((pn_link_t*) lnk->receiver);
+ QPID_LOG(debug, "In ConnectionContext::get(), current=" << current);
+ if (current) {
+ qpid::messaging::MessageImpl& impl = MessageImplAccess::get(message);
+ boost::shared_ptr<EncodedMessage> encoded(new EncodedMessage(pn_delivery_pending(current)));
+ ssize_t read = pn_link_recv(lnk->receiver, encoded->getData(), encoded->getSize());
+ if (read < 0) throw qpid::messaging::MessagingException("Failed to read message");
+ encoded->trim((size_t) read);
+ QPID_LOG(debug, "Received message of " << encoded->getSize() << " bytes: ");
+ encoded->init(impl);
+ impl.setEncoded(encoded);
+ impl.setInternalId(ssn->record(current));
+ pn_link_advance(lnk->receiver);
+ return true;
+ } else if (until > qpid::sys::now()) {
+ wait();
+ } else {
+ return false;
+ }
+ }
+ return false;
+}
+
+void ConnectionContext::acknowledge(boost::shared_ptr<SessionContext> ssn, qpid::messaging::Message* message, bool cumulative)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ if (message) {
+ ssn->acknowledge(MessageImplAccess::get(*message).getInternalId(), cumulative);
+ } else {
+ ssn->acknowledge();
+ }
+ wakeupDriver();
+}
+
+
+void ConnectionContext::attach(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<SenderContext> lnk)
+{
+ lnk->configure();
+ attach(ssn->session, (pn_link_t*) lnk->sender);
+ if (!pn_link_remote_target((pn_link_t*) lnk->sender)) {
+ std::string msg("No such target : ");
+ msg += lnk->getTarget();
+ throw qpid::messaging::NotFound(msg);
+ }
+}
+
+void ConnectionContext::attach(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<ReceiverContext> lnk)
+{
+ lnk->configure();
+ attach(ssn->session, lnk->receiver, lnk->capacity);
+ if (!pn_link_remote_source(lnk->receiver)) {
+ std::string msg("No such source : ");
+ msg += lnk->getSource();
+ throw qpid::messaging::NotFound(msg);
+ }
+}
+
+void ConnectionContext::attach(pn_session_t* /*session*/, pn_link_t* link, int credit)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ QPID_LOG(debug, "Attaching link " << link << ", state=" << pn_link_state(link));
+ pn_link_open(link);
+ QPID_LOG(debug, "Link attached " << link << ", state=" << pn_link_state(link));
+ if (credit) pn_link_flow(link, credit);
+ wakeupDriver();
+ while (pn_link_state(link) & PN_REMOTE_UNINIT) {
+ QPID_LOG(debug, "waiting for confirmation of link attach for " << link << ", state=" << pn_link_state(link));
+ wait();
+ }
+}
+
+void ConnectionContext::send(boost::shared_ptr<SenderContext> snd, const qpid::messaging::Message& message, bool sync)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ SenderContext::Delivery* delivery(0);
+ while (!(delivery = snd->send(message))) {
+ QPID_LOG(debug, "Waiting for capacity...");
+ wait();//wait for capacity
+ }
+ wakeupDriver();
+ if (sync) {
+ while (!delivery->accepted()) {
+ QPID_LOG(debug, "Waiting for confirmation...");
+ wait();//wait until message has been confirmed
+ }
+ }
+}
+
+void ConnectionContext::setCapacity(boost::shared_ptr<SenderContext> sender, uint32_t capacity)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ sender->setCapacity(capacity);
+}
+uint32_t ConnectionContext::getCapacity(boost::shared_ptr<SenderContext> sender)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ return sender->getCapacity();
+}
+uint32_t ConnectionContext::getUnsettled(boost::shared_ptr<SenderContext> sender)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ return sender->getUnsettled();
+}
+
+void ConnectionContext::setCapacity(boost::shared_ptr<ReceiverContext> receiver, uint32_t capacity)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ receiver->setCapacity(capacity);
+ pn_link_flow((pn_link_t*) receiver->receiver, receiver->getCapacity());
+ wakeupDriver();
+}
+uint32_t ConnectionContext::getCapacity(boost::shared_ptr<ReceiverContext> receiver)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ return receiver->getCapacity();
+}
+uint32_t ConnectionContext::getAvailable(boost::shared_ptr<ReceiverContext> receiver)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ return receiver->getAvailable();
+}
+uint32_t ConnectionContext::getUnsettled(boost::shared_ptr<ReceiverContext> receiver)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ return receiver->getUnsettled();
+}
+
+void ConnectionContext::activateOutput()
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ wakeupDriver();
+}
+/**
+ * Expects lock to be held by caller
+ */
+void ConnectionContext::wakeupDriver()
+{
+ switch (state) {
+ case CONNECTED:
+ haveOutput = true;
+ transport->activateOutput();
+ QPID_LOG(debug, "wakeupDriver()");
+ break;
+ case DISCONNECTED:
+ case CONNECTING:
+ QPID_LOG(error, "wakeupDriver() called while not connected");
+ break;
+ }
+}
+
+void ConnectionContext::wait()
+{
+ lock.wait();
+ if (state == DISCONNECTED) {
+ throw qpid::messaging::TransportFailure("Disconnected");
+ }
+ //check for any closed links, sessions or indeed the connection
+}
+
+boost::shared_ptr<SessionContext> ConnectionContext::newSession(bool transactional, const std::string& n)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ if (transactional) throw qpid::messaging::MessagingException("Transactions not yet supported");
+ std::string name = n.empty() ? qpid::framing::Uuid(true).str() : n;
+ SessionMap::const_iterator i = sessions.find(name);
+ if (i == sessions.end()) {
+ boost::shared_ptr<SessionContext> s(new SessionContext(connection));
+ s->session = pn_session(connection);
+ pn_session_open(s->session);
+ sessions[name] = s;
+ wakeupDriver();
+ while (pn_session_state(s->session) & PN_REMOTE_UNINIT) {
+ wait();
+ }
+ return s;
+ } else {
+ throw qpid::messaging::KeyError(std::string("Session already exists: ") + name);
+ }
+
+}
+boost::shared_ptr<SessionContext> ConnectionContext::getSession(const std::string& name) const
+{
+ SessionMap::const_iterator i = sessions.find(name);
+ if (i == sessions.end()) {
+ throw qpid::messaging::KeyError(std::string("No such session") + name);
+ } else {
+ return i->second;
+ }
+}
+
+void ConnectionContext::setOption(const std::string& name, const qpid::types::Variant& value)
+{
+ set(name, value);
+}
+
+std::string ConnectionContext::getAuthenticatedUsername()
+{
+ return sasl.get() ? sasl->getAuthenticatedUsername() : std::string();
+}
+
+std::size_t ConnectionContext::decode(const char* buffer, std::size_t size)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ QPID_LOG(trace, id << " decode(" << size << ")");
+ if (readHeader) {
+ size_t decoded = readProtocolHeader(buffer, size);
+ if (decoded < size) {
+ decoded += decode(buffer + decoded, size - decoded);
+ }
+ return decoded;
+ }
+
+ //TODO: Fix pn_engine_input() to take const buffer
+ ssize_t n = pn_transport_input(engine, const_cast<char*>(buffer), size);
+ if (n > 0 || n == PN_EOS) {
+ //If engine returns EOS, have no way of knowing how many bytes
+ //it processed, but can assume none need to be reprocessed so
+ //consider them all read:
+ if (n == PN_EOS) n = size;
+ QPID_LOG_CAT(debug, network, id << " decoded " << n << " bytes from " << size)
+ pn_transport_tick(engine, 0);
+ lock.notifyAll();
+ return n;
+ } else if (n == PN_ERR) {
+ throw qpid::Exception(QPID_MSG("Error on input: " << getError()));
+ } else {
+ return 0;
+ }
+
+}
+std::size_t ConnectionContext::encode(char* buffer, std::size_t size)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ QPID_LOG(trace, id << " encode(" << size << ")");
+ if (writeHeader) {
+ size_t encoded = writeProtocolHeader(buffer, size);
+ if (encoded < size) {
+ encoded += encode(buffer + encoded, size - encoded);
+ }
+ return encoded;
+ }
+
+ ssize_t n = pn_transport_output(engine, buffer, size);
+ if (n > 0) {
+ QPID_LOG_CAT(debug, network, id << " encoded " << n << " bytes from " << size)
+ haveOutput = true;
+ return n;
+ } else if (n == PN_ERR) {
+ throw qpid::Exception(QPID_MSG("Error on output: " << getError()));
+ } else if (n == PN_EOS) {
+ haveOutput = false;
+ return 0;//Is this right?
+ } else {
+ haveOutput = false;
+ return 0;
+ }
+}
+bool ConnectionContext::canEncode()
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ return haveOutput && state == CONNECTED;
+}
+void ConnectionContext::closed()
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ state = DISCONNECTED;
+ lock.notifyAll();
+}
+void ConnectionContext::opened()
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock);
+ state = CONNECTED;
+ lock.notifyAll();
+}
+bool ConnectionContext::isClosed() const
+{
+ return !isOpen();
+}
+namespace {
+qpid::framing::ProtocolVersion AMQP_1_0_PLAIN(1,0,qpid::framing::ProtocolVersion::AMQP);
+}
+
+std::string ConnectionContext::getError()
+{
+ std::stringstream text;
+ pn_error_t* cerror = pn_connection_error(connection);
+ if (cerror) text << "connection error " << pn_error_text(cerror);
+ pn_error_t* terror = pn_transport_error(engine);
+ if (terror) text << "transport error " << pn_error_text(terror);
+ return text.str();
+}
+
+framing::ProtocolVersion ConnectionContext::getVersion() const
+{
+ return AMQP_1_0_PLAIN;
+}
+
+std::size_t ConnectionContext::readProtocolHeader(const char* buffer, std::size_t size)
+{
+ framing::ProtocolInitiation pi(getVersion());
+ if (size >= pi.encodedSize()) {
+ readHeader = false;
+ qpid::framing::Buffer out(const_cast<char*>(buffer), size);
+ pi.decode(out);
+ QPID_LOG_CAT(debug, protocol, id << " read protocol header: " << pi);
+ return pi.encodedSize();
+ } else {
+ return 0;
+ }
+}
+std::size_t ConnectionContext::writeProtocolHeader(char* buffer, std::size_t size)
+{
+ framing::ProtocolInitiation pi(getVersion());
+ if (size >= pi.encodedSize()) {
+ QPID_LOG_CAT(debug, protocol, id << " writing protocol header: " << pi);
+ writeHeader = false;
+ qpid::framing::Buffer out(buffer, size);
+ pi.encode(out);
+ return pi.encodedSize();
+ } else {
+ QPID_LOG_CAT(debug, protocol, id << " insufficient buffer for protocol header: " << size)
+ return 0;
+ }
+}
+bool ConnectionContext::useSasl()
+{
+ return !(mechanism == "none" || mechanism == "NONE" || mechanism == "None");
+}
+
+qpid::sys::Codec& ConnectionContext::getCodec()
+{
+ return codecSwitch;
+}
+
+ConnectionContext::CodecSwitch::CodecSwitch(ConnectionContext& p) : parent(p) {}
+std::size_t ConnectionContext::CodecSwitch::decode(const char* buffer, std::size_t size)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(parent.lock);
+ size_t decoded = 0;
+ if (parent.sasl.get() && !parent.sasl->authenticated()) {
+ decoded = parent.sasl->decode(buffer, size);
+ if (!parent.sasl->authenticated()) return decoded;
+ }
+ if (decoded < size) {
+ if (parent.sasl.get() && parent.sasl->getSecurityLayer()) decoded += parent.sasl->getSecurityLayer()->decode(buffer+decoded, size-decoded);
+ else decoded += parent.decode(buffer+decoded, size-decoded);
+ }
+ return decoded;
+}
+std::size_t ConnectionContext::CodecSwitch::encode(char* buffer, std::size_t size)
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(parent.lock);
+ size_t encoded = 0;
+ if (parent.sasl.get() && parent.sasl->canEncode()) {
+ encoded += parent.sasl->encode(buffer, size);
+ if (!parent.sasl->authenticated()) return encoded;
+ }
+ if (encoded < size) {
+ if (parent.sasl.get() && parent.sasl->getSecurityLayer()) encoded += parent.sasl->getSecurityLayer()->encode(buffer+encoded, size-encoded);
+ else encoded += parent.encode(buffer+encoded, size-encoded);
+ }
+ return encoded;
+}
+bool ConnectionContext::CodecSwitch::canEncode()
+{
+ qpid::sys::ScopedLock<qpid::sys::Monitor> l(parent.lock);
+ if (parent.sasl.get()) {
+ if (parent.sasl->canEncode()) return true;
+ else if (!parent.sasl->authenticated()) return false;
+ else if (parent.sasl->getSecurityLayer()) return parent.sasl->getSecurityLayer()->canEncode();
+ }
+ return parent.canEncode();
+}
+
+
+}}} // namespace qpid::messaging::amqp
diff --git a/cpp/src/qpid/messaging/amqp/ConnectionContext.h b/cpp/src/qpid/messaging/amqp/ConnectionContext.h
new file mode 100644
index 0000000000..3718184365
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/ConnectionContext.h
@@ -0,0 +1,150 @@
+#ifndef QPID_MESSAGING_AMQP_CONNECTIONCONTEXT_H
+#define QPID_MESSAGING_AMQP_CONNECTIONCONTEXT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <deque>
+#include <map>
+#include <memory>
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include "qpid/Url.h"
+#include "qpid/messaging/ConnectionOptions.h"
+#include "qpid/sys/AtomicValue.h"
+#include "qpid/sys/ConnectionCodec.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/types/Variant.h"
+#include "qpid/messaging/amqp/TransportContext.h"
+
+struct pn_connection_t;
+struct pn_link_t;
+struct pn_session_t;
+struct pn_transport_t;
+
+
+namespace qpid {
+namespace framing {
+class ProtocolVersion;
+}
+namespace messaging {
+class Duration;
+class Message;
+namespace amqp {
+
+class DriverImpl;
+class ReceiverContext;
+class Sasl;
+class SessionContext;
+class SenderContext;
+class Transport;
+
+/**
+ *
+ */
+class ConnectionContext : public qpid::sys::ConnectionCodec, public qpid::messaging::ConnectionOptions, public TransportContext
+{
+ public:
+ ConnectionContext(const std::string& url, const qpid::types::Variant::Map& options);
+ ~ConnectionContext();
+ void open();
+ bool isOpen() const;
+ void close();
+ boost::shared_ptr<SessionContext> newSession(bool transactional, const std::string& name);
+ boost::shared_ptr<SessionContext> getSession(const std::string& name) const;
+ void endSession(boost::shared_ptr<SessionContext>);
+ void attach(boost::shared_ptr<SessionContext>, boost::shared_ptr<SenderContext>);
+ void attach(boost::shared_ptr<SessionContext>, boost::shared_ptr<ReceiverContext>);
+ void send(boost::shared_ptr<SenderContext> ctxt, const qpid::messaging::Message& message, bool sync);
+ bool fetch(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<ReceiverContext> lnk, qpid::messaging::Message& message, qpid::messaging::Duration timeout);
+ bool get(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<ReceiverContext> lnk, qpid::messaging::Message& message, qpid::messaging::Duration timeout);
+ void acknowledge(boost::shared_ptr<SessionContext> ssn, qpid::messaging::Message* message, bool cumulative);
+
+ void setOption(const std::string& name, const qpid::types::Variant& value);
+ std::string getAuthenticatedUsername();
+
+ void setCapacity(boost::shared_ptr<SenderContext>, uint32_t);
+ uint32_t getCapacity(boost::shared_ptr<SenderContext>);
+ uint32_t getUnsettled(boost::shared_ptr<SenderContext>);
+
+ void setCapacity(boost::shared_ptr<ReceiverContext>, uint32_t);
+ uint32_t getCapacity(boost::shared_ptr<ReceiverContext>);
+ uint32_t getAvailable(boost::shared_ptr<ReceiverContext>);
+ uint32_t getUnsettled(boost::shared_ptr<ReceiverContext>);
+
+
+ void activateOutput();
+ qpid::sys::Codec& getCodec();
+ //ConnectionCodec interface:
+ std::size_t decode(const char* buffer, std::size_t size);
+ std::size_t encode(char* buffer, std::size_t size);
+ bool canEncode();
+ void closed();
+ bool isClosed() const;
+ framing::ProtocolVersion getVersion() const;
+ //additionally, Transport needs:
+ void opened();//signal successful connection
+
+ private:
+ typedef std::map<std::string, boost::shared_ptr<SessionContext> > SessionMap;
+ qpid::Url url;
+
+ boost::shared_ptr<DriverImpl> driver;
+ boost::shared_ptr<Transport> transport;
+
+ pn_transport_t* engine;
+ pn_connection_t* connection;
+ SessionMap sessions;
+ mutable qpid::sys::Monitor lock;
+ bool writeHeader;
+ bool readHeader;
+ bool haveOutput;
+ std::string id;
+ enum {
+ DISCONNECTED,
+ CONNECTING,
+ CONNECTED
+ } state;
+ std::auto_ptr<Sasl> sasl;
+ class CodecSwitch : public qpid::sys::Codec
+ {
+ public:
+ CodecSwitch(ConnectionContext&);
+ std::size_t decode(const char* buffer, std::size_t size);
+ std::size_t encode(char* buffer, std::size_t size);
+ bool canEncode();
+ private:
+ ConnectionContext& parent;
+ };
+ CodecSwitch codecSwitch;
+
+ void wait();
+ void wakeupDriver();
+ void attach(pn_session_t*, pn_link_t*, int credit=0);
+
+ std::size_t readProtocolHeader(const char* buffer, std::size_t size);
+ std::size_t writeProtocolHeader(char* buffer, std::size_t size);
+ std::string getError();
+ bool useSasl();
+};
+
+}}} // namespace qpid::messaging::amqp
+
+#endif /*!QPID_MESSAGING_AMQP_CONNECTIONCONTEXT_H*/
diff --git a/cpp/src/qpid/messaging/amqp/ConnectionHandle.cpp b/cpp/src/qpid/messaging/amqp/ConnectionHandle.cpp
new file mode 100644
index 0000000000..0c4ec2bfcb
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/ConnectionHandle.cpp
@@ -0,0 +1,84 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "ConnectionHandle.h"
+#include "ConnectionContext.h"
+#include "SessionHandle.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/messaging/ProtocolRegistry.h"
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+// Static constructor which registers this implementation in the ProtocolRegistry
+namespace {
+ConnectionImpl* create(const std::string& u, const qpid::types::Variant::Map& o)
+{
+ return new ConnectionHandle(u, o);
+}
+
+struct StaticInit
+{
+ StaticInit()
+ {
+ ProtocolRegistry::add("amqp1.0", &create);
+ };
+} init;
+}
+
+ConnectionHandle::ConnectionHandle(const std::string& url, const qpid::types::Variant::Map& options) : connection(new ConnectionContext(url, options)) {}
+ConnectionHandle::ConnectionHandle(boost::shared_ptr<ConnectionContext> c) : connection(c) {}
+
+void ConnectionHandle::open()
+{
+ connection->open();
+}
+
+bool ConnectionHandle::isOpen() const
+{
+ return connection->isOpen();
+}
+
+void ConnectionHandle::close()
+{
+ connection->close();
+}
+
+Session ConnectionHandle::newSession(bool transactional, const std::string& name)
+{
+ return qpid::messaging::Session(new SessionHandle(connection, connection->newSession(transactional, name)));
+}
+
+Session ConnectionHandle::getSession(const std::string& name) const
+{
+ return qpid::messaging::Session(new SessionHandle(connection, connection->getSession(name)));
+}
+
+void ConnectionHandle::setOption(const std::string& name, const qpid::types::Variant& value)
+{
+ connection->setOption(name, value);
+}
+
+std::string ConnectionHandle::getAuthenticatedUsername()
+{
+ return connection->getAuthenticatedUsername();
+}
+
+}}} // namespace qpid::messaging::amqp
diff --git a/cpp/src/qpid/messaging/amqp/ConnectionHandle.h b/cpp/src/qpid/messaging/amqp/ConnectionHandle.h
new file mode 100644
index 0000000000..d1eb27f6de
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/ConnectionHandle.h
@@ -0,0 +1,58 @@
+#ifndef QPID_MESSAGING_AMQP_CONNECTIONHANDLE_H
+#define QPID_MESSAGING_AMQP_CONNECTIONHANDLE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <boost/shared_ptr.hpp>
+#include "qpid/messaging/ConnectionImpl.h"
+#include "qpid/types/Variant.h"
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+
+class ConnectionContext;
+/**
+ * Handles are directly referenced by applications; Contexts are
+ * referenced by Handles. This allows a graph structure that
+ * remains intact as long as the application references any part
+ * of it, but that can be automatically reclaimed if the whole
+ * graph becomes unreferenced.
+ */
+class ConnectionHandle : public qpid::messaging::ConnectionImpl
+{
+ public:
+ ConnectionHandle(const std::string& url, const qpid::types::Variant::Map& options);
+ ConnectionHandle(boost::shared_ptr<ConnectionContext>);
+ void open();
+ bool isOpen() const;
+ void close();
+ Session newSession(bool transactional, const std::string& name);
+ Session getSession(const std::string& name) const;
+ void setOption(const std::string& name, const qpid::types::Variant& value);
+ std::string getAuthenticatedUsername();
+ private:
+ boost::shared_ptr<ConnectionContext> connection;
+};
+
+}}} // namespace qpid::messaging::amqp_1.0
+
+#endif /*!QPID_MESSAGING_AMQP_CONNECTIONHANDLE_H*/
diff --git a/cpp/src/qpid/messaging/amqp/DriverImpl.cpp b/cpp/src/qpid/messaging/amqp/DriverImpl.cpp
new file mode 100644
index 0000000000..16307b3c22
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/DriverImpl.cpp
@@ -0,0 +1,74 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "DriverImpl.h"
+#include "Transport.h"
+#include "qpid/messaging/exceptions.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+
+DriverImpl::DriverImpl() : poller(new qpid::sys::Poller)
+{
+ start();
+}
+DriverImpl::~DriverImpl()
+{
+ stop();
+}
+
+void DriverImpl::start()
+{
+ thread = qpid::sys::Thread(*poller);
+ QPID_LOG(debug, "Driver started");
+}
+
+void DriverImpl::stop()
+{
+ QPID_LOG(debug, "Driver stopped");
+ poller->shutdown();
+ thread.join();
+}
+
+boost::shared_ptr<Transport> DriverImpl::getTransport(const std::string& protocol, TransportContext& connection)
+{
+ boost::shared_ptr<Transport> t(Transport::create(protocol, connection, poller));
+ if (!t) throw qpid::messaging::ConnectionError("No such transport: " + protocol);
+ return t;
+}
+
+
+qpid::sys::Mutex DriverImpl::defaultLock;
+boost::weak_ptr<DriverImpl> DriverImpl::theDefault;
+boost::shared_ptr<DriverImpl> DriverImpl::getDefault()
+{
+ qpid::sys::Mutex::ScopedLock l(defaultLock);
+ boost::shared_ptr<DriverImpl> p = theDefault.lock();
+ if (!p) {
+ p = boost::shared_ptr<DriverImpl>(new DriverImpl);
+ theDefault = p;
+ }
+ return p;
+}
+
+}}} // namespace qpid::messaging::amqp
diff --git a/cpp/src/qpid/sys/ClusterSafe.cpp b/cpp/src/qpid/messaging/amqp/DriverImpl.h
index dd37615145..354fa1ae35 100644
--- a/cpp/src/qpid/sys/ClusterSafe.cpp
+++ b/cpp/src/qpid/messaging/amqp/DriverImpl.h
@@ -1,3 +1,6 @@
+#ifndef QPID_MESSAGING_AMQP_DRIVERIMPL_H
+#define QPID_MESSAGING_AMQP_DRIVERIMPL_H
+
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -18,49 +21,40 @@
* under the License.
*
*/
-
-#include "ClusterSafe.h"
-#include "qpid/log/Statement.h"
+#include "qpid/sys/Mutex.h"
#include "qpid/sys/Thread.h"
-#include <stdlib.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
namespace qpid {
namespace sys {
-
-namespace {
-bool inCluster = false;
-QPID_TSS bool inContext = false;
-}
-
-bool isClusterSafe() { return !inCluster || inContext; }
-
-void assertClusterSafe() {
- if (!isClusterSafe()) {
- QPID_LOG(critical, "Modified cluster state outside of cluster context");
- ::abort();
- }
-}
-
-ClusterSafeScope::ClusterSafeScope() {
- save = inContext;
- inContext = true;
-}
-
-ClusterSafeScope::~ClusterSafeScope() {
- assert(inContext);
- inContext = save;
-}
-
-ClusterUnsafeScope::ClusterUnsafeScope() {
- save = inContext;
- inContext = false;
-}
-
-ClusterUnsafeScope::~ClusterUnsafeScope() {
- assert(!inContext);
- inContext = save;
+class Poller;
}
-
-void enableClusterSafe() { inCluster = true; }
-
-}} // namespace qpid::sys
+namespace messaging {
+namespace amqp {
+class TransportContext;
+class Transport;
+/**
+ *
+ */
+class DriverImpl
+{
+ public:
+ DriverImpl();
+ ~DriverImpl();
+
+ void start();
+ void stop();
+
+ boost::shared_ptr<Transport> getTransport(const std::string& protocol, TransportContext& connection);
+
+ static boost::shared_ptr<DriverImpl> getDefault();
+ private:
+ boost::shared_ptr<qpid::sys::Poller> poller;
+ qpid::sys::Thread thread;
+ static qpid::sys::Mutex defaultLock;
+ static boost::weak_ptr<DriverImpl> theDefault;
+};
+}}} // namespace qpid::messaging::amqp
+
+#endif /*!QPID_MESSAGING_AMQP_DRIVERIMPL_H*/
diff --git a/cpp/src/qpid/messaging/amqp/EncodedMessage.cpp b/cpp/src/qpid/messaging/amqp/EncodedMessage.cpp
new file mode 100644
index 0000000000..54de3eae45
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/EncodedMessage.cpp
@@ -0,0 +1,263 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/amqp/EncodedMessage.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/MessageImpl.h"
+#include "qpid/amqp/Decoder.h"
+#include <boost/lexical_cast.hpp>
+#include <string.h>
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+
+using namespace qpid::amqp;
+
+EncodedMessage::EncodedMessage(size_t s) : size(s), data(size ? new char[size] : 0)
+{
+ init();
+}
+
+EncodedMessage::EncodedMessage() : size(0), data(0)
+{
+ init();
+}
+
+EncodedMessage::EncodedMessage(const EncodedMessage& other) : size(other.size), data(size ? new char[size] : 0)
+{
+ init();
+}
+
+void EncodedMessage::init()
+{
+ //init all CharSequence members
+ deliveryAnnotations.init();
+ messageAnnotations.init();
+ userId.init();
+ to.init();
+ subject.init();
+ replyTo.init();
+ contentType.init();
+ contentEncoding.init();
+ groupId.init();
+ replyToGroupId.init();
+ applicationProperties.init();
+ body.init();
+ footer.init();
+}
+
+EncodedMessage::~EncodedMessage()
+{
+ delete[] data;
+}
+
+size_t EncodedMessage::getSize() const
+{
+ return size;
+}
+void EncodedMessage::trim(size_t t)
+{
+ size = t;
+}
+void EncodedMessage::resize(size_t s)
+{
+ delete[] data;
+ size = s;
+ data = new char[size];
+}
+
+char* EncodedMessage::getData()
+{
+ return data;
+}
+const char* EncodedMessage::getData() const
+{
+ return data;
+}
+
+void EncodedMessage::init(qpid::messaging::MessageImpl& impl)
+{
+ //initial scan of raw data
+ qpid::amqp::Decoder decoder(data, size);
+ InitialScan reader(*this, impl);
+ decoder.read(reader);
+ bareMessage = reader.getBareMessage();
+ if (bareMessage.data && !bareMessage.size) {
+ bareMessage.size = (data + size) - bareMessage.data;
+ }
+
+}
+void EncodedMessage::populate(qpid::types::Variant::Map& map) const
+{
+ //decode application properties
+ if (applicationProperties) {
+ qpid::amqp::Decoder decoder(applicationProperties.data, applicationProperties.size);
+ decoder.readMap(map);
+ }
+ //add in 'x-amqp-' prefixed values
+ if (!!firstAcquirer) {
+ map["x-amqp-first-acquirer"] = firstAcquirer.get();
+ }
+ if (!!deliveryCount) {
+ map["x-amqp-delivery-count"] = deliveryCount.get();
+ }
+ if (to) {
+ map["x-amqp-delivery-count"] = to.str();
+ }
+ if (!!absoluteExpiryTime) {
+ map["x-amqp-absolute-expiry-time"] = absoluteExpiryTime.get();
+ }
+ if (!!creationTime) {
+ map["x-amqp-creation-time"] = creationTime.get();
+ }
+ if (groupId) {
+ map["x-amqp-group-id"] = groupId.str();
+ }
+ if (!!groupSequence) {
+ map["x-amqp-qroup-sequence"] = groupSequence.get();
+ }
+ if (replyToGroupId) {
+ map["x-amqp-reply-to-group-id"] = replyToGroupId.str();
+ }
+ //add in any annotations
+ if (deliveryAnnotations) {
+ qpid::types::Variant::Map& annotations = map["x-amqp-delivery-annotations"].asMap();
+ qpid::amqp::Decoder decoder(deliveryAnnotations.data, deliveryAnnotations.size);
+ decoder.readMap(annotations);
+ }
+ if (messageAnnotations) {
+ qpid::types::Variant::Map& annotations = map["x-amqp-message-annotations"].asMap();
+ qpid::amqp::Decoder decoder(messageAnnotations.data, messageAnnotations.size);
+ decoder.readMap(annotations);
+ }
+}
+qpid::amqp::CharSequence EncodedMessage::getBareMessage() const
+{
+ return bareMessage;
+}
+
+void EncodedMessage::getReplyTo(qpid::messaging::Address& a) const
+{
+ a = qpid::messaging::Address(replyTo.str());
+}
+void EncodedMessage::getSubject(std::string& s) const
+{
+ s.assign(subject.data, subject.size);
+}
+void EncodedMessage::getContentType(std::string& s) const
+{
+ s.assign(contentType.data, contentType.size);
+}
+void EncodedMessage::getUserId(std::string& s) const
+{
+ s.assign(userId.data, userId.size);
+}
+void EncodedMessage::getMessageId(std::string& s) const
+{
+ messageId.assign(s);
+}
+void EncodedMessage::getCorrelationId(std::string& s) const
+{
+ correlationId.assign(s);
+}
+void EncodedMessage::getBody(std::string& s) const
+{
+ s.assign(body.data, body.size);
+}
+
+qpid::amqp::CharSequence EncodedMessage::getBody() const
+{
+ return body;
+}
+
+bool EncodedMessage::hasHeaderChanged(const qpid::messaging::MessageImpl& msg) const
+{
+ if (!durable) {
+ if (msg.isDurable()) return true;
+ } else {
+ if (durable.get() != msg.isDurable()) return true;
+ }
+
+ if (!priority) {
+ if (msg.getPriority() != 4) return true;
+ } else {
+ if (priority.get() != msg.getPriority()) return true;
+ }
+
+ if (msg.getTtl() && (!ttl || msg.getTtl() != ttl.get())) {
+ return true;
+ }
+
+ //first-acquirer can't be changed via Message interface as yet
+
+ if (msg.isRedelivered() && (!deliveryCount || deliveryCount.get() == 0)) {
+ return true;
+ }
+
+ return false;
+}
+
+
+EncodedMessage::InitialScan::InitialScan(EncodedMessage& e, qpid::messaging::MessageImpl& m) : em(e), mi(m)
+{
+ //set up defaults as needed:
+ mi.setPriority(4);
+}
+//header:
+void EncodedMessage::InitialScan::onDurable(bool b) { mi.setDurable(b); em.durable = b; }
+void EncodedMessage::InitialScan::onPriority(uint8_t i) { mi.setPriority(i); em.priority = i; }
+void EncodedMessage::InitialScan::onTtl(uint32_t i) { mi.setTtl(i); em.ttl = i; }
+void EncodedMessage::InitialScan::onFirstAcquirer(bool b) { em.firstAcquirer = b; }
+void EncodedMessage::InitialScan::onDeliveryCount(uint32_t i)
+{
+ mi.setRedelivered(i);
+ em.deliveryCount = i;
+}
+
+//properties:
+void EncodedMessage::InitialScan::onMessageId(uint64_t v) { em.messageId.set(v); }
+void EncodedMessage::InitialScan::onMessageId(const qpid::amqp::CharSequence& v, qpid::types::VariantType t) { em.messageId.set(v, t); }
+void EncodedMessage::InitialScan::onUserId(const qpid::amqp::CharSequence& v) { em.userId = v; }
+void EncodedMessage::InitialScan::onTo(const qpid::amqp::CharSequence& v) { em.to = v; }
+void EncodedMessage::InitialScan::onSubject(const qpid::amqp::CharSequence& v) { em.subject = v; }
+void EncodedMessage::InitialScan::onReplyTo(const qpid::amqp::CharSequence& v) { em.replyTo = v;}
+void EncodedMessage::InitialScan::onCorrelationId(uint64_t v) { em.correlationId.set(v); }
+void EncodedMessage::InitialScan::onCorrelationId(const qpid::amqp::CharSequence& v, qpid::types::VariantType t) { em.correlationId.set(v, t); }
+void EncodedMessage::InitialScan::onContentType(const qpid::amqp::CharSequence& v) { em.contentType = v; }
+void EncodedMessage::InitialScan::onContentEncoding(const qpid::amqp::CharSequence& v) { em.contentEncoding = v; }
+void EncodedMessage::InitialScan::onAbsoluteExpiryTime(int64_t i) { em.absoluteExpiryTime = i; }
+void EncodedMessage::InitialScan::onCreationTime(int64_t i) { em.creationTime = i; }
+void EncodedMessage::InitialScan::onGroupId(const qpid::amqp::CharSequence& v) { em.groupId = v; }
+void EncodedMessage::InitialScan::onGroupSequence(uint32_t i) { em.groupSequence = i; }
+void EncodedMessage::InitialScan::onReplyToGroupId(const qpid::amqp::CharSequence& v) { em.replyToGroupId = v; }
+
+void EncodedMessage::InitialScan::onApplicationProperties(const qpid::amqp::CharSequence& v) { em.applicationProperties = v; }
+void EncodedMessage::InitialScan::onDeliveryAnnotations(const qpid::amqp::CharSequence& v) { em.deliveryAnnotations = v; }
+void EncodedMessage::InitialScan::onMessageAnnotations(const qpid::amqp::CharSequence& v) { em.messageAnnotations = v; }
+void EncodedMessage::InitialScan::onBody(const qpid::amqp::CharSequence& v, const qpid::amqp::Descriptor&)
+{
+ //TODO: how to communicate the type, i.e. descriptor?
+ em.body = v;
+}
+void EncodedMessage::InitialScan::onBody(const qpid::types::Variant&, const qpid::amqp::Descriptor&) {}
+void EncodedMessage::InitialScan::onFooter(const qpid::amqp::CharSequence& v) { em.footer = v; }
+
+}}} // namespace qpid::messaging::amqp
diff --git a/cpp/src/qpid/messaging/amqp/EncodedMessage.h b/cpp/src/qpid/messaging/amqp/EncodedMessage.h
new file mode 100644
index 0000000000..09a9d948d5
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/EncodedMessage.h
@@ -0,0 +1,177 @@
+#ifndef QPID_MESSAGING_AMQP_ENCODEDMESSAGE_H
+#define QPID_MESSAGING_AMQP_ENCODEDMESSAGE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/CharSequence.h"
+#include "qpid/amqp/MessageId.h"
+#include "qpid/amqp/MessageReader.h"
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/types/Variant.h"
+#include <boost/optional.hpp>
+
+namespace qpid {
+namespace amqp {
+struct Descriptor;
+}
+namespace messaging {
+class Address;
+class MessageImpl;
+namespace amqp {
+
+/**
+ * Used to 'lazy-decode' an AMQP 1.0 message.
+ *
+ * There are four categories of data item:
+ *
+ * (i) simple, fixed width primitives - priority, ttl, durability,
+ * delivery count - for which lazy-decoding doesn't buy much. These
+ * are decoded unconditionally on an initial scan of the message.
+ *
+ * (ii) standard variable length string properties - subject,
+ * message-id, user-id etc - which require conversion to a std::string
+ * for returning to the application. By delaying the conversion of
+ * these to a std::string we can avoid allocation & copying until it
+ * is actually required. The initial scan of the message merely
+ * records the position of these strings within the raw message data.
+ *
+ * (iii) custom, application defined headers. These form a map, and
+ * again, delaying the creation of that map until it is actually
+ * required can be advantageous. The initial scan of the message merely
+ * records the position of this section within the raw message data.
+ *
+ * (iv) the body content. This may be retreived as a std::string, or
+ * as a char*. Avoiding conversion to the string until it is required
+ * is advantageous. The initial scan of the message merely records the
+ * position of this section within the raw message data.
+ *
+ * At present the Message class only explicitly exposes some of the
+ * standard property and headers defined by AMQP 1.0. The remainder
+ * will have to be accessed through the message 'headers' map, using
+ * the 'x-amqp-' prefix.
+ */
+class EncodedMessage
+{
+ public:
+ EncodedMessage();
+ EncodedMessage(size_t);
+ EncodedMessage(const EncodedMessage&);
+ ~EncodedMessage();
+
+
+ size_t getSize() const;
+ char* getData();
+ const char* getData() const;
+ void trim(size_t);
+ void resize(size_t);
+
+ void getReplyTo(qpid::messaging::Address&) const;
+ void getSubject(std::string&) const;
+ void getContentType(std::string&) const;
+ void getMessageId(std::string&) const;
+ void getUserId(std::string&) const;
+ void getCorrelationId(std::string&) const;
+
+ void init(qpid::messaging::MessageImpl&);
+ void populate(qpid::types::Variant::Map&) const;
+ void getBody(std::string&) const;
+ qpid::amqp::CharSequence getBareMessage() const;
+ qpid::amqp::CharSequence getBody() const;
+ bool hasHeaderChanged(const qpid::messaging::MessageImpl&) const;
+ private:
+ size_t size;
+ char* data;
+
+ class InitialScan : public qpid::amqp::MessageReader
+ {
+ public:
+ InitialScan(EncodedMessage& e, qpid::messaging::MessageImpl& m);
+ //header:
+ void onDurable(bool b);
+ void onPriority(uint8_t i);
+ void onTtl(uint32_t i);
+ void onFirstAcquirer(bool b);
+ void onDeliveryCount(uint32_t i);
+ //properties:
+ void onMessageId(uint64_t);
+ void onMessageId(const qpid::amqp::CharSequence&, qpid::types::VariantType);
+ void onUserId(const qpid::amqp::CharSequence& v);
+ void onTo(const qpid::amqp::CharSequence& v);
+ void onSubject(const qpid::amqp::CharSequence& v);
+ void onReplyTo(const qpid::amqp::CharSequence& v);
+ void onCorrelationId(uint64_t);
+ void onCorrelationId(const qpid::amqp::CharSequence&, qpid::types::VariantType);
+ void onContentType(const qpid::amqp::CharSequence& v);
+ void onContentEncoding(const qpid::amqp::CharSequence& v);
+ void onAbsoluteExpiryTime(int64_t i);
+ void onCreationTime(int64_t);
+ void onGroupId(const qpid::amqp::CharSequence&);
+ void onGroupSequence(uint32_t);
+ void onReplyToGroupId(const qpid::amqp::CharSequence&);
+
+ void onApplicationProperties(const qpid::amqp::CharSequence&);
+ void onDeliveryAnnotations(const qpid::amqp::CharSequence&);
+ void onMessageAnnotations(const qpid::amqp::CharSequence&);
+ void onBody(const qpid::amqp::CharSequence&, const qpid::amqp::Descriptor&);
+ void onBody(const qpid::types::Variant&, const qpid::amqp::Descriptor&);
+ void onFooter(const qpid::amqp::CharSequence&);
+ private:
+ EncodedMessage& em;
+ qpid::messaging::MessageImpl& mi;
+ };
+ //header:
+ boost::optional<bool> durable;
+ boost::optional<uint8_t> priority;
+ boost::optional<uint32_t> ttl;
+ boost::optional<bool> firstAcquirer;
+ boost::optional<uint32_t> deliveryCount;
+ //annotations:
+ qpid::amqp::CharSequence deliveryAnnotations;
+ qpid::amqp::CharSequence messageAnnotations;
+
+ qpid::amqp::CharSequence bareMessage;//properties, application-properties and content
+ //properties:
+ qpid::amqp::MessageId messageId;
+ qpid::amqp::CharSequence userId;
+ qpid::amqp::CharSequence to;
+ qpid::amqp::CharSequence subject;
+ qpid::amqp::CharSequence replyTo;
+ qpid::amqp::MessageId correlationId;
+ qpid::amqp::CharSequence contentType;
+ qpid::amqp::CharSequence contentEncoding;
+ boost::optional<int64_t> absoluteExpiryTime;
+ boost::optional<int64_t> creationTime;
+ qpid::amqp::CharSequence groupId;
+ boost::optional<uint32_t> groupSequence;
+ qpid::amqp::CharSequence replyToGroupId;
+ //application-properties:
+ qpid::amqp::CharSequence applicationProperties;
+ qpid::amqp::CharSequence body;
+ //footer:
+ qpid::amqp::CharSequence footer;
+
+ void init();
+ //not implemented:
+ EncodedMessage& operator=(const EncodedMessage&);
+};
+}}} // namespace qpid::messaging::amqp
+
+#endif /*!QPID_MESSAGING_ENCODEDMESSAGE_H*/
diff --git a/cpp/src/qpid/messaging/amqp/ReceiverContext.cpp b/cpp/src/qpid/messaging/amqp/ReceiverContext.cpp
new file mode 100644
index 0000000000..414793c7fd
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/ReceiverContext.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/messaging/amqp/ReceiverContext.h"
+#include "qpid/messaging/amqp/AddressHelper.h"
+#include "qpid/messaging/Duration.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/amqp/descriptors.h"
+extern "C" {
+#include <proton/engine.h>
+}
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+//TODO: proper conversion to wide string for address
+ReceiverContext::ReceiverContext(pn_session_t* session, const std::string& n, const qpid::messaging::Address& a)
+ : name(n),
+ address(a),
+ receiver(pn_receiver(session, name.c_str())),
+ capacity(0) {}
+ReceiverContext::~ReceiverContext()
+{
+ pn_link_free(receiver);
+}
+
+void ReceiverContext::setCapacity(uint32_t c)
+{
+ if (c != capacity) {
+ //stop
+ capacity = c;
+ //reissue credit
+ }
+}
+
+uint32_t ReceiverContext::getCapacity()
+{
+ return capacity;
+}
+
+uint32_t ReceiverContext::getAvailable()
+{
+ uint32_t count(0);
+ for (pn_delivery_t* d = pn_unsettled_head(receiver); d; d = pn_unsettled_next(d)) {
+ ++count;
+ if (d == pn_link_current(receiver)) break;
+ }
+ return count;
+}
+
+uint32_t ReceiverContext::getUnsettled()
+{
+ uint32_t count(0);
+ for (pn_delivery_t* d = pn_unsettled_head(receiver); d; d = pn_unsettled_next(d)) {
+ ++count;
+ }
+ return count;
+}
+
+void ReceiverContext::close()
+{
+
+}
+
+const std::string& ReceiverContext::getName() const
+{
+ return name;
+}
+
+const std::string& ReceiverContext::getSource() const
+{
+ return address.getName();
+}
+namespace {
+pn_bytes_t convert(const std::string& s)
+{
+ pn_bytes_t result;
+ result.start = const_cast<char*>(s.data());
+ result.size = s.size();
+ return result;
+}
+bool hasWildcards(const std::string& key)
+{
+ return key.find('*') != std::string::npos || key.find('#') != std::string::npos;
+}
+
+uint64_t getFilterDescriptor(const std::string& key)
+{
+ return hasWildcards(key) ? qpid::amqp::filters::LEGACY_TOPIC_FILTER_CODE : qpid::amqp::filters::LEGACY_DIRECT_FILTER_CODE;
+}
+}
+
+void ReceiverContext::configure() const
+{
+ configure(pn_link_source(receiver));
+}
+void ReceiverContext::configure(pn_terminus_t* source) const
+{
+ pn_terminus_set_address(source, address.getName().c_str());
+ //dynamic create:
+ AddressHelper helper(address);
+ if (helper.createEnabled(AddressHelper::FOR_RECEIVER)) {
+ helper.setNodeProperties(source);
+ }
+
+ if (!address.getSubject().empty()) {
+ //filter:
+ pn_data_t* filter = pn_terminus_filter(source);
+ pn_data_put_map(filter);
+ pn_data_enter(filter);
+ pn_data_put_symbol(filter, convert("subject"));
+ //TODO: At present inserting described values into the map doesn't seem to work; correct this once resolved
+ //pn_data_put_described(filter);
+ //pn_data_enter(filter);
+ //pn_data_put_ulong(filter, getFilterDescriptor(address.getSubject()));
+ pn_data_put_string(filter, convert(address.getSubject()));
+ //pn_data_exit(filter);
+ pn_data_exit(filter);
+ }
+}
+
+bool ReceiverContext::isClosed() const
+{
+ return false;//TODO
+}
+
+
+
+}}} // namespace qpid::messaging::amqp
diff --git a/cpp/src/qpid/messaging/amqp/ReceiverContext.h b/cpp/src/qpid/messaging/amqp/ReceiverContext.h
new file mode 100644
index 0000000000..34ecdda6be
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/ReceiverContext.h
@@ -0,0 +1,68 @@
+#ifndef QPID_MESSAGING_AMQP_RECEIVERCONTEXT_H
+#define QPID_MESSAGING_AMQP_RECEIVERCONTEXT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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 <string>
+#include "qpid/sys/IntegerTypes.h"
+
+struct pn_link_t;
+struct pn_session_t;
+struct pn_terminus_t;
+
+namespace qpid {
+namespace messaging {
+
+class Duration;
+class Message;
+
+namespace amqp {
+
+/**
+ *
+ */
+class ReceiverContext
+{
+ public:
+ ReceiverContext(pn_session_t* session, const std::string& name, const qpid::messaging::Address& source);
+ ~ReceiverContext();
+ void setCapacity(uint32_t);
+ uint32_t getCapacity();
+ uint32_t getAvailable();
+ uint32_t getUnsettled();
+ void attach();
+ void close();
+ const std::string& getName() const;
+ const std::string& getSource() const;
+ bool isClosed() const;
+ void configure() const;
+ private:
+ friend class ConnectionContext;
+ const std::string name;
+ const Address address;
+ pn_link_t* receiver;
+ uint32_t capacity;
+ void configure(pn_terminus_t*) const;
+};
+}}} // namespace qpid::messaging::amqp
+
+#endif /*!QPID_MESSAGING_AMQP_RECEIVERCONTEXT_H*/
diff --git a/cpp/src/qpid/messaging/amqp/ReceiverHandle.cpp b/cpp/src/qpid/messaging/amqp/ReceiverHandle.cpp
new file mode 100644
index 0000000000..9bf64ebb8d
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/ReceiverHandle.cpp
@@ -0,0 +1,106 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "ReceiverHandle.h"
+#include "ConnectionContext.h"
+#include "SessionContext.h"
+#include "SessionHandle.h"
+#include "ReceiverContext.h"
+#include "qpid/messaging/Duration.h"
+#include "qpid/messaging/exceptions.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Session.h"
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+
+ReceiverHandle::ReceiverHandle(boost::shared_ptr<ConnectionContext> c,
+ boost::shared_ptr<SessionContext> s,
+ boost::shared_ptr<ReceiverContext> r
+) : connection(c), session(s), receiver(r) {}
+
+
+bool ReceiverHandle::get(qpid::messaging::Message& message, qpid::messaging::Duration timeout)
+{
+ return connection->get(session, receiver, message, timeout);
+}
+
+qpid::messaging::Message ReceiverHandle::get(qpid::messaging::Duration timeout)
+{
+ qpid::messaging::Message result;
+ if (!get(result, timeout)) throw qpid::messaging::NoMessageAvailable();
+ return result;
+}
+
+bool ReceiverHandle::fetch(qpid::messaging::Message& message, qpid::messaging::Duration timeout)
+{
+ return connection->fetch(session, receiver, message, timeout);
+}
+
+qpid::messaging::Message ReceiverHandle::fetch(qpid::messaging::Duration timeout)
+{
+ qpid::messaging::Message result;
+ if (!fetch(result, timeout)) throw qpid::messaging::NoMessageAvailable();
+ return result;
+}
+
+void ReceiverHandle::setCapacity(uint32_t capacity)
+{
+ connection->setCapacity(receiver, capacity);
+}
+
+uint32_t ReceiverHandle::getCapacity()
+{
+ return connection->getCapacity(receiver);
+}
+
+uint32_t ReceiverHandle::getAvailable()
+{
+ return connection->getAvailable(receiver);
+}
+
+uint32_t ReceiverHandle::getUnsettled()
+{
+ return connection->getUnsettled(receiver);
+}
+
+void ReceiverHandle::close()
+{
+ session->closeReceiver(getName());
+}
+
+const std::string& ReceiverHandle::getName() const
+{
+ return receiver->getName();
+}
+
+qpid::messaging::Session ReceiverHandle::getSession() const
+{
+ //create new SessionHandle instance; i.e. create new handle that shares the same context
+ return qpid::messaging::Session(new SessionHandle(connection, session));
+}
+
+bool ReceiverHandle::isClosed() const
+{
+ return receiver->isClosed();
+}
+
+}}} // namespace qpid::messaging::amqp
diff --git a/cpp/src/qpid/messaging/amqp/ReceiverHandle.h b/cpp/src/qpid/messaging/amqp/ReceiverHandle.h
new file mode 100644
index 0000000000..a1a6f26025
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/ReceiverHandle.h
@@ -0,0 +1,63 @@
+#ifndef QPID_MESSAGING_AMQP_RECEIVERHANDLE_H
+#define QPID_MESSAGING_AMQP_RECEIVERHANDLE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <boost/shared_ptr.hpp>
+#include "qpid/messaging/ReceiverImpl.h"
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+
+class ConnectionContext;
+class SessionContext;
+class ReceiverContext;
+/**
+ *
+ */
+class ReceiverHandle : public qpid::messaging::ReceiverImpl
+{
+ public:
+ ReceiverHandle(boost::shared_ptr<ConnectionContext>,
+ boost::shared_ptr<SessionContext>,
+ boost::shared_ptr<ReceiverContext>
+ );
+ bool get(Message& message, qpid::messaging::Duration timeout);
+ qpid::messaging::Message get(qpid::messaging::Duration timeout);
+ bool fetch(Message& message, qpid::messaging::Duration timeout);
+ qpid::messaging::Message fetch(qpid::messaging::Duration timeout);
+ void setCapacity(uint32_t);
+ uint32_t getCapacity();
+ uint32_t getAvailable();
+ uint32_t getUnsettled();
+ void close();
+ const std::string& getName() const;
+ qpid::messaging::Session getSession() const;
+ bool isClosed() const;
+ private:
+ boost::shared_ptr<ConnectionContext> connection;
+ boost::shared_ptr<SessionContext> session;
+ boost::shared_ptr<ReceiverContext> receiver;
+};
+}}} // namespace qpid::messaging::amqp
+
+#endif /*!QPID_MESSAGING_AMQP_RECEIVERHANDLE_H*/
diff --git a/cpp/src/qpid/messaging/amqp/Sasl.cpp b/cpp/src/qpid/messaging/amqp/Sasl.cpp
new file mode 100644
index 0000000000..a8bae1adda
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/Sasl.cpp
@@ -0,0 +1,157 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "ConnectionContext.h"
+#include "qpid/messaging/amqp/Sasl.h"
+#include "qpid/messaging/exceptions.h"
+#include "qpid/sys/SecurityLayer.h"
+#include "qpid/log/Statement.h"
+#include "qpid/Sasl.h"
+#include "qpid/SaslFactory.h"
+#include "qpid/StringUtils.h"
+#include <sstream>
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+
+Sasl::Sasl(const std::string& id, ConnectionContext& c, const std::string& hostname_)
+ : qpid::amqp::SaslClient(id), context(c),
+ sasl(qpid::SaslFactory::getInstance().create(c.username, c.password, c.service, hostname_, c.minSsf, c.maxSsf, false)),
+ hostname(hostname_), readHeader(true), writeHeader(true), haveOutput(false), state(NONE) {}
+
+std::size_t Sasl::decode(const char* buffer, std::size_t size)
+{
+ size_t decoded = 0;
+ if (readHeader) {
+ decoded += readProtocolHeader(buffer, size);
+ readHeader = !decoded;
+ }
+ if (state == NONE && decoded < size) {
+ decoded += read(buffer + decoded, size - decoded);
+ }
+ QPID_LOG(trace, id << " Sasl::decode(" << size << "): " << decoded);
+ return decoded;
+}
+
+std::size_t Sasl::encode(char* buffer, std::size_t size)
+{
+ size_t encoded = 0;
+ if (writeHeader) {
+ encoded += writeProtocolHeader(buffer, size);
+ writeHeader = !encoded;
+ }
+ if (encoded < size) {
+ encoded += write(buffer + encoded, size - encoded);
+ }
+ haveOutput = (encoded == size);
+ QPID_LOG(trace, id << " Sasl::encode(" << size << "): " << encoded);
+ return encoded;
+}
+
+bool Sasl::canEncode()
+{
+ QPID_LOG(trace, id << " Sasl::canEncode(): " << writeHeader << " || " << haveOutput);
+ return writeHeader || haveOutput;
+}
+
+void Sasl::mechanisms(const std::string& offered)
+{
+ QPID_LOG_CAT(debug, protocol, id << " Received SASL-MECHANISMS(" << offered << ")");
+ std::string response;
+
+ std::string mechanisms;
+ if (context.mechanism.size()) {
+ std::vector<std::string> allowed = split(context.mechanism, " ");
+ std::vector<std::string> supported = split(offered, " ");
+ std::stringstream intersection;
+ for (std::vector<std::string>::const_iterator i = allowed.begin(); i != allowed.end(); ++i) {
+ if (std::find(supported.begin(), supported.end(), *i) != supported.end()) {
+ intersection << *i << " ";
+ }
+ }
+ mechanisms = intersection.str();
+ } else {
+ mechanisms = offered;
+ }
+
+ if (sasl->start(mechanisms, response)) {
+ init(sasl->getMechanism(), &response, hostname.size() ? &hostname : 0);
+ } else {
+ init(sasl->getMechanism(), 0, hostname.size() ? &hostname : 0);
+ }
+ haveOutput = true;
+ context.activateOutput();
+}
+void Sasl::challenge(const std::string& challenge)
+{
+ QPID_LOG_CAT(debug, protocol, id << " Received SASL-CHALLENGE(" << challenge.size() << " bytes)");
+ std::string r = sasl->step(challenge);
+ response(&r);
+ haveOutput = true;
+ context.activateOutput();
+}
+namespace {
+const std::string EMPTY;
+}
+void Sasl::challenge()
+{
+ QPID_LOG_CAT(debug, protocol, id << " Received SASL-CHALLENGE(null)");
+ std::string r = sasl->step(EMPTY);
+ response(&r);
+}
+void Sasl::outcome(uint8_t result, const std::string& extra)
+{
+ QPID_LOG_CAT(debug, protocol, id << " Received SASL-OUTCOME(" << result << ", " << extra << ")");
+ outcome(result);
+}
+void Sasl::outcome(uint8_t result)
+{
+ QPID_LOG_CAT(debug, protocol, id << " Received SASL-OUTCOME(" << result << ")");
+ if (result) state = FAILED;
+ else state = SUCCEEDED;
+
+ securityLayer = sasl->getSecurityLayer(context.maxFrameSize);
+ if (securityLayer.get()) {
+ securityLayer->init(&context);
+ }
+ context.activateOutput();
+}
+
+qpid::sys::Codec* Sasl::getSecurityLayer()
+{
+ return securityLayer.get();
+}
+
+bool Sasl::authenticated()
+{
+ switch (state) {
+ case SUCCEEDED: return true;
+ case FAILED: throw qpid::messaging::UnauthorizedAccess("Failed to authenticate");
+ case NONE: default: return false;
+ }
+}
+
+std::string Sasl::getAuthenticatedUsername()
+{
+ return sasl->getUserId();
+}
+
+}}} // namespace qpid::messaging::amqp
diff --git a/cpp/src/qpid/messaging/amqp/Sasl.h b/cpp/src/qpid/messaging/amqp/Sasl.h
new file mode 100644
index 0000000000..6657779fdc
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/Sasl.h
@@ -0,0 +1,72 @@
+#ifndef QPID_MESSAGING_AMQP_SASL_H
+#define QPID_MESSAGING_AMQP_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 "qpid/sys/Codec.h"
+#include "qpid/amqp/SaslClient.h"
+#include <memory>
+
+namespace qpid {
+class Sasl;
+namespace sys {
+class SecurityLayer;
+}
+namespace messaging {
+class ConnectionOptions;
+namespace amqp {
+class ConnectionContext;
+
+/**
+ *
+ */
+class Sasl : public qpid::sys::Codec, qpid::amqp::SaslClient
+{
+ public:
+ Sasl(const std::string& id, ConnectionContext& context, const std::string& hostname);
+ std::size_t decode(const char* buffer, std::size_t size);
+ std::size_t encode(char* buffer, std::size_t size);
+ bool canEncode();
+
+ bool authenticated();
+ qpid::sys::Codec* getSecurityLayer();
+ std::string getAuthenticatedUsername();
+ private:
+ ConnectionContext& context;
+ std::auto_ptr<qpid::Sasl> sasl;
+ std::string hostname;
+ bool readHeader;
+ bool writeHeader;
+ bool haveOutput;
+ enum {
+ NONE, FAILED, SUCCEEDED
+ } state;
+ std::auto_ptr<qpid::sys::SecurityLayer> securityLayer;
+
+ void mechanisms(const std::string&);
+ void challenge(const std::string&);
+ void challenge(); //null != empty string
+ void outcome(uint8_t result, const std::string&);
+ void outcome(uint8_t result);
+};
+}}} // namespace qpid::messaging::amqp
+
+#endif /*!QPID_MESSAGING_AMQP_SASL_H*/
diff --git a/cpp/src/qpid/messaging/amqp/SenderContext.cpp b/cpp/src/qpid/messaging/amqp/SenderContext.cpp
new file mode 100644
index 0000000000..96c4437b89
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/SenderContext.cpp
@@ -0,0 +1,363 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/amqp/SenderContext.h"
+#include "qpid/messaging/amqp/EncodedMessage.h"
+#include "qpid/messaging/amqp/AddressHelper.h"
+#include "qpid/amqp/descriptors.h"
+#include "qpid/amqp/MessageEncoder.h"
+#include "qpid/messaging/exceptions.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/MessageImpl.h"
+#include "qpid/log/Statement.h"
+extern "C" {
+#include <proton/engine.h>
+}
+#include <boost/shared_ptr.hpp>
+#include <string.h>
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+//TODO: proper conversion to wide string for address
+SenderContext::SenderContext(pn_session_t* session, const std::string& n, const qpid::messaging::Address& a)
+ : name(n),
+ address(a),
+ sender(pn_sender(session, n.c_str())), capacity(1000) {}
+
+SenderContext::~SenderContext()
+{
+ pn_link_free(sender);
+}
+
+void SenderContext::close()
+{
+
+}
+
+void SenderContext::setCapacity(uint32_t c)
+{
+ if (c < deliveries.size()) throw qpid::messaging::SenderError("Desired capacity is less than unsettled message count!");
+ capacity = c;
+}
+
+uint32_t SenderContext::getCapacity()
+{
+ return capacity;
+}
+
+uint32_t SenderContext::getUnsettled()
+{
+ return processUnsettled();
+}
+
+const std::string& SenderContext::getName() const
+{
+ return name;
+}
+
+const std::string& SenderContext::getTarget() const
+{
+ return address.getName();
+}
+
+SenderContext::Delivery* SenderContext::send(const qpid::messaging::Message& message)
+{
+ if (processUnsettled() < capacity && pn_link_credit(sender)) {
+ deliveries.push_back(Delivery(nextId++));
+ Delivery& delivery = deliveries.back();
+ delivery.encode(MessageImplAccess::get(message), address);
+ delivery.send(sender);
+ return &delivery;
+ } else {
+ return 0;
+ }
+}
+
+uint32_t SenderContext::processUnsettled()
+{
+ //remove accepted messages from front of deque
+ while (!deliveries.empty() && deliveries.front().accepted()) {
+ deliveries.front().settle();
+ deliveries.pop_front();
+ }
+ return deliveries.size();
+}
+namespace {
+class HeaderAdapter : public qpid::amqp::MessageEncoder::Header
+{
+ public:
+ HeaderAdapter(const qpid::messaging::MessageImpl& impl) : msg(impl) {}
+ virtual bool isDurable() const
+ {
+ return msg.isDurable();
+ }
+ virtual uint8_t getPriority() const
+ {
+ return msg.getPriority();
+ }
+ virtual bool hasTtl() const
+ {
+ return msg.getTtl();
+ }
+ virtual uint32_t getTtl() const
+ {
+ return msg.getTtl();
+ }
+ virtual bool isFirstAcquirer() const
+ {
+ return false;
+ }
+ virtual uint32_t getDeliveryCount() const
+ {
+ return msg.isRedelivered() ? 1 : 0;
+ }
+ private:
+ const qpid::messaging::MessageImpl& msg;
+};
+const std::string EMPTY;
+
+class PropertiesAdapter : public qpid::amqp::MessageEncoder::Properties
+{
+ public:
+ PropertiesAdapter(const qpid::messaging::MessageImpl& impl, const std::string& s) : msg(impl), subject(s) {}
+ bool hasMessageId() const
+ {
+ return getMessageId().size();
+ }
+ std::string getMessageId() const
+ {
+ return msg.getMessageId();
+ }
+
+ bool hasUserId() const
+ {
+ return getUserId().size();
+ }
+
+ std::string getUserId() const
+ {
+ return msg.getUserId();
+ }
+
+ bool hasTo() const
+ {
+ return false;//not yet supported
+ }
+
+ std::string getTo() const
+ {
+ return EMPTY;//not yet supported
+ }
+
+ bool hasSubject() const
+ {
+ return subject.size() || getSubject().size();
+ }
+
+ std::string getSubject() const
+ {
+ return subject.size() ? subject : msg.getSubject();
+ }
+
+ bool hasReplyTo() const
+ {
+ return msg.getReplyTo();
+ }
+
+ std::string getReplyTo() const
+ {
+ return msg.getReplyTo().str();
+ }
+
+ bool hasCorrelationId() const
+ {
+ return getCorrelationId().size();
+ }
+
+ std::string getCorrelationId() const
+ {
+ return msg.getCorrelationId();
+ }
+
+ bool hasContentType() const
+ {
+ return getContentType().size();
+ }
+
+ std::string getContentType() const
+ {
+ return msg.getContentType();
+ }
+
+ bool hasContentEncoding() const
+ {
+ return false;//not yet supported
+ }
+
+ std::string getContentEncoding() const
+ {
+ return EMPTY;//not yet supported
+ }
+
+ bool hasAbsoluteExpiryTime() const
+ {
+ return false;//not yet supported
+ }
+
+ int64_t getAbsoluteExpiryTime() const
+ {
+ return 0;//not yet supported
+ }
+
+ bool hasCreationTime() const
+ {
+ return false;//not yet supported
+ }
+
+ int64_t getCreationTime() const
+ {
+ return 0;//not yet supported
+ }
+
+ bool hasGroupId() const
+ {
+ return false;//not yet supported
+ }
+
+ std::string getGroupId() const
+ {
+ return EMPTY;//not yet supported
+ }
+
+ bool hasGroupSequence() const
+ {
+ return false;//not yet supported
+ }
+
+ uint32_t getGroupSequence() const
+ {
+ return 0;//not yet supported
+ }
+
+ bool hasReplyToGroupId() const
+ {
+ return false;//not yet supported
+ }
+
+ std::string getReplyToGroupId() const
+ {
+ return EMPTY;//not yet supported
+ }
+ private:
+ const qpid::messaging::MessageImpl& msg;
+ const std::string subject;
+};
+
+bool changedSubject(const qpid::messaging::MessageImpl& msg, const qpid::messaging::Address& address)
+{
+ return address.getSubject().size() && address.getSubject() != msg.getSubject();
+}
+
+}
+
+SenderContext::Delivery::Delivery(int32_t i) : id(i), token(0) {}
+
+void SenderContext::Delivery::encode(const qpid::messaging::MessageImpl& msg, const qpid::messaging::Address& address)
+{
+ boost::shared_ptr<const EncodedMessage> original = msg.getEncoded();
+
+ if (original && !changedSubject(msg, address)) { //still have the content as received, send at least the bare message unaltered
+ //do we need to alter the header? are durable, priority, ttl, first-acquirer, delivery-count different from what was received?
+ if (original->hasHeaderChanged(msg)) {
+ //since as yet have no annotations, just write the revised header then the rest of the message as received
+ encoded.resize(16/*max header size*/ + original->getBareMessage().size);
+ qpid::amqp::MessageEncoder encoder(encoded.getData(), encoded.getSize());
+ HeaderAdapter header(msg);
+ encoder.writeHeader(header);
+ ::memcpy(encoded.getData() + encoder.getPosition(), original->getBareMessage().data, original->getBareMessage().size);
+ } else {
+ //since as yet have no annotations, if the header hasn't
+ //changed and we still have the original bare message, can
+ //send the entire content as is
+ encoded.resize(original->getSize());
+ ::memcpy(encoded.getData(), original->getData(), original->getSize());
+ }
+ } else {
+ HeaderAdapter header(msg);
+ PropertiesAdapter properties(msg, address.getSubject());
+ //compute size:
+ encoded.resize(qpid::amqp::MessageEncoder::getEncodedSize(header, properties, msg.getHeaders(), msg.getBytes()));
+ QPID_LOG(debug, "Sending message, buffer is " << encoded.getSize() << " bytes")
+ qpid::amqp::MessageEncoder encoder(encoded.getData(), encoded.getSize());
+ //write header:
+ encoder.writeHeader(header);
+ //write delivery-annotations, write message-annotations (none yet supported)
+ //write properties
+ encoder.writeProperties(properties);
+ //write application-properties
+ encoder.writeApplicationProperties(msg.getHeaders());
+ //write body
+ if (msg.getBytes().size()) encoder.writeBinary(msg.getBytes(), &qpid::amqp::message::DATA);//structured content not yet directly supported
+ if (encoder.getPosition() < encoded.getSize()) {
+ QPID_LOG(debug, "Trimming buffer from " << encoded.getSize() << " to " << encoder.getPosition());
+ encoded.trim(encoder.getPosition());
+ }
+ //write footer (no annotations yet supported)
+ }
+}
+void SenderContext::Delivery::send(pn_link_t* sender)
+{
+ pn_delivery_tag_t tag;
+ tag.size = sizeof(id);
+ tag.bytes = reinterpret_cast<const char*>(&id);
+ token = pn_delivery(sender, tag);
+ pn_link_send(sender, encoded.getData(), encoded.getSize());
+ pn_link_advance(sender);
+}
+
+bool SenderContext::Delivery::accepted()
+{
+ return pn_delivery_remote_state(token) == PN_ACCEPTED;
+}
+void SenderContext::Delivery::settle()
+{
+ pn_delivery_settle(token);
+}
+void SenderContext::configure() const
+{
+ configure(pn_link_target(sender));
+}
+void SenderContext::configure(pn_terminus_t* target) const
+{
+ pn_terminus_set_address(target, address.getName().c_str());
+ //dynamic create:
+ AddressHelper helper(address);
+ if (helper.createEnabled(AddressHelper::FOR_SENDER)) {
+ helper.setNodeProperties(target);
+ }
+}
+
+bool SenderContext::settled()
+{
+ return processUnsettled() == 0;
+}
+
+}}} // namespace qpid::messaging::amqp
diff --git a/cpp/src/qpid/messaging/amqp/SenderContext.h b/cpp/src/qpid/messaging/amqp/SenderContext.h
new file mode 100644
index 0000000000..3595379e70
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/SenderContext.h
@@ -0,0 +1,90 @@
+#ifndef QPID_MESSAGING_AMQP_SENDERCONTEXT_H
+#define QPID_MESSAGING_AMQP_SENDERCONTEXT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <deque>
+#include <string>
+#include <vector>
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/amqp/EncodedMessage.h"
+
+struct pn_delivery_t;
+struct pn_link_t;
+struct pn_session_t;
+struct pn_terminus_t;
+
+namespace qpid {
+namespace messaging {
+
+class Message;
+class MessageImpl;
+
+namespace amqp {
+/**
+ *
+ */
+class SenderContext
+{
+ public:
+ class Delivery
+ {
+ public:
+ Delivery(int32_t id);
+ void encode(const qpid::messaging::MessageImpl& message, const qpid::messaging::Address&);
+ void send(pn_link_t*);
+ bool accepted();
+ void settle();
+ private:
+ int32_t id;
+ pn_delivery_t* token;
+ EncodedMessage encoded;
+ };
+
+ SenderContext(pn_session_t* session, const std::string& name, const qpid::messaging::Address& target);
+ ~SenderContext();
+ void close();
+ void setCapacity(uint32_t);
+ uint32_t getCapacity();
+ uint32_t getUnsettled();
+ const std::string& getName() const;
+ const std::string& getTarget() const;
+ Delivery* send(const qpid::messaging::Message& message);
+ void configure() const;
+ bool settled();
+ private:
+ friend class ConnectionContext;
+ typedef std::deque<Delivery> Deliveries;
+
+ const std::string name;
+ const qpid::messaging::Address address;
+ pn_link_t* sender;
+ int32_t nextId;
+ Deliveries deliveries;
+ uint32_t capacity;
+
+ uint32_t processUnsettled();
+ void configure(pn_terminus_t*) const;
+};
+}}} // namespace qpid::messaging::amqp
+
+#endif /*!QPID_MESSAGING_AMQP_SENDERCONTEXT_H*/
diff --git a/cpp/src/qpid/messaging/amqp/SenderHandle.cpp b/cpp/src/qpid/messaging/amqp/SenderHandle.cpp
new file mode 100644
index 0000000000..b7168e5b31
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/SenderHandle.cpp
@@ -0,0 +1,75 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "SenderHandle.h"
+#include "ConnectionContext.h"
+#include "SessionContext.h"
+#include "SessionHandle.h"
+#include "SenderContext.h"
+#include "qpid/messaging/Duration.h"
+#include "qpid/messaging/exceptions.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Session.h"
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+
+SenderHandle::SenderHandle(boost::shared_ptr<ConnectionContext> c,
+ boost::shared_ptr<SessionContext> s,
+ boost::shared_ptr<SenderContext> sndr
+) : connection(c), session(s), sender(sndr) {}
+
+void SenderHandle::send(const Message& message, bool sync)
+{
+ connection->send(sender, message, sync);
+}
+
+void SenderHandle::close()
+{
+ session->closeSender(getName());
+}
+
+void SenderHandle::setCapacity(uint32_t capacity)
+{
+ connection->setCapacity(sender, capacity);
+}
+
+uint32_t SenderHandle::getCapacity()
+{
+ return connection->getCapacity(sender);
+}
+
+uint32_t SenderHandle::getUnsettled()
+{
+ return connection->getUnsettled(sender);
+}
+
+const std::string& SenderHandle::getName() const
+{
+ return sender->getName();
+}
+
+qpid::messaging::Session SenderHandle::getSession() const
+{
+ return qpid::messaging::Session(new SessionHandle(connection, session));
+}
+
+}}} // namespace qpid::messaging::amqp
diff --git a/cpp/src/qpid/messaging/amqp/SenderHandle.h b/cpp/src/qpid/messaging/amqp/SenderHandle.h
new file mode 100644
index 0000000000..3c6b666582
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/SenderHandle.h
@@ -0,0 +1,58 @@
+#ifndef QPID_MESSAGING_AMQP_SENDERHANDLE_H
+#define QPID_MESSAGING_AMQP_SENDERHANDLE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <boost/shared_ptr.hpp>
+#include "qpid/messaging/SenderImpl.h"
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+
+class ConnectionContext;
+class SessionContext;
+class SenderContext;
+/**
+ *
+ */
+class SenderHandle : public qpid::messaging::SenderImpl
+{
+ public:
+ SenderHandle(boost::shared_ptr<ConnectionContext> connection,
+ boost::shared_ptr<SessionContext> session,
+ boost::shared_ptr<SenderContext> sender
+ );
+ void send(const Message& message, bool sync);
+ void close();
+ void setCapacity(uint32_t);
+ uint32_t getCapacity();
+ uint32_t getUnsettled();
+ const std::string& getName() const;
+ Session getSession() const;
+ private:
+ boost::shared_ptr<ConnectionContext> connection;
+ boost::shared_ptr<SessionContext> session;
+ boost::shared_ptr<SenderContext> sender;
+};
+}}} // namespace qpid::messaging::amqp
+
+#endif /*!QPID_MESSAGING_AMQP_SENDERHANDLE_H*/
diff --git a/cpp/src/qpid/messaging/amqp/SessionContext.cpp b/cpp/src/qpid/messaging/amqp/SessionContext.cpp
new file mode 100644
index 0000000000..9bdc658bc7
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/SessionContext.cpp
@@ -0,0 +1,156 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "SessionContext.h"
+#include "SenderContext.h"
+#include "ReceiverContext.h"
+#include <boost/format.hpp>
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Duration.h"
+#include "qpid/messaging/exceptions.h"
+#include "qpid/log/Statement.h"
+extern "C" {
+#include <proton/engine.h>
+}
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+
+SessionContext::SessionContext(pn_connection_t* connection) : session(pn_session(connection)) {}
+SessionContext::~SessionContext()
+{
+ senders.clear(); receivers.clear();
+ pn_session_free(session);
+}
+
+boost::shared_ptr<SenderContext> SessionContext::createSender(const qpid::messaging::Address& address)
+{
+ std::string name = address.getName();
+
+ int count = 1;
+ for (SenderMap::const_iterator i = senders.find(name); i != senders.end(); i = senders.find(name)) {
+ name = (boost::format("%1%_%2%") % address.getName() % ++count).str();
+ }
+ boost::shared_ptr<SenderContext> s(new SenderContext(session, name, address));
+ senders[name] = s;
+ return s;
+}
+
+boost::shared_ptr<ReceiverContext> SessionContext::createReceiver(const qpid::messaging::Address& address)
+{
+ std::string name = address.getName();
+
+ int count = 1;
+ for (ReceiverMap::const_iterator i = receivers.find(name); i != receivers.end(); i = receivers.find(name)) {
+ name = (boost::format("%1%_%2%") % address.getName() % ++count).str();
+ }
+ boost::shared_ptr<ReceiverContext> r(new ReceiverContext(session, name, address));
+ receivers[name] = r;
+ return r;
+}
+
+boost::shared_ptr<SenderContext> SessionContext::getSender(const std::string& name) const
+{
+ SenderMap::const_iterator i = senders.find(name);
+ if (i == senders.end()) {
+ throw qpid::messaging::KeyError(std::string("No such sender") + name);
+ } else {
+ return i->second;
+ }
+}
+
+boost::shared_ptr<ReceiverContext> SessionContext::getReceiver(const std::string& name) const
+{
+ ReceiverMap::const_iterator i = receivers.find(name);
+ if (i == receivers.end()) {
+ throw qpid::messaging::KeyError(std::string("No such receiver") + name);
+ } else {
+ return i->second;
+ }
+}
+
+void SessionContext::closeReceiver(const std::string&)
+{
+
+}
+
+void SessionContext::closeSender(const std::string&)
+{
+
+}
+
+boost::shared_ptr<ReceiverContext> SessionContext::nextReceiver(qpid::messaging::Duration /*timeout*/)
+{
+ return boost::shared_ptr<ReceiverContext>();
+}
+
+uint32_t SessionContext::getReceivable()
+{
+ return 0;//TODO
+}
+
+uint32_t SessionContext::getUnsettledAcks()
+{
+ return 0;//TODO
+}
+
+qpid::framing::SequenceNumber SessionContext::record(pn_delivery_t* delivery)
+{
+ qpid::framing::SequenceNumber id = next++;
+ unacked[id] = delivery;
+ QPID_LOG(debug, "Recorded delivery " << id << " -> " << delivery);
+ return id;
+}
+
+void SessionContext::acknowledge(DeliveryMap::iterator begin, DeliveryMap::iterator end)
+{
+ for (DeliveryMap::iterator i = begin; i != end; ++i) {
+ QPID_LOG(debug, "Setting disposition for delivery " << i->first << " -> " << i->second);
+ pn_delivery_update(i->second, PN_ACCEPTED);
+ pn_delivery_settle(i->second);//TODO: different settlement modes?
+ }
+ unacked.erase(begin, end);
+}
+
+void SessionContext::acknowledge()
+{
+ QPID_LOG(debug, "acknowledging all " << unacked.size() << " messages");
+ acknowledge(unacked.begin(), unacked.end());
+}
+
+void SessionContext::acknowledge(const qpid::framing::SequenceNumber& id, bool cumulative)
+{
+ DeliveryMap::iterator i = unacked.find(id);
+ if (i != unacked.end()) {
+ acknowledge(cumulative ? unacked.begin() : i, ++i);
+ }
+}
+
+bool SessionContext::settled()
+{
+ bool result = true;
+ for (SenderMap::iterator i = senders.begin(); i != senders.end(); ++i) {
+ if (!i->second->settled()) result = false;
+ }
+ return result;
+}
+
+}}} // namespace qpid::messaging::amqp
diff --git a/cpp/src/qpid/messaging/amqp/SessionContext.h b/cpp/src/qpid/messaging/amqp/SessionContext.h
new file mode 100644
index 0000000000..eca30a0e97
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/SessionContext.h
@@ -0,0 +1,81 @@
+#ifndef QPID_MESSAGING_AMQP_SESSIONCONTEXT_H
+#define QPID_MESSAGING_AMQP_SESSIONCONTEXT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <map>
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/framing/SequenceNumber.h"
+
+struct pn_connection_t;
+struct pn_session_t;
+struct pn_delivery_t;
+
+namespace qpid {
+namespace messaging {
+
+class Address;
+class Duration;
+
+namespace amqp {
+
+class ConnectionContext;
+class SenderContext;
+class ReceiverContext;
+/**
+ *
+ */
+class SessionContext
+{
+ public:
+ SessionContext(pn_connection_t*);
+ ~SessionContext();
+ boost::shared_ptr<SenderContext> createSender(const qpid::messaging::Address& address);
+ boost::shared_ptr<ReceiverContext> createReceiver(const qpid::messaging::Address& address);
+ boost::shared_ptr<SenderContext> getSender(const std::string& name) const;
+ boost::shared_ptr<ReceiverContext> getReceiver(const std::string& name) const;
+ void closeReceiver(const std::string&);
+ void closeSender(const std::string&);
+ boost::shared_ptr<ReceiverContext> nextReceiver(qpid::messaging::Duration timeout);
+ uint32_t getReceivable();
+ uint32_t getUnsettledAcks();
+ bool settled();
+ private:
+ friend class ConnectionContext;
+ typedef std::map<std::string, boost::shared_ptr<SenderContext> > SenderMap;
+ typedef std::map<std::string, boost::shared_ptr<ReceiverContext> > ReceiverMap;
+ typedef std::map<qpid::framing::SequenceNumber, pn_delivery_t*> DeliveryMap;
+ pn_session_t* session;
+ SenderMap senders;
+ ReceiverMap receivers;
+ DeliveryMap unacked;
+ qpid::framing::SequenceNumber next;
+
+ qpid::framing::SequenceNumber record(pn_delivery_t*);
+ void acknowledge();
+ void acknowledge(const qpid::framing::SequenceNumber& id, bool cummulative);
+ void acknowledge(DeliveryMap::iterator begin, DeliveryMap::iterator end);
+};
+}}} // namespace qpid::messaging::amqp
+
+#endif /*!QPID_MESSAGING_AMQP_SESSIONCONTEXT_H*/
diff --git a/cpp/src/qpid/messaging/amqp/SessionHandle.cpp b/cpp/src/qpid/messaging/amqp/SessionHandle.cpp
new file mode 100644
index 0000000000..bf79771ca4
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/SessionHandle.cpp
@@ -0,0 +1,148 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "SessionHandle.h"
+#include "ConnectionContext.h"
+#include "ConnectionHandle.h"
+#include "ReceiverContext.h"
+#include "ReceiverHandle.h"
+#include "SenderContext.h"
+#include "SenderHandle.h"
+#include "SessionContext.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/Duration.h"
+#include "qpid/messaging/exceptions.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+
+SessionHandle::SessionHandle(boost::shared_ptr<ConnectionContext> c, boost::shared_ptr<SessionContext> s) : connection(c), session(s) {}
+
+void SessionHandle::commit()
+{
+
+}
+
+void SessionHandle::rollback()
+{
+
+}
+
+void SessionHandle::acknowledge(bool /*sync*/)
+{
+ connection->acknowledge(session, 0, false);
+}
+
+void SessionHandle::acknowledge(qpid::messaging::Message& msg, bool cumulative)
+{
+ //TODO: handle cumulative
+ connection->acknowledge(session, &msg, cumulative);
+}
+
+void SessionHandle::reject(qpid::messaging::Message&)
+{
+
+}
+
+void SessionHandle::release(qpid::messaging::Message&)
+{
+
+}
+
+void SessionHandle::close()
+{
+ connection->endSession(session);
+}
+
+void SessionHandle::sync(bool /*block*/)
+{
+
+}
+
+qpid::messaging::Sender SessionHandle::createSender(const qpid::messaging::Address& address)
+{
+ boost::shared_ptr<SenderContext> sender = session->createSender(address);
+ connection->attach(session, sender);
+ return qpid::messaging::Sender(new SenderHandle(connection, session, sender));
+}
+
+qpid::messaging::Receiver SessionHandle::createReceiver(const qpid::messaging::Address& address)
+{
+ boost::shared_ptr<ReceiverContext> receiver = session->createReceiver(address);
+ connection->attach(session, receiver);
+ return qpid::messaging::Receiver(new ReceiverHandle(connection, session, receiver));
+}
+
+bool SessionHandle::nextReceiver(Receiver& receiver, Duration timeout)
+{
+ boost::shared_ptr<ReceiverContext> r = session->nextReceiver(timeout);
+ if (r) {
+ //TODO: cache handles in this case to avoid frequent allocation
+ receiver = qpid::messaging::Receiver(new ReceiverHandle(connection, session, r));
+ return true;
+ } else {
+ return false;
+ }
+}
+
+qpid::messaging::Receiver SessionHandle::nextReceiver(Duration timeout)
+{
+ qpid::messaging::Receiver r;
+ if (nextReceiver(r, timeout)) return r;
+ else throw qpid::messaging::NoMessageAvailable();
+}
+
+uint32_t SessionHandle::getReceivable()
+{
+ return session->getReceivable();
+}
+
+uint32_t SessionHandle::getUnsettledAcks()
+{
+ return session->getUnsettledAcks();
+}
+
+Sender SessionHandle::getSender(const std::string& name) const
+{
+ return qpid::messaging::Sender(new SenderHandle(connection, session, session->getSender(name)));
+}
+
+Receiver SessionHandle::getReceiver(const std::string& name) const
+{
+ return qpid::messaging::Receiver(new ReceiverHandle(connection, session, session->getReceiver(name)));
+}
+
+Connection SessionHandle::getConnection() const
+{
+ return qpid::messaging::Connection(new ConnectionHandle(connection));
+}
+
+void SessionHandle::checkError()
+{
+
+}
+
+
+}}} // namespace qpid::messaging::amqp
diff --git a/cpp/src/qpid/messaging/amqp/SessionHandle.h b/cpp/src/qpid/messaging/amqp/SessionHandle.h
new file mode 100644
index 0000000000..5e843aaacc
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/SessionHandle.h
@@ -0,0 +1,64 @@
+#ifndef QPID_MESSAGING_AMQP_SESSIONIMPL_H
+#define QPID_MESSAGING_AMQP_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 <boost/shared_ptr.hpp>
+#include "qpid/messaging/SessionImpl.h"
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+
+class ConnectionContext;
+class SessionContext;
+/**
+ *
+ */
+class SessionHandle : public qpid::messaging::SessionImpl
+{
+ public:
+ SessionHandle(boost::shared_ptr<ConnectionContext>, boost::shared_ptr<SessionContext>);
+ void commit();
+ void rollback();
+ void acknowledge(bool sync);
+ void acknowledge(Message&, bool);
+ void reject(Message&);
+ void release(Message&);
+ void close();
+ void sync(bool block);
+ qpid::messaging::Sender createSender(const Address& address);
+ qpid::messaging::Receiver createReceiver(const Address& address);
+ bool nextReceiver(Receiver& receiver, Duration timeout);
+ qpid::messaging::Receiver nextReceiver(Duration timeout);
+ uint32_t getReceivable();
+ uint32_t getUnsettledAcks();
+ qpid::messaging::Sender getSender(const std::string& name) const;
+ qpid::messaging::Receiver getReceiver(const std::string& name) const;
+ qpid::messaging::Connection getConnection() const;
+ void checkError();
+ private:
+ boost::shared_ptr<ConnectionContext> connection;
+ boost::shared_ptr<SessionContext> session;
+};
+}}} // namespace qpid::messaging::amqp
+
+#endif /*!QPID_MESSAGING_AMQP_SESSIONIMPL_H*/
diff --git a/cpp/src/qpid/messaging/amqp/SslTransport.cpp b/cpp/src/qpid/messaging/amqp/SslTransport.cpp
new file mode 100644
index 0000000000..ea2375cb26
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/SslTransport.cpp
@@ -0,0 +1,160 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "SslTransport.h"
+#include "TransportContext.h"
+#include "qpid/sys/ssl/SslSocket.h"
+#include "qpid/sys/AsynchIO.h"
+#include "qpid/sys/ConnectionCodec.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/log/Statement.h"
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+using namespace qpid::sys;
+using namespace qpid::sys::ssl;
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+
+// Static constructor which registers connector here
+namespace {
+Transport* create(TransportContext& c, Poller::shared_ptr p)
+{
+ return new SslTransport(c, p);
+}
+
+struct StaticInit
+{
+ StaticInit()
+ {
+ Transport::add("ssl", &create);
+ };
+} init;
+}
+
+
+SslTransport::SslTransport(TransportContext& c, boost::shared_ptr<Poller> p) : context(c), connector(0), aio(0), poller(p) {}
+
+void SslTransport::connect(const std::string& host, const std::string& port)
+{
+ assert(!connector);
+ assert(!aio);
+ connector = AsynchConnector::create(
+ socket,
+ host, port,
+ boost::bind(&SslTransport::connected, this, _1),
+ boost::bind(&SslTransport::failed, this, _3));
+
+ connector->start(poller);
+}
+
+void SslTransport::failed(const std::string& msg)
+{
+ QPID_LOG(debug, "Failed to connect: " << msg);
+ socket.close();
+ context.closed();
+}
+
+void SslTransport::connected(const Socket&)
+{
+ context.opened();
+ aio = AsynchIO::create(socket,
+ boost::bind(&SslTransport::read, this, _1, _2),
+ boost::bind(&SslTransport::eof, this, _1),
+ boost::bind(&SslTransport::disconnected, this, _1),
+ boost::bind(&SslTransport::socketClosed, this, _1, _2),
+ 0, // nobuffs
+ boost::bind(&SslTransport::write, this, _1));
+ aio->createBuffers(std::numeric_limits<uint16_t>::max());//note: AMQP 1.0 _can_ handle large frame sizes
+ id = boost::str(boost::format("[%1%]") % socket.getFullAddress());
+ aio->start(poller);
+}
+
+void SslTransport::read(AsynchIO&, AsynchIO::BufferBase* buffer)
+{
+ int32_t decoded = context.getCodec().decode(buffer->bytes+buffer->dataStart, buffer->dataCount);
+ if (decoded < buffer->dataCount) {
+ // Adjust buffer for used bytes and then "unread them"
+ buffer->dataStart += decoded;
+ buffer->dataCount -= decoded;
+ aio->unread(buffer);
+ } else {
+ // Give whole buffer back to aio subsystem
+ aio->queueReadBuffer(buffer);
+ }
+}
+
+void SslTransport::write(AsynchIO&)
+{
+ if (context.getCodec().canEncode()) {
+ AsynchIO::BufferBase* buffer = aio->getQueuedBuffer();
+ if (buffer) {
+ size_t encoded = context.getCodec().encode(buffer->bytes, buffer->byteCount);
+
+ buffer->dataStart = 0;
+ buffer->dataCount = encoded;
+ aio->queueWrite(buffer);
+ }
+ }
+
+}
+
+void SslTransport::close()
+{
+ QPID_LOG(debug, id << " SslTransport closing...");
+ if (aio)
+ aio->queueWriteClose();
+}
+
+void SslTransport::eof(AsynchIO&)
+{
+ close();
+}
+
+void SslTransport::disconnected(AsynchIO&)
+{
+ close();
+ socketClosed(*aio, socket);
+}
+
+void SslTransport::socketClosed(AsynchIO&, const Socket&)
+{
+ if (aio)
+ aio->queueForDeletion();
+ context.closed();
+ QPID_LOG(debug, id << " Socket closed");
+}
+
+void SslTransport::abort()
+{
+ if (aio) {
+ // Established connection
+ aio->requestCallback(boost::bind(&SslTransport::eof, this, _1));
+ }
+}
+
+void SslTransport::activateOutput()
+{
+ if (aio) aio->notifyPendingWrite();
+}
+
+}}} // namespace qpid::messaging::amqp
diff --git a/cpp/src/qpid/messaging/amqp/SslTransport.h b/cpp/src/qpid/messaging/amqp/SslTransport.h
new file mode 100644
index 0000000000..f67ab95673
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/SslTransport.h
@@ -0,0 +1,74 @@
+#ifndef QPID_MESSAGING_AMQP_SSLTRANSPORT_H
+#define QPID_MESSAGING_AMQP_SSLTRANSPORT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/amqp/Transport.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/ssl/SslSocket.h"
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace sys {
+class ConnectionCodec;
+class Poller;
+class AsynchConnector;
+class AsynchIO;
+class AsynchIOBufferBase;
+}
+
+namespace messaging {
+namespace amqp {
+class TransportContext;
+
+class SslTransport : public Transport
+{
+ public:
+ SslTransport(TransportContext&, boost::shared_ptr<qpid::sys::Poller> p);
+
+ void connect(const std::string& host, const std::string& port);
+
+ void activateOutput();
+ void abort();
+ void close();
+
+ private:
+ qpid::sys::ssl::SslSocket socket;
+ TransportContext& context;
+ qpid::sys::AsynchConnector* connector;
+ qpid::sys::AsynchIO* aio;
+ boost::shared_ptr<qpid::sys::Poller> poller;
+ bool closed;
+ std::string id;
+
+ void connected(const qpid::sys::Socket&);
+ void failed(const std::string& msg);
+ void read(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*);
+ void write(qpid::sys::AsynchIO&);
+ void eof(qpid::sys::AsynchIO&);
+ void disconnected(qpid::sys::AsynchIO&);
+ void socketClosed(qpid::sys::AsynchIO&, const qpid::sys::Socket&);
+
+ friend class DriverImpl;
+};
+}}} // namespace qpid::messaging::amqp
+
+#endif /*!QPID_MESSAGING_AMQP_SSLTRANSPORT_H*/
diff --git a/cpp/src/qpid/messaging/amqp/TcpTransport.cpp b/cpp/src/qpid/messaging/amqp/TcpTransport.cpp
new file mode 100644
index 0000000000..98022d634c
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/TcpTransport.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 "TcpTransport.h"
+#include "ConnectionContext.h"
+#include "qpid/sys/AsynchIO.h"
+#include "qpid/sys/ConnectionCodec.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/log/Statement.h"
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+using namespace qpid::sys;
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+// Static constructor which registers connector here
+namespace {
+Transport* create(TransportContext& c, Poller::shared_ptr p)
+{
+ return new TcpTransport(c, p);
+}
+
+struct StaticInit
+{
+ StaticInit()
+ {
+ Transport::add("tcp", &create);
+ };
+} init;
+}
+
+TcpTransport::TcpTransport(TransportContext& c, boost::shared_ptr<Poller> p) : socket(createSocket()), context(c), connector(0), aio(0), poller(p) {}
+
+void TcpTransport::connect(const std::string& host, const std::string& port)
+{
+ assert(!connector);
+ assert(!aio);
+ connector = AsynchConnector::create(
+ *socket,
+ host, port,
+ boost::bind(&TcpTransport::connected, this, _1),
+ boost::bind(&TcpTransport::failed, this, _3));
+
+ connector->start(poller);
+}
+
+void TcpTransport::failed(const std::string& msg)
+{
+ QPID_LOG(debug, "Failed to connect: " << msg);
+ connector = 0;
+ socket->close();
+ context.closed();
+}
+
+void TcpTransport::connected(const Socket&)
+{
+ context.opened();
+ connector = 0;
+ aio = AsynchIO::create(*socket,
+ boost::bind(&TcpTransport::read, this, _1, _2),
+ boost::bind(&TcpTransport::eof, this, _1),
+ boost::bind(&TcpTransport::disconnected, this, _1),
+ boost::bind(&TcpTransport::socketClosed, this, _1, _2),
+ 0, // nobuffs
+ boost::bind(&TcpTransport::write, this, _1));
+ aio->createBuffers(std::numeric_limits<uint16_t>::max());//note: AMQP 1.0 _can_ handle large frame sizes
+ id = boost::str(boost::format("[%1%]") % socket->getFullAddress());
+ aio->start(poller);
+}
+
+void TcpTransport::read(AsynchIO&, AsynchIO::BufferBase* buffer)
+{
+ int32_t decoded = context.getCodec().decode(buffer->bytes+buffer->dataStart, buffer->dataCount);
+ if (decoded < buffer->dataCount) {
+ // Adjust buffer for used bytes and then "unread them"
+ buffer->dataStart += decoded;
+ buffer->dataCount -= decoded;
+ aio->unread(buffer);
+ } else {
+ // Give whole buffer back to aio subsystem
+ aio->queueReadBuffer(buffer);
+ }
+}
+
+void TcpTransport::write(AsynchIO&)
+{
+ if (context.getCodec().canEncode()) {
+ AsynchIO::BufferBase* buffer = aio->getQueuedBuffer();
+ if (buffer) {
+ size_t encoded = context.getCodec().encode(buffer->bytes, buffer->byteCount);
+
+ buffer->dataStart = 0;
+ buffer->dataCount = encoded;
+ aio->queueWrite(buffer);
+ }
+ }
+
+}
+
+void TcpTransport::close()
+{
+ QPID_LOG(debug, id << " TcpTransport closing...");
+ if (aio)
+ aio->queueWriteClose();
+}
+
+void TcpTransport::eof(AsynchIO&)
+{
+ close();
+}
+
+void TcpTransport::disconnected(AsynchIO&)
+{
+ close();
+ socketClosed(*aio, *socket);
+}
+
+void TcpTransport::socketClosed(AsynchIO&, const Socket&)
+{
+ if (aio)
+ aio->queueForDeletion();
+ context.closed();
+ QPID_LOG(debug, id << " Socket closed");
+}
+
+void TcpTransport::abort()
+{
+ if (aio) {
+ // Established connection
+ aio->requestCallback(boost::bind(&TcpTransport::eof, this, _1));
+ } else if (connector) {
+ // We're still connecting
+ connector->stop();
+ failed("Connection timedout");
+ }
+}
+
+void TcpTransport::activateOutput()
+{
+ if (aio) aio->notifyPendingWrite();
+}
+
+}}} // namespace qpid::messaging::amqp
diff --git a/cpp/src/qpid/messaging/amqp/TcpTransport.h b/cpp/src/qpid/messaging/amqp/TcpTransport.h
new file mode 100644
index 0000000000..8c1087abb3
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/TcpTransport.h
@@ -0,0 +1,71 @@
+#ifndef QPID_MESSAGING_AMQP_TCPTRANSPORT_H
+#define QPID_MESSAGING_AMQP_TCPTRANSPORT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/amqp/Transport.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Socket.h"
+#include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace sys {
+class ConnectionCodec;
+class AsynchConnector;
+class AsynchIO;
+class AsynchIOBufferBase;
+class Poller;
+}
+namespace messaging {
+namespace amqp {
+class TransportContext;
+
+class TcpTransport : public Transport
+{
+ public:
+ TcpTransport(TransportContext&, boost::shared_ptr<qpid::sys::Poller>);
+
+ void connect(const std::string& host, const std::string& port);
+
+ void activateOutput();
+ void abort();
+ void close();
+
+ private:
+ boost::scoped_ptr<qpid::sys::Socket> socket;
+ TransportContext& context;
+ qpid::sys::AsynchConnector* connector;
+ qpid::sys::AsynchIO* aio;
+ boost::shared_ptr<qpid::sys::Poller> poller;
+ std::string id;
+
+ void connected(const qpid::sys::Socket&);
+ void failed(const std::string& msg);
+ void read(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*);
+ void write(qpid::sys::AsynchIO&);
+ void eof(qpid::sys::AsynchIO&);
+ void disconnected(qpid::sys::AsynchIO&);
+ void socketClosed(qpid::sys::AsynchIO&, const qpid::sys::Socket&);
+};
+}}} // namespace qpid::messaging::amqp
+
+#endif /*!QPID_MESSAGING_AMQP_TCPTRANSPORT_H*/
diff --git a/cpp/src/qpid/messaging/amqp/Transport.cpp b/cpp/src/qpid/messaging/amqp/Transport.cpp
new file mode 100644
index 0000000000..21f51046b1
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/Transport.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/amqp/Transport.h"
+#include "qpid/messaging/amqp/TransportContext.h"
+#include <map>
+#include <string>
+
+namespace qpid {
+namespace messaging {
+namespace amqp {
+namespace {
+typedef std::map<std::string, Transport::Factory*> Registry;
+
+Registry& theRegistry()
+{
+ static Registry factories;
+ return factories;
+}
+}
+
+Transport* Transport::create(const std::string& name, TransportContext& context, boost::shared_ptr<qpid::sys::Poller> poller)
+{
+ Registry::const_iterator i = theRegistry().find(name);
+ if (i != theRegistry().end()) return (i->second)(context, poller);
+ else return 0;
+}
+void Transport::add(const std::string& name, Factory* factory)
+{
+ theRegistry()[name] = factory;
+}
+
+}}} // namespace qpid::messaging::amqp
diff --git a/cpp/src/qpid/messaging/amqp/Transport.h b/cpp/src/qpid/messaging/amqp/Transport.h
new file mode 100644
index 0000000000..ee021f645b
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/Transport.h
@@ -0,0 +1,48 @@
+#ifndef QPID_MESSAGING_AMQP_TRANSPORT_H
+#define QPID_MESSAGING_AMQP_TRANSPORT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/sys/OutputControl.h"
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace sys {
+class Poller;
+}
+namespace messaging {
+namespace amqp {
+class TransportContext;
+
+class Transport : public qpid::sys::OutputControl
+{
+ public:
+ virtual ~Transport() {}
+ virtual void connect(const std::string& host, const std::string& port) = 0;
+ virtual void close() = 0;
+
+ typedef Transport* Factory(TransportContext&, boost::shared_ptr<qpid::sys::Poller>);
+ static Transport* create(const std::string& name, TransportContext&, boost::shared_ptr<qpid::sys::Poller>);
+ static void add(const std::string& name, Factory* factory);
+};
+}}} // namespace qpid::messaging::amqp
+
+#endif /*!QPID_MESSAGING_AMQP_TRANSPORT_H*/
diff --git a/cpp/src/qpid/messaging/amqp/TransportContext.h b/cpp/src/qpid/messaging/amqp/TransportContext.h
new file mode 100644
index 0000000000..57192b5976
--- /dev/null
+++ b/cpp/src/qpid/messaging/amqp/TransportContext.h
@@ -0,0 +1,47 @@
+#ifndef QPID_MESSAGING_AMQP_TRANSPORTCONTEXT_H
+#define QPID_MESSAGING_AMQP_TRANSPORTCONTEXT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 Codec;
+}
+namespace messaging {
+namespace amqp {
+
+/**
+ * Interface to be supplied by 'users' of Transport interface, in
+ * order to provide codec and handle callbaskc for opening and closing
+ * of connection.
+ */
+class TransportContext
+{
+ public:
+ virtual ~TransportContext() {}
+ virtual qpid::sys::Codec& getCodec() = 0;
+ virtual void closed() = 0;
+ virtual void opened() = 0;
+ private:
+};
+}}} // namespace qpid::messaging::amqp
+
+#endif /*!QPID_MESSAGING_AMQP_TRANSPORTCONTEXT_H*/
diff --git a/cpp/src/qpid/store/CMakeLists.txt b/cpp/src/qpid/store/CMakeLists.txt
index 9abdf0ae3d..31623f8e84 100644
--- a/cpp/src/qpid/store/CMakeLists.txt
+++ b/cpp/src/qpid/store/CMakeLists.txt
@@ -42,6 +42,7 @@ if (CMAKE_COMPILER_IS_GNUCXX)
set_target_properties (store PROPERTIES
PREFIX ""
+ COMPILE_DEFINITIONS _IN_QPID_BROKER
LINK_FLAGS "${GCC_CATCH_UNDEFINED}")
endif (CMAKE_COMPILER_IS_GNUCXX)
@@ -54,7 +55,9 @@ if (CMAKE_SYSTEM_NAME STREQUAL Windows)
endif (MSVC)
endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
-set_target_properties (store PROPERTIES VERSION ${qpidc_version})
+set_target_properties (store PROPERTIES
+ COMPILE_DEFINITIONS _IN_QPID_BROKER
+ VERSION ${qpidc_version})
install (TARGETS store # RUNTIME
DESTINATION ${QPIDD_MODULE_DIR}
COMPONENT ${QPID_COMPONENT_BROKER})
@@ -81,6 +84,7 @@ if (BUILD_MSSQL)
ms-sql/State.cpp
ms-sql/TplRecordset.cpp
ms-sql/VariantHelper.cpp)
+ set_target_properties (mssql_store PROPERTIES COMPILE_DEFINITIONS _IN_QPID_BROKER)
target_link_libraries (mssql_store qpidbroker qpidcommon ${Boost_PROGRAM_OPTIONS_LIBRARY})
install (TARGETS mssql_store # RUNTIME
DESTINATION ${QPIDD_MODULE_DIR}
@@ -110,6 +114,7 @@ if (BUILD_MSCLFS)
ms-sql/State.cpp
ms-sql/VariantHelper.cpp)
include_directories(ms-sql)
+ set_target_properties (msclfs_store PROPERTIES COMPILE_DEFINITIONS _IN_QPID_BROKER)
target_link_libraries (msclfs_store qpidbroker qpidcommon ${Boost_PROGRAM_OPTIONS_LIBRARY} clfsw32.lib)
install (TARGETS msclfs_store # RUNTIME
DESTINATION ${QPIDD_MODULE_DIR}
diff --git a/cpp/src/qpid/store/MessageStorePlugin.cpp b/cpp/src/qpid/store/MessageStorePlugin.cpp
index d72200c2ba..b869ecadbc 100644
--- a/cpp/src/qpid/store/MessageStorePlugin.cpp
+++ b/cpp/src/qpid/store/MessageStorePlugin.cpp
@@ -137,12 +137,6 @@ MessageStorePlugin::providerAvailable(const std::string name,
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
diff --git a/cpp/src/qpid/store/MessageStorePlugin.h b/cpp/src/qpid/store/MessageStorePlugin.h
index 4a9bb2aecb..1fcde6683d 100644
--- a/cpp/src/qpid/store/MessageStorePlugin.h
+++ b/cpp/src/qpid/store/MessageStorePlugin.h
@@ -24,18 +24,22 @@
#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 "qpid/management/Manageable.h"
#include <string>
using namespace qpid;
namespace qpid {
+
+namespace broker {
+class Broker;
+class PersistableExchange;
+class PersistableMessage;
+class PersistableQueue;
+}
+
namespace store {
class StorageProvider;
@@ -82,18 +86,6 @@ class MessageStorePlugin :
/**
* @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
diff --git a/cpp/src/qpid/store/StorageProvider.h b/cpp/src/qpid/store/StorageProvider.h
index d162cc58ec..de12ffb869 100644
--- a/cpp/src/qpid/store/StorageProvider.h
+++ b/cpp/src/qpid/store/StorageProvider.h
@@ -143,20 +143,6 @@ public:
/**
* @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
diff --git a/cpp/src/qpid/store/ms-clfs/MSSqlClfsProvider.cpp b/cpp/src/qpid/store/ms-clfs/MSSqlClfsProvider.cpp
index 586aaaf980..90785263d3 100644
--- a/cpp/src/qpid/store/ms-clfs/MSSqlClfsProvider.cpp
+++ b/cpp/src/qpid/store/ms-clfs/MSSqlClfsProvider.cpp
@@ -26,6 +26,7 @@
#include <string>
#include <windows.h>
#include <clfsw32.h>
+#include <qpid/broker/Broker.h>
#include <qpid/broker/RecoverableQueue.h>
#include <qpid/log/Statement.h>
#include <qpid/store/MessageStorePlugin.h>
@@ -108,20 +109,6 @@ public:
/**
* @name Methods inherited from qpid::broker::MessageStore
*/
- //@{
- /**
- * If called after init() but before recovery, will discard the database
- * and reinitialize using an empty store dir. If @a pushDownStoreFiles
- * is true, the content of the store dir will be moved to a backup dir
- * inside the store dir. This is used when cluster nodes recover and must
- * get their content from a cluster sync rather than directly from the
- * store.
- *
- * @param pushDownStoreFiles If true, will move content of the store dir
- * into a subdir, leaving the store dir
- * otherwise empty.
- */
- virtual void truncateInit(const bool pushDownStoreFiles = false);
/**
* Record the existence of a durable queue
@@ -467,11 +454,6 @@ MSSqlClfsProvider::activate(MessageStorePlugin &store)
}
void
-MSSqlClfsProvider::truncateInit(const bool pushDownStoreFiles)
-{
-}
-
-void
MSSqlClfsProvider::create(PersistableQueue& queue,
const qpid::framing::FieldTable& /*args needed for jrnl*/)
{
diff --git a/cpp/src/qpid/store/ms-sql/MSSqlProvider.cpp b/cpp/src/qpid/store/ms-sql/MSSqlProvider.cpp
index 7f22db3d02..1432cc8fca 100644
--- a/cpp/src/qpid/store/ms-sql/MSSqlProvider.cpp
+++ b/cpp/src/qpid/store/ms-sql/MSSqlProvider.cpp
@@ -92,20 +92,6 @@ public:
/**
* @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
@@ -392,11 +378,6 @@ MSSqlProvider::activate(MessageStorePlugin &store)
}
void
-MSSqlProvider::truncateInit(const bool pushDownStoreFiles)
-{
-}
-
-void
MSSqlProvider::create(PersistableQueue& queue,
const qpid::framing::FieldTable& /*args needed for jrnl*/)
{
diff --git a/cpp/src/qpid/store/ms-sql/MessageRecordset.cpp b/cpp/src/qpid/store/ms-sql/MessageRecordset.cpp
index b62a333df6..495f1a08c2 100644
--- a/cpp/src/qpid/store/ms-sql/MessageRecordset.cpp
+++ b/cpp/src/qpid/store/ms-sql/MessageRecordset.cpp
@@ -147,7 +147,7 @@ MessageRecordset::recover(qpid::broker::RecoveryManager& recoverer,
// Now, do we need the rest of the content?
long contentLength = blobSize - headerFieldLength - headerSize;
- if (msg->loadContent(contentLength)) {
+ if (contentLength > 0 && msg->loadContent(contentLength)) {
BlobAdapter content(contentLength);
content =
rs->Fields->Item["fieldTableBlob"]->GetChunk(contentLength);
diff --git a/cpp/src/qpid/sys/AggregateOutput.cpp b/cpp/src/qpid/sys/AggregateOutput.cpp
index fc95f46fb9..ebc5689ce5 100644
--- a/cpp/src/qpid/sys/AggregateOutput.cpp
+++ b/cpp/src/qpid/sys/AggregateOutput.cpp
@@ -32,8 +32,6 @@ void AggregateOutput::abort() { control.abort(); }
void AggregateOutput::activateOutput() { control.activateOutput(); }
-void AggregateOutput::giveReadCredit(int32_t credit) { control.giveReadCredit(credit); }
-
namespace {
// Clear the busy flag and notify waiting threads in destructor.
struct ScopedBusy {
@@ -51,6 +49,7 @@ bool AggregateOutput::doOutput() {
while (!tasks.empty()) {
OutputTask* t=tasks.front();
tasks.pop_front();
+ taskSet.erase(t);
bool didOutput;
{
// Allow concurrent call to addOutputTask.
@@ -59,7 +58,9 @@ bool AggregateOutput::doOutput() {
didOutput = t->doOutput();
}
if (didOutput) {
- tasks.push_back(t);
+ if (taskSet.insert(t).second) {
+ tasks.push_back(t);
+ }
return true;
}
}
@@ -68,12 +69,15 @@ bool AggregateOutput::doOutput() {
void AggregateOutput::addOutputTask(OutputTask* task) {
Mutex::ScopedLock l(lock);
- tasks.push_back(task);
+ if (taskSet.insert(task).second) {
+ tasks.push_back(task);
+ }
}
void AggregateOutput::removeOutputTask(OutputTask* task) {
Mutex::ScopedLock l(lock);
while (busy) lock.wait();
+ taskSet.erase(task);
tasks.erase(std::remove(tasks.begin(), tasks.end(), task), tasks.end());
}
@@ -81,6 +85,7 @@ void AggregateOutput::removeAll()
{
Mutex::ScopedLock l(lock);
while (busy) lock.wait();
+ taskSet.clear();
tasks.clear();
}
diff --git a/cpp/src/qpid/sys/AggregateOutput.h b/cpp/src/qpid/sys/AggregateOutput.h
index d7c0ff29e3..e9dbd5a4cc 100644
--- a/cpp/src/qpid/sys/AggregateOutput.h
+++ b/cpp/src/qpid/sys/AggregateOutput.h
@@ -28,6 +28,7 @@
#include <algorithm>
#include <deque>
+#include <set>
namespace qpid {
namespace sys {
@@ -44,9 +45,11 @@ namespace sys {
class QPID_COMMON_CLASS_EXTERN AggregateOutput : public OutputTask, public OutputControl
{
typedef std::deque<OutputTask*> TaskList;
+ typedef std::set<OutputTask*> TaskSet;
Monitor lock;
TaskList tasks;
+ TaskSet taskSet;
bool busy;
OutputControl& control;
@@ -56,7 +59,6 @@ class QPID_COMMON_CLASS_EXTERN AggregateOutput : public OutputTask, public Outpu
// 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.
diff --git a/cpp/src/qpid/sys/AsynchIO.h b/cpp/src/qpid/sys/AsynchIO.h
index b2eaaac9de..679665f8ad 100644
--- a/cpp/src/qpid/sys/AsynchIO.h
+++ b/cpp/src/qpid/sys/AsynchIO.h
@@ -21,9 +21,11 @@
*
*/
-#include "qpid/sys/IntegerTypes.h"
#include "qpid/CommonImportExport.h"
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/sys/SecuritySettings.h"
+
#include <string.h>
#include <boost/function.hpp>
@@ -56,6 +58,7 @@ class AsynchConnector {
public:
typedef boost::function1<void, const Socket&> ConnectedCallback;
typedef boost::function3<void, const Socket&, int, const std::string&> FailedCallback;
+ typedef boost::function1<void, AsynchConnector&> RequestCallback;
// Call create() to allocate a new AsynchConnector object with the
// specified poller, addressing, and callbacks.
@@ -70,6 +73,7 @@ public:
FailedCallback failCb);
virtual void start(boost::shared_ptr<Poller> poller) = 0;
virtual void stop() {};
+ virtual void requestCallback(RequestCallback) = 0;
protected:
AsynchConnector() {}
virtual ~AsynchConnector() {}
@@ -155,11 +159,11 @@ public:
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;
+ virtual SecuritySettings getSecuritySettings() = 0;
+
protected:
// Derived class manages lifetime; must be constructed using the
// static create() method. Deletes not allowed from outside.
diff --git a/cpp/src/qpid/sys/AsynchIOHandler.cpp b/cpp/src/qpid/sys/AsynchIOHandler.cpp
index 2e117a3fb7..cf08b482e6 100644
--- a/cpp/src/qpid/sys/AsynchIOHandler.cpp
+++ b/cpp/src/qpid/sys/AsynchIOHandler.cpp
@@ -51,15 +51,15 @@ struct ProtocolTimeoutTask : public sys::TimerTask {
}
};
-AsynchIOHandler::AsynchIOHandler(const std::string& id, ConnectionCodec::Factory* f) :
+AsynchIOHandler::AsynchIOHandler(const std::string& id, ConnectionCodec::Factory* f, bool isClient0, bool nodict0) :
identifier(id),
aio(0),
factory(f),
codec(0),
reads(0),
readError(false),
- isClient(false),
- readCredit(InfiniteCredit)
+ isClient(isClient0),
+ nodict(nodict0)
{}
AsynchIOHandler::~AsynchIOHandler() {
@@ -97,25 +97,20 @@ void AsynchIOHandler::abort() {
if (!readError) {
aio->requestCallback(boost::bind(&AsynchIOHandler::eof, this, _1));
}
+ aio->queueWriteClose();
}
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();
+namespace {
+ SecuritySettings getSecuritySettings(AsynchIO* aio, bool nodict)
+ {
+ SecuritySettings settings = aio->getSecuritySettings();
+ settings.nodict = nodict;
+ return settings;
+ }
}
void AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) {
@@ -123,26 +118,6 @@ void AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) {
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();
- }
- }
- }
-
++reads;
size_t decoded = 0;
if (codec) { // Already initiated
@@ -168,13 +143,16 @@ void AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) {
QPID_LOG(debug, "RECV [" << identifier << "]: INIT(" << protocolInit << ")");
try {
- codec = factory->create(protocolInit.getVersion(), *this, identifier, SecuritySettings());
+ codec = factory->create(protocolInit.getVersion(), *this, identifier, getSecuritySettings(aio, nodict));
if (!codec) {
//TODO: may still want to revise this...
//send valid version header & close connection.
write(framing::ProtocolInitiation(framing::highestProtocolVersion));
readError = true;
aio->queueWriteClose();
+ } else {
+ //read any further data that may already have been sent
+ decoded += codec->decode(buff->bytes+buff->dataStart+in.getPosition(), buff->dataCount-in.getPosition());
}
} catch (const std::exception& e) {
QPID_LOG(error, e.what());
@@ -223,7 +201,7 @@ void AsynchIOHandler::nobuffs(AsynchIO&) {
void AsynchIOHandler::idle(AsynchIO&){
if (isClient && codec == 0) {
- codec = factory->create(*this, identifier, SecuritySettings());
+ codec = factory->create(*this, identifier, getSecuritySettings(aio, nodict));
write(framing::ProtocolInitiation(codec->getVersion()));
// We've just sent the protocol negotiation so we can cancel the timeout for that
// This is not ideal, because we've not received anything yet, but heartbeats will
diff --git a/cpp/src/qpid/sys/AsynchIOHandler.h b/cpp/src/qpid/sys/AsynchIOHandler.h
index fd0bc140e5..d93e24fd4c 100644
--- a/cpp/src/qpid/sys/AsynchIOHandler.h
+++ b/cpp/src/qpid/sys/AsynchIOHandler.h
@@ -51,24 +51,19 @@ class AsynchIOHandler : public OutputControl {
uint32_t reads;
bool readError;
bool isClient;
- AtomicValue<int32_t> readCredit;
- static const int32_t InfiniteCredit = -1;
- Mutex creditLock;
+ bool nodict;
boost::intrusive_ptr<sys::TimerTask> timeoutTimerTask;
void write(const framing::ProtocolInitiation&);
public:
- QPID_COMMON_EXTERN AsynchIOHandler(const std::string& id, qpid::sys::ConnectionCodec::Factory* f );
+ QPID_COMMON_EXTERN AsynchIOHandler(const std::string& id, qpid::sys::ConnectionCodec::Factory* f, bool isClient, bool nodict);
QPID_COMMON_EXTERN ~AsynchIOHandler();
QPID_COMMON_EXTERN void init(AsynchIO* a, Timer& timer, uint32_t maxTime);
- QPID_COMMON_INLINE_EXTERN void setClient() { isClient = true; }
-
// Output side
QPID_COMMON_EXTERN void abort();
QPID_COMMON_EXTERN void activateOutput();
- QPID_COMMON_EXTERN void giveReadCredit(int32_t credit);
// Input side
QPID_COMMON_EXTERN void readbuff(AsynchIO& aio, AsynchIOBufferBase* buff);
diff --git a/cpp/src/qpid/sys/ClusterSafe.h b/cpp/src/qpid/sys/ClusterSafe.h
deleted file mode 100644
index 27e4eb46a5..0000000000
--- a/cpp/src/qpid/sys/ClusterSafe.h
+++ /dev/null
@@ -1,87 +0,0 @@
-#ifndef QPID_SYS_CLUSTERSAFE_H
-#define QPID_SYS_CLUSTERSAFE_H
-
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#include "qpid/CommonImportExport.h"
-
-namespace qpid {
-namespace sys {
-
-/**
- * Assertion to add to code that modifies clustered state.
- *
- * In a non-clustered broker this is a no-op.
- *
- * In a clustered broker, checks that it is being called
- * in a context where it is safe to modify clustered state.
- * If not it aborts the process as this is a serious bug.
- *
- * This function is in the common library rather than the cluster
- * library because it is called by code in the broker library.
- */
-QPID_COMMON_EXTERN void assertClusterSafe();
-
-/**
- * In a non-clustered broker, returns true.
- *
- * In a clustered broker returns true if we are in a context where it
- * is safe to modify cluster state.
- *
- * This function is in the common library rather than the cluster
- * library because it is called by code in the broker library.
- */
-QPID_COMMON_EXTERN bool isClusterSafe();
-
-/**
- * Mark a scope as cluster safe. Sets isClusterSafe in constructor and resets
- * to previous value in destructor.
- */
-class ClusterSafeScope {
- public:
- ClusterSafeScope();
- ~ClusterSafeScope();
- private:
- bool save;
-};
-
-/**
- * Mark a scope as cluster unsafe. Clears isClusterSafe in constructor and resets
- * to previous value in destructor.
- */
-class ClusterUnsafeScope {
- public:
- QPID_COMMON_EXTERN ClusterUnsafeScope();
- QPID_COMMON_EXTERN ~ClusterUnsafeScope();
- private:
- bool save;
-};
-
-/**
- * Enable cluster-safe assertions. By default they are no-ops.
- * Called by cluster code.
- */
-void enableClusterSafe();
-
-}} // namespace qpid::sys
-
-#endif /*!QPID_SYS_CLUSTERSAFE_H*/
diff --git a/cpp/src/qpid/sys/Codec.h b/cpp/src/qpid/sys/Codec.h
index ace721fbcc..e398403e47 100644
--- a/cpp/src/qpid/sys/Codec.h
+++ b/cpp/src/qpid/sys/Codec.h
@@ -42,7 +42,7 @@ class Codec
/** Encode into buffer, return number of bytes encoded */
- virtual std::size_t encode(const char* buffer, std::size_t size) = 0;
+ virtual std::size_t encode(char* buffer, std::size_t size) = 0;
/** Return true if we have data to encode */
virtual bool canEncode() = 0;
diff --git a/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h b/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h
index 95a08d15ae..53d56ad716 100644
--- a/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h
+++ b/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h
@@ -45,7 +45,6 @@ class ConnectionOutputHandlerPtr : public ConnectionOutputHandler
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:
diff --git a/cpp/src/qpid/sys/FileSysDir.h b/cpp/src/qpid/sys/FileSysDir.h
index ffe7823f0a..7432fe39c9 100755
--- a/cpp/src/qpid/sys/FileSysDir.h
+++ b/cpp/src/qpid/sys/FileSysDir.h
@@ -54,6 +54,15 @@ class FileSysDir
void mkdir(void);
+ typedef void Callback(const std::string&);
+
+ /**
+ * Call the Callback function for every regular file in the directory
+ *
+ * @param cb Callback function that receives the full path to the file
+ */
+ void forEachFile(Callback cb) const;
+
std::string getPath () { return dirPath; }
};
diff --git a/cpp/src/qpid/sys/OutputControl.h b/cpp/src/qpid/sys/OutputControl.h
index eae99beb0f..0d801e9d16 100644
--- a/cpp/src/qpid/sys/OutputControl.h
+++ b/cpp/src/qpid/sys/OutputControl.h
@@ -1,3 +1,6 @@
+#ifndef QPID_SYS_OUTPUT_CONTROL_H
+#define QPID_SYS_OUTPUT_CONTROL_H
+
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -21,9 +24,6 @@
#include "qpid/sys/IntegerTypes.h"
-#ifndef _OutputControl_
-#define _OutputControl_
-
namespace qpid {
namespace sys {
@@ -33,11 +33,10 @@ namespace sys {
virtual ~OutputControl() {}
virtual void abort() = 0;
virtual void activateOutput() = 0;
- virtual void giveReadCredit(int32_t credit) = 0;
};
}
}
-#endif
+#endif /*!QPID_SYS_OUTPUT_CONTROL_H*/
diff --git a/cpp/src/qpid/sys/ProtocolFactory.h b/cpp/src/qpid/sys/ProtocolFactory.h
index 4d198a92da..236398c111 100644
--- a/cpp/src/qpid/sys/ProtocolFactory.h
+++ b/cpp/src/qpid/sys/ProtocolFactory.h
@@ -42,10 +42,10 @@ class ProtocolFactory : public qpid::SharedObject<ProtocolFactory>
virtual void accept(boost::shared_ptr<Poller>, ConnectionCodec::Factory*) = 0;
virtual void connect(
boost::shared_ptr<Poller>,
+ const std::string& name,
const std::string& host, const std::string& port,
ConnectionCodec::Factory* codec,
ConnectFailedCallback failed) = 0;
- virtual bool supports(const std::string& /*capability*/) { return false; }
};
inline ProtocolFactory::~ProtocolFactory() {}
diff --git a/cpp/src/qpid/sys/RdmaIOPlugin.cpp b/cpp/src/qpid/sys/RdmaIOPlugin.cpp
index b491d28d0a..51cc0ed109 100644
--- a/cpp/src/qpid/sys/RdmaIOPlugin.cpp
+++ b/cpp/src/qpid/sys/RdmaIOPlugin.cpp
@@ -23,6 +23,7 @@
#include "qpid/Plugin.h"
#include "qpid/broker/Broker.h"
+#include "qpid/broker/NameGenerator.h"
#include "qpid/framing/AMQP_HighestVersion.h"
#include "qpid/log/Statement.h"
#include "qpid/sys/rdma/RdmaIO.h"
@@ -67,7 +68,6 @@ class RdmaIOHandler : public OutputControl {
void close();
void abort();
void activateOutput();
- void giveReadCredit(int32_t credit);
void initProtocolOut();
// Input side
@@ -83,7 +83,7 @@ class RdmaIOHandler : public OutputControl {
};
RdmaIOHandler::RdmaIOHandler(Rdma::Connection::intrusive_ptr c, qpid::sys::ConnectionCodec::Factory* f) :
- identifier(c->getFullName()),
+ identifier(broker::QPID_NAME_PREFIX+c->getFullName()),
factory(f),
codec(0),
readError(false),
@@ -199,10 +199,6 @@ 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
@@ -250,7 +246,7 @@ class RdmaIOProtocolFactory : public ProtocolFactory {
public:
RdmaIOProtocolFactory(int16_t port, int backlog);
void accept(Poller::shared_ptr, ConnectionCodec::Factory*);
- void connect(Poller::shared_ptr, const string& host, const std::string& port, ConnectionCodec::Factory*, ConnectFailedCallback);
+ void connect(Poller::shared_ptr, const std::string& name, const string& host, const std::string& port, ConnectionCodec::Factory*, ConnectFailedCallback);
uint16_t getPort() const;
@@ -371,6 +367,7 @@ void RdmaIOProtocolFactory::connected(Poller::shared_ptr poller, Rdma::Connectio
void RdmaIOProtocolFactory::connect(
Poller::shared_ptr poller,
+ const std::string& /*name*/,
const std::string& host, const std::string& port,
ConnectionCodec::Factory* f,
ConnectFailedCallback failed)
diff --git a/cpp/src/qpid/sys/SecurityLayer.h b/cpp/src/qpid/sys/SecurityLayer.h
index 52bc40e352..317ada16de 100644
--- a/cpp/src/qpid/sys/SecurityLayer.h
+++ b/cpp/src/qpid/sys/SecurityLayer.h
@@ -33,8 +33,12 @@ namespace sys {
class SecurityLayer : public Codec
{
public:
+ SecurityLayer(int ssf_) : ssf(ssf_) {}
+ int getSsf() const { return ssf; }
virtual void init(Codec*) = 0;
virtual ~SecurityLayer() {}
+ private:
+ int ssf;
};
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/SecuritySettings.h b/cpp/src/qpid/sys/SecuritySettings.h
index bfcd08fd0f..d595cad660 100644
--- a/cpp/src/qpid/sys/SecuritySettings.h
+++ b/cpp/src/qpid/sys/SecuritySettings.h
@@ -21,6 +21,8 @@
* under the License.
*
*/
+#include <string>
+
namespace qpid {
namespace sys {
diff --git a/cpp/src/qpid/sys/Socket.h b/cpp/src/qpid/sys/Socket.h
index defec4879c..38183bd5fd 100644
--- a/cpp/src/qpid/sys/Socket.h
+++ b/cpp/src/qpid/sys/Socket.h
@@ -22,7 +22,6 @@
*
*/
-#include "qpid/sys/IOHandle.h"
#include "qpid/sys/IntegerTypes.h"
#include "qpid/CommonImportExport.h"
#include <string>
@@ -31,45 +30,43 @@ namespace qpid {
namespace sys {
class Duration;
+class IOHandle;
class SocketAddress;
-class QPID_COMMON_CLASS_EXTERN Socket : public IOHandle
+class Socket
{
public:
- /** Create a socket wrapper for descriptor. */
- QPID_COMMON_EXTERN Socket();
+ virtual ~Socket() {};
- /** Create a new Socket which is the same address family as this one */
- QPID_COMMON_EXTERN Socket* createSameTypeSocket() const;
+ virtual operator const IOHandle&() const = 0;
/** Set socket non blocking */
- void setNonblocking() const;
+ virtual void setNonblocking() const = 0;
- QPID_COMMON_EXTERN void setTcpNoDelay() const;
+ virtual void setTcpNoDelay() const = 0;
- QPID_COMMON_EXTERN void connect(const std::string& host, const std::string& port) const;
- QPID_COMMON_EXTERN void connect(const SocketAddress&) const;
+ virtual void connect(const SocketAddress&) const = 0;
+ virtual void finishConnect(const SocketAddress&) const = 0;
- QPID_COMMON_EXTERN void close() const;
+ virtual void close() const = 0;
/** Bind to a port and start listening.
*@param port 0 means choose an available port.
*@param backlog maximum number of pending connections.
*@return The bound port.
*/
- QPID_COMMON_EXTERN int listen(const std::string& host = "", const std::string& port = "0", int backlog = 10) const;
- QPID_COMMON_EXTERN int listen(const SocketAddress&, int backlog = 10) const;
+ virtual int listen(const SocketAddress&, int backlog = 10) const = 0;
/**
* Returns an address (host and port) for the remote end of the
* socket
*/
- QPID_COMMON_EXTERN std::string getPeerAddress() const;
+ virtual std::string getPeerAddress() const = 0;
/**
* Returns an address (host and port) for the local end of the
* socket
*/
- QPID_COMMON_EXTERN std::string getLocalAddress() const;
+ virtual std::string getLocalAddress() const = 0;
/**
* Returns the full address of the connection: local and remote host and port.
@@ -80,31 +77,24 @@ public:
* Returns the error code stored in the socket. This may be used
* to determine the result of a non-blocking connect.
*/
- QPID_COMMON_EXTERN int getError() const;
+ virtual int getError() const = 0;
/** Accept a connection from a socket that is already listening
* and has an incoming connection
*/
- QPID_COMMON_EXTERN Socket* accept() const;
+ virtual Socket* accept() const = 0;
- // TODO The following are raw operations, maybe they need better wrapping?
- QPID_COMMON_EXTERN int read(void *buf, size_t count) const;
- QPID_COMMON_EXTERN int write(const void *buf, size_t count) const;
+ virtual int read(void *buf, size_t count) const = 0;
+ virtual int write(const void *buf, size_t count) const = 0;
-private:
- /** Create socket */
- void createSocket(const SocketAddress&) const;
-
-public:
- /** Construct socket with existing handle */
- Socket(IOHandlePrivate*);
-
-protected:
- mutable std::string localname;
- mutable std::string peername;
- mutable bool nonblocking;
- mutable bool nodelay;
+ /* Transport security related: */
+ virtual int getKeyLen() const = 0;
+ virtual std::string getClientAuthId() const = 0;
};
+/** Make the default socket for whatever platform we are executing on
+ */
+QPID_COMMON_EXTERN Socket* createSocket();
+
}}
#endif /*!_sys_Socket_h*/
diff --git a/cpp/src/qpid/sys/SocketAddress.h b/cpp/src/qpid/sys/SocketAddress.h
index dcca109d94..a4da5cca79 100644
--- a/cpp/src/qpid/sys/SocketAddress.h
+++ b/cpp/src/qpid/sys/SocketAddress.h
@@ -44,11 +44,12 @@ public:
QPID_COMMON_EXTERN bool nextAddress();
QPID_COMMON_EXTERN std::string asString(bool numeric=true) const;
+ QPID_COMMON_EXTERN std::string getHost() const;
QPID_COMMON_EXTERN void setAddrInfoPort(uint16_t port);
QPID_COMMON_EXTERN static std::string asString(::sockaddr const * const addr, size_t addrlen);
QPID_COMMON_EXTERN static uint16_t getPort(::sockaddr const * const addr);
-
+
private:
std::string host;
diff --git a/cpp/src/qpid/sys/SslPlugin.cpp b/cpp/src/qpid/sys/SslPlugin.cpp
index 069e97758e..a40da24eb8 100644
--- a/cpp/src/qpid/sys/SslPlugin.cpp
+++ b/cpp/src/qpid/sys/SslPlugin.cpp
@@ -22,19 +22,19 @@
#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/broker/Broker.h"
+#include "qpid/broker/NameGenerator.h"
+#include "qpid/log/Statement.h"
#include "qpid/sys/AsynchIOHandler.h"
#include "qpid/sys/AsynchIO.h"
-#include "qpid/sys/ssl/SslIo.h"
+#include "qpid/sys/ssl/util.h"
#include "qpid/sys/ssl/SslSocket.h"
-#include "qpid/broker/Broker.h"
-#include "qpid/log/Statement.h"
+#include "qpid/sys/SocketAddress.h"
+#include "qpid/sys/SystemInfo.h"
+#include "qpid/sys/Poller.h"
#include <boost/bind.hpp>
-#include <memory>
-
+#include <boost/ptr_container/ptr_vector.hpp>
namespace qpid {
namespace sys {
@@ -64,38 +64,32 @@ struct SslServerOptions : ssl::SslOptions
}
};
-template <class T>
-class SslProtocolFactoryTmpl : public ProtocolFactory {
- private:
-
- typedef SslAcceptorTmpl<T> SslAcceptor;
-
+class SslProtocolFactory : public ProtocolFactory {
+ boost::ptr_vector<Socket> listeners;
+ boost::ptr_vector<AsynchAcceptor> acceptors;
Timer& brokerTimer;
uint32_t maxNegotiateTime;
+ uint16_t listeningPort;
const bool tcpNoDelay;
- T listener;
- const uint16_t listeningPort;
- std::auto_ptr<SslAcceptor> acceptor;
bool nodict;
public:
- SslProtocolFactoryTmpl(const SslServerOptions&, int backlog, bool nodelay, Timer& timer, uint32_t maxTime);
+ SslProtocolFactory(const qpid::broker::Broker::Options& opts, const SslServerOptions& options,
+ Timer& timer);
void accept(Poller::shared_ptr, ConnectionCodec::Factory*);
- void connect(Poller::shared_ptr, const std::string& host, const std::string& port,
+ void connect(Poller::shared_ptr, const std::string& name, const std::string& host, const std::string& port,
ConnectionCodec::Factory*,
- boost::function2<void, int, std::string> failed);
+ ConnectFailedCallback);
uint16_t getPort() const;
- bool supports(const std::string& capability);
private:
- void established(Poller::shared_ptr, const Socket&, ConnectionCodec::Factory*,
- bool isClient);
+ void establishedIncoming(Poller::shared_ptr, const Socket&, ConnectionCodec::Factory*);
+ void establishedOutgoing(Poller::shared_ptr, const Socket&, ConnectionCodec::Factory*, const std::string&);
+ void establishedCommon(AsynchIOHandler*, Poller::shared_ptr , const Socket&);
+ void connectFailed(const Socket&, int, const std::string&, ConnectFailedCallback);
};
-typedef SslProtocolFactoryTmpl<SslSocket> SslProtocolFactory;
-typedef SslProtocolFactoryTmpl<SslMuxSocket> SslMuxProtocolFactory;
-
// Static instance to initialise plugin
static struct SslPlugin : public Plugin {
@@ -124,7 +118,7 @@ static struct SslPlugin : public Plugin {
}
}
}
-
+
void initialize(Target& target) {
QPID_LOG(trace, "Initialising SSL plugin");
broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
@@ -139,19 +133,16 @@ static struct SslPlugin : public Plugin {
const broker::Broker::Options& opts = broker->getOptions();
- ProtocolFactory::shared_ptr protocol(options.multiplex ?
- static_cast<ProtocolFactory*>(new SslMuxProtocolFactory(options,
- opts.connectionBacklog,
- opts.tcpNoDelay,
- broker->getTimer(), opts.maxNegotiateTime)) :
- static_cast<ProtocolFactory*>(new SslProtocolFactory(options,
- opts.connectionBacklog,
- opts.tcpNoDelay,
- broker->getTimer(), opts.maxNegotiateTime)));
- QPID_LOG(notice, "Listening for " <<
- (options.multiplex ? "SSL or TCP" : "SSL") <<
- " connections on TCP port " <<
- protocol->getPort());
+ ProtocolFactory::shared_ptr protocol(
+ static_cast<ProtocolFactory*>(new SslProtocolFactory(opts, options, broker->getTimer())));
+
+ if (protocol->getPort()!=0 ) {
+ QPID_LOG(notice, "Listening for " <<
+ (options.multiplex ? "SSL or TCP" : "SSL") <<
+ " connections on TCP/TCP6 port " <<
+ protocol->getPort());
+ }
+
broker->registerProtocolFactory("ssl", protocol);
} catch (const std::exception& e) {
QPID_LOG(error, "Failed to initialise SSL plugin: " << e.what());
@@ -161,99 +152,133 @@ static struct SslPlugin : public Plugin {
}
} sslPlugin;
-template <class T>
-SslProtocolFactoryTmpl<T>::SslProtocolFactoryTmpl(const SslServerOptions& options, int backlog, bool nodelay, Timer& timer, uint32_t maxTime) :
+namespace {
+ // Expand list of Interfaces and addresses to a list of addresses
+ std::vector<std::string> expandInterfaces(const std::vector<std::string>& interfaces) {
+ std::vector<std::string> addresses;
+ // If there are no specific interfaces listed use a single "" to listen on every interface
+ if (interfaces.empty()) {
+ addresses.push_back("");
+ return addresses;
+ }
+ for (unsigned i = 0; i < interfaces.size(); ++i) {
+ const std::string& interface = interfaces[i];
+ if (!(SystemInfo::getInterfaceAddresses(interface, addresses))) {
+ // We don't have an interface of that name -
+ // Check for IPv6 ('[' ']') brackets and remove them
+ // then pass to be looked up directly
+ if (interface[0]=='[' && interface[interface.size()-1]==']') {
+ addresses.push_back(interface.substr(1, interface.size()-2));
+ } else {
+ addresses.push_back(interface);
+ }
+ }
+ }
+ return addresses;
+ }
+}
+
+SslProtocolFactory::SslProtocolFactory(const qpid::broker::Broker::Options& opts, const SslServerOptions& options,
+ Timer& timer) :
brokerTimer(timer),
- maxNegotiateTime(maxTime),
- tcpNoDelay(nodelay), listeningPort(listener.listen(options.port, backlog, options.certName, options.clientAuth)),
+ maxNegotiateTime(opts.maxNegotiateTime),
+ tcpNoDelay(opts.tcpNoDelay),
nodict(options.nodict)
-{}
-
-void SslEstablished(Poller::shared_ptr poller, const qpid::sys::SslSocket& s,
- ConnectionCodec::Factory* f, bool isClient,
- Timer& timer, uint32_t maxTime, bool tcpNoDelay, bool nodict) {
- qpid::sys::ssl::SslHandler* async = new qpid::sys::ssl::SslHandler(s.getFullAddress(), f, nodict);
-
- if (tcpNoDelay) {
- s.setTcpNoDelay(tcpNoDelay);
- QPID_LOG(info, "Set TCP_NODELAY on connection to " << s.getPeerAddress());
+{
+ std::vector<std::string> addresses = expandInterfaces(opts.listenInterfaces);
+ if (addresses.empty()) {
+ // We specified some interfaces, but couldn't find addresses for them
+ QPID_LOG(warning, "SSL: No specified network interfaces found: Not Listening");
+ listeningPort = 0;
}
- if (isClient) {
- async->setClient();
+ for (unsigned i = 0; i<addresses.size(); ++i) {
+ QPID_LOG(debug, "Using interface: " << addresses[i]);
+ SocketAddress sa(addresses[i], boost::lexical_cast<std::string>(options.port));
+
+ // We must have at least one resolved address
+ QPID_LOG(info, "Listening to: " << sa.asString())
+ Socket* s = options.multiplex ?
+ new SslMuxSocket(options.certName, options.clientAuth) :
+ new SslSocket(options.certName, options.clientAuth);
+ uint16_t lport = s->listen(sa, opts.connectionBacklog);
+ QPID_LOG(debug, "Listened to: " << lport);
+ listeners.push_back(s);
+
+ listeningPort = lport;
+
+ // Try any other resolved addresses
+ while (sa.nextAddress()) {
+ // Hack to ensure that all listening connections are on the same port
+ sa.setAddrInfoPort(listeningPort);
+ QPID_LOG(info, "Listening to: " << sa.asString())
+ Socket* s = options.multiplex ?
+ new SslMuxSocket(options.certName, options.clientAuth) :
+ new SslSocket(options.certName, options.clientAuth);
+ uint16_t lport = s->listen(sa, opts.connectionBacklog);
+ QPID_LOG(debug, "Listened to: " << lport);
+ listeners.push_back(s);
+ }
}
-
- 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,timer, maxTime);
- aio->start(poller);
-}
-
-template <>
-void SslProtocolFactory::established(Poller::shared_ptr poller, const Socket& s,
- ConnectionCodec::Factory* f, bool isClient) {
- const SslSocket *sslSock = dynamic_cast<const SslSocket*>(&s);
-
- SslEstablished(poller, *sslSock, f, isClient, brokerTimer, maxNegotiateTime, tcpNoDelay, nodict);
}
-template <class T>
-uint16_t SslProtocolFactoryTmpl<T>::getPort() const {
- return listeningPort; // Immutable no need for lock.
+void SslProtocolFactory::establishedIncoming(Poller::shared_ptr poller, const Socket& s,
+ ConnectionCodec::Factory* f) {
+ AsynchIOHandler* async = new AsynchIOHandler(broker::QPID_NAME_PREFIX+s.getFullAddress(), f, false, false);
+ establishedCommon(async, poller, s);
}
-template <class T>
-void SslProtocolFactoryTmpl<T>::accept(Poller::shared_ptr poller,
- ConnectionCodec::Factory* fact) {
- acceptor.reset(
- new SslAcceptor(listener,
- boost::bind(&SslProtocolFactoryTmpl<T>::established,
- this, poller, _1, fact, false)));
- acceptor->start(poller);
+void SslProtocolFactory::establishedOutgoing(Poller::shared_ptr poller, const Socket& s,
+ ConnectionCodec::Factory* f, const std::string& name) {
+ AsynchIOHandler* async = new AsynchIOHandler(name, f, true, false);
+ establishedCommon(async, poller, s);
}
-template <>
-void SslMuxProtocolFactory::established(Poller::shared_ptr poller, const Socket& s,
- ConnectionCodec::Factory* f, bool isClient) {
- const SslSocket *sslSock = dynamic_cast<const SslSocket*>(&s);
-
- if (sslSock) {
- SslEstablished(poller, *sslSock, f, isClient, brokerTimer, maxNegotiateTime, tcpNoDelay, nodict);
- return;
- }
-
- AsynchIOHandler* async = new AsynchIOHandler(s.getFullAddress(), f);
-
+void SslProtocolFactory::establishedCommon(AsynchIOHandler* async, Poller::shared_ptr poller, const Socket& s) {
if (tcpNoDelay) {
s.setTcpNoDelay();
QPID_LOG(info, "Set TCP_NODELAY on connection to " << s.getPeerAddress());
}
- if (isClient) {
- async->setClient();
- }
- AsynchIO* aio = AsynchIO::create
- (s,
- boost::bind(&AsynchIOHandler::readbuff, async, _1, _2),
- boost::bind(&AsynchIOHandler::eof, async, _1),
- boost::bind(&AsynchIOHandler::disconnect, async, _1),
- boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2),
- boost::bind(&AsynchIOHandler::nobuffs, async, _1),
- boost::bind(&AsynchIOHandler::idle, async, _1));
+ 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, brokerTimer, maxNegotiateTime);
aio->start(poller);
}
-template <class T>
-void SslProtocolFactoryTmpl<T>::connect(
+uint16_t SslProtocolFactory::getPort() const {
+ return listeningPort; // Immutable no need for lock.
+}
+
+void SslProtocolFactory::accept(Poller::shared_ptr poller,
+ ConnectionCodec::Factory* fact) {
+ for (unsigned i = 0; i<listeners.size(); ++i) {
+ acceptors.push_back(
+ AsynchAcceptor::create(listeners[i],
+ boost::bind(&SslProtocolFactory::establishedIncoming, this, poller, _1, fact)));
+ acceptors[i].start(poller);
+ }
+}
+
+void SslProtocolFactory::connectFailed(
+ const Socket& s, int ec, const std::string& emsg,
+ ConnectFailedCallback failedCb)
+{
+ failedCb(ec, emsg);
+ s.close();
+ delete &s;
+}
+
+void SslProtocolFactory::connect(
Poller::shared_ptr poller,
+ const std::string& name,
const std::string& host, const std::string& port,
ConnectionCodec::Factory* fact,
ConnectFailedCallback failed)
@@ -264,31 +289,23 @@ void SslProtocolFactoryTmpl<T>::connect(
// shutdown. The allocated SslConnector frees itself when it
// is no longer needed.
- qpid::sys::ssl::SslSocket* socket = new qpid::sys::ssl::SslSocket();
- new SslConnector(*socket, poller, host, port,
- boost::bind(&SslProtocolFactoryTmpl<T>::established, this, poller, _1, fact, true),
- failed);
-}
-
-namespace
-{
-const std::string SSL = "ssl";
-}
-
-template <>
-bool SslProtocolFactory::supports(const std::string& capability)
-{
- std::string s = capability;
- transform(s.begin(), s.end(), s.begin(), tolower);
- return s == SSL;
-}
-
-template <>
-bool SslMuxProtocolFactory::supports(const std::string& capability)
-{
- std::string s = capability;
- transform(s.begin(), s.end(), s.begin(), tolower);
- return s == SSL || s == "tcp";
+ Socket* socket = new qpid::sys::ssl::SslSocket();
+ try {
+ AsynchConnector* c = AsynchConnector::create(
+ *socket,
+ host,
+ port,
+ boost::bind(&SslProtocolFactory::establishedOutgoing,
+ this, poller, _1, fact, name),
+ boost::bind(&SslProtocolFactory::connectFailed,
+ this, _1, _2, _3, failed));
+ c->start(poller);
+ } catch (std::exception&) {
+ // TODO: Design question - should we do the error callback and also throw?
+ int errCode = socket->getError();
+ connectFailed(*socket, errCode, strError(errCode), failed);
+ throw;
+ }
}
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/TCPIOPlugin.cpp b/cpp/src/qpid/sys/TCPIOPlugin.cpp
index ed7cc3748d..1ef8708cd0 100644
--- a/cpp/src/qpid/sys/TCPIOPlugin.cpp
+++ b/cpp/src/qpid/sys/TCPIOPlugin.cpp
@@ -20,15 +20,17 @@
*/
#include "qpid/sys/ProtocolFactory.h"
-#include "qpid/sys/AsynchIOHandler.h"
-#include "qpid/sys/AsynchIO.h"
#include "qpid/Plugin.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/NameGenerator.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/AsynchIOHandler.h"
+#include "qpid/sys/AsynchIO.h"
#include "qpid/sys/Socket.h"
#include "qpid/sys/SocketAddress.h"
+#include "qpid/sys/SystemInfo.h"
#include "qpid/sys/Poller.h"
-#include "qpid/broker/Broker.h"
-#include "qpid/log/Statement.h"
#include <boost/bind.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
@@ -47,20 +49,19 @@ class AsynchIOProtocolFactory : public ProtocolFactory {
const bool tcpNoDelay;
public:
- AsynchIOProtocolFactory(const std::string& host, const std::string& port,
- int backlog, bool nodelay,
- Timer& timer, uint32_t maxTime,
- bool shouldListen);
+ AsynchIOProtocolFactory(const qpid::broker::Broker::Options& opts, Timer& timer, bool shouldListen);
void accept(Poller::shared_ptr, ConnectionCodec::Factory*);
- void connect(Poller::shared_ptr, const std::string& host, const std::string& port,
+ void connect(Poller::shared_ptr, const std::string& name,
+ const std::string& host, const std::string& port,
ConnectionCodec::Factory*,
ConnectFailedCallback);
uint16_t getPort() const;
private:
- void established(Poller::shared_ptr, const Socket&, ConnectionCodec::Factory*,
- bool isClient);
+ void establishedIncoming(Poller::shared_ptr, const Socket&, ConnectionCodec::Factory*);
+ void establishedOutgoing(Poller::shared_ptr, const Socket&, ConnectionCodec::Factory*, const std::string&);
+ void establishedCommon(AsynchIOHandler*, Poller::shared_ptr , const Socket&);
void connectFailed(const Socket&, int, const std::string&, ConnectFailedCallback);
};
@@ -93,14 +94,9 @@ static class TCPIOPlugin : public Plugin {
bool shouldListen = !sslMultiplexEnabled();
ProtocolFactory::shared_ptr protocolt(
- new AsynchIOProtocolFactory(
- "", boost::lexical_cast<std::string>(opts.port),
- opts.connectionBacklog,
- opts.tcpNoDelay,
- broker->getTimer(), opts.maxNegotiateTime,
- shouldListen));
-
- if (shouldListen) {
+ new AsynchIOProtocolFactory(opts, broker->getTimer(),shouldListen));
+
+ if (shouldListen && protocolt->getPort()!=0 ) {
QPID_LOG(notice, "Listening on TCP/TCP6 port " << protocolt->getPort());
}
@@ -109,54 +105,93 @@ static class TCPIOPlugin : public Plugin {
}
} tcpPlugin;
-AsynchIOProtocolFactory::AsynchIOProtocolFactory(const std::string& host, const std::string& port,
- int backlog, bool nodelay,
- Timer& timer, uint32_t maxTime,
- bool shouldListen) :
+namespace {
+ // Expand list of Interfaces and addresses to a list of addresses
+ std::vector<std::string> expandInterfaces(const std::vector<std::string>& interfaces) {
+ std::vector<std::string> addresses;
+ // If there are no specific interfaces listed use a single "" to listen on every interface
+ if (interfaces.empty()) {
+ addresses.push_back("");
+ return addresses;
+ }
+ for (unsigned i = 0; i < interfaces.size(); ++i) {
+ const std::string& interface = interfaces[i];
+ if (!(SystemInfo::getInterfaceAddresses(interface, addresses))) {
+ // We don't have an interface of that name -
+ // Check for IPv6 ('[' ']') brackets and remove them
+ // then pass to be looked up directly
+ if (interface[0]=='[' && interface[interface.size()-1]==']') {
+ addresses.push_back(interface.substr(1, interface.size()-2));
+ } else {
+ addresses.push_back(interface);
+ }
+ }
+ }
+ return addresses;
+ }
+}
+
+AsynchIOProtocolFactory::AsynchIOProtocolFactory(const qpid::broker::Broker::Options& opts, Timer& timer, bool shouldListen) :
brokerTimer(timer),
- maxNegotiateTime(maxTime),
- tcpNoDelay(nodelay)
+ maxNegotiateTime(opts.maxNegotiateTime),
+ tcpNoDelay(opts.tcpNoDelay)
{
if (!shouldListen) {
- listeningPort = boost::lexical_cast<uint16_t>(port);
+ listeningPort = boost::lexical_cast<uint16_t>(opts.port);
return;
}
- SocketAddress sa(host, port);
-
- // We must have at least one resolved address
- QPID_LOG(info, "Listening to: " << sa.asString())
- Socket* s = new Socket;
- uint16_t lport = s->listen(sa, backlog);
- QPID_LOG(debug, "Listened to: " << lport);
- listeners.push_back(s);
+ std::vector<std::string> addresses = expandInterfaces(opts.listenInterfaces);
+ if (addresses.empty()) {
+ // We specified some interfaces, but couldn't find addresses for them
+ QPID_LOG(warning, "TCP/TCP6: No specified network interfaces found: Not Listening");
+ listeningPort = 0;
+ }
- listeningPort = lport;
+ for (unsigned i = 0; i<addresses.size(); ++i) {
+ QPID_LOG(debug, "Using interface: " << addresses[i]);
+ SocketAddress sa(addresses[i], boost::lexical_cast<std::string>(opts.port));
- // Try any other resolved addresses
- while (sa.nextAddress()) {
- // Hack to ensure that all listening connections are on the same port
- sa.setAddrInfoPort(listeningPort);
+ // We must have at least one resolved address
QPID_LOG(info, "Listening to: " << sa.asString())
- Socket* s = new Socket;
- uint16_t lport = s->listen(sa, backlog);
+ Socket* s = createSocket();
+ uint16_t lport = s->listen(sa, opts.connectionBacklog);
QPID_LOG(debug, "Listened to: " << lport);
listeners.push_back(s);
+
+ listeningPort = lport;
+
+ // Try any other resolved addresses
+ while (sa.nextAddress()) {
+ // Hack to ensure that all listening connections are on the same port
+ sa.setAddrInfoPort(listeningPort);
+ QPID_LOG(info, "Listening to: " << sa.asString())
+ Socket* s = createSocket();
+ uint16_t lport = s->listen(sa, opts.connectionBacklog);
+ QPID_LOG(debug, "Listened to: " << lport);
+ listeners.push_back(s);
+ }
}
+}
+void AsynchIOProtocolFactory::establishedIncoming(Poller::shared_ptr poller, const Socket& s,
+ ConnectionCodec::Factory* f) {
+ AsynchIOHandler* async = new AsynchIOHandler(broker::QPID_NAME_PREFIX+s.getFullAddress(), f, false, false);
+ establishedCommon(async, poller, s);
}
-void AsynchIOProtocolFactory::established(Poller::shared_ptr poller, const Socket& s,
- ConnectionCodec::Factory* f, bool isClient) {
- AsynchIOHandler* async = new AsynchIOHandler(s.getFullAddress(), f);
+void AsynchIOProtocolFactory::establishedOutgoing(Poller::shared_ptr poller, const Socket& s,
+ ConnectionCodec::Factory* f, const std::string& name) {
+ AsynchIOHandler* async = new AsynchIOHandler(name, f, true, false);
+ establishedCommon(async, poller, s);
+}
+void AsynchIOProtocolFactory::establishedCommon(AsynchIOHandler* async, Poller::shared_ptr poller, const Socket& s) {
if (tcpNoDelay) {
s.setTcpNoDelay();
QPID_LOG(info, "Set TCP_NODELAY on connection to " << s.getPeerAddress());
}
- if (isClient)
- async->setClient();
AsynchIO* aio = AsynchIO::create
(s,
boost::bind(&AsynchIOHandler::readbuff, async, _1, _2),
@@ -179,7 +214,7 @@ void AsynchIOProtocolFactory::accept(Poller::shared_ptr poller,
for (unsigned i = 0; i<listeners.size(); ++i) {
acceptors.push_back(
AsynchAcceptor::create(listeners[i],
- boost::bind(&AsynchIOProtocolFactory::established, this, poller, _1, fact, false)));
+ boost::bind(&AsynchIOProtocolFactory::establishedIncoming, this, poller, _1, fact)));
acceptors[i].start(poller);
}
}
@@ -195,6 +230,7 @@ void AsynchIOProtocolFactory::connectFailed(
void AsynchIOProtocolFactory::connect(
Poller::shared_ptr poller,
+ const std::string& name,
const std::string& host, const std::string& port,
ConnectionCodec::Factory* fact,
ConnectFailedCallback failed)
@@ -204,14 +240,14 @@ void AsynchIOProtocolFactory::connect(
// upon connection failure or by the AsynchIO upon connection
// shutdown. The allocated AsynchConnector frees itself when it
// is no longer needed.
- Socket* socket = new Socket();
+ Socket* socket = createSocket();
try {
AsynchConnector* c = AsynchConnector::create(
*socket,
host,
port,
- boost::bind(&AsynchIOProtocolFactory::established,
- this, poller, _1, fact, true),
+ boost::bind(&AsynchIOProtocolFactory::establishedOutgoing,
+ this, poller, _1, fact, name),
boost::bind(&AsynchIOProtocolFactory::connectFailed,
this, _1, _2, _3, failed));
c->start(poller);
diff --git a/cpp/src/qpid/sys/Timer.cpp b/cpp/src/qpid/sys/Timer.cpp
index 973c6bd8b7..f8eef2c9ec 100644
--- a/cpp/src/qpid/sys/Timer.cpp
+++ b/cpp/src/qpid/sys/Timer.cpp
@@ -96,18 +96,13 @@ void TimerTask::cancel() {
state = CANCELLED;
}
-void TimerTask::setFired() {
- // Set nextFireTime to just before now, making readyToFire() true.
- nextFireTime = AbsTime(sys::now(), Duration(-1));
-}
-
-
+// TODO AStitcher 21/08/09 The threshholds for emitting warnings are a little arbitrary
Timer::Timer() :
active(false),
late(50 * TIME_MSEC),
overran(2 * TIME_MSEC),
lateCancel(500 * TIME_MSEC),
- warn(5 * TIME_SEC)
+ warn(60 * TIME_SEC)
{
start();
}
@@ -133,7 +128,6 @@ public:
}
};
-// TODO AStitcher 21/08/09 The threshholds for emitting warnings are a little arbitrary
void Timer::run()
{
Monitor::ScopedLock l(monitor);
@@ -151,10 +145,6 @@ void Timer::run()
{
TimerTaskCallbackScope s(*t);
if (s) {
- {
- Monitor::ScopedUnlock u(monitor);
- drop(t);
- }
if (delay > lateCancel) {
QPID_LOG(debug, t->name << " cancelled timer woken up " <<
delay / TIME_MSEC << "ms late");
@@ -171,8 +161,8 @@ void Timer::run()
if (!tasks.empty()) {
overrun = Duration(tasks.top()->nextFireTime, end);
}
- bool warningsEnabled;
- QPID_LOG_TEST(warning, warningsEnabled);
+ bool warningsEnabled; // TimerWarning enabled
+ QPID_LOG_TEST(debug, warningsEnabled); // TimerWarning emitted at debug level
if (warningsEnabled) {
if (overrun > overran) {
if (delay > overran) // if delay is significant to an overrun.
@@ -235,9 +225,6 @@ void Timer::fire(boost::intrusive_ptr<TimerTask> t) {
}
}
-// Provided for subclasses: called when a task is droped.
-void Timer::drop(boost::intrusive_ptr<TimerTask>) {}
-
bool operator<(const intrusive_ptr<TimerTask>& a,
const intrusive_ptr<TimerTask>& b)
{
diff --git a/cpp/src/qpid/sys/Timer.h b/cpp/src/qpid/sys/Timer.h
index 5731b8d977..5045009609 100644
--- a/cpp/src/qpid/sys/Timer.h
+++ b/cpp/src/qpid/sys/Timer.h
@@ -67,10 +67,6 @@ class TimerTask : public RefCounted {
std::string getName() const { return name; }
- // Move the nextFireTime so readyToFire is true.
- // Used by the cluster, where tasks are fired on cluster events, not on local time.
- QPID_COMMON_EXTERN void setFired();
-
protected:
// Must be overridden with callback
virtual void fire() = 0;
@@ -99,7 +95,7 @@ class Timer : private Runnable {
protected:
QPID_COMMON_EXTERN virtual void fire(boost::intrusive_ptr<TimerTask> task);
- QPID_COMMON_EXTERN virtual void drop(boost::intrusive_ptr<TimerTask> task);
+
// Allow derived classes to change the late/overran thresholds.
Duration late;
Duration overran;
diff --git a/cpp/src/qpid/sys/TimerWarnings.cpp b/cpp/src/qpid/sys/TimerWarnings.cpp
index 85e26da54a..00fb0d9db6 100644
--- a/cpp/src/qpid/sys/TimerWarnings.cpp
+++ b/cpp/src/qpid/sys/TimerWarnings.cpp
@@ -56,18 +56,18 @@ void TimerWarnings::log() {
std::string task = i->first;
TaskStats& stats = i->second;
if (stats.lateDelay.count)
- QPID_LOG(info, task << " task late "
+ QPID_LOG(debug, task << " task late "
<< stats.lateDelay.count << " times by "
<< stats.lateDelay.average()/TIME_MSEC << "ms on average.");
if (stats.overranOverrun.count)
- QPID_LOG(info, task << " task overran "
+ QPID_LOG(debug, task << " task overran "
<< stats.overranOverrun.count << " times by "
<< stats.overranOverrun.average()/TIME_MSEC << "ms (taking "
<< stats.overranTime.average() << "ns) on average.");
if (stats.lateAndOverranOverrun.count)
- QPID_LOG(info, task << " task late and overran "
+ QPID_LOG(debug, task << " task late and overran "
<< stats.lateAndOverranOverrun.count << " times: late "
<< stats.lateAndOverranDelay.average()/TIME_MSEC << "ms, overran "
<< stats.lateAndOverranOverrun.average()/TIME_MSEC << "ms (taking "
diff --git a/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp b/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp
index 29b91f3e7a..79d9d08a59 100644
--- a/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp
+++ b/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp
@@ -29,8 +29,8 @@ 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),
+CyrusSecurityLayer::CyrusSecurityLayer(sasl_conn_t* c, uint16_t maxFrameSize, int ssf) :
+ SecurityLayer(ssf), conn(c), decrypted(0), decryptedSize(0), encrypted(0), encryptedSize(0), codec(0), maxInputSize(0),
decodeBuffer(maxFrameSize), encodeBuffer(maxFrameSize), encoded(0)
{
const void* value(0);
@@ -68,7 +68,7 @@ size_t CyrusSecurityLayer::decode(const char* input, size_t size)
return size;
}
-size_t CyrusSecurityLayer::encode(const char* buffer, size_t size)
+size_t CyrusSecurityLayer::encode(char* buffer, size_t size)
{
size_t processed = 0;//records how many bytes have been written to buffer
do {
@@ -92,12 +92,12 @@ size_t CyrusSecurityLayer::encode(const char* buffer, size_t size)
//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);
+ ::memcpy(buffer + processed, encrypted, remaining);
processed += remaining;
encrypted += remaining;
encryptedSize -= remaining;
} else {
- ::memcpy(const_cast<char*>(buffer + processed), encrypted, encryptedSize);
+ ::memcpy(buffer + processed, encrypted, encryptedSize);
processed += encryptedSize;
encrypted = 0;
encryptedSize = 0;
diff --git a/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.h b/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.h
index 1645cf1a58..ae86ba5569 100644
--- a/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.h
+++ b/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.h
@@ -37,9 +37,9 @@ namespace cyrus {
class CyrusSecurityLayer : public qpid::sys::SecurityLayer
{
public:
- CyrusSecurityLayer(sasl_conn_t*, uint16_t maxFrameSize);
+ CyrusSecurityLayer(sasl_conn_t*, uint16_t maxFrameSize, int ssf);
size_t decode(const char* buffer, size_t size);
- size_t encode(const char* buffer, size_t size);
+ size_t encode(char* buffer, size_t size);
bool canEncode();
void init(qpid::sys::Codec*);
private:
diff --git a/cpp/src/qpid/sys/epoll/EpollPoller.cpp b/cpp/src/qpid/sys/epoll/EpollPoller.cpp
index c23403c66d..6fdf99637f 100644
--- a/cpp/src/qpid/sys/epoll/EpollPoller.cpp
+++ b/cpp/src/qpid/sys/epoll/EpollPoller.cpp
@@ -20,7 +20,6 @@
*/
#include "qpid/sys/Poller.h"
-#include "qpid/sys/IOHandle.h"
#include "qpid/sys/Mutex.h"
#include "qpid/sys/AtomicCount.h"
#include "qpid/sys/DeletionManager.h"
@@ -64,12 +63,12 @@ class PollerHandlePrivate {
};
::__uint32_t events;
- const IOHandlePrivate* ioHandle;
+ const IOHandle* ioHandle;
PollerHandle* pollerHandle;
FDStat stat;
Mutex lock;
- PollerHandlePrivate(const IOHandlePrivate* h, PollerHandle* p) :
+ PollerHandlePrivate(const IOHandle* h, PollerHandle* p) :
events(0),
ioHandle(h),
pollerHandle(p),
@@ -77,7 +76,7 @@ class PollerHandlePrivate {
}
int fd() const {
- return toFd(ioHandle);
+ return ioHandle->fd;
}
bool isActive() const {
@@ -138,7 +137,7 @@ class PollerHandlePrivate {
};
PollerHandle::PollerHandle(const IOHandle& h) :
- impl(new PollerHandlePrivate(h.impl, this))
+ impl(new PollerHandlePrivate(&h, this))
{}
PollerHandle::~PollerHandle() {
@@ -385,6 +384,7 @@ void PollerPrivate::resetMode(PollerHandlePrivate& eh) {
int rc = ::epoll_ctl(epollFd, EPOLL_CTL_MOD, eh.fd(), &epe);
// If something has closed the fd in the meantime try adding it back
if (rc ==-1 && errno == ENOENT) {
+ eh.setIdle(); // Reset our handle as if starting from scratch
rc = ::epoll_ctl(epollFd, EPOLL_CTL_ADD, eh.fd(), &epe);
}
QPID_POSIX_CHECK(rc);
diff --git a/cpp/src/qpid/sys/posix/AsynchIO.cpp b/cpp/src/qpid/sys/posix/AsynchIO.cpp
index 31355627cd..353a55f50c 100644
--- a/cpp/src/qpid/sys/posix/AsynchIO.cpp
+++ b/cpp/src/qpid/sys/posix/AsynchIO.cpp
@@ -143,6 +143,7 @@ class AsynchConnector : public qpid::sys::AsynchConnector,
private:
void connComplete(DispatchHandle& handle);
+ void requestedCall(RequestCallback rCb);
private:
ConnectedCallback connCallback;
@@ -158,6 +159,7 @@ public:
FailedCallback failCb);
void start(Poller::shared_ptr poller);
void stop();
+ void requestCallback(RequestCallback rCb);
};
AsynchConnector::AsynchConnector(const Socket& s,
@@ -191,11 +193,30 @@ void AsynchConnector::stop()
stopWatch();
}
+void AsynchConnector::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(&AsynchConnector::requestedCall, this, callback));
+}
+
+void AsynchConnector::requestedCall(RequestCallback callback) {
+ assert(callback);
+ callback(*this);
+}
+
void AsynchConnector::connComplete(DispatchHandle& h)
{
int errCode = socket.getError();
if (errCode == 0) {
h.stopWatch();
+ try {
+ socket.finishConnect(sa);
+ } catch (const std::exception& e) {
+ failCallback(socket, 0, e.what());
+ DispatchHandle::doDelete();
+ return;
+ }
connCallback(socket);
} else {
// Retry while we cause an immediate exception
@@ -247,10 +268,9 @@ public:
virtual void notifyPendingWrite();
virtual void queueWriteClose();
virtual bool writeQueueEmpty();
- virtual void startReading();
- virtual void stopReading();
virtual void requestCallback(RequestCallback);
virtual BufferBase* getQueuedBuffer();
+ virtual SecuritySettings getSecuritySettings();
private:
~AsynchIO();
@@ -282,13 +302,6 @@ private:
* 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,
@@ -307,8 +320,7 @@ AsynchIO::AsynchIO(const Socket& s,
idleCallback(iCb),
socket(s),
queuedClose(false),
- writePending(false),
- readingStopped(false) {
+ writePending(false) {
s.setNonblocking();
}
@@ -344,7 +356,7 @@ void AsynchIO::queueReadBuffer(BufferBase* buff) {
bool queueWasEmpty = bufferQueue.empty();
bufferQueue.push_back(buff);
- if (queueWasEmpty && !readingStopped)
+ if (queueWasEmpty)
DispatchHandle::rewatchRead();
}
@@ -354,7 +366,7 @@ void AsynchIO::unread(BufferBase* buff) {
bool queueWasEmpty = bufferQueue.empty();
bufferQueue.push_front(buff);
- if (queueWasEmpty && !readingStopped)
+ if (queueWasEmpty)
DispatchHandle::rewatchRead();
}
@@ -386,17 +398,6 @@ 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?)
@@ -429,11 +430,6 @@ AsynchIO::BufferBase* AsynchIO::getQueuedBuffer() {
* to put it in and reading is not stopped by flow control.
*/
void AsynchIO::readable(DispatchHandle& h) {
- if (readingStopped) {
- // We have been flow controlled.
- QPID_PROBE1(asynchio_read_flowcontrolled, &h);
- return;
- }
AbsTime readStartTime = AbsTime::now();
size_t total = 0;
int readCalls = 0;
@@ -455,12 +451,6 @@ void AsynchIO::readable(DispatchHandle& h) {
total += rc;
readCallback(*this, buff);
- if (readingStopped) {
- // We have been flow controlled.
- QPID_PROBE4(asynchio_read_finished_flowcontrolled, &h, duration, total, readCalls);
- break;
- }
-
if (rc != readCount) {
// If we didn't fill the read buffer then time to stop reading
QPID_PROBE4(asynchio_read_finished_done, &h, duration, total, readCalls);
@@ -626,6 +616,13 @@ void AsynchIO::close(DispatchHandle& h) {
}
}
+SecuritySettings AsynchIO::getSecuritySettings() {
+ SecuritySettings settings;
+ settings.ssf = socket.getKeyLen();
+ settings.authid = socket.getClientAuthId();
+ return settings;
+}
+
} // namespace posix
AsynchAcceptor* AsynchAcceptor::create(const Socket& s,
diff --git a/cpp/src/qpid/sys/posix/Socket.cpp b/cpp/src/qpid/sys/posix/BSDSocket.cpp
index 77ae1af60c..7c31b13ae9 100644
--- a/cpp/src/qpid/sys/posix/Socket.cpp
+++ b/cpp/src/qpid/sys/posix/BSDSocket.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "qpid/sys/Socket.h"
+#include "qpid/sys/posix/BSDSocket.h"
#include "qpid/sys/SocketAddress.h"
#include "qpid/sys/posix/check.h"
@@ -67,25 +67,41 @@ uint16_t getLocalPort(int fd)
}
}
-Socket::Socket() :
- IOHandle(new IOHandlePrivate),
+BSDSocket::BSDSocket() :
+ fd(-1),
+ handle(new IOHandle),
nonblocking(false),
nodelay(false)
{}
-Socket::Socket(IOHandlePrivate* h) :
- IOHandle(h),
+Socket* createSocket()
+{
+ return new BSDSocket;
+}
+
+BSDSocket::BSDSocket(int fd0) :
+ fd(fd0),
+ handle(new IOHandle(fd)),
nonblocking(false),
nodelay(false)
{}
-void Socket::createSocket(const SocketAddress& sa) const
+BSDSocket::~BSDSocket()
+{}
+
+BSDSocket::operator const IOHandle&() const
+{
+ return *handle;
+}
+
+void BSDSocket::createSocket(const SocketAddress& sa) const
{
- int& socket = impl->fd;
- if (socket != -1) Socket::close();
+ int& socket = fd;
+ if (socket != -1) BSDSocket::close();
int s = ::socket(getAddrInfo(sa).ai_family, getAddrInfo(sa).ai_socktype, 0);
if (s < 0) throw QPID_POSIX_ERROR(errno);
socket = s;
+ *handle = IOHandle(s);
try {
if (nonblocking) setNonblocking();
@@ -98,50 +114,31 @@ void Socket::createSocket(const SocketAddress& sa) const
} catch (std::exception&) {
::close(s);
socket = -1;
+ *handle = IOHandle();
throw;
}
}
-Socket* Socket::createSameTypeSocket() const {
- int& socket = impl->fd;
- // Socket currently has no actual socket attached
- if (socket == -1)
- return new Socket;
-
- ::sockaddr_storage sa;
- ::socklen_t salen = sizeof(sa);
- QPID_POSIX_CHECK(::getsockname(socket, (::sockaddr*)&sa, &salen));
- int s = ::socket(sa.ss_family, SOCK_STREAM, 0); // Currently only work with SOCK_STREAM
- if (s < 0) throw QPID_POSIX_ERROR(errno);
- return new Socket(new IOHandlePrivate(s));
-}
-
-void Socket::setNonblocking() const {
- int& socket = impl->fd;
+void BSDSocket::setNonblocking() const {
+ int& socket = fd;
nonblocking = true;
if (socket != -1) {
QPID_POSIX_CHECK(::fcntl(socket, F_SETFL, O_NONBLOCK));
}
}
-void Socket::setTcpNoDelay() const
+void BSDSocket::setTcpNoDelay() const
{
- int& socket = impl->fd;
+ int& socket = fd;
nodelay = true;
if (socket != -1) {
int flag = 1;
- int result = ::setsockopt(impl->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag));
+ int result = ::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag));
QPID_POSIX_CHECK(result);
}
}
-void Socket::connect(const std::string& host, const std::string& port) const
-{
- SocketAddress sa(host, port);
- connect(sa);
-}
-
-void Socket::connect(const SocketAddress& addr) const
+void BSDSocket::connect(const SocketAddress& addr) const
{
// The display name for an outbound connection needs to be the name that was specified
// for the address rather than a resolved IP address as we don't know which of
@@ -154,7 +151,7 @@ void Socket::connect(const SocketAddress& addr) const
createSocket(addr);
- const int& socket = impl->fd;
+ const int& socket = fd;
// TODO the correct thing to do here is loop on failure until you've used all the returned addresses
if ((::connect(socket, getAddrInfo(addr).ai_addr, getAddrInfo(addr).ai_addrlen) < 0) &&
(errno != EINPROGRESS)) {
@@ -165,11 +162,6 @@ void Socket::connect(const SocketAddress& addr) const
// remote port (which is unoccupied) as the port to bind the local
// end of the socket, resulting in a "circular" connection.
//
- // This seems like something the OS should prevent but I have
- // confirmed that sporadic hangs in
- // cluster_tests.LongTests.test_failover on RHEL5 are caused by
- // such a circular connection.
- //
// Raise an error if we see such a connection, since we know there is
// no listener on the peer address.
//
@@ -179,26 +171,25 @@ void Socket::connect(const SocketAddress& addr) const
}
}
+void BSDSocket::finishConnect(const SocketAddress&) const
+{
+}
+
void
-Socket::close() const
+BSDSocket::close() const
{
- int& socket = impl->fd;
+ int& socket = fd;
if (socket == -1) return;
if (::close(socket) < 0) throw QPID_POSIX_ERROR(errno);
socket = -1;
+ *handle = IOHandle();
}
-int Socket::listen(const std::string& host, const std::string& port, int backlog) const
-{
- SocketAddress sa(host, port);
- return listen(sa, backlog);
-}
-
-int Socket::listen(const SocketAddress& sa, int backlog) const
+int BSDSocket::listen(const SocketAddress& sa, int backlog) const
{
createSocket(sa);
- const int& socket = impl->fd;
+ const int& socket = fd;
int yes=1;
QPID_POSIX_CHECK(::setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)));
@@ -210,11 +201,11 @@ int Socket::listen(const SocketAddress& sa, int backlog) const
return getLocalPort(socket);
}
-Socket* Socket::accept() const
+Socket* BSDSocket::accept() const
{
- int afd = ::accept(impl->fd, 0, 0);
+ int afd = ::accept(fd, 0, 0);
if ( afd >= 0) {
- Socket* s = new Socket(new IOHandlePrivate(afd));
+ BSDSocket* s = new BSDSocket(afd);
s->localname = localname;
return s;
}
@@ -223,41 +214,51 @@ Socket* Socket::accept() const
else throw QPID_POSIX_ERROR(errno);
}
-int Socket::read(void *buf, size_t count) const
+int BSDSocket::read(void *buf, size_t count) const
{
- return ::read(impl->fd, buf, count);
+ return ::read(fd, buf, count);
}
-int Socket::write(const void *buf, size_t count) const
+int BSDSocket::write(const void *buf, size_t count) const
{
- return ::write(impl->fd, buf, count);
+ return ::write(fd, buf, count);
}
-std::string Socket::getPeerAddress() const
+std::string BSDSocket::getPeerAddress() const
{
if (peername.empty()) {
- peername = getName(impl->fd, false);
+ peername = getName(fd, false);
}
return peername;
}
-std::string Socket::getLocalAddress() const
+std::string BSDSocket::getLocalAddress() const
{
if (localname.empty()) {
- localname = getName(impl->fd, true);
+ localname = getName(fd, true);
}
return localname;
}
-int Socket::getError() const
+int BSDSocket::getError() const
{
int result;
socklen_t rSize = sizeof (result);
- if (::getsockopt(impl->fd, SOL_SOCKET, SO_ERROR, &result, &rSize) < 0)
+ if (::getsockopt(fd, SOL_SOCKET, SO_ERROR, &result, &rSize) < 0)
throw QPID_POSIX_ERROR(errno);
return result;
}
+int BSDSocket::getKeyLen() const
+{
+ return 0;
+}
+
+std::string BSDSocket::getClientAuthId() const
+{
+ return std::string();
+}
+
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/posix/BSDSocket.h b/cpp/src/qpid/sys/posix/BSDSocket.h
new file mode 100644
index 0000000000..862d36c1b9
--- /dev/null
+++ b/cpp/src/qpid/sys/posix/BSDSocket.h
@@ -0,0 +1,113 @@
+#ifndef QPID_SYS_BSDSOCKET_H
+#define QPID_SYS_BSDSOCKET_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/IntegerTypes.h"
+#include "qpid/CommonImportExport.h"
+#include <string>
+
+#include <boost/scoped_ptr.hpp>
+
+namespace qpid {
+namespace sys {
+
+class Duration;
+class IOHandle;
+class SocketAddress;
+
+namespace ssl {
+class SslMuxSocket;
+}
+
+class QPID_COMMON_CLASS_EXTERN BSDSocket : public Socket
+{
+public:
+ /** Create a socket wrapper for descriptor. */
+ QPID_COMMON_EXTERN BSDSocket();
+ QPID_COMMON_EXTERN ~BSDSocket();
+
+ QPID_COMMON_EXTERN operator const IOHandle&() const;
+
+ /** Set socket non blocking */
+ QPID_COMMON_EXTERN virtual void setNonblocking() const;
+
+ QPID_COMMON_EXTERN virtual void setTcpNoDelay() const;
+
+ QPID_COMMON_EXTERN virtual void connect(const SocketAddress&) const;
+ QPID_COMMON_EXTERN virtual void finishConnect(const SocketAddress&) const;
+
+ QPID_COMMON_EXTERN virtual void close() const;
+
+ /** Bind to a port and start listening.
+ *@return The bound port number
+ */
+ QPID_COMMON_EXTERN virtual int listen(const SocketAddress&, int backlog = 10) const;
+
+ /**
+ * Returns an address (host and port) for the remote end of the
+ * socket
+ */
+ QPID_COMMON_EXTERN std::string getPeerAddress() const;
+ /**
+ * Returns an address (host and port) for the local end of the
+ * socket
+ */
+ QPID_COMMON_EXTERN std::string getLocalAddress() const;
+
+ /**
+ * Returns the error code stored in the socket. This may be used
+ * to determine the result of a non-blocking connect.
+ */
+ QPID_COMMON_EXTERN int getError() const;
+
+ /** Accept a connection from a socket that is already listening
+ * and has an incoming connection
+ */
+ QPID_COMMON_EXTERN virtual Socket* accept() const;
+
+ // TODO The following are raw operations, maybe they need better wrapping?
+ QPID_COMMON_EXTERN virtual int read(void *buf, size_t count) const;
+ QPID_COMMON_EXTERN virtual int write(const void *buf, size_t count) const;
+
+ QPID_COMMON_EXTERN int getKeyLen() const;
+ QPID_COMMON_EXTERN std::string getClientAuthId() const;
+
+protected:
+ /** Create socket */
+ void createSocket(const SocketAddress&) const;
+
+ mutable int fd;
+ mutable boost::scoped_ptr<IOHandle> handle;
+ mutable std::string localname;
+ mutable std::string peername;
+ mutable bool nonblocking;
+ mutable bool nodelay;
+
+ /** Construct socket with existing handle */
+ BSDSocket(int fd);
+ friend class qpid::sys::ssl::SslMuxSocket; // Needed for this constructor
+};
+
+}}
+#endif /*!QPID_SYS_BSDSOCKET_H*/
diff --git a/cpp/src/qpid/sys/posix/FileSysDir.cpp b/cpp/src/qpid/sys/posix/FileSysDir.cpp
index 22dc487e74..cec580164d 100755
--- a/cpp/src/qpid/sys/posix/FileSysDir.cpp
+++ b/cpp/src/qpid/sys/posix/FileSysDir.cpp
@@ -18,6 +18,7 @@
#include "qpid/sys/FileSysDir.h"
#include "qpid/sys/StrError.h"
+#include "qpid/log/Statement.h"
#include "qpid/Exception.h"
#include <sys/types.h>
@@ -25,6 +26,8 @@
#include <fcntl.h>
#include <cerrno>
#include <unistd.h>
+#include <dirent.h>
+#include <stdlib.h>
namespace qpid {
namespace sys {
@@ -51,4 +54,27 @@ void FileSysDir::mkdir(void)
throw Exception ("Can't create directory: " + dirPath);
}
+void FileSysDir::forEachFile(Callback cb) const {
+
+ ::dirent** namelist;
+
+ int n = scandir(dirPath.c_str(), &namelist, 0, alphasort);
+ if (n == -1) throw Exception (strError(errno) + ": Can't scan directory: " + dirPath);
+
+ for (int i = 0; i<n; ++i) {
+ std::string fullpath = dirPath + "/" + namelist[i]->d_name;
+ // Filter out non files/stat problems etc.
+ struct ::stat s;
+ // Can't throw here without leaking memory, so just do nothing with
+ // entries for which stat() fails.
+ if (!::stat(fullpath.c_str(), &s)) {
+ if (S_ISREG(s.st_mode)) {
+ cb(fullpath);
+ }
+ }
+ ::free(namelist[i]);
+ }
+ ::free(namelist);
+}
+
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/posix/IOHandle.cpp b/cpp/src/qpid/sys/posix/IOHandle.cpp
index 9c049ee1de..d3f502a63c 100644
--- a/cpp/src/qpid/sys/posix/IOHandle.cpp
+++ b/cpp/src/qpid/sys/posix/IOHandle.cpp
@@ -19,26 +19,11 @@
*
*/
-#include "qpid/sys/IOHandle.h"
-
#include "qpid/sys/posix/PrivatePosix.h"
namespace qpid {
namespace sys {
-int toFd(const IOHandlePrivate* h)
-{
- return h->fd;
-}
-
NullIOHandle DummyIOHandle;
-IOHandle::IOHandle(IOHandlePrivate* h) :
- impl(h)
-{}
-
-IOHandle::~IOHandle() {
- delete impl;
-}
-
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/posix/PollableCondition.cpp b/cpp/src/qpid/sys/posix/PollableCondition.cpp
index abff8a5be8..aa129faf20 100644
--- a/cpp/src/qpid/sys/posix/PollableCondition.cpp
+++ b/cpp/src/qpid/sys/posix/PollableCondition.cpp
@@ -21,7 +21,6 @@
#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"
@@ -58,14 +57,14 @@ 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)
+) : cb(cb), parent(parent)
{
int fds[2];
if (::pipe(fds) == -1)
throw ErrnoException(QPID_MSG("Can't create PollableCondition"));
- impl->fd = fds[0];
+ fd = fds[0];
writeFd = fds[1];
- if (::fcntl(impl->fd, F_SETFL, O_NONBLOCK) == -1)
+ if (::fcntl(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"));
diff --git a/cpp/src/qpid/sys/posix/PosixPoller.cpp b/cpp/src/qpid/sys/posix/PosixPoller.cpp
index eb0c3384d1..ae839b2e20 100644
--- a/cpp/src/qpid/sys/posix/PosixPoller.cpp
+++ b/cpp/src/qpid/sys/posix/PosixPoller.cpp
@@ -88,12 +88,12 @@ class PollerHandlePrivate {
};
short events;
- const IOHandlePrivate* ioHandle;
+ const IOHandle* ioHandle;
PollerHandle* pollerHandle;
FDStat stat;
Mutex lock;
- PollerHandlePrivate(const IOHandlePrivate* h, PollerHandle* p) :
+ PollerHandlePrivate(const IOHandle* h, PollerHandle* p) :
events(0),
ioHandle(h),
pollerHandle(p),
@@ -101,7 +101,7 @@ class PollerHandlePrivate {
}
int fd() const {
- return toFd(ioHandle);
+ return ioHandle->fd;
}
bool isActive() const {
@@ -162,7 +162,7 @@ class PollerHandlePrivate {
};
PollerHandle::PollerHandle(const IOHandle& h) :
- impl(new PollerHandlePrivate(h.impl, this))
+ impl(new PollerHandlePrivate(&h, this))
{}
PollerHandle::~PollerHandle() {
diff --git a/cpp/src/qpid/sys/posix/SocketAddress.cpp b/cpp/src/qpid/sys/posix/SocketAddress.cpp
index 344bd28669..cd23442226 100644
--- a/cpp/src/qpid/sys/posix/SocketAddress.cpp
+++ b/cpp/src/qpid/sys/posix/SocketAddress.cpp
@@ -102,6 +102,11 @@ std::string SocketAddress::asString(bool numeric) const
return asString(ai.ai_addr, ai.ai_addrlen);
}
+std::string SocketAddress::getHost() const
+{
+ return host;
+}
+
bool SocketAddress::nextAddress() {
bool r = currentAddrInfo->ai_next != 0;
if (r)
diff --git a/cpp/src/qpid/sys/posix/SystemInfo.cpp b/cpp/src/qpid/sys/posix/SystemInfo.cpp
index cfd2c64aee..ea7f521f2b 100755
--- a/cpp/src/qpid/sys/posix/SystemInfo.cpp
+++ b/cpp/src/qpid/sys/posix/SystemInfo.cpp
@@ -21,7 +21,6 @@
#include "qpid/log/Statement.h"
#include "qpid/sys/SystemInfo.h"
#include "qpid/sys/posix/check.h"
-#include <set>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
@@ -33,6 +32,7 @@
#include <iostream>
#include <fstream>
#include <sstream>
+#include <map>
#include <netdb.h>
#include <string.h>
@@ -77,84 +77,70 @@ inline bool isLoopback(const ::sockaddr* addr) {
}
}
-void SystemInfo::getLocalIpAddresses (uint16_t port,
- std::vector<Address> &addrList) {
- ::ifaddrs* ifaddr = 0;
- QPID_POSIX_CHECK(::getifaddrs(&ifaddr));
- for (::ifaddrs* ifap = ifaddr; ifap != 0; ifap = ifap->ifa_next) {
- if (ifap->ifa_addr == 0) continue;
- if (isLoopback(ifap->ifa_addr)) continue;
- int family = ifap->ifa_addr->sa_family;
- switch (family) {
- case AF_INET6: {
- // Ignore link local addresses as:
- // * The scope id is illegal in URL syntax
- // * Clients won't be able to use a link local address
- // without adding their own (potentially different) scope id
- sockaddr_in6* sa6 = (sockaddr_in6*)((void*)ifap->ifa_addr);
- if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) break;
- // Fallthrough
- }
- case AF_INET: {
- char dispName[NI_MAXHOST];
- int rc = ::getnameinfo(
- ifap->ifa_addr,
- (family == AF_INET)
- ? sizeof(struct sockaddr_in)
- : sizeof(struct sockaddr_in6),
- dispName, sizeof(dispName),
- 0, 0, NI_NUMERICHOST);
- if (rc != 0) {
- throw QPID_POSIX_ERROR(rc);
- }
- string addr(dispName);
- addrList.push_back(Address(TCP, addr, port));
- break;
- }
- default:
- continue;
+namespace {
+ inline socklen_t sa_len(::sockaddr* sa)
+ {
+ switch (sa->sa_family) {
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+ default:
+ return sizeof(struct sockaddr_storage);
}
}
- ::freeifaddrs(ifaddr);
- if (addrList.empty()) {
- addrList.push_back(Address(TCP, LOOPBACK, port));
+ inline bool isInetOrInet6(::sockaddr* sa) {
+ switch (sa->sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ return true;
+ default:
+ return false;
+ }
+ }
+ typedef std::map<std::string, std::vector<std::string> > InterfaceInfo;
+ std::map<std::string, std::vector<std::string> > cachedInterfaces;
+
+ void cacheInterfaceInfo() {
+ // Get interface info
+ ::ifaddrs* interfaceInfo;
+ QPID_POSIX_CHECK( ::getifaddrs(&interfaceInfo) );
+
+ char name[NI_MAXHOST];
+ for (::ifaddrs* info = interfaceInfo; info != 0; info = info->ifa_next) {
+
+ // Only use IPv4/IPv6 interfaces
+ if (!isInetOrInet6(info->ifa_addr)) continue;
+
+ int rc=::getnameinfo(info->ifa_addr, sa_len(info->ifa_addr),
+ name, sizeof(name), 0, 0,
+ NI_NUMERICHOST);
+ if (rc >= 0) {
+ std::string address(name);
+ cachedInterfaces[info->ifa_name].push_back(address);
+ } else {
+ throw qpid::Exception(QPID_MSG(gai_strerror(rc)));
+ }
+ }
+ ::freeifaddrs(interfaceInfo);
}
}
-namespace {
-struct AddrInfo {
- struct addrinfo* ptr;
- AddrInfo(const std::string& host) : ptr(0) {
- ::addrinfo hints;
- ::memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC; // Allow both IPv4 and IPv6
- if (::getaddrinfo(host.c_str(), NULL, &hints, &ptr) != 0)
- ptr = 0;
- }
- ~AddrInfo() { if (ptr) ::freeaddrinfo(ptr); }
-};
+bool SystemInfo::getInterfaceAddresses(const std::string& interface, std::vector<std::string>& addresses) {
+ if ( cachedInterfaces.empty() ) cacheInterfaceInfo();
+ InterfaceInfo::iterator i = cachedInterfaces.find(interface);
+ if ( i==cachedInterfaces.end() ) return false;
+ std::copy(i->second.begin(), i->second.end(), std::back_inserter(addresses));
+ return true;
}
-bool SystemInfo::isLocalHost(const std::string& host) {
- std::vector<Address> myAddrs;
- getLocalIpAddresses(0, myAddrs);
- std::set<string> localHosts;
- for (std::vector<Address>::const_iterator i = myAddrs.begin(); i != myAddrs.end(); ++i)
- localHosts.insert(i->host);
- // Resolve host
- AddrInfo ai(host);
- if (!ai.ptr) return false;
- for (struct addrinfo *res = ai.ptr; res != NULL; res = res->ai_next) {
- if (isLoopback(res->ai_addr)) return true;
- // Get string form of IP addr
- char addr[NI_MAXHOST] = "";
- int error = ::getnameinfo(res->ai_addr, res->ai_addrlen, addr, NI_MAXHOST, NULL, 0,
- NI_NUMERICHOST | NI_NUMERICSERV);
- if (error) return false;
- if (localHosts.find(addr) != localHosts.end()) return true;
+void SystemInfo::getInterfaceNames(std::vector<std::string>& names ) {
+ if ( cachedInterfaces.empty() ) cacheInterfaceInfo();
+
+ for (InterfaceInfo::const_iterator i = cachedInterfaces.begin(); i!=cachedInterfaces.end(); ++i) {
+ names.push_back(i->first);
}
- return false;
}
void SystemInfo::getSystemId (std::string &osName,
@@ -205,4 +191,11 @@ string SystemInfo::getProcessName()
return value;
}
+// Always true. Only Windows has exception cases.
+bool SystemInfo::threadSafeShutdown()
+{
+ return true;
+}
+
+
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/rdma/rdma_wrap.cpp b/cpp/src/qpid/sys/rdma/rdma_wrap.cpp
index efe454c5be..889ee9ff75 100644
--- a/cpp/src/qpid/sys/rdma/rdma_wrap.cpp
+++ b/cpp/src/qpid/sys/rdma/rdma_wrap.cpp
@@ -105,7 +105,7 @@ namespace Rdma {
}
QueuePair::QueuePair(boost::shared_ptr< ::rdma_cm_id > i) :
- qpid::sys::IOHandle(new qpid::sys::IOHandlePrivate),
+ handle(new qpid::sys::IOHandle),
pd(allocPd(i->verbs)),
cchannel(mkCChannel(i->verbs)),
scq(mkCq(i->verbs, DEFAULT_CQ_ENTRIES, 0, cchannel.get())),
@@ -113,7 +113,7 @@ namespace Rdma {
outstandingSendEvents(0),
outstandingRecvEvents(0)
{
- impl->fd = cchannel->fd;
+ handle->fd = cchannel->fd;
// Set cq context to this QueuePair object so we can find
// ourselves again
@@ -163,6 +163,11 @@ namespace Rdma {
// The buffers vectors automatically deletes all the buffers we've allocated
}
+ QueuePair::operator qpid::sys::IOHandle&() const
+ {
+ return *handle;
+ }
+
// Create buffers to use for writing
void QueuePair::createSendBuffers(int sendBufferCount, int bufferSize, int reserved)
{
@@ -359,11 +364,11 @@ namespace Rdma {
// Wrap the passed in rdma_cm_id with a Connection
// this basically happens only on connection request
Connection::Connection(::rdma_cm_id* i) :
- qpid::sys::IOHandle(new qpid::sys::IOHandlePrivate),
+ handle(new qpid::sys::IOHandle),
id(mkId(i)),
context(0)
{
- impl->fd = id->channel->fd;
+ handle->fd = id->channel->fd;
// Just overwrite the previous context as it will
// have come from the listening connection
@@ -372,12 +377,12 @@ namespace Rdma {
}
Connection::Connection() :
- qpid::sys::IOHandle(new qpid::sys::IOHandlePrivate),
+ handle(new qpid::sys::IOHandle),
channel(mkEChannel()),
id(mkId(channel.get(), this, RDMA_PS_TCP)),
context(0)
{
- impl->fd = channel->fd;
+ handle->fd = channel->fd;
}
Connection::~Connection() {
@@ -385,6 +390,11 @@ namespace Rdma {
id->context = 0;
}
+ Connection::operator qpid::sys::IOHandle&() const
+ {
+ return *handle;
+ }
+
void Connection::ensureQueuePair() {
assert(id.get());
diff --git a/cpp/src/qpid/sys/rdma/rdma_wrap.h b/cpp/src/qpid/sys/rdma/rdma_wrap.h
index 8e3429027b..5f84793a5b 100644
--- a/cpp/src/qpid/sys/rdma/rdma_wrap.h
+++ b/cpp/src/qpid/sys/rdma/rdma_wrap.h
@@ -28,6 +28,7 @@
#include "qpid/sys/Mutex.h"
#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/ptr_container/ptr_deque.hpp>
@@ -116,9 +117,10 @@ 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.
- class QueuePair : public qpid::sys::IOHandle, public qpid::RefCounted {
+ class QueuePair : public qpid::RefCounted {
friend class Connection;
+ boost::scoped_ptr< qpid::sys::IOHandle > handle;
boost::shared_ptr< ::ibv_pd > pd;
boost::shared_ptr< ::ibv_mr > smr;
boost::shared_ptr< ::ibv_mr > rmr;
@@ -139,6 +141,8 @@ namespace Rdma {
public:
typedef boost::intrusive_ptr<QueuePair> intrusive_ptr;
+ operator qpid::sys::IOHandle&() const;
+
// Create a buffers to use for writing
void createSendBuffers(int sendBufferCount, int dataSize, int headerSize);
@@ -195,7 +199,8 @@ namespace Rdma {
// registered buffers can't be shared between different connections
// (this can only happen between connections on the same controller in any case,
// so needs careful management if used)
- class Connection : public qpid::sys::IOHandle, public qpid::RefCounted {
+ class Connection : public qpid::RefCounted {
+ boost::scoped_ptr< qpid::sys::IOHandle > handle;
boost::shared_ptr< ::rdma_event_channel > channel;
boost::shared_ptr< ::rdma_cm_id > id;
QueuePair::intrusive_ptr qp;
@@ -216,6 +221,8 @@ namespace Rdma {
public:
typedef boost::intrusive_ptr<Connection> intrusive_ptr;
+ operator qpid::sys::IOHandle&() const;
+
static intrusive_ptr make();
static intrusive_ptr find(::rdma_cm_id* i);
diff --git a/cpp/src/qpid/sys/solaris/SystemInfo.cpp b/cpp/src/qpid/sys/solaris/SystemInfo.cpp
index e5856f55e6..0e754e048b 100755
--- a/cpp/src/qpid/sys/solaris/SystemInfo.cpp
+++ b/cpp/src/qpid/sys/solaris/SystemInfo.cpp
@@ -60,31 +60,6 @@ bool SystemInfo::getLocalHostname(Address &address) {
static const string LOCALHOST("127.0.0.1");
static const string TCP("tcp");
-void SystemInfo::getLocalIpAddresses(uint16_t port,
- std::vector<Address> &addrList) {
- int s = socket(PF_INET, SOCK_STREAM, 0);
- for (int i=1;;i++) {
- struct lifreq ifr;
- ifr.lifr_index = i;
- if (::ioctl(s, SIOCGIFADDR, &ifr) < 0) {
- break;
- }
- struct sockaddr *sa = static_cast<struct sockaddr *>((void *) &ifr.lifr_addr);
- if (sa->sa_family != AF_INET) {
- // TODO: Url parsing currently can't cope with IPv6 addresses, defer for now
- break;
- }
- struct sockaddr_in *sin = static_cast<struct sockaddr_in *>((void *)sa);
- std::string addr(inet_ntoa(sin->sin_addr));
- if (addr != LOCALHOST)
- addrList.push_back(Address(TCP, addr, port));
- }
- if (addrList.empty()) {
- addrList.push_back(Address(TCP, LOCALHOST, port));
- }
- close (s);
-}
-
void SystemInfo::getSystemId(std::string &osName,
std::string &nodeName,
std::string &release,
@@ -126,4 +101,10 @@ string SystemInfo::getProcessName()
return value;
}
+// Always true. Only Windows has exception cases.
+bool SystemInfo::threadSafeShutdown()
+{
+ return true;
+}
+
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/ssl/SslHandler.cpp b/cpp/src/qpid/sys/ssl/SslHandler.cpp
deleted file mode 100644
index eeb8c26a76..0000000000
--- a/cpp/src/qpid/sys/ssl/SslHandler.cpp
+++ /dev/null
@@ -1,217 +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 "qpid/sys/ssl/SslHandler.h"
-#include "qpid/sys/ssl/SslIo.h"
-#include "qpid/sys/ssl/SslSocket.h"
-#include "qpid/sys/Timer.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 {
-
-
-struct ProtocolTimeoutTask : public sys::TimerTask {
- SslHandler& handler;
- std::string id;
-
- ProtocolTimeoutTask(const std::string& i, const Duration& timeout, SslHandler& h) :
- TimerTask(timeout, "ProtocolTimeout"),
- handler(h),
- id(i)
- {}
-
- void fire() {
- // If this fires it means that we didn't negotiate the connection in the timeout period
- // Schedule closing the connection for the io thread
- QPID_LOG(error, "Connection " << id << " No protocol received closing");
- handler.abort();
- }
-};
-
-SslHandler::SslHandler(std::string id, ConnectionCodec::Factory* f, bool _nodict) :
- identifier(id),
- aio(0),
- factory(f),
- codec(0),
- readError(false),
- isClient(false),
- nodict(_nodict)
-{}
-
-SslHandler::~SslHandler() {
- if (codec)
- codec->closed();
- if (timeoutTimerTask)
- timeoutTimerTask->cancel();
- delete codec;
-}
-
-void SslHandler::init(SslIO* a, Timer& timer, uint32_t maxTime) {
- aio = a;
-
- // Start timer for this connection
- timeoutTimerTask = new ProtocolTimeoutTask(identifier, maxTime*TIME_MSEC, *this);
- timer.add(timeoutTimerTask);
-
- // Give connection some buffers to use
- aio->createBuffers();
-}
-
-void SslHandler::write(const framing::ProtocolInitiation& data)
-{
- QPID_LOG(debug, "SENT [" << identifier << "]: INIT(" << data << ")");
- SslIO::BufferBase* buff = aio->getQueuedBuffer();
- assert(buff);
- framing::Buffer out(buff->bytes, buff->byteCount);
- data.encode(out);
- buff->dataCount = data.encodedSize();
- aio->queueWrite(buff);
-}
-
-void SslHandler::abort() {
- // Don't disconnect if we're already disconnecting
- if (!readError) {
- 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)) {
- // We've just got the protocol negotiation so we can cancel the timeout for that
- timeoutTimerTask->cancel();
-
- decoded = in.getPosition();
- QPID_LOG(debug, "RECV [" << identifier << "]: INIT(" << protocolInit << ")");
- try {
- codec = factory->create(protocolInit.getVersion(), *this, identifier, getSecuritySettings(aio));
- if (!codec) {
- //TODO: may still want to revise this...
- //send valid version header & close connection.
- write(framing::ProtocolInitiation(framing::highestProtocolVersion));
- readError = true;
- aio->queueWriteClose();
- }
- } catch (const std::exception& e) {
- QPID_LOG(error, e.what());
- readError = true;
- aio->queueWriteClose();
- }
- }
- }
- // TODO: unreading needs to go away, and when we can cope
- // with multiple sub-buffers in the general buffer scheme, it will
- if (decoded != size_t(buff->dataCount)) {
- // Adjust buffer for used bytes and then "unread them"
- buff->dataStart += decoded;
- buff->dataCount -= decoded;
- aio->unread(buff);
- } else {
- // Give whole buffer back to aio subsystem
- aio->queueReadBuffer(buff);
- }
-}
-
-void SslHandler::eof(SslIO&) {
- QPID_LOG(debug, "DISCONNECTED [" << identifier << "]");
- if (codec) codec->closed();
- aio->queueWriteClose();
-}
-
-void SslHandler::closedSocket(SslIO&, const SslSocket& s) {
- // If we closed with data still to send log a warning
- if (!aio->writeQueueEmpty()) {
- QPID_LOG(warning, "CLOSING [" << identifier << "] unsent data (probably due to client disconnect)");
- }
- delete &s;
- aio->queueForDeletion();
- delete this;
-}
-
-void SslHandler::disconnect(SslIO& a) {
- // treat the same as eof
- eof(a);
-}
-
-// Notifications
-void SslHandler::nobuffs(SslIO&) {
-}
-
-void SslHandler::idle(SslIO&){
- if (isClient && codec == 0) {
- codec = factory->create(*this, identifier, getSecuritySettings(aio));
- write(framing::ProtocolInitiation(codec->getVersion()));
- // We've just sent the protocol negotiation so we can cancel the timeout for that
- // This is not ideal, because we've not received anything yet, but heartbeats will
- // be active soon
- timeoutTimerTask->cancel();
- return;
- }
- if (codec == 0) return;
- if (!codec->canEncode()) {
- return;
- }
- SslIO::BufferBase* buff = aio->getQueuedBuffer();
- if (buff) {
- size_t encoded=codec->encode(buff->bytes, buff->byteCount);
- buff->dataCount = encoded;
- aio->queueWrite(buff);
- }
- if (codec->isClosed())
- aio->queueWriteClose();
-}
-
-SecuritySettings SslHandler::getSecuritySettings(SslIO* aio)
-{
- SecuritySettings settings = aio->getSecuritySettings();
- settings.nodict = nodict;
- return settings;
-}
-
-
-}}} // namespace qpid::sys::ssl
diff --git a/cpp/src/qpid/sys/ssl/SslHandler.h b/cpp/src/qpid/sys/ssl/SslHandler.h
deleted file mode 100644
index 14814b0281..0000000000
--- a/cpp/src/qpid/sys/ssl/SslHandler.h
+++ /dev/null
@@ -1,85 +0,0 @@
-#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"
-
-#include <boost/intrusive_ptr.hpp>
-
-namespace qpid {
-
-namespace framing {
- class ProtocolInitiation;
-}
-
-namespace sys {
-
-class Timer;
-class TimerTask;
-
-namespace ssl {
-
-class SslIO;
-struct SslIOBufferBase;
-class SslSocket;
-
-class SslHandler : public OutputControl {
- std::string identifier;
- SslIO* aio;
- ConnectionCodec::Factory* factory;
- ConnectionCodec* codec;
- bool readError;
- bool isClient;
- bool nodict;
- boost::intrusive_ptr<sys::TimerTask> timeoutTimerTask;
-
- void write(const framing::ProtocolInitiation&);
- qpid::sys::SecuritySettings getSecuritySettings(SslIO* aio);
-
- public:
- SslHandler(std::string id, ConnectionCodec::Factory* f, bool nodict);
- ~SslHandler();
- void init(SslIO* a, Timer& timer, uint32_t maxTime);
-
- 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
deleted file mode 100644
index bbfb703170..0000000000
--- a/cpp/src/qpid/sys/ssl/SslIo.cpp
+++ /dev/null
@@ -1,470 +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 "qpid/sys/ssl/SslIo.h"
-#include "qpid/sys/ssl/SslSocket.h"
-#include "qpid/sys/ssl/check.h"
-
-#include "qpid/sys/Time.h"
-#include "qpid/sys/posix/check.h"
-#include "qpid/log/Statement.h"
-
-// TODO The basic algorithm here is not really POSIX specific and with a bit more abstraction
-// could (should) be promoted to be platform portable
-#include <unistd.h>
-#include <sys/socket.h>
-#include <signal.h>
-#include <errno.h>
-#include <string.h>
-
-#include <boost/bind.hpp>
-
-namespace qpid {
-namespace sys {
-namespace 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 threadReadCount = 0;
-__thread int threadWriteTotal = 0;
-__thread int threadWriteCount = 0;
-__thread int64_t threadMaxIoTimeNs = 2 * 1000000; // start at 2ms
-}
-
-/*
- * Asynch Acceptor
- */
-
-template <class T>
-SslAcceptorTmpl<T>::SslAcceptorTmpl(const T& s, Callback callback) :
- acceptedCallback(callback),
- handle(s, boost::bind(&SslAcceptorTmpl<T>::readable, this, _1), 0, 0),
- socket(s) {
-
- s.setNonblocking();
- ignoreSigpipe();
-}
-
-template <class T>
-SslAcceptorTmpl<T>::~SslAcceptorTmpl()
-{
- handle.stopWatch();
-}
-
-template <class T>
-void SslAcceptorTmpl<T>::start(Poller::shared_ptr poller) {
- handle.startWatch(poller);
-}
-
-/*
- * We keep on accepting as long as there is something to accept
- */
-template <class T>
-void SslAcceptorTmpl<T>::readable(DispatchHandle& h) {
- Socket* s;
- do {
- errno = 0;
- // TODO: Currently we ignore the peers address, perhaps we should
- // log it or use it for connection acceptance.
- try {
- s = socket.accept();
- if (s) {
- acceptedCallback(*s);
- } else {
- break;
- }
- } catch (const std::exception& e) {
- QPID_LOG(error, "Could not accept socket: " << e.what());
- }
- } while (true);
-
- h.rewatch();
-}
-
-// Explicitly instantiate the templates we need
-template class SslAcceptorTmpl<SslSocket>;
-template class SslAcceptorTmpl<SslMuxSocket>;
-
-/*
- * Asynch Connector
- */
-
-SslConnector::SslConnector(const SslSocket& s,
- Poller::shared_ptr poller,
- std::string hostname,
- std::string port,
- ConnectedCallback connCb,
- FailedCallback failCb) :
- DispatchHandle(s,
- 0,
- boost::bind(&SslConnector::connComplete, this, _1),
- boost::bind(&SslConnector::connComplete, this, _1)),
- connCallback(connCb),
- failCallback(failCb),
- socket(s)
-{
- //TODO: would be better for connect to be performed on a
- //non-blocking socket, but that doesn't work at present so connect
- //blocks until complete
- try {
- socket.connect(hostname, port);
- socket.setNonblocking();
- startWatch(poller);
- } catch(std::exception& e) {
- failure(-1, std::string(e.what()));
- }
-}
-
-void SslConnector::connComplete(DispatchHandle& h)
-{
- int errCode = socket.getError();
-
- h.stopWatch();
- if (errCode == 0) {
- connCallback(socket);
- DispatchHandle::doDelete();
- } else {
- // TODO: This need to be fixed as strerror isn't thread safe
- failure(errCode, std::string(::strerror(errCode)));
- }
-}
-
-void SslConnector::failure(int errCode, std::string message)
-{
- if (failCallback)
- failCallback(errCode, message);
-
- socket.close();
- delete &socket;
-
- DispatchHandle::doDelete();
-}
-
-/*
- * Asynch reader/writer
- */
-SslIO::SslIO(const SslSocket& s,
- ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb,
- ClosedCallback cCb, BuffersEmptyCallback eCb, IdleCallback iCb) :
-
- DispatchHandle(s,
- boost::bind(&SslIO::readable, this, _1),
- boost::bind(&SslIO::writeable, this, _1),
- boost::bind(&SslIO::disconnected, this, _1)),
- readCallback(rCb),
- eofCallback(eofCb),
- disCallback(disCb),
- closedCallback(cCb),
- emptyCallback(eCb),
- idleCallback(iCb),
- socket(s),
- queuedClose(false),
- writePending(false) {
-
- s.setNonblocking();
-}
-
-SslIO::~SslIO() {
-}
-
-void SslIO::queueForDeletion() {
- DispatchHandle::doDelete();
-}
-
-void SslIO::start(Poller::shared_ptr poller) {
- DispatchHandle::startWatch(poller);
-}
-
-void SslIO::createBuffers(uint32_t size) {
- // Allocate all the buffer memory at once
- bufferMemory.reset(new char[size*BufferCount]);
-
- // Create the Buffer structs in a vector
- // And push into the buffer queue
- buffers.reserve(BufferCount);
- for (uint32_t i = 0; i < BufferCount; i++) {
- buffers.push_back(BufferBase(&bufferMemory[i*size], size));
- queueReadBuffer(&buffers[i]);
- }
-}
-
-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();
-}
-
-void SslIO::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(&SslIO::requestedCall, this, callback));
-}
-
-void SslIO::requestedCall(RequestCallback callback) {
- assert(callback);
- callback(*this);
-}
-
-/** 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) {
- 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;
-
- 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()) > threadMaxIoTimeNs) {
- break;
- }
-
- } else {
- // Put buffer back (at front so it doesn't interfere with unread buffers)
- bufferQueue.push_front(buff);
- assert(buff);
-
- // Eof or other side has gone away
- if (rc == 0 || errno == ECONNRESET) {
- eofCallback(*this);
- h.unwatchRead();
- break;
- } else if (errno == EAGAIN) {
- // We have just put a buffer back so we know
- // we can carry on watching for reads
- break;
- } else {
- // Report error then just treat as a socket disconnect
- QPID_LOG(error, "Error reading socket: " << getErrorString(PR_GetError()));
- eofCallback(*this);
- h.unwatchRead();
- break;
- }
- }
- } else {
- // Something to read but no buffer
- if (emptyCallback) {
- emptyCallback(*this);
- }
- // If we still have no buffers we can't do anything more
- if (bufferQueue.empty()) {
- h.unwatchRead();
- break;
- }
-
- }
- } while (true);
-
- ++threadReadCount;
- return;
-}
-
-/*
- * We carry on writing whilst we have data to write and we can write
- */
-void SslIO::writeable(DispatchHandle& h) {
- AbsTime writeStartTime = AbsTime::now();
- 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;
-
- // 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);
-
- // Stop writing if we've overrun our timeslot
- if (Duration(writeStartTime, AbsTime::now()) > threadMaxIoTimeNs) {
- break;
- }
- } else {
- // Put buffer back
- writeQueue.push_back(buff);
- if (errno == ECONNRESET || errno == EPIPE) {
- // Just stop watching for write here - we'll get a
- // disconnect callback soon enough
- h.unwatchWrite();
- break;
- } else if (errno == EAGAIN) {
- // We have just put a buffer back so we know
- // we can carry on watching for writes
- break;
- } else {
- QPID_LOG(error, "Error writing to socket: " << getErrorString(PR_GetError()));
- h.unwatchWrite();
- break;
- }
- }
- } else {
- // If we're waiting to close the socket then can do it now as there is nothing to write
- if (queuedClose) {
- close(h);
- break;
- }
- // Fd is writable, but nothing to write
- if (idleCallback) {
- writePending = false;
- idleCallback(*this);
- }
- // If we still have no buffers to write we can't do anything more
- if (writeQueue.empty() && !writePending && !queuedClose) {
- h.unwatchWrite();
- // The following handles the case where writePending is
- // set to true after the test above; in this case its
- // possible that the unwatchWrite overwrites the
- // desired rewatchWrite so we correct that here
- if (writePending)
- h.rewatchWrite();
- break;
- }
- }
- } while (true);
-
- ++threadWriteCount;
- return;
-}
-
-void SslIO::disconnected(DispatchHandle& h) {
- // If we've already queued close do it instead of disconnected callback
- if (queuedClose) {
- close(h);
- } else if (disCallback) {
- disCallback(*this);
- h.unwatch();
- }
-}
-
-/*
- * Close the socket and callback to say we've done it
- */
-void SslIO::close(DispatchHandle& h) {
- h.stopWatch();
- socket.close();
- if (closedCallback) {
- closedCallback(*this, socket);
- }
-}
-
-SecuritySettings SslIO::getSecuritySettings() {
- SecuritySettings settings;
- settings.ssf = socket.getKeyLen();
- settings.authid = socket.getClientAuthId();
- return settings;
-}
-
-}}}
diff --git a/cpp/src/qpid/sys/ssl/SslIo.h b/cpp/src/qpid/sys/ssl/SslIo.h
deleted file mode 100644
index f3112bfa65..0000000000
--- a/cpp/src/qpid/sys/ssl/SslIo.h
+++ /dev/null
@@ -1,193 +0,0 @@
-#ifndef _sys_ssl_SslIO
-#define _sys_ssl_SslIO
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#include "qpid/sys/DispatchHandle.h"
-#include "qpid/sys/SecuritySettings.h"
-
-#include <boost/function.hpp>
-#include <boost/shared_array.hpp>
-#include <deque>
-
-namespace qpid {
-namespace sys {
-
-class Socket;
-
-namespace ssl {
-
-class SslSocket;
-
-/*
- * Asynchronous ssl acceptor: accepts connections then does a callback
- * with the accepted fd
- */
-template <class T>
-class SslAcceptorTmpl {
-public:
- typedef boost::function1<void, const Socket&> Callback;
-
-private:
- Callback acceptedCallback;
- qpid::sys::DispatchHandle handle;
- const T& socket;
-
-public:
- SslAcceptorTmpl(const T& s, Callback callback);
- ~SslAcceptorTmpl();
- void start(qpid::sys::Poller::shared_ptr poller);
-
-private:
- void readable(qpid::sys::DispatchHandle& handle);
-};
-
-/*
- * Asynchronous ssl connector: starts the process of initiating a
- * connection and invokes a callback when completed or failed.
- */
-class SslConnector : private qpid::sys::DispatchHandle {
-public:
- typedef boost::function1<void, const SslSocket&> ConnectedCallback;
- typedef boost::function2<void, int, std::string> FailedCallback;
-
-private:
- ConnectedCallback connCallback;
- FailedCallback failCallback;
- const SslSocket& socket;
-
-public:
- SslConnector(const SslSocket& socket,
- Poller::shared_ptr poller,
- std::string hostname,
- std::string port,
- ConnectedCallback connCb,
- FailedCallback failCb = 0);
-
-private:
- void connComplete(DispatchHandle& handle);
- void failure(int, std::string);
-};
-
-struct SslIOBufferBase {
- char* bytes;
- 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;
- typedef boost::function1<void, SslIO&> RequestCallback;
-
- SslIO(const SslSocket& s,
- ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb,
- ClosedCallback cCb = 0, BuffersEmptyCallback eCb = 0, IdleCallback iCb = 0);
-private:
- ReadCallback readCallback;
- EofCallback eofCallback;
- DisconnectCallback disCallback;
- ClosedCallback closedCallback;
- BuffersEmptyCallback emptyCallback;
- IdleCallback idleCallback;
- const SslSocket& socket;
- std::deque<BufferBase*> bufferQueue;
- std::deque<BufferBase*> writeQueue;
- std::vector<BufferBase> buffers;
- boost::shared_array<char> bufferMemory;
- 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:
- /*
- * Size of IO buffers - this is the maximum possible frame size + 1
- */
- const static uint32_t MaxBufferSize = 65536;
-
- /*
- * Number of IO buffers allocated - I think the code can only use 2 -
- * 1 for reading and 1 for writing, allocate 4 for safety
- */
- const static uint32_t BufferCount = 4;
-
- void queueForDeletion();
-
- void start(qpid::sys::Poller::shared_ptr poller);
- void createBuffers(uint32_t size = MaxBufferSize);
- void queueReadBuffer(BufferBase* buff);
- void unread(BufferBase* buff);
- void queueWrite(BufferBase* buff);
- void notifyPendingWrite();
- void queueWriteClose();
- bool writeQueueEmpty() { return writeQueue.empty(); }
- void requestCallback(RequestCallback);
- BufferBase* getQueuedBuffer();
-
- qpid::sys::SecuritySettings getSecuritySettings();
-
-private:
- ~SslIO();
- void readable(qpid::sys::DispatchHandle& handle);
- void writeable(qpid::sys::DispatchHandle& handle);
- void disconnected(qpid::sys::DispatchHandle& handle);
- void requestedCall(RequestCallback);
- 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
index 30234bb686..a328e49c13 100644
--- a/cpp/src/qpid/sys/ssl/SslSocket.cpp
+++ b/cpp/src/qpid/sys/ssl/SslSocket.cpp
@@ -20,6 +20,7 @@
*/
#include "qpid/sys/ssl/SslSocket.h"
+#include "qpid/sys/SocketAddress.h"
#include "qpid/sys/ssl/check.h"
#include "qpid/sys/ssl/util.h"
#include "qpid/Exception.h"
@@ -52,28 +53,6 @@ namespace sys {
namespace ssl {
namespace {
-std::string getService(int fd, bool local)
-{
- ::sockaddr_storage name; // big enough for any socket address
- ::socklen_t namelen = sizeof(name);
-
- int result = -1;
- if (local) {
- result = ::getsockname(fd, (::sockaddr*)&name, &namelen);
- } else {
- result = ::getpeername(fd, (::sockaddr*)&name, &namelen);
- }
-
- QPID_POSIX_CHECK(result);
-
- char servName[NI_MAXSERV];
- if (int rc=::getnameinfo((::sockaddr*)&name, namelen, 0, 0,
- servName, sizeof(servName),
- NI_NUMERICHOST | NI_NUMERICSERV) != 0)
- throw QPID_POSIX_ERROR(rc);
- return servName;
-}
-
const std::string DOMAIN_SEPARATOR("@");
const std::string DC_SEPARATOR(".");
const std::string DC("DC");
@@ -101,14 +80,18 @@ std::string getDomainFromSubject(std::string subject)
}
return domain;
}
-
}
-SslSocket::SslSocket() : socket(0), prototype(0)
+SslSocket::SslSocket(const std::string& certName, bool clientAuth) :
+ nssSocket(0), certname(certName), 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));
+ //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));
+ }
}
/**
@@ -116,25 +99,44 @@ SslSocket::SslSocket() : socket(0), prototype(0)
* returned from accept. Because we use posix accept rather than
* PR_Accept, we have to reset the handshake.
*/
-SslSocket::SslSocket(IOHandlePrivate* ioph, PRFileDesc* model) : Socket(ioph), socket(0), prototype(0)
+SslSocket::SslSocket(int fd, PRFileDesc* model) : BSDSocket(fd), nssSocket(0), prototype(0)
{
- socket = SSL_ImportFD(model, PR_ImportTCPSocket(impl->fd));
- NSS_CHECK(SSL_ResetHandshake(socket, true));
+ nssSocket = SSL_ImportFD(model, PR_ImportTCPSocket(fd));
+ NSS_CHECK(SSL_ResetHandshake(nssSocket, PR_TRUE));
}
void SslSocket::setNonblocking() const
{
+ if (!nssSocket) {
+ BSDSocket::setNonblocking();
+ return;
+ }
PRSocketOptionData option;
option.option = PR_SockOpt_Nonblocking;
option.value.non_blocking = true;
- PR_SetSocketOption(socket, &option);
+ PR_SetSocketOption(nssSocket, &option);
}
-void SslSocket::connect(const std::string& host, const std::string& port) const
+void SslSocket::setTcpNoDelay() const
{
- std::stringstream namestream;
- namestream << host << ":" << port;
- connectname = namestream.str();
+ if (!nssSocket) {
+ BSDSocket::setTcpNoDelay();
+ return;
+ }
+ PRSocketOptionData option;
+ option.option = PR_SockOpt_NoDelay;
+ option.value.no_delay = true;
+ PR_SetSocketOption(nssSocket, &option);
+}
+
+void SslSocket::connect(const SocketAddress& addr) const
+{
+ BSDSocket::connect(addr);
+}
+
+void SslSocket::finishConnect(const SocketAddress& addr) const
+{
+ nssSocket = SSL_ImportFD(0, PR_ImportTCPSocket(fd));
void* arg;
// Use the connection's cert-name if it has one; else use global cert-name
@@ -145,75 +147,48 @@ void SslSocket::connect(const std::string& host, const std::string& port) const
} else {
arg = const_cast<char*>(SslOptions::global.certName.c_str());
}
- NSS_CHECK(SSL_GetClientAuthDataHook(socket, NSS_GetClientAuthData, arg));
- NSS_CHECK(SSL_SetURL(socket, host.data()));
-
- char hostBuffer[PR_NETDB_BUF_SIZE];
- PRHostEnt hostEntry;
- PR_CHECK(PR_GetHostByName(host.data(), hostBuffer, PR_NETDB_BUF_SIZE, &hostEntry));
- PRNetAddr address;
- int value = PR_EnumerateHostEnt(0, &hostEntry, boost::lexical_cast<PRUint16>(port), &address);
- if (value < 0) {
- throw Exception(QPID_MSG("Error getting address for host: " << ErrorString()));
- } else if (value == 0) {
- throw Exception(QPID_MSG("Could not resolve address for host."));
- }
- PR_CHECK(PR_Connect(socket, &address, PR_INTERVAL_NO_TIMEOUT));
- NSS_CHECK(SSL_ForceHandshake(socket));
+ NSS_CHECK(SSL_GetClientAuthDataHook(nssSocket, NSS_GetClientAuthData, arg));
+
+ url = addr.getHost();
+ NSS_CHECK(SSL_SetURL(nssSocket, url.data()));
+
+ NSS_CHECK(SSL_ResetHandshake(nssSocket, PR_FALSE));
+ NSS_CHECK(SSL_ForceHandshake(nssSocket));
}
void SslSocket::close() const
{
- if (impl->fd > 0) {
- PR_Close(socket);
- impl->fd = -1;
+ if (!nssSocket) {
+ BSDSocket::close();
+ return;
+ }
+ if (fd > 0) {
+ PR_Close(nssSocket);
+ fd = -1;
}
}
-int SslSocket::listen(uint16_t port, int backlog, const std::string& certName, bool clientAuth) const
+int SslSocket::listen(const SocketAddress& sa, int backlog) 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 << "'"));
+ std::string cName( (certname == "") ? "localhost.localdomain" : certname);
+ CERTCertificate *cert = PK11_FindCertFromNickname(const_cast<char*>(cName.c_str()), 0);
+ if (!cert) throw Exception(QPID_MSG("Failed to load certificate '" << cName << "'"));
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);
+ return BSDSocket::listen(sa, backlog);
}
-SslSocket* SslSocket::accept() const
+Socket* SslSocket::accept() const
{
QPID_LOG(trace, "Accepting SSL connection.");
- int afd = ::accept(impl->fd, 0, 0);
+ int afd = ::accept(fd, 0, 0);
if ( afd >= 0) {
- return new SslSocket(new IOHandlePrivate(afd), prototype);
+ return new SslSocket(afd, prototype);
} else if (errno == EAGAIN) {
return 0;
} else {
@@ -297,17 +272,22 @@ static bool isSslStream(int afd) {
return isSSL2Handshake || isSSL3Handshake;
}
+SslMuxSocket::SslMuxSocket(const std::string& certName, bool clientAuth) :
+ SslSocket(certName, clientAuth)
+{
+}
+
Socket* SslMuxSocket::accept() const
{
- int afd = ::accept(impl->fd, 0, 0);
+ int afd = ::accept(fd, 0, 0);
if (afd >= 0) {
QPID_LOG(trace, "Accepting connection with optional SSL wrapper.");
if (isSslStream(afd)) {
QPID_LOG(trace, "Accepted SSL connection.");
- return new SslSocket(new IOHandlePrivate(afd), prototype);
+ return new SslSocket(afd, prototype);
} else {
QPID_LOG(trace, "Accepted Plaintext connection.");
- return new Socket(new IOHandlePrivate(afd));
+ return new BSDSocket(afd);
}
} else if (errno == EAGAIN) {
return 0;
@@ -318,32 +298,12 @@ Socket* SslMuxSocket::accept() const
int SslSocket::read(void *buf, size_t count) const
{
- return PR_Read(socket, buf, count);
+ return PR_Read(nssSocket, buf, count);
}
int SslSocket::write(const void *buf, size_t count) const
{
- return PR_Write(socket, buf, count);
-}
-
-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());
-}
-
-void SslSocket::setTcpNoDelay(bool nodelay) const
-{
- if (nodelay) {
- PRSocketOptionData option;
- option.option = PR_SockOpt_NoDelay;
- option.value.no_delay = true;
- PR_SetSocketOption(socket, &option);
- }
+ return PR_Write(nssSocket, buf, count);
}
void SslSocket::setCertName(const std::string& name)
@@ -359,7 +319,7 @@ int SslSocket::getKeyLen() const
int keySize = 0;
SECStatus rc;
- rc = SSL_SecurityStatus( socket,
+ rc = SSL_SecurityStatus( nssSocket,
&enabled,
NULL,
NULL,
@@ -374,7 +334,7 @@ int SslSocket::getKeyLen() const
std::string SslSocket::getClientAuthId() const
{
std::string authId;
- CERTCertificate* cert = SSL_PeerCertificate(socket);
+ CERTCertificate* cert = SSL_PeerCertificate(nssSocket);
if (cert) {
authId = CERT_GetCommonName(&(cert->subject));
/*
diff --git a/cpp/src/qpid/sys/ssl/SslSocket.h b/cpp/src/qpid/sys/ssl/SslSocket.h
index eabadcbe23..fc97059cfd 100644
--- a/cpp/src/qpid/sys/ssl/SslSocket.h
+++ b/cpp/src/qpid/sys/ssl/SslSocket.h
@@ -23,7 +23,7 @@
*/
#include "qpid/sys/IOHandle.h"
-#include "qpid/sys/Socket.h"
+#include "qpid/sys/posix/BSDSocket.h"
#include <nspr.h>
#include <string>
@@ -37,55 +37,54 @@ class Duration;
namespace ssl {
-class SslSocket : public qpid::sys::Socket
+class SslSocket : public qpid::sys::BSDSocket
{
public:
- /** Create a socket wrapper for descriptor. */
- SslSocket();
+ /** Create a socket wrapper for descriptor.
+ *@param certName name of certificate to use to identify the socket
+ */
+ SslSocket(const std::string& certName = "", bool clientAuth = false);
/** Set socket non blocking */
void setNonblocking() const;
/** Set tcp-nodelay */
- void setTcpNoDelay(bool nodelay) const;
+ void setTcpNoDelay() const;
/** Set SSL cert-name. Allows the cert-name to be set per
* connection, overriding global cert-name settings from
* NSSInit().*/
void setCertName(const std::string& certName);
- void connect(const std::string& host, const std::string& port) const;
+ void connect(const SocketAddress&) const;
+ void finishConnect(const SocketAddress&) 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;
+ int listen(const SocketAddress&, int backlog = 10) const;
/**
* Accept a connection from a socket that is already listening
* and has an incoming connection
*/
- SslSocket* accept() const;
+ virtual 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;
- uint16_t getLocalPort() const;
- uint16_t getRemotePort() const;
-
int getKeyLen() const;
std::string getClientAuthId() const;
protected:
- mutable std::string connectname;
- mutable PRFileDesc* socket;
+ mutable PRFileDesc* nssSocket;
std::string certname;
+ mutable std::string url;
/**
* 'model' socket, with configuration to use when importing
@@ -94,13 +93,14 @@ protected:
*/
mutable PRFileDesc* prototype;
- SslSocket(IOHandlePrivate* ioph, PRFileDesc* model);
- friend class SslMuxSocket;
+ SslSocket(int fd, PRFileDesc* model);
+ friend class SslMuxSocket; // Needed for this constructor
};
class SslMuxSocket : public SslSocket
{
public:
+ SslMuxSocket(const std::string& certName = "", bool clientAuth = false);
Socket* accept() const;
};
diff --git a/cpp/src/qpid/sys/ssl/util.cpp b/cpp/src/qpid/sys/ssl/util.cpp
index 3078e894df..de5d638b09 100644
--- a/cpp/src/qpid/sys/ssl/util.cpp
+++ b/cpp/src/qpid/sys/ssl/util.cpp
@@ -31,8 +31,6 @@
#include <iostream>
#include <fstream>
-#include <boost/filesystem/operations.hpp>
-#include <boost/filesystem/path.hpp>
namespace qpid {
namespace sys {
@@ -82,15 +80,14 @@ 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());
- }
-}
+ if (retry || passwordFile.empty()) return 0;
+ std::ifstream file(passwordFile.c_str());
+ if (!file) return 0;
+
+ std::string password;
+ file >> password;
+ return PL_strdup(password.c_str());
+}
void initNSS(const SslOptions& options, bool server)
{
diff --git a/cpp/src/qpid/sys/windows/AsynchIO.cpp b/cpp/src/qpid/sys/windows/AsynchIO.cpp
index 355acbe0e6..b36ee9f941 100644
--- a/cpp/src/qpid/sys/windows/AsynchIO.cpp
+++ b/cpp/src/qpid/sys/windows/AsynchIO.cpp
@@ -24,6 +24,8 @@
#include "qpid/sys/AsynchIO.h"
#include "qpid/sys/Mutex.h"
#include "qpid/sys/Socket.h"
+#include "qpid/sys/windows/WinSocket.h"
+#include "qpid/sys/SocketAddress.h"
#include "qpid/sys/Poller.h"
#include "qpid/sys/Thread.h"
#include "qpid/sys/Time.h"
@@ -50,8 +52,8 @@ namespace {
* The function pointers for AcceptEx and ConnectEx need to be looked up
* at run time.
*/
-const LPFN_ACCEPTEX lookUpAcceptEx(const qpid::sys::Socket& s) {
- SOCKET h = toSocketHandle(s);
+const LPFN_ACCEPTEX lookUpAcceptEx(const qpid::sys::IOHandle& io) {
+ SOCKET h = io.fd;
GUID guidAcceptEx = WSAID_ACCEPTEX;
DWORD dwBytes = 0;
LPFN_ACCEPTEX fnAcceptEx;
@@ -93,12 +95,14 @@ private:
AsynchAcceptor::Callback acceptedCallback;
const Socket& socket;
+ const SOCKET wSocket;
const LPFN_ACCEPTEX fnAcceptEx;
};
AsynchAcceptor::AsynchAcceptor(const Socket& s, Callback callback)
: acceptedCallback(callback),
socket(s),
+ wSocket(IOHandle(s).fd),
fnAcceptEx(lookUpAcceptEx(s)) {
s.setNonblocking();
@@ -121,8 +125,8 @@ void AsynchAcceptor::restart(void) {
this,
socket);
BOOL status;
- status = fnAcceptEx(toSocketHandle(socket),
- toSocketHandle(*result->newSocket),
+ status = fnAcceptEx(wSocket,
+ IOHandle(*result->newSocket).fd,
result->addressBuffer,
0,
AsynchAcceptResult::SOCKADDRMAXLEN,
@@ -133,16 +137,30 @@ void AsynchAcceptor::restart(void) {
}
+Socket* createSameTypeSocket(const Socket& sock) {
+ SOCKET socket = IOHandle(sock).fd;
+ // Socket currently has no actual socket attached
+ if (socket == INVALID_SOCKET)
+ return new WinSocket;
+
+ ::sockaddr_storage sa;
+ ::socklen_t salen = sizeof(sa);
+ QPID_WINSOCK_CHECK(::getsockname(socket, (::sockaddr*)&sa, &salen));
+ SOCKET s = ::socket(sa.ss_family, SOCK_STREAM, 0); // Currently only work with SOCK_STREAM
+ if (s == INVALID_SOCKET) throw QPID_WINDOWS_ERROR(WSAGetLastError());
+ return new WinSocket(s);
+}
+
AsynchAcceptResult::AsynchAcceptResult(AsynchAcceptor::Callback cb,
AsynchAcceptor *acceptor,
- const Socket& listener)
+ const Socket& lsocket)
: callback(cb), acceptor(acceptor),
- listener(toSocketHandle(listener)),
- newSocket(listener.createSameTypeSocket()) {
+ listener(IOHandle(lsocket).fd),
+ newSocket(createSameTypeSocket(lsocket)) {
}
void AsynchAcceptResult::success(size_t /*bytesTransferred*/) {
- ::setsockopt (toSocketHandle(*newSocket),
+ ::setsockopt (IOHandle(*newSocket).fd,
SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT,
(char*)&listener,
@@ -180,6 +198,7 @@ public:
ConnectedCallback connCb,
FailedCallback failCb = 0);
void start(Poller::shared_ptr poller);
+ void requestCallback(RequestCallback rCb);
};
AsynchConnector::AsynchConnector(const Socket& sock,
@@ -195,7 +214,7 @@ AsynchConnector::AsynchConnector(const Socket& sock,
void AsynchConnector::start(Poller::shared_ptr)
{
try {
- socket.connect(hostname, port);
+ socket.connect(SocketAddress(hostname, port));
socket.setNonblocking();
connCallback(socket);
} catch(std::exception& e) {
@@ -205,6 +224,13 @@ void AsynchConnector::start(Poller::shared_ptr)
}
}
+// This can never be called in the current windows code as connect
+// is blocking and requestCallback only makes sense if connect is
+// non-blocking with the results returned via a poller callback.
+void AsynchConnector::requestCallback(RequestCallback rCb)
+{
+}
+
} // namespace windows
AsynchAcceptor* AsynchAcceptor::create(const Socket& s,
@@ -260,8 +286,6 @@ public:
virtual void notifyPendingWrite();
virtual void queueWriteClose();
virtual bool writeQueueEmpty();
- virtual void startReading();
- virtual void stopReading();
virtual void requestCallback(RequestCallback);
/**
@@ -272,6 +296,8 @@ public:
*/
virtual BufferBase* getQueuedBuffer();
+ virtual SecuritySettings getSecuritySettings(void);
+
private:
ReadCallback readCallback;
EofCallback eofCallback;
@@ -319,6 +345,12 @@ private:
void close(void);
/**
+ * startReading initiates reading, readComplete() is
+ * called when the read completes.
+ */
+ void startReading();
+
+ /**
* readComplete is called when a read request is complete.
*
* @param result Results of the operation.
@@ -362,7 +394,7 @@ class CallbackHandle : public IOHandle {
public:
CallbackHandle(AsynchIoResult::Completer completeCb,
AsynchIO::RequestCallback reqCb = 0) :
- IOHandle(new IOHandlePrivate (INVALID_SOCKET, completeCb, reqCb))
+ IOHandle(INVALID_SOCKET, completeCb, reqCb)
{}
};
@@ -515,7 +547,7 @@ void AsynchIO::startReading() {
DWORD bytesReceived = 0, flags = 0;
InterlockedIncrement(&opsInProgress);
readInProgress = true;
- int status = WSARecv(toSocketHandle(socket),
+ int status = WSARecv(IOHandle(socket).fd,
const_cast<LPWSABUF>(result->getWSABUF()), 1,
&bytesReceived,
&flags,
@@ -537,15 +569,6 @@ void AsynchIO::startReading() {
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
@@ -613,7 +636,7 @@ void AsynchIO::startWrite(AsynchIO::BufferBase* buff) {
buff,
buff->dataCount);
DWORD bytesSent = 0;
- int status = WSASend(toSocketHandle(socket),
+ int status = WSASend(IOHandle(socket).fd,
const_cast<LPWSABUF>(result->getWSABUF()), 1,
&bytesSent,
0,
@@ -639,6 +662,13 @@ void AsynchIO::close(void) {
notifyClosed();
}
+SecuritySettings AsynchIO::getSecuritySettings() {
+ SecuritySettings settings;
+ settings.ssf = socket.getKeyLen();
+ settings.authid = socket.getClientAuthId();
+ return settings;
+}
+
void AsynchIO::readComplete(AsynchReadResult *result) {
int status = result->getStatus();
size_t bytes = result->getTransferred();
@@ -683,7 +713,8 @@ void AsynchIO::writeComplete(AsynchWriteResult *result) {
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?
+ // What to do with real error??? Save the Buffer? TBD.
+ queueReadBuffer(buff); // All done; back to the pool
}
}
diff --git a/cpp/src/qpid/sys/windows/FileSysDir.cpp b/cpp/src/qpid/sys/windows/FileSysDir.cpp
index 88f1637d48..e090747715 100644
--- a/cpp/src/qpid/sys/windows/FileSysDir.cpp
+++ b/cpp/src/qpid/sys/windows/FileSysDir.cpp
@@ -24,6 +24,9 @@
#include <sys/stat.h>
#include <direct.h>
#include <errno.h>
+#include <windows.h>
+#include <strsafe.h>
+
namespace qpid {
namespace sys {
@@ -50,4 +53,36 @@ void FileSysDir::mkdir(void)
throw Exception ("Can't create directory: " + dirPath);
}
+void FileSysDir::forEachFile(Callback cb) const {
+
+ WIN32_FIND_DATAA findFileData;
+ char szDir[MAX_PATH];
+ size_t dirPathLength;
+ HANDLE hFind = INVALID_HANDLE_VALUE;
+
+ // create dirPath+"\*" in szDir
+ StringCchLength (dirPath.c_str(), MAX_PATH, &dirPathLength);
+
+ if (dirPathLength > (MAX_PATH - 3)) {
+ throw Exception ("Directory path is too long: " + dirPath);
+ }
+
+ StringCchCopy(szDir, MAX_PATH, dirPath.c_str());
+ StringCchCat(szDir, MAX_PATH, TEXT("\\*"));
+
+ // Special work for first file
+ hFind = FindFirstFileA(szDir, &findFileData);
+ if (INVALID_HANDLE_VALUE == hFind) {
+ return;
+ }
+
+ // process everything that isn't a directory
+ do {
+ if (!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ std::string fileName(findFileData.cFileName);
+ cb(fileName);
+ }
+ } while (FindNextFile(hFind, &findFileData) != 0);
+}
+
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/windows/IOHandle.cpp b/cpp/src/qpid/sys/windows/IOHandle.cpp
index 250737cb99..19a1c44875 100755
--- a/cpp/src/qpid/sys/windows/IOHandle.cpp
+++ b/cpp/src/qpid/sys/windows/IOHandle.cpp
@@ -19,24 +19,11 @@
*
*/
-#include "qpid/sys/IOHandle.h"
#include "qpid/sys/windows/IoHandlePrivate.h"
#include <windows.h>
namespace qpid {
namespace sys {
-SOCKET toFd(const IOHandlePrivate* h)
-{
- return h->fd;
-}
-
-IOHandle::IOHandle(IOHandlePrivate* h) :
- impl(h)
-{}
-
-IOHandle::~IOHandle() {
- delete impl;
-}
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/windows/IoHandlePrivate.h b/cpp/src/qpid/sys/windows/IoHandlePrivate.h
index 5943db5cc7..4529ad93ec 100755
--- a/cpp/src/qpid/sys/windows/IoHandlePrivate.h
+++ b/cpp/src/qpid/sys/windows/IoHandlePrivate.h
@@ -38,15 +38,14 @@ namespace sys {
// 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);
-
+class IOHandle {
public:
- IOHandlePrivate(SOCKET f = INVALID_SOCKET,
- windows::AsynchIoResult::Completer cb = 0,
- AsynchIO::RequestCallback reqCallback = 0) :
- fd(f), event(cb), cbRequest(reqCallback)
+ IOHandle(SOCKET f = INVALID_SOCKET,
+ windows::AsynchIoResult::Completer cb = 0,
+ AsynchIO::RequestCallback reqCallback = 0) :
+ fd(f),
+ event(cb),
+ cbRequest(reqCallback)
{}
SOCKET fd;
@@ -54,8 +53,6 @@ public:
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
index c81cef87b0..ecb33c5517 100755
--- a/cpp/src/qpid/sys/windows/IocpPoller.cpp
+++ b/cpp/src/qpid/sys/windows/IocpPoller.cpp
@@ -22,7 +22,7 @@
#include "qpid/sys/Poller.h"
#include "qpid/sys/Mutex.h"
#include "qpid/sys/Dispatcher.h"
-
+#include "qpid/sys/IOHandle.h"
#include "qpid/sys/windows/AsynchIoResult.h"
#include "qpid/sys/windows/IoHandlePrivate.h"
#include "qpid/sys/windows/check.h"
@@ -55,7 +55,7 @@ class PollerHandlePrivate {
};
PollerHandle::PollerHandle(const IOHandle& h) :
- impl(new PollerHandlePrivate(toSocketHandle(static_cast<const Socket&>(h)), h.impl->event, h.impl->cbRequest))
+ impl(new PollerHandlePrivate(h.fd, h.event, h.cbRequest))
{}
PollerHandle::~PollerHandle() {
diff --git a/cpp/src/qpid/sys/windows/PollableCondition.cpp b/cpp/src/qpid/sys/windows/PollableCondition.cpp
index bb637be0a6..3e2a5fb36c 100644
--- a/cpp/src/qpid/sys/windows/PollableCondition.cpp
+++ b/cpp/src/qpid/sys/windows/PollableCondition.cpp
@@ -52,14 +52,14 @@ private:
PollableCondition& parent;
boost::shared_ptr<sys::Poller> poller;
LONG isSet;
+ LONG isDispatching;
};
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)
+ : IOHandle(INVALID_SOCKET, boost::bind(&PollableConditionPrivate::dispatch, this, _1)),
+ cb(cb), parent(parent), poller(poller), isSet(0), isDispatching(0)
{
}
@@ -78,7 +78,12 @@ void PollableConditionPrivate::poke()
void PollableConditionPrivate::dispatch(windows::AsynchIoResult *result)
{
delete result; // Poller::monitorHandle() allocates this
+ // If isDispatching is already set, just return. Else, enter.
+ if (::InterlockedCompareExchange(&isDispatching, 1, 0) == 1)
+ return;
cb(parent);
+ LONG oops = ::InterlockedDecrement(&isDispatching); // Result must be 0
+ assert(!oops);
if (isSet)
poke();
}
diff --git a/cpp/src/qpid/sys/windows/QpidDllMain.h b/cpp/src/qpid/sys/windows/QpidDllMain.h
new file mode 100644
index 0000000000..74eaf0256a
--- /dev/null
+++ b/cpp/src/qpid/sys/windows/QpidDllMain.h
@@ -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 this file once in each DLL that relies on SystemInfo.h:
+ * threadSafeShutdown(). Note that Thread.cpp has a more elaborate
+ * DllMain, that also provides this functionality separately.
+ *
+ * Teardown is in the reverse order of the DLL dependencies used
+ * during the load phase. The calls to DllMain and the static
+ * destructors are from the same thread, so no locking is necessary
+ * and there is no downside to an invocation of DllMain by multiple
+ * Qpid DLLs.
+ */
+
+#ifdef _DLL
+
+#include <qpid/ImportExport.h>
+#include <windows.h>
+
+namespace qpid {
+namespace sys {
+namespace windows {
+
+QPID_IMPORT bool processExiting;
+QPID_IMPORT bool libraryUnloading;
+
+}}} // namespace qpid::sys::SystemInfo
+
+
+BOOL APIENTRY DllMain(HMODULE hm, DWORD reason, LPVOID reserved) {
+ switch (reason) {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+
+ case DLL_PROCESS_DETACH:
+ // Remember how the process is terminating this DLL.
+ if (reserved != NULL) {
+ qpid::sys::windows::processExiting = true;
+ // Danger: all threading suspect, including indirect use of malloc or locks.
+ // Think twice before adding more functionality here.
+ return TRUE;
+ }
+ else {
+ qpid::sys::windows::libraryUnloading = true;
+ }
+ break;
+ }
+ return TRUE;
+}
+
+
+#endif
diff --git a/cpp/src/qpid/sys/windows/SslAsynchIO.cpp b/cpp/src/qpid/sys/windows/SslAsynchIO.cpp
index d263f00ab3..e48c799b29 100644
--- a/cpp/src/qpid/sys/windows/SslAsynchIO.cpp
+++ b/cpp/src/qpid/sys/windows/SslAsynchIO.cpp
@@ -209,18 +209,6 @@ bool SslAsynchIO::writeQueueEmpty() {
return aio->writeQueueEmpty();
}
-/*
- * Initiate a read operation. AsynchIO::readComplete() will be
- * called when the read is complete and data is available.
- */
-void SslAsynchIO::startReading() {
- aio->startReading();
-}
-
-void SslAsynchIO::stopReading() {
- aio->stopReading();
-}
-
// Queue the specified callback for invocation from an I/O thread.
void SslAsynchIO::requestCallback(RequestCallback callback) {
aio->requestCallback(callback);
@@ -241,11 +229,15 @@ AsynchIO::BufferBase* SslAsynchIO::getQueuedBuffer() {
return sslBuff;
}
-unsigned int SslAsynchIO::getSslKeySize() {
+SecuritySettings SslAsynchIO::getSecuritySettings() {
SecPkgContext_KeyInfo info;
memset(&info, 0, sizeof(info));
::QueryContextAttributes(&ctxtHandle, SECPKG_ATTR_KEY_INFO, &info);
- return info.KeySize;
+
+ SecuritySettings settings;
+ settings.ssf = info.KeySize;
+ settings.authid = std::string();
+ return settings;
}
void SslAsynchIO::negotiationDone() {
diff --git a/cpp/src/qpid/sys/windows/SslAsynchIO.h b/cpp/src/qpid/sys/windows/SslAsynchIO.h
index e9d9e8d629..2f6842b135 100644
--- a/cpp/src/qpid/sys/windows/SslAsynchIO.h
+++ b/cpp/src/qpid/sys/windows/SslAsynchIO.h
@@ -77,12 +77,9 @@ public:
virtual void notifyPendingWrite();
virtual void queueWriteClose();
virtual bool writeQueueEmpty();
- virtual void startReading();
- virtual void stopReading();
virtual void requestCallback(RequestCallback);
virtual BufferBase* getQueuedBuffer();
-
- QPID_COMMON_EXTERN unsigned int getSslKeySize();
+ virtual SecuritySettings getSecuritySettings(void);
protected:
CredHandle credHandle;
diff --git a/cpp/src/qpid/sys/windows/SystemInfo.cpp b/cpp/src/qpid/sys/windows/SystemInfo.cpp
index cef78dcc60..fb58d53b81 100755
--- a/cpp/src/qpid/sys/windows/SystemInfo.cpp
+++ b/cpp/src/qpid/sys/windows/SystemInfo.cpp
@@ -25,7 +25,8 @@
#include "qpid/sys/SystemInfo.h"
#include "qpid/sys/IntegerTypes.h"
-#include "qpid/Exception.h"
+#include "qpid/Exception.h"
+#include "qpid/log/Statement.h"
#include <assert.h>
#include <winsock2.h>
@@ -66,39 +67,10 @@ bool SystemInfo::getLocalHostname (Address &address) {
static const std::string LOCALHOST("127.0.0.1");
static const std::string TCP("tcp");
-void SystemInfo::getLocalIpAddresses (uint16_t port,
- std::vector<Address> &addrList) {
- enum { MAX_URL_INTERFACES = 100 };
-
- SOCKET s = socket (PF_INET, SOCK_STREAM, 0);
- if (s != INVALID_SOCKET) {
- INTERFACE_INFO interfaces[MAX_URL_INTERFACES];
- DWORD filledBytes = 0;
- WSAIoctl (s,
- SIO_GET_INTERFACE_LIST,
- 0,
- 0,
- interfaces,
- sizeof (interfaces),
- &filledBytes,
- 0,
- 0);
- unsigned int interfaceCount = filledBytes / sizeof (INTERFACE_INFO);
- for (unsigned int i = 0; i < interfaceCount; ++i) {
- if (interfaces[i].iiFlags & IFF_UP) {
- std::string addr(inet_ntoa(interfaces[i].iiAddress.AddressIn.sin_addr));
- if (addr != LOCALHOST)
- addrList.push_back(Address(TCP, addr, port));
- }
- }
- closesocket (s);
- }
-}
-
-bool SystemInfo::isLocalHost(const std::string& candidateHost) {
- // FIXME aconway 2012-05-03: not implemented.
- assert(0);
- throw Exception("Not implemented: isLocalHost");
+// Null function which always fails to find an network interface name
+bool SystemInfo::getInterfaceAddresses(const std::string&, std::vector<std::string>&)
+{
+ return false;
}
void SystemInfo::getSystemId (std::string &osName,
@@ -208,4 +180,29 @@ std::string SystemInfo::getProcessName()
return name;
}
+
+#ifdef _DLL
+namespace windows {
+// set from one or more Qpid DLLs: i.e. in DllMain with DLL_PROCESS_DETACH
+QPID_EXPORT bool processExiting = false;
+QPID_EXPORT bool libraryUnloading = false;
+}
+#endif
+
+bool SystemInfo::threadSafeShutdown()
+{
+#ifdef _DLL
+ if (!windows::processExiting && !windows::libraryUnloading) {
+ // called before exit() or FreeLibrary(), or by a DLL without
+ // a participating DllMain.
+ QPID_LOG(warning, "invalid query for shutdown state");
+ throw qpid::Exception(QPID_MSG("Unable to determine shutdown state."));
+ }
+ return !windows::processExiting;
+#else
+ // Not a DLL: shutdown can only be by exit() or return from main().
+ return false;
+#endif
+}
+
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/windows/Thread.cpp b/cpp/src/qpid/sys/windows/Thread.cpp
index 23b0033be4..b342c9da1d 100755
--- a/cpp/src/qpid/sys/windows/Thread.cpp
+++ b/cpp/src/qpid/sys/windows/Thread.cpp
@@ -27,6 +27,7 @@
#include "qpid/sys/Thread.h"
#include "qpid/sys/Runnable.h"
#include "qpid/sys/windows/check.h"
+#include "qpid/sys/SystemInfo.h"
#include <process.h>
#include <windows.h>
@@ -274,8 +275,17 @@ Thread Thread::current() {
#ifdef _DLL
+namespace qpid {
+namespace sys {
+namespace windows {
+
+extern bool processExiting;
+extern bool libraryUnloading;
+
+}}} // namespace qpid::sys::SystemInfo
+
// DllMain: called possibly many times in a process lifetime if dll
-// loaded and freed repeatedly . Be mindful of Windows loader lock
+// loaded and freed repeatedly. Be mindful of Windows loader lock
// and other DllMain restrictions.
BOOL APIENTRY DllMain(HMODULE hm, DWORD reason, LPVOID reserved) {
@@ -290,10 +300,12 @@ BOOL APIENTRY DllMain(HMODULE hm, DWORD reason, LPVOID reserved) {
if (reserved != NULL) {
// process exit(): threads are stopped arbitrarily and
// possibly in an inconsistent state. Not even threadLock
- // can be trusted. All static destructors have been
- // called at this point and any resources this unit knows
- // about will be released as part of process tear down by
- // the OS. Accordingly, do nothing.
+ // can be trusted. All static destructors for this unit
+ // are pending and face the same unsafe environment.
+ // Any resources this unit knows about will be released as
+ // part of process tear down by the OS. Accordingly, skip
+ // any clean up tasks.
+ qpid::sys::windows::processExiting = true;
return TRUE;
}
else {
@@ -301,6 +313,7 @@ BOOL APIENTRY DllMain(HMODULE hm, DWORD reason, LPVOID reserved) {
// encouraged to clean up to avoid leaks. Mostly we just
// want any straggler threads to finish and notify
// threadsDone as the last thing they do.
+ qpid::sys::windows::libraryUnloading = true;
while (1) {
{
ScopedCriticalSection l(threadLock);
diff --git a/cpp/src/qpid/sys/windows/Socket.cpp b/cpp/src/qpid/sys/windows/WinSocket.cpp
index a4374260cc..b2d2d79c63 100644
--- a/cpp/src/qpid/sys/windows/Socket.cpp
+++ b/cpp/src/qpid/sys/windows/WinSocket.cpp
@@ -19,18 +19,12 @@
*
*/
-#include "qpid/sys/Socket.h"
+#include "qpid/sys/windows/WinSocket.h"
#include "qpid/sys/SocketAddress.h"
#include "qpid/sys/windows/check.h"
#include "qpid/sys/windows/IoHandlePrivate.h"
-
-// Ensure we get all of winsock2.h
-#ifndef _WIN32_WINNT
-#define _WIN32_WINNT 0x0501
-#endif
-
-#include <winsock2.h>
+#include "qpid/sys/SystemInfo.h"
namespace qpid {
namespace sys {
@@ -67,7 +61,8 @@ public:
}
~WinSockSetup() {
- WSACleanup();
+ if (SystemInfo::threadSafeShutdown())
+ WSACleanup();
}
public:
@@ -106,22 +101,32 @@ uint16_t getLocalPort(int fd)
}
} // namespace
-Socket::Socket() :
- IOHandle(new IOHandlePrivate),
+WinSocket::WinSocket() :
+ handle(new IOHandle),
nonblocking(false),
nodelay(false)
{}
-Socket::Socket(IOHandlePrivate* h) :
- IOHandle(h),
+Socket* createSocket()
+{
+ return new WinSocket;
+}
+
+WinSocket::WinSocket(SOCKET fd) :
+ handle(new IOHandle(fd)),
nonblocking(false),
nodelay(false)
{}
-void Socket::createSocket(const SocketAddress& sa) const
+WinSocket::operator const IOHandle&() const
{
- SOCKET& socket = impl->fd;
- if (socket != INVALID_SOCKET) Socket::close();
+ return *handle;
+}
+
+void WinSocket::createSocket(const SocketAddress& sa) const
+{
+ SOCKET& socket = handle->fd;
+ if (socket != INVALID_SOCKET) WinSocket::close();
SOCKET s = ::socket (getAddrInfo(sa).ai_family,
getAddrInfo(sa).ai_socktype,
@@ -139,39 +144,19 @@ void Socket::createSocket(const SocketAddress& sa) const
}
}
-Socket* Socket::createSameTypeSocket() const {
- SOCKET& socket = impl->fd;
- // Socket currently has no actual socket attached
- if (socket == INVALID_SOCKET)
- return new Socket;
-
- ::sockaddr_storage sa;
- ::socklen_t salen = sizeof(sa);
- QPID_WINSOCK_CHECK(::getsockname(socket, (::sockaddr*)&sa, &salen));
- SOCKET s = ::socket(sa.ss_family, SOCK_STREAM, 0); // Currently only work with SOCK_STREAM
- if (s == INVALID_SOCKET) throw QPID_WINDOWS_ERROR(WSAGetLastError());
- return new Socket(new IOHandlePrivate(s));
-}
-
-void Socket::setNonblocking() const {
+void WinSocket::setNonblocking() const {
u_long nonblock = 1;
- QPID_WINSOCK_CHECK(ioctlsocket(impl->fd, FIONBIO, &nonblock));
-}
-
-void Socket::connect(const std::string& host, const std::string& port) const
-{
- SocketAddress sa(host, port);
- connect(sa);
+ QPID_WINSOCK_CHECK(ioctlsocket(handle->fd, FIONBIO, &nonblock));
}
void
-Socket::connect(const SocketAddress& addr) const
+WinSocket::connect(const SocketAddress& addr) const
{
peername = addr.asString(false);
createSocket(addr);
- const SOCKET& socket = impl->fd;
+ const SOCKET& socket = handle->fd;
int err;
WSASetLastError(0);
if ((::connect(socket, getAddrInfo(addr).ai_addr, getAddrInfo(addr).ai_addrlen) != 0) &&
@@ -180,44 +165,43 @@ Socket::connect(const SocketAddress& addr) const
}
void
-Socket::close() const
+WinSocket::finishConnect(const SocketAddress&) const
{
- SOCKET& socket = impl->fd;
+}
+
+void
+WinSocket::close() const
+{
+ SOCKET& socket = handle->fd;
if (socket == INVALID_SOCKET) return;
QPID_WINSOCK_CHECK(closesocket(socket));
socket = INVALID_SOCKET;
}
-int Socket::write(const void *buf, size_t count) const
+int WinSocket::write(const void *buf, size_t count) const
{
- const SOCKET& socket = impl->fd;
+ const SOCKET& socket = handle->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
+int WinSocket::read(void *buf, size_t count) const
{
- const SOCKET& socket = impl->fd;
+ const SOCKET& socket = handle->fd;
int received = ::recv(socket, (char *)buf, count, 0);
if (received == SOCKET_ERROR)
return -1;
return received;
}
-int Socket::listen(const std::string& host, const std::string& port, int backlog) const
-{
- SocketAddress sa(host, port);
- return listen(sa, backlog);
-}
-
-int Socket::listen(const SocketAddress& addr, int backlog) const
+int WinSocket::listen(const SocketAddress& addr, int backlog) const
{
createSocket(addr);
- const SOCKET& socket = impl->fd;
+ const SOCKET& socket = handle->fd;
BOOL yes=1;
QPID_WINSOCK_CHECK(setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)));
@@ -229,48 +213,48 @@ int Socket::listen(const SocketAddress& addr, int backlog) const
return getLocalPort(socket);
}
-Socket* Socket::accept() const
+Socket* WinSocket::accept() const
{
- SOCKET afd = ::accept(impl->fd, 0, 0);
+ SOCKET afd = ::accept(handle->fd, 0, 0);
if (afd != INVALID_SOCKET)
- return new Socket(new IOHandlePrivate(afd));
+ return new WinSocket(afd);
else if (WSAGetLastError() == EAGAIN)
return 0;
else throw QPID_WINDOWS_ERROR(WSAGetLastError());
}
-std::string Socket::getPeerAddress() const
+std::string WinSocket::getPeerAddress() const
{
if (peername.empty()) {
- peername = getName(impl->fd, false);
+ peername = getName(handle->fd, false);
}
return peername;
}
-std::string Socket::getLocalAddress() const
+std::string WinSocket::getLocalAddress() const
{
if (localname.empty()) {
- localname = getName(impl->fd, true);
+ localname = getName(handle->fd, true);
}
return localname;
}
-int Socket::getError() const
+int WinSocket::getError() const
{
int result;
socklen_t rSize = sizeof (result);
- QPID_WINSOCK_CHECK(::getsockopt(impl->fd, SOL_SOCKET, SO_ERROR, (char *)&result, &rSize));
+ QPID_WINSOCK_CHECK(::getsockopt(handle->fd, SOL_SOCKET, SO_ERROR, (char *)&result, &rSize));
return result;
}
-void Socket::setTcpNoDelay() const
+void WinSocket::setTcpNoDelay() const
{
- SOCKET& socket = impl->fd;
+ SOCKET& socket = handle->fd;
nodelay = true;
if (socket != INVALID_SOCKET) {
int flag = 1;
- int result = setsockopt(impl->fd,
+ int result = setsockopt(handle->fd,
IPPROTO_TCP,
TCP_NODELAY,
(char *)&flag,
@@ -279,14 +263,14 @@ void Socket::setTcpNoDelay() const
}
}
-inline IOHandlePrivate* IOHandlePrivate::getImpl(const qpid::sys::IOHandle &h)
+int WinSocket::getKeyLen() const
{
- return h.impl;
+ return 0;
}
-SOCKET toSocketHandle(const Socket& s)
+std::string WinSocket::getClientAuthId() const
{
- return IOHandlePrivate::getImpl(s)->fd;
+ return std::string();
}
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/windows/WinSocket.h b/cpp/src/qpid/sys/windows/WinSocket.h
new file mode 100644
index 0000000000..bee6a58e7a
--- /dev/null
+++ b/cpp/src/qpid/sys/windows/WinSocket.h
@@ -0,0 +1,118 @@
+#ifndef QPID_SYS_WINDOWS_BSDSOCKET_H
+#define QPID_SYS_WINDOWS_BSDSOCKET_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/IntegerTypes.h"
+#include "qpid/CommonImportExport.h"
+#include <string>
+
+#include <boost/scoped_ptr.hpp>
+
+// Ensure we get all of winsock2.h
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
+
+#include <winsock2.h>
+
+namespace qpid {
+namespace sys {
+
+namespace windows {
+Socket* createSameTypeSocket(const Socket&);
+}
+
+class Duration;
+class IOHandle;
+class SocketAddress;
+
+class QPID_COMMON_CLASS_EXTERN WinSocket : public Socket
+{
+public:
+ /** Create a socket wrapper for descriptor. */
+ QPID_COMMON_EXTERN WinSocket();
+
+ QPID_COMMON_EXTERN operator const IOHandle&() const;
+
+ /** Set socket non blocking */
+ QPID_COMMON_EXTERN virtual void setNonblocking() const;
+
+ QPID_COMMON_EXTERN virtual void setTcpNoDelay() const;
+
+ QPID_COMMON_EXTERN virtual void connect(const SocketAddress&) const;
+ QPID_COMMON_EXTERN virtual void finishConnect(const SocketAddress&) const;
+
+ QPID_COMMON_EXTERN virtual void close() const;
+
+ /** Bind to a port and start listening.
+ *@return The bound port number
+ */
+ QPID_COMMON_EXTERN virtual int listen(const SocketAddress&, int backlog = 10) const;
+
+ /**
+ * Returns an address (host and port) for the remote end of the
+ * socket
+ */
+ QPID_COMMON_EXTERN std::string getPeerAddress() const;
+ /**
+ * Returns an address (host and port) for the local end of the
+ * socket
+ */
+ QPID_COMMON_EXTERN std::string getLocalAddress() const;
+
+ /**
+ * Returns the error code stored in the socket. This may be used
+ * to determine the result of a non-blocking connect.
+ */
+ QPID_COMMON_EXTERN int getError() const;
+
+ /** Accept a connection from a socket that is already listening
+ * and has an incoming connection
+ */
+ QPID_COMMON_EXTERN virtual Socket* accept() const;
+
+ // TODO The following are raw operations, maybe they need better wrapping?
+ QPID_COMMON_EXTERN virtual int read(void *buf, size_t count) const;
+ QPID_COMMON_EXTERN virtual int write(const void *buf, size_t count) const;
+
+ QPID_COMMON_EXTERN int getKeyLen() const;
+ QPID_COMMON_EXTERN std::string getClientAuthId() const;
+
+protected:
+ /** Create socket */
+ void createSocket(const SocketAddress&) const;
+
+ mutable boost::scoped_ptr<IOHandle> handle;
+ mutable std::string localname;
+ mutable std::string peername;
+ mutable bool nonblocking;
+ mutable bool nodelay;
+
+ /** Construct socket with existing handle */
+ friend Socket* qpid::sys::windows::createSameTypeSocket(const Socket&);
+ WinSocket(SOCKET fd);
+};
+
+}}
+#endif /*!QPID_SYS_WINDOWS_BSDSOCKET_H*/
diff --git a/cpp/src/qpid/types/Variant.cpp b/cpp/src/qpid/types/Variant.cpp
index d332fffa5e..8c9837e765 100644
--- a/cpp/src/qpid/types/Variant.cpp
+++ b/cpp/src/qpid/types/Variant.cpp
@@ -110,21 +110,28 @@ class VariantImpl
} value;
std::string encoding;//optional encoding for variable length data
- template<class T> T convertFromString() const
+ template<class T> T convertFromString() const
{
const std::string& s = *value.string;
+
try {
- T r = boost::lexical_cast<T>(s);
- //lexical_cast won't fail if string is a negative number and T is unsigned
- //So check that and allow special case of negative zero
- //else its a non-zero negative number so throw exception at end of function
- if (std::numeric_limits<T>::is_signed || s.find('-') != 0 || r == 0) {
- return r;
+ // Extra shenanigans to work around negative zero
+ // conversion error in older GCC libs.
+ if ( s[0] != '-' ) {
+ return boost::lexical_cast<T>(s);
+ } else {
+ T r = boost::lexical_cast<T>(s.substr(1));
+ if (std::numeric_limits<T>::is_signed) {
+ return -r;
+ } else {
+ if (r==0) return 0;
+ }
}
} catch(const boost::bad_lexical_cast&) {
}
throw InvalidConversion(QPID_MSG("Cannot convert " << s));
}
+
};
@@ -650,7 +657,7 @@ VariantImpl* VariantImpl::create(const Variant& v)
}
}
-Variant::Variant() : impl(0) {}
+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)) {}
@@ -893,6 +900,8 @@ bool operator==(const Variant& a, const Variant& b)
return a.isEqualTo(b);
}
+bool operator!=(const Variant& a, const Variant& b) { return !(a == b); }
+
bool Variant::isEqualTo(const Variant& other) const
{
return impl && impl->isEqualTo(*other.impl);
diff --git a/cpp/src/qpidd.cpp b/cpp/src/qpidd.cpp
index b5686c6ab8..e17dea3164 100644
--- a/cpp/src/qpidd.cpp
+++ b/cpp/src/qpidd.cpp
@@ -41,6 +41,18 @@ int run_broker(int argc, char *argv[], bool hidden)
{
BootstrapOptions bootOptions(argv[0]);
string defaultPath (bootOptions.module.loadDir);
+
+ // --version causes print and exit
+ if (bootOptions.findArg(argc, argv, "version")) {
+ cout << "qpidd (" << qpid::product << ") version "
+ << qpid::version << endl;
+ return 0;
+ }
+
+ // --help sets a flag so that its presence is known despite
+ // subsequent parse problems.
+ bool helpArgSeen = bootOptions.findArg(argc, argv, "help");
+
// Parse only the common, load, and log options to see which
// modules need to be loaded. Once the modules are loaded,
// the command line will be re-parsed with all of the
@@ -51,32 +63,42 @@ int run_broker(int argc, char *argv[], bool hidden)
bootOptions.log.sinkOptions->detached();
qpid::log::Logger::instance().configure(bootOptions.log);
} catch (const std::exception& e) {
+ if (helpArgSeen) {
+ // provide help even when parsing fails
+ bootOptions.usage();
+ }
// Couldn't configure logging so write the message direct to stderr.
- cerr << "Unexpected error: " << e.what() << endl;
+ cerr << endl << "Unexpected error: " << e.what() << endl;
return 1;
}
for (vector<string>::iterator iter = bootOptions.module.load.begin();
iter != bootOptions.module.load.end();
iter++)
- qpid::tryShlib (iter->data(), false);
+ qpid::tryShlib (*iter);
if (!bootOptions.module.noLoad) {
bool isDefault = defaultPath == bootOptions.module.loadDir;
qpid::loadModuleDir (bootOptions.module.loadDir, isDefault);
}
- // Parse options
- options.reset(new QpiddOptions(argv[0]));
- options->parse(argc, argv, options->common.config);
+ // Parse options. In the second pass, do not allow unknown options.
+ // All the modules have been added now, so any unknown options
+ // should be flagged as errors.
+ try {
+ options.reset(new QpiddOptions(argv[0]));
+ options->parse(argc, argv, options->common.config, false);
+ } catch (const std::exception& /*e*/) {
+ if (helpArgSeen) {
+ // provide help even when parsing fails
+ options->usage();
+ }
+ throw;
+ }
// Options that just print information.
- if (options->common.help || options->common.version) {
- if (options->common.version)
- cout << "qpidd (" << qpid::product << ") version "
- << qpid::version << endl;
- else if (options->common.help)
- options->usage();
+ if (helpArgSeen) {
+ options->usage();
return 0;
}
diff --git a/cpp/src/qpidd.h b/cpp/src/qpidd.h
index f7f84d11da..08356ab29e 100644
--- a/cpp/src/qpidd.h
+++ b/cpp/src/qpidd.h
@@ -37,9 +37,10 @@ namespace broker {
struct BootstrapOptions : public qpid::Options {
qpid::CommonOptions common;
qpid::ModuleOptions module;
- qpid::log::Options log;
+ qpid::log::Options log;
BootstrapOptions(const char *argv0);
+ void usage() const;
};
// Each platform derives an options struct from QpiddOptionsPrivate, adding
diff --git a/cpp/src/rdma.cmake b/cpp/src/rdma.cmake
index b0d92facab..1d355e7ae6 100644
--- a/cpp/src/rdma.cmake
+++ b/cpp/src/rdma.cmake
@@ -64,11 +64,12 @@ if (BUILD_RDMA)
add_library (rdmawrap SHARED ${rdma_SOURCES})
target_link_libraries (rdmawrap qpidcommon rdmacm ibverbs)
- set_target_properties (rdmawrap PROPERTIES VERSION ${rdmawrap_version})
+ set_target_properties (rdmawrap PROPERTIES
+ LINK_FLAGS "${CATCH_UNDEFINED}"
+ VERSION ${rdmawrap_version})
if (CMAKE_COMPILER_IS_GNUCXX)
set_target_properties(rdmawrap PROPERTIES
- COMPILE_FLAGS -Wno-missing-field-initializers
- LINK_FLAGS ${GCC_CATCH_UNDEFINED})
+ COMPILE_FLAGS -Wno-missing-field-initializers)
endif (CMAKE_COMPILER_IS_GNUCXX)
install (TARGETS rdmawrap
@@ -78,12 +79,13 @@ if (BUILD_RDMA)
add_library (rdma MODULE qpid/sys/RdmaIOPlugin.cpp)
target_link_libraries (rdma qpidbroker rdmawrap)
set_target_properties (rdma PROPERTIES
+ COMPILE_DEFINITIONS _IN_QPID_BROKER
+ LINK_FLAGS "${CATCH_UNDEFINED}"
PREFIX "")
if (CMAKE_COMPILER_IS_GNUCXX)
set_target_properties(rdma PROPERTIES
- COMPILE_FLAGS -Wno-missing-field-initializers
- LINK_FLAGS ${GCC_CATCH_UNDEFINED})
+ COMPILE_FLAGS -Wno-missing-field-initializers)
endif (CMAKE_COMPILER_IS_GNUCXX)
install (TARGETS rdma
@@ -93,12 +95,12 @@ if (BUILD_RDMA)
add_library (rdmaconnector MODULE qpid/client/RdmaConnector.cpp)
target_link_libraries (rdmaconnector qpidclient rdmawrap)
set_target_properties (rdmaconnector PROPERTIES
+ LINK_FLAGS "${CATCH_UNDEFINED}"
PREFIX "")
if (CMAKE_COMPILER_IS_GNUCXX)
set_target_properties(rdmaconnector PROPERTIES
- COMPILE_FLAGS -Wno-missing-field-initializers
- LINK_FLAGS ${GCC_CATCH_UNDEFINED})
+ COMPILE_FLAGS -Wno-missing-field-initializers)
endif (CMAKE_COMPILER_IS_GNUCXX)
install (TARGETS rdmaconnector
diff --git a/cpp/src/ssl.cmake b/cpp/src/ssl.cmake
index d66f59cfff..b7ad58b9f0 100644
--- a/cpp/src/ssl.cmake
+++ b/cpp/src/ssl.cmake
@@ -69,8 +69,6 @@ if (BUILD_SSL)
qpid/sys/ssl/util.cpp
qpid/sys/ssl/SslSocket.h
qpid/sys/ssl/SslSocket.cpp
- qpid/sys/ssl/SslIo.h
- qpid/sys/ssl/SslIo.cpp
)
add_library (sslcommon SHARED ${sslcommon_SOURCES})
@@ -87,14 +85,13 @@ if (BUILD_SSL)
set (ssl_SOURCES
qpid/sys/SslPlugin.cpp
- qpid/sys/ssl/SslHandler.h
- qpid/sys/ssl/SslHandler.cpp
)
add_library (ssl MODULE ${ssl_SOURCES})
target_link_libraries (ssl qpidbroker sslcommon ${Boost_PROGRAM_OPTIONS_LIBRARY})
set_target_properties (ssl PROPERTIES
PREFIX ""
- COMPILE_FLAGS ${NSS_COMPILE_FLAGS})
+ COMPILE_FLAGS "${NSS_COMPILE_FLAGS}"
+ COMPILE_DEFINITIONS _IN_QPID_BROKER)
if (CMAKE_COMPILER_IS_GNUCXX)
set_target_properties(ssl PROPERTIES
LINK_FLAGS "${GCC_CATCH_UNDEFINED}")
@@ -104,7 +101,7 @@ if (BUILD_SSL)
DESTINATION ${QPIDD_MODULE_DIR}
COMPONENT ${QPID_COMPONENT_BROKER})
- add_library (sslconnector MODULE qpid/client/SslConnector.cpp)
+ add_library (sslconnector MODULE qpid/client/SslConnector.cpp qpid/messaging/amqp/SslTransport.cpp)
target_link_libraries (sslconnector qpidclient sslcommon)
set_target_properties (sslconnector PROPERTIES
PREFIX ""
diff --git a/cpp/src/ssl.mk b/cpp/src/ssl.mk
index 4dba9bb61c..aaaf14ed54 100644
--- a/cpp/src/ssl.mk
+++ b/cpp/src/ssl.mk
@@ -25,9 +25,7 @@ libsslcommon_la_SOURCES = \
qpid/sys/ssl/util.h \
qpid/sys/ssl/util.cpp \
qpid/sys/ssl/SslSocket.h \
- qpid/sys/ssl/SslSocket.cpp \
- qpid/sys/ssl/SslIo.h \
- qpid/sys/ssl/SslIo.cpp
+ qpid/sys/ssl/SslSocket.cpp
SSLCOMMON_VERSION_INFO = 2:0:0
libsslcommon_la_LDFLAGS = -version-info $(SSLCOMMON_VERSION_INFO)
@@ -37,13 +35,11 @@ libsslcommon_la_CXXFLAGS=$(AM_CXXFLAGS) $(SSL_CFLAGS)
lib_LTLIBRARIES += libsslcommon.la
ssl_la_SOURCES = \
- qpid/sys/SslPlugin.cpp \
- qpid/sys/ssl/SslHandler.h \
- qpid/sys/ssl/SslHandler.cpp
+ qpid/sys/SslPlugin.cpp
ssl_la_LIBADD= libqpidbroker.la libsslcommon.la
-ssl_la_CXXFLAGS=$(AM_CXXFLAGS) $(SSL_CFLAGS)
+ssl_la_CXXFLAGS=$(AM_CXXFLAGS) $(SSL_CFLAGS) -D_IN_QPID_BROKER
ssl_la_LDFLAGS = $(PLUGINLDFLAGS)
@@ -52,6 +48,13 @@ dmoduleexec_LTLIBRARIES += ssl.la
sslconnector_la_SOURCES = \
qpid/client/SslConnector.cpp
+if HAVE_PROTON
+sslconnector_la_SOURCES += \
+ qpid/messaging/amqp/SslTransport.cpp \
+ qpid/messaging/amqp/SslTransport.h
+endif #HAVE_PROTON
+
+
sslconnector_la_LIBADD = \
libqpidclient.la \
libsslcommon.la
diff --git a/cpp/src/tests/.valgrind.supp b/cpp/src/tests/.valgrind.supp
index 2c6a1509ff..1a24a9178e 100644
--- a/cpp/src/tests/.valgrind.supp
+++ b/cpp/src/tests/.valgrind.supp
@@ -39,21 +39,6 @@
fun:_sasl_load_plugins
fun:sasl_client_init
}
-{
- Benign leak in CPG - patched version.
- Memcheck:Leak
- fun:*
- fun:openais_service_connect
- fun:cpg_initialize
-}
-
-{
- Benign error in libcpg.
- Memcheck:Param
- socketcall.sendmsg(msg.msg_iov[i])
- obj:*/libpthread-2.5.so
- obj:*/libcpg.so.2.0.0
-}
{
Uninitialised value problem in _dl_relocate (F7, F8)
@@ -161,14 +146,6 @@
}
{
- CPG error - seems benign.
- Memcheck:Param
- socketcall.sendmsg(msg.msg_iov[i])
- obj:*
- obj:*/libcpg.so.2.0.0
-}
-
-{
Known leak in boost.thread 1.33.1. Wildcards for 64/32 bit diffs.
Memcheck:Leak
fun:*
diff --git a/cpp/src/tests/BrokerMgmtAgent.cpp b/cpp/src/tests/BrokerMgmtAgent.cpp
index 02aa87f876..29c3faf809 100644
--- a/cpp/src/tests/BrokerMgmtAgent.cpp
+++ b/cpp/src/tests/BrokerMgmtAgent.cpp
@@ -22,6 +22,7 @@
#include "unit_test.h"
#include "MessagingFixture.h"
#include "qpid/management/Buffer.h"
+#include "qpid/management/ManagementAgent.h"
#include "qpid/messaging/Message.h"
#include "qpid/amqp_0_10/Codecs.h"
#include "qpid/log/Logger.h"
@@ -41,751 +42,345 @@ using namespace qpid::types;
namespace qpid {
- namespace tests {
-
- namespace _qmf = qmf::org::apache::qpid::broker::mgmt::test;
- namespace {
-
- typedef boost::shared_ptr<_qmf::TestObject> TestObjectPtr;
- typedef std::vector<TestObjectPtr> TestObjectVector;
-
- // Instantiates a broker and its internal management agent. Provides
- // factories for constructing Receivers for object indication messages.
- //
- class AgentFixture
- {
- MessagingFixture *mFix;
-
- public:
- AgentFixture( unsigned int pubInterval=10,
- bool qmfV2=false,
- qpid::broker::Broker::Options opts = qpid::broker::Broker::Options())
- {
- opts.enableMgmt=true;
- opts.qmf2Support=qmfV2;
- opts.mgmtPubInterval=pubInterval;
- mFix = new MessagingFixture(opts, true);
-
- _qmf::TestObject::registerSelf(getBrokerAgent());
- };
- ~AgentFixture()
- {
- delete mFix;
- };
- ::qpid::management::ManagementAgent *getBrokerAgent() { return mFix->broker->getManagementAgent(); }
- Receiver createV1DataIndRcvr( const std::string package, const std::string klass )
- {
- return mFix->session.createReceiver(std::string("kqueue; {create: always, delete: always, "
- "node: {type: queue, "
- "x-bindings: [{exchange: qpid.management, "
- "key: 'console.obj.1.0.")
- + package + std::string(".") + klass
- + std::string("'}]}}"));
- };
- Receiver createV2DataIndRcvr( const std::string package, const std::string klass )
- {
- std::string p(package);
- std::replace(p.begin(), p.end(), '.', '_');
- std::string k(klass);
- std::replace(k.begin(), k.end(), '.', '_');
-
- return mFix->session.createReceiver(std::string("kqueue; {create: always, delete: always, "
- "node: {type: queue, "
- "x-bindings: [{exchange: qmf.default.topic, "
- "key: 'agent.ind.data.")
- + p + std::string(".") + k
- + std::string("'}]}}"));
- };
- };
-
-
- // A "management object" that supports the TestObject
- //
- class TestManageable : public qpid::management::Manageable
- {
- management::ManagementObject* mgmtObj;
- const std::string key;
- public:
- TestManageable(management::ManagementAgent *agent, std::string _key)
- : key(_key)
- {
- _qmf::TestObject *tmp = new _qmf::TestObject(agent, this);
-
- // seed it with some default values...
- tmp->set_string1(key);
- tmp->set_bool1(true);
- qpid::types::Variant::Map vMap;
- vMap["one"] = qpid::types::Variant(1);
- vMap["two"] = qpid::types::Variant("two");
- vMap["three"] = qpid::types::Variant("whatever");
- tmp->set_map1(vMap);
-
- mgmtObj = tmp;
- };
- ~TestManageable() { mgmtObj = 0; /* deleted by agent on shutdown */ };
- management::ManagementObject* GetManagementObject() const { return mgmtObj; };
- static void validateTestObjectProperties(_qmf::TestObject& to)
- {
- // verify the default values are as expected. We don't check 'string1',
- // as it is the object key, and is unique for each object (no default value).
- BOOST_CHECK(to.get_bool1() == true);
- BOOST_CHECK(to.get_map1().size() == 3);
- qpid::types::Variant::Map mappy = to.get_map1();
- BOOST_CHECK(1 == (unsigned int)mappy["one"]);
- BOOST_CHECK(mappy["two"].asString() == std::string("two"));
- BOOST_CHECK(mappy["three"].asString() == std::string("whatever"));
- };
- };
-
-
- // decode a V1 Content Indication message
- //
- void decodeV1ObjectUpdates(const Message& inMsg, TestObjectVector& objs, const size_t objLen)
- {
- const size_t MAX_BUFFER_SIZE=65536;
- char tmp[MAX_BUFFER_SIZE];
-
- objs.clear();
-
- BOOST_CHECK(inMsg.getContent().size() <= MAX_BUFFER_SIZE);
-
- ::memcpy(tmp, inMsg.getContent().data(), inMsg.getContent().size());
- Buffer buf(tmp, inMsg.getContent().size());
-
- while (buf.available() > 8) { // 8 == qmf v1 header size
- BOOST_CHECK_EQUAL(buf.getOctet(), 'A');
- BOOST_CHECK_EQUAL(buf.getOctet(), 'M');
- BOOST_CHECK_EQUAL(buf.getOctet(), '2');
- BOOST_CHECK_EQUAL(buf.getOctet(), 'c'); // opcode == content indication
- // @@todo: kag: how do we skip 'i' entries???
- buf.getLong(); // ignore sequence
-
- std::string str1; // decode content body as string
- buf.getRawData(str1, objLen);
-
- TestObjectPtr fake(new _qmf::TestObject(0,0));
- fake->readProperties( str1 );
- objs.push_back(fake);
- }
- }
-
-
- // decode a V2 Content Indication message
- //
- void decodeV2ObjectUpdates(const qpid::messaging::Message& inMsg, TestObjectVector& objs)
- {
- objs.clear();
-
- BOOST_CHECK_EQUAL(inMsg.getContentType(), std::string("amqp/list"));
-
- const ::qpid::types::Variant::Map& m = inMsg.getProperties();
- Variant::Map::const_iterator iter = m.find(std::string("qmf.opcode"));
- BOOST_CHECK(iter != m.end());
- BOOST_CHECK_EQUAL(iter->second.asString(), std::string("_data_indication"));
-
- Variant::List vList;
- ::qpid::amqp_0_10::ListCodec::decode(inMsg.getContent(), vList);
-
- for (Variant::List::iterator lIter = vList.begin(); lIter != vList.end(); lIter++) {
- TestObjectPtr fake(new _qmf::TestObject(0,0));
- fake->readTimestamps(lIter->asMap());
- fake->mapDecodeValues((lIter->asMap())["_values"].asMap());
- objs.push_back(fake);
- }
- }
- }
-
- QPID_AUTO_TEST_SUITE(BrokerMgmtAgent)
-
- // verify that an object that is added to the broker's management database is
- // published correctly. Furthermore, verify that it is published once after
- // it has been deleted.
- //
- QPID_AUTO_TEST_CASE(v1ObjPublish)
- {
- AgentFixture* fix = new AgentFixture(3);
- management::ManagementAgent* agent;
- agent = fix->getBrokerAgent();
-
- // create a manageable test object
- TestManageable *tm = new TestManageable(agent, std::string("obj1"));
- uint32_t objLen = tm->GetManagementObject()->writePropertiesSize();
-
- Receiver r1 = fix->createV1DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
-
- agent->addObject(tm->GetManagementObject(), 1);
-
- // wait for the object to be published
- Message m1;
- BOOST_CHECK(r1.fetch(m1, Duration::SECOND * 6));
-
- TestObjectVector objs;
- decodeV1ObjectUpdates(m1, objs, objLen);
- BOOST_CHECK(objs.size() > 0);
-
- for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
-
- TestManageable::validateTestObjectProperties(**oIter);
-
- qpid::types::Variant::Map mappy;
- (*oIter)->writeTimestamps(mappy);
- BOOST_CHECK(0 == mappy["_delete_ts"].asUint64()); // not deleted
- }
-
- // destroy the object
-
- tm->GetManagementObject()->resourceDestroy();
-
- // wait for the deleted object to be published
-
- bool isDeleted = false;
- while (!isDeleted && r1.fetch(m1, Duration::SECOND * 6)) {
-
- decodeV1ObjectUpdates(m1, objs, objLen);
- BOOST_CHECK(objs.size() > 0);
-
- for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
-
- TestManageable::validateTestObjectProperties(**oIter);
-
- qpid::types::Variant::Map mappy;
- (*oIter)->writeTimestamps(mappy);
- if (mappy["_delete_ts"].asUint64() != 0)
- isDeleted = true;
- }
- }
-
- BOOST_CHECK(isDeleted);
-
- r1.close();
- delete fix;
- delete tm;
- }
-
- // Repeat the previous test, but with V2-based object support
- //
- QPID_AUTO_TEST_CASE(v2ObjPublish)
- {
- AgentFixture* fix = new AgentFixture(3, true);
- management::ManagementAgent* agent;
- agent = fix->getBrokerAgent();
-
- TestManageable *tm = new TestManageable(agent, std::string("obj2"));
-
- Receiver r1 = fix->createV2DataIndRcvr(tm->GetManagementObject()->getPackageName(), "#");
-
- agent->addObject(tm->GetManagementObject(), "testobj-1");
-
- // wait for the object to be published
- Message m1;
- BOOST_CHECK(r1.fetch(m1, Duration::SECOND * 6));
-
- TestObjectVector objs;
- decodeV2ObjectUpdates(m1, objs);
- BOOST_CHECK(objs.size() > 0);
-
- for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
-
- TestManageable::validateTestObjectProperties(**oIter);
-
- qpid::types::Variant::Map mappy;
- (*oIter)->writeTimestamps(mappy);
- BOOST_CHECK(0 == mappy["_delete_ts"].asUint64());
- }
-
- // destroy the object
-
- tm->GetManagementObject()->resourceDestroy();
-
- // wait for the deleted object to be published
-
- bool isDeleted = false;
- while (!isDeleted && r1.fetch(m1, Duration::SECOND * 6)) {
-
- decodeV2ObjectUpdates(m1, objs);
- BOOST_CHECK(objs.size() > 0);
-
- for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
-
- TestManageable::validateTestObjectProperties(**oIter);
-
- qpid::types::Variant::Map mappy;
- (*oIter)->writeTimestamps(mappy);
- if (mappy["_delete_ts"].asUint64() != 0)
- isDeleted = true;
- }
- }
-
- BOOST_CHECK(isDeleted);
-
- r1.close();
- delete fix;
- delete tm;
- }
-
-
- // verify that a deleted object is exported correctly using the
- // exportDeletedObjects() method. V1 testcase.
- //
- QPID_AUTO_TEST_CASE(v1ExportDelObj)
- {
- AgentFixture* fix = new AgentFixture(3);
- management::ManagementAgent* agent;
- agent = fix->getBrokerAgent();
-
- // create a manageable test object
- TestManageable *tm = new TestManageable(agent, std::string("myObj"));
- uint32_t objLen = tm->GetManagementObject()->writePropertiesSize();
-
- Receiver r1 = fix->createV1DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
-
- agent->addObject(tm->GetManagementObject(), 1);
-
- // wait for the object to be published
- Message m1;
- BOOST_CHECK(r1.fetch(m1, Duration::SECOND * 6));
-
- TestObjectVector objs;
- decodeV1ObjectUpdates(m1, objs, objLen);
- BOOST_CHECK(objs.size() > 0);
-
- // destroy the object, then immediately export (before the next poll cycle)
-
- ::qpid::management::ManagementAgent::DeletedObjectList delObjs;
- tm->GetManagementObject()->resourceDestroy();
- agent->exportDeletedObjects( delObjs );
- BOOST_CHECK(delObjs.size() == 1);
-
- // wait for the deleted object to be published
-
- bool isDeleted = false;
- while (!isDeleted && r1.fetch(m1, Duration::SECOND * 6)) {
-
- decodeV1ObjectUpdates(m1, objs, objLen);
- BOOST_CHECK(objs.size() > 0);
-
- for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
-
- TestManageable::validateTestObjectProperties(**oIter);
-
- qpid::types::Variant::Map mappy;
- (*oIter)->writeTimestamps(mappy);
- if (mappy["_delete_ts"].asUint64() != 0)
- isDeleted = true;
- }
- }
-
- BOOST_CHECK(isDeleted);
-
- // verify there are no deleted objects to export now.
-
- agent->exportDeletedObjects( delObjs );
- BOOST_CHECK(delObjs.size() == 0);
-
- r1.close();
- delete fix;
- delete tm;
- }
-
-
- // verify that a deleted object is imported correctly using the
- // importDeletedObjects() method. V1 testcase.
- //
- QPID_AUTO_TEST_CASE(v1ImportDelObj)
- {
- AgentFixture* fix = new AgentFixture(3);
- management::ManagementAgent* agent;
- agent = fix->getBrokerAgent();
-
- // create a manageable test object
- TestManageable *tm = new TestManageable(agent, std::string("anObj"));
- uint32_t objLen = tm->GetManagementObject()->writePropertiesSize();
-
- Receiver r1 = fix->createV1DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
+namespace tests {
+
+namespace _qmf = qmf::org::apache::qpid::broker::mgmt::test;
+namespace {
+
+typedef boost::shared_ptr<_qmf::TestObject> TestObjectPtr;
+typedef std::vector<TestObjectPtr> TestObjectVector;
+
+// Instantiates a broker and its internal management agent. Provides
+// factories for constructing Receivers for object indication messages.
+//
+class AgentFixture
+{
+ MessagingFixture *mFix;
+
+ public:
+ AgentFixture( unsigned int pubInterval=10,
+ bool qmfV2=false,
+ qpid::broker::Broker::Options opts = qpid::broker::Broker::Options())
+ {
+ opts.enableMgmt=true;
+ opts.qmf2Support=qmfV2;
+ opts.mgmtPubInterval=pubInterval;
+ mFix = new MessagingFixture(opts, true);
+
+ _qmf::TestObject::registerSelf(getBrokerAgent());
+ };
+ ~AgentFixture()
+ {
+ delete mFix;
+ };
+ ::qpid::management::ManagementAgent *getBrokerAgent() { return mFix->broker->getManagementAgent(); }
+ Receiver createV1DataIndRcvr( const std::string package, const std::string klass )
+ {
+ return mFix->session.createReceiver(std::string("kqueue; {create: always, delete: always, "
+ "node: {type: queue, "
+ "x-bindings: [{exchange: qpid.management, "
+ "key: 'console.obj.1.0.")
+ + package + std::string(".") + klass
+ + std::string("'}]}}"));
+ };
+ Receiver createV2DataIndRcvr( const std::string package, const std::string klass )
+ {
+ std::string p(package);
+ std::replace(p.begin(), p.end(), '.', '_');
+ std::string k(klass);
+ std::replace(k.begin(), k.end(), '.', '_');
+
+ return mFix->session.createReceiver(std::string("kqueue; {create: always, delete: always, "
+ "node: {type: queue, "
+ "x-bindings: [{exchange: qmf.default.topic, "
+ "key: 'agent.ind.data.")
+ + p + std::string(".") + k
+ + std::string("'}]}}"));
+ };
+};
+
+
+// A "management object" that supports the TestObject
+//
+class TestManageable : public qpid::management::Manageable
+{
+ management::ManagementObject::shared_ptr mgmtObj;
+ const std::string key;
+ public:
+ TestManageable(management::ManagementAgent *agent, std::string _key)
+ : key(_key)
+ {
+ _qmf::TestObject::shared_ptr tmp(new _qmf::TestObject(agent, this));
+
+ // seed it with some default values...
+ tmp->set_string1(key);
+ tmp->set_bool1(true);
+ qpid::types::Variant::Map vMap;
+ vMap["one"] = qpid::types::Variant(1);
+ vMap["two"] = qpid::types::Variant("two");
+ vMap["three"] = qpid::types::Variant("whatever");
+ tmp->set_map1(vMap);
+
+ mgmtObj = tmp;
+ };
+ ~TestManageable() { mgmtObj.reset(); }
+ management::ManagementObject::shared_ptr GetManagementObject() const { return mgmtObj; };
+ static void validateTestObjectProperties(_qmf::TestObject& to)
+ {
+ // verify the default values are as expected. We don't check 'string1',
+ // as it is the object key, and is unique for each object (no default value).
+ BOOST_CHECK(to.get_bool1() == true);
+ BOOST_CHECK(to.get_map1().size() == 3);
+ qpid::types::Variant::Map mappy = to.get_map1();
+ BOOST_CHECK(1 == (unsigned int)mappy["one"]);
+ BOOST_CHECK(mappy["two"].asString() == std::string("two"));
+ BOOST_CHECK(mappy["three"].asString() == std::string("whatever"));
+ };
+};
+
+
+// decode a V1 Content Indication message
+//
+void decodeV1ObjectUpdates(const Message& inMsg, TestObjectVector& objs, const size_t objLen)
+{
+ const size_t MAX_BUFFER_SIZE=65536;
+ char tmp[MAX_BUFFER_SIZE];
+
+ objs.clear();
+
+ BOOST_CHECK(inMsg.getContent().size() <= MAX_BUFFER_SIZE);
+
+ ::memcpy(tmp, inMsg.getContent().data(), inMsg.getContent().size());
+ Buffer buf(tmp, inMsg.getContent().size());
+
+ while (buf.available() > 8) { // 8 == qmf v1 header size
+ BOOST_CHECK_EQUAL(buf.getOctet(), 'A');
+ BOOST_CHECK_EQUAL(buf.getOctet(), 'M');
+ BOOST_CHECK_EQUAL(buf.getOctet(), '2');
+ BOOST_CHECK_EQUAL(buf.getOctet(), 'c'); // opcode == content indication
+ // @@todo: kag: how do we skip 'i' entries???
+ buf.getLong(); // ignore sequence
+
+ std::string str1; // decode content body as string
+ buf.getRawData(str1, objLen);
+
+ TestObjectPtr fake(new _qmf::TestObject(0,0));
+ fake->readProperties( str1 );
+ objs.push_back(fake);
+ }
+}
- agent->addObject(tm->GetManagementObject(), 1);
- // wait for the object to be published
- Message m1;
- BOOST_CHECK(r1.fetch(m1, Duration::SECOND * 6));
+// decode a V2 Content Indication message
+//
+void decodeV2ObjectUpdates(const qpid::messaging::Message& inMsg, TestObjectVector& objs)
+{
+ objs.clear();
- TestObjectVector objs;
- decodeV1ObjectUpdates(m1, objs, objLen);
- BOOST_CHECK(objs.size() > 0);
+ BOOST_CHECK_EQUAL(inMsg.getContentType(), std::string("amqp/list"));
- // destroy the object, then immediately export (before the next poll cycle)
+ const ::qpid::types::Variant::Map& m = inMsg.getProperties();
+ Variant::Map::const_iterator iter = m.find(std::string("qmf.opcode"));
+ BOOST_CHECK(iter != m.end());
+ BOOST_CHECK_EQUAL(iter->second.asString(), std::string("_data_indication"));
- ::qpid::management::ManagementAgent::DeletedObjectList delObjs;
- tm->GetManagementObject()->resourceDestroy();
- agent->exportDeletedObjects( delObjs );
- BOOST_CHECK(delObjs.size() == 1);
+ Variant::List vList;
+ ::qpid::amqp_0_10::ListCodec::decode(inMsg.getContent(), vList);
- // destroy the broker, and reinistantiate a new one without populating it
- // with a TestObject.
+ for (Variant::List::iterator lIter = vList.begin(); lIter != vList.end(); lIter++) {
+ TestObjectPtr fake(new _qmf::TestObject(0,0));
+ fake->readTimestamps(lIter->asMap());
+ fake->mapDecodeValues((lIter->asMap())["_values"].asMap());
+ objs.push_back(fake);
+ }
+}
+}
- r1.close();
- delete fix;
- delete tm; // should no longer be necessary
+QPID_AUTO_TEST_SUITE(BrokerMgmtAgent)
- fix = new AgentFixture(3);
- r1 = fix->createV1DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
- agent = fix->getBrokerAgent();
- agent->importDeletedObjects( delObjs );
+// verify that an object that is added to the broker's management database is
+// published correctly. Furthermore, verify that it is published once after
+// it has been deleted.
+//
+QPID_AUTO_TEST_CASE(v1ObjPublish)
+{
+ AgentFixture* fix = new AgentFixture(3);
+ management::ManagementAgent* agent;
+ agent = fix->getBrokerAgent();
- // wait for the deleted object to be published
+ // create a manageable test object
+ TestManageable *tm = new TestManageable(agent, std::string("obj1"));
+ uint32_t objLen = tm->GetManagementObject()->writePropertiesSize();
- bool isDeleted = false;
- while (!isDeleted && r1.fetch(m1, Duration::SECOND * 6)) {
+ Receiver r1 = fix->createV1DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
- decodeV1ObjectUpdates(m1, objs, objLen);
- BOOST_CHECK(objs.size() > 0);
+ agent->addObject(tm->GetManagementObject(), 1);
- for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+ // wait for the object to be published
+ Message m1;
+ BOOST_CHECK(r1.fetch(m1, Duration::SECOND * 6));
- TestManageable::validateTestObjectProperties(**oIter);
+ TestObjectVector objs;
+ decodeV1ObjectUpdates(m1, objs, objLen);
+ BOOST_CHECK(objs.size() > 0);
- qpid::types::Variant::Map mappy;
- (*oIter)->writeTimestamps(mappy);
- if (mappy["_delete_ts"].asUint64() != 0)
- isDeleted = true;
- }
- }
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
- BOOST_CHECK(isDeleted);
+ TestManageable::validateTestObjectProperties(**oIter);
- // verify there are no deleted objects to export now.
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ BOOST_CHECK(0 == mappy["_delete_ts"].asUint64()); // not deleted
+ }
- agent->exportDeletedObjects( delObjs );
- BOOST_CHECK(delObjs.size() == 0);
+ // destroy the object
- r1.close();
- delete fix;
- }
+ tm->GetManagementObject()->resourceDestroy();
+ // wait for the deleted object to be published
- // verify that an object that is added and deleted prior to the
- // first poll cycle is accounted for by the export
- //
- QPID_AUTO_TEST_CASE(v1ExportFastDelObj)
- {
- AgentFixture* fix = new AgentFixture(3);
- management::ManagementAgent* agent;
- agent = fix->getBrokerAgent();
+ bool isDeleted = false;
+ while (!isDeleted && r1.fetch(m1, Duration::SECOND * 6)) {
- // create a manageable test object
- TestManageable *tm = new TestManageable(agent, std::string("objectifyMe"));
+ decodeV1ObjectUpdates(m1, objs, objLen);
+ BOOST_CHECK(objs.size() > 0);
- // add, then immediately delete and export the object...
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
- ::qpid::management::ManagementAgent::DeletedObjectList delObjs;
- agent->addObject(tm->GetManagementObject(), 999);
- tm->GetManagementObject()->resourceDestroy();
- agent->exportDeletedObjects( delObjs );
- BOOST_CHECK(delObjs.size() == 1);
+ TestManageable::validateTestObjectProperties(**oIter);
- delete fix;
- delete tm;
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ if (mappy["_delete_ts"].asUint64() != 0)
+ isDeleted = true;
}
+ }
+ BOOST_CHECK(isDeleted);
- // Verify that we can export and import multiple deleted objects correctly.
- //
- QPID_AUTO_TEST_CASE(v1ImportMultiDelObj)
- {
- AgentFixture* fix = new AgentFixture(3);
- management::ManagementAgent* agent;
- agent = fix->getBrokerAgent();
-
- Receiver r1 = fix->createV1DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
-
- // populate the agent with multiple test objects
- const size_t objCount = 50;
- std::vector<TestManageable *> tmv;
- uint32_t objLen;
-
- for (size_t i = 0; i < objCount; i++) {
- std::stringstream key;
- key << "testobj-" << std::setfill('x') << std::setw(4) << i;
- // (no, seriously, I didn't just do that.)
- // Note well: we have to keep the key string length EXACTLY THE SAME
- // FOR ALL OBJECTS, so objLen will be the same. Otherwise the
- // decodeV1ObjectUpdates() will fail (v1 lacks explict encoded length).
- TestManageable *tm = new TestManageable(agent, key.str());
- objLen = tm->GetManagementObject()->writePropertiesSize();
- agent->addObject(tm->GetManagementObject(), i + 1);
- tmv.push_back(tm);
- }
+ r1.close();
+ delete fix;
+ delete tm;
+}
- // wait for the objects to be published
- Message m1;
- uint32_t msgCount = 0;
- while(r1.fetch(m1, Duration::SECOND * 6)) {
- TestObjectVector objs;
- decodeV1ObjectUpdates(m1, objs, objLen);
- msgCount += objs.size();
- }
+// Repeat the previous test, but with V2-based object support
+//
+QPID_AUTO_TEST_CASE(v2ObjPublish)
+{
+ AgentFixture* fix = new AgentFixture(3, true);
+ management::ManagementAgent* agent;
+ agent = fix->getBrokerAgent();
- BOOST_CHECK_EQUAL(msgCount, objCount);
+ TestManageable *tm = new TestManageable(agent, std::string("obj2"));
- // destroy some of the objects, then immediately export (before the next poll cycle)
+ Receiver r1 = fix->createV2DataIndRcvr(tm->GetManagementObject()->getPackageName(), "#");
- uint32_t delCount = 0;
- for (size_t i = 0; i < objCount; i += 2) {
- tmv[i]->GetManagementObject()->resourceDestroy();
- delCount++;
- }
+ agent->addObject(tm->GetManagementObject(), "testobj-1");
- ::qpid::management::ManagementAgent::DeletedObjectList delObjs;
- agent->exportDeletedObjects( delObjs );
- BOOST_CHECK_EQUAL(delObjs.size(), delCount);
+ // wait for the object to be published
+ Message m1;
+ BOOST_CHECK(r1.fetch(m1, Duration::SECOND * 6));
- // destroy the broker, and reinistantiate a new one without populating it
- // with TestObjects.
+ TestObjectVector objs;
+ decodeV2ObjectUpdates(m1, objs);
+ BOOST_CHECK(objs.size() > 0);
- r1.close();
- delete fix;
- while (tmv.size()) {
- delete tmv.back();
- tmv.pop_back();
- }
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
- fix = new AgentFixture(3);
- r1 = fix->createV1DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
- agent = fix->getBrokerAgent();
- agent->importDeletedObjects( delObjs );
+ TestManageable::validateTestObjectProperties(**oIter);
- // wait for the deleted object to be published, verify the count
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ BOOST_CHECK(0 == mappy["_delete_ts"].asUint64());
+ }
- uint32_t countDels = 0;
- while (r1.fetch(m1, Duration::SECOND * 6)) {
- TestObjectVector objs;
- decodeV1ObjectUpdates(m1, objs, objLen);
- BOOST_CHECK(objs.size() > 0);
+ // destroy the object
-
- for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+ tm->GetManagementObject()->resourceDestroy();
- TestManageable::validateTestObjectProperties(**oIter);
+ // wait for the deleted object to be published
- qpid::types::Variant::Map mappy;
- (*oIter)->writeTimestamps(mappy);
- if (mappy["_delete_ts"].asUint64() != 0)
- countDels++;
- }
- }
+ bool isDeleted = false;
+ while (!isDeleted && r1.fetch(m1, Duration::SECOND * 6)) {
- // make sure we get the correct # of deleted objects
- BOOST_CHECK_EQUAL(countDels, delCount);
+ decodeV2ObjectUpdates(m1, objs);
+ BOOST_CHECK(objs.size() > 0);
- // verify there are no deleted objects to export now.
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
- agent->exportDeletedObjects( delObjs );
- BOOST_CHECK(delObjs.size() == 0);
+ TestManageable::validateTestObjectProperties(**oIter);
- r1.close();
- delete fix;
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ if (mappy["_delete_ts"].asUint64() != 0)
+ isDeleted = true;
}
+ }
- // Verify that we can export and import multiple deleted objects correctly.
- // QMF V2 variant
- QPID_AUTO_TEST_CASE(v2ImportMultiDelObj)
- {
- AgentFixture* fix = new AgentFixture(3, true);
- management::ManagementAgent* agent;
- agent = fix->getBrokerAgent();
-
- Receiver r1 = fix->createV2DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
-
- // populate the agent with multiple test objects
- const size_t objCount = 50;
- std::vector<TestManageable *> tmv;
-
- for (size_t i = 0; i < objCount; i++) {
- std::stringstream key;
- key << "testobj-" << i;
- TestManageable *tm = new TestManageable(agent, key.str());
- if (tm->GetManagementObject()->writePropertiesSize()) {}
- agent->addObject(tm->GetManagementObject(), key.str());
- tmv.push_back(tm);
- }
-
- // wait for the objects to be published
- Message m1;
- uint32_t msgCount = 0;
- while(r1.fetch(m1, Duration::SECOND * 6)) {
- TestObjectVector objs;
- decodeV2ObjectUpdates(m1, objs);
- msgCount += objs.size();
- }
-
- BOOST_CHECK_EQUAL(msgCount, objCount);
-
- // destroy some of the objects, then immediately export (before the next poll cycle)
-
- uint32_t delCount = 0;
- for (size_t i = 0; i < objCount; i += 2) {
- tmv[i]->GetManagementObject()->resourceDestroy();
- delCount++;
- }
-
- ::qpid::management::ManagementAgent::DeletedObjectList delObjs;
- agent->exportDeletedObjects( delObjs );
- BOOST_CHECK_EQUAL(delObjs.size(), delCount);
-
- // destroy the broker, and reinistantiate a new one without populating it
- // with TestObjects.
-
- r1.close();
- delete fix;
- while (tmv.size()) {
- delete tmv.back();
- tmv.pop_back();
- }
-
- fix = new AgentFixture(3, true);
- r1 = fix->createV2DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
- agent = fix->getBrokerAgent();
- agent->importDeletedObjects( delObjs );
-
- // wait for the deleted object to be published, verify the count
-
- uint32_t countDels = 0;
- while (r1.fetch(m1, Duration::SECOND * 6)) {
- TestObjectVector objs;
- decodeV2ObjectUpdates(m1, objs);
- BOOST_CHECK(objs.size() > 0);
-
- for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
-
- TestManageable::validateTestObjectProperties(**oIter);
-
- qpid::types::Variant::Map mappy;
- (*oIter)->writeTimestamps(mappy);
- if (mappy["_delete_ts"].asUint64() != 0)
- countDels++;
- }
- }
-
- // make sure we get the correct # of deleted objects
- BOOST_CHECK_EQUAL(countDels, delCount);
-
- // verify there are no deleted objects to export now.
-
- agent->exportDeletedObjects( delObjs );
- BOOST_CHECK(delObjs.size() == 0);
+ BOOST_CHECK(isDeleted);
- r1.close();
- delete fix;
- }
+ r1.close();
+ delete fix;
+ delete tm;
+}
- // See QPID-2997
- QPID_AUTO_TEST_CASE(v2RapidRestoreObj)
- {
- AgentFixture* fix = new AgentFixture(3, true);
- management::ManagementAgent* agent;
- agent = fix->getBrokerAgent();
-
- // two objects, same ObjID
- TestManageable *tm1 = new TestManageable(agent, std::string("obj2"));
- TestManageable *tm2 = new TestManageable(agent, std::string("obj2"));
-
- Receiver r1 = fix->createV2DataIndRcvr(tm1->GetManagementObject()->getPackageName(), "#");
-
- // add, then immediately delete and re-add a copy of the object
- agent->addObject(tm1->GetManagementObject(), "testobj-1");
- tm1->GetManagementObject()->resourceDestroy();
- agent->addObject(tm2->GetManagementObject(), "testobj-1");
-
- // expect: a delete notification, then an update notification
- TestObjectVector objs;
- bool isDeleted = false;
- bool isAdvertised = false;
- size_t count = 0;
- Message m1;
- while (r1.fetch(m1, Duration::SECOND * 6)) {
-
- decodeV2ObjectUpdates(m1, objs);
- BOOST_CHECK(objs.size() > 0);
-
- for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
- count++;
- TestManageable::validateTestObjectProperties(**oIter);
-
- qpid::types::Variant::Map mappy;
- (*oIter)->writeTimestamps(mappy);
- if (mappy["_delete_ts"].asUint64() != 0) {
- isDeleted = true;
- BOOST_CHECK(isAdvertised == false); // delete must be first
- } else {
- isAdvertised = true;
- BOOST_CHECK(isDeleted == true); // delete must be first
- }
- }
+// See QPID-2997
+QPID_AUTO_TEST_CASE(v2RapidRestoreObj)
+{
+ AgentFixture* fix = new AgentFixture(3, true);
+ management::ManagementAgent* agent;
+ agent = fix->getBrokerAgent();
+
+ // two objects, same ObjID
+ TestManageable *tm1 = new TestManageable(agent, std::string("obj2"));
+ TestManageable *tm2 = new TestManageable(agent, std::string("obj2"));
+
+ Receiver r1 = fix->createV2DataIndRcvr(tm1->GetManagementObject()->getPackageName(), "#");
+
+ // add, then immediately delete and re-add a copy of the object
+ agent->addObject(tm1->GetManagementObject(), "testobj-1");
+ tm1->GetManagementObject()->resourceDestroy();
+ agent->addObject(tm2->GetManagementObject(), "testobj-1");
+
+ // expect: a delete notification, then an update notification
+ TestObjectVector objs;
+ bool isDeleted = false;
+ bool isAdvertised = false;
+ size_t count = 0;
+ Message m1;
+ while (r1.fetch(m1, Duration::SECOND * 6)) {
+
+ decodeV2ObjectUpdates(m1, objs);
+ BOOST_CHECK(objs.size() > 0);
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+ count++;
+ TestManageable::validateTestObjectProperties(**oIter);
+
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ if (mappy["_delete_ts"].asUint64() != 0) {
+ isDeleted = true;
+ BOOST_CHECK(isAdvertised == false); // delete must be first
+ } else {
+ isAdvertised = true;
+ BOOST_CHECK(isDeleted == true); // delete must be first
}
-
- BOOST_CHECK(isDeleted);
- BOOST_CHECK(isAdvertised);
- BOOST_CHECK(count == 2);
-
- r1.close();
- delete fix;
- delete tm1;
- delete tm2;
}
+ }
- // See QPID-2997
- QPID_AUTO_TEST_CASE(v2DuplicateErrorObj)
- {
- AgentFixture* fix = new AgentFixture(3, true);
- management::ManagementAgent* agent;
- agent = fix->getBrokerAgent();
-
- // turn off the expected error log message
- qpid::log::Options logOpts;
- logOpts.selectors.clear();
- logOpts.selectors.push_back("critical+");
- qpid::log::Logger::instance().configure(logOpts);
-
- // two objects, same ObjID
- TestManageable *tm1 = new TestManageable(agent, std::string("obj2"));
- TestManageable *tm2 = new TestManageable(agent, std::string("obj2"));
- // Keep a pointer to the ManagementObject. This test simulates a user-caused error
- // case (duplicate objects) where the broker has no choice but to leak a management
- // object (safest assumption). To prevent valgrind from flagging this leak, we
- // manually clean up the object at the end of the test.
- management::ManagementObject *save = tm2->GetManagementObject();
-
- Receiver r1 = fix->createV2DataIndRcvr(tm1->GetManagementObject()->getPackageName(), "#");
-
- // add, then immediately delete and re-add a copy of the object
- agent->addObject(tm1->GetManagementObject(), "testobj-1");
- agent->addObject(tm2->GetManagementObject(), "testobj-1");
-
- TestObjectVector objs;
- size_t count = 0;
- Message m1;
- while (r1.fetch(m1, Duration::SECOND * 6)) {
-
- decodeV2ObjectUpdates(m1, objs);
- BOOST_CHECK(objs.size() > 0);
-
- for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
- count++;
- TestManageable::validateTestObjectProperties(**oIter);
- }
- }
-
- BOOST_CHECK(count == 1); // only one should be accepted.
+ BOOST_CHECK(isDeleted);
+ BOOST_CHECK(isAdvertised);
+ BOOST_CHECK(count == 2);
- r1.close();
- delete fix;
- delete tm1;
- delete tm2;
- delete save;
- }
+ r1.close();
+ delete fix;
+ delete tm1;
+ delete tm2;
+}
- QPID_AUTO_TEST_SUITE_END()
- }
+QPID_AUTO_TEST_SUITE_END()
+}
}
diff --git a/cpp/src/tests/CMakeLists.txt b/cpp/src/tests/CMakeLists.txt
index be7864fcb4..12e9cbed95 100644
--- a/cpp/src/tests/CMakeLists.txt
+++ b/cpp/src/tests/CMakeLists.txt
@@ -98,66 +98,78 @@ endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
# when running the tests. If you want to build a subset of the tests run
# ccmake and set unit_tests_to_build to the set you want to build.
-set(unit_tests_to_build
- exception_test
- RefCounted
- SessionState
- logging
- AsyncCompletion
- Url
- Uuid
- Shlib
- FieldValue
- FieldTable
+set(all_unit_tests
+ AccumulatedAckTest
Array
- QueueOptionsTest
- InlineAllocator
- InlineVector
- ClientSessionTest
- MessagingSessionTests
- SequenceSet
- StringUtils
- RangeSet
+ AsyncCompletion
AtomicValue
- QueueTest
- AccumulatedAckTest
- DtxWorkRecordTest
+ ClientMessage
+ ClientMessageTest
+ ClientSessionTest
+ ConsoleTest
DeliveryRecordTest
+ DtxWorkRecordTest
+ exception_test
ExchangeTest
+ FieldTable
+ FieldValue
+ FrameDecoder
+ FramingTest
HeadersExchangeTest
+ HeaderTest
+ InlineAllocator
+ InlineVector
+ logging
+ ManagementTest
+ MessageReplayTracker
MessageTest
+ MessagingSessionTests
+ PollableCondition
+ ProxyTest
QueueDepth
- QueueRegistryTest
- QueuePolicyTest
QueueFlowLimitTest
- FramingTest
- HeaderTest
+ QueueOptionsTest
+ QueuePolicyTest
+ QueueRegistryTest
+ QueueTest
+ RangeSet
+ RefCounted
+ RetryList
SequenceNumberTest
+ SequenceSet
+ SessionState
+ Shlib
+ StringUtils
+ SystemInfo
TimerTest
TopicExchangeTest
TxBufferTest
- ManagementTest
- MessageReplayTracker
- ConsoleTest
- ProxyTest
- RetryList
- FrameDecoder
- ClientMessageTest
- PollableCondition
+ Url
+ Uuid
Variant
- ClientMessage
- SystemInfo
${xml_tests}
+ )
+
+set(unit_tests_to_build
+ ""
CACHE STRING "Which unit tests to build"
)
mark_as_advanced(unit_tests_to_build)
+# If no unit_test specifically set then use all unit tests
+if (unit_tests_to_build)
+set(actual_unit_tests ${unit_tests_to_build})
+else()
+set(actual_unit_tests ${all_unit_tests})
+endif()
+
add_executable (unit_test unit_test
- ${unit_tests_to_build} ${platform_test_additions})
+ ${actual_unit_tests} ${platform_test_additions})
target_link_libraries (unit_test
${qpid_test_boost_libs}
qpidmessaging qpidbroker qmfconsole)
+set_target_properties (unit_test PROPERTIES COMPILE_DEFINITIONS _IN_QPID_BROKER)
remember_location(unit_test)
add_library (shlibtest MODULE shlibtest.cpp)
@@ -274,8 +286,12 @@ remember_location(msg_group_test)
# qpid-perftest and qpid-latency-test are generally useful so install them
-install (TARGETS qpid-perftest qpid-latency-test RUNTIME
- DESTINATION ${QPID_INSTALL_BINDIR})
+install (TARGETS
+ qpid-perftest qpid-latency-test qpid-client-test
+ qpid-ping
+ qpid-receive qpid-send qpid-topic-listener qpid-topic-publisher receiver sender
+ qpid-txtest
+ RUNTIME DESTINATION ${QPID_INSTALL_TESTDIR})
# This should ideally be done as part of the test run, but I don't know a way
# to get these arguments and the working directory set like Makefile.am does,
@@ -309,10 +325,12 @@ if (PYTHON_EXECUTABLE)
add_test (ha_tests ${test_wrap} ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/ha_tests.py)
add_test (ipv6_test ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/ipv6_test${test_script_suffix})
add_test (federation_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_federation_tests${test_script_suffix})
+ add_test (federation_sys_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_federation_sys_tests${test_script_suffix})
if (BUILD_ACL)
add_test (acl_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_acl_tests${test_script_suffix})
endif (BUILD_ACL)
add_test (dynamic_log_level_test ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/dynamic_log_level_test${test_script_suffix})
+add_test (dynamic_log_hires_timestamp ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/dynamic_log_hires_timestamp${test_script_suffix})
if (BUILD_MSSQL)
add_test (store_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_store_tests${test_script_suffix} MSSQL)
endif (BUILD_MSSQL)
@@ -323,7 +341,9 @@ endif (PYTHON_EXECUTABLE)
add_library(test_store MODULE test_store.cpp)
target_link_libraries (test_store qpidbroker qpidcommon)
-set_target_properties (test_store PROPERTIES PREFIX "")
+set_target_properties (test_store PROPERTIES
+ COMPILE_DEFINITIONS _IN_QPID_BROKER
+ PREFIX "")
add_library (dlclose_noop MODULE dlclose_noop.c)
@@ -333,10 +353,16 @@ add_library (dlclose_noop MODULE dlclose_noop.c)
#
## Longer running stability tests, not run by default check: target.
## Not run under valgrind, too slow
-#LONG_TESTS=fanout_perftest shared_perftest multiq_perftest topic_perftest run_failover_soak
+#LONG_TESTS=fanout_perftest shared_perftest multiq_perftest topic_perftest
#EXTRA_DIST+=$(LONG_TESTS) run_perftest
#check-long:
# $(MAKE) check TESTS="start_broker $(LONG_TESTS) stop_broker" VALGRIND=
# Include async store tests
include(asyncstore.cmake)
+
+#
+# legacystore
+#
+#add_subdirectory(legacystore)
+
diff --git a/cpp/src/tests/ClusterFailover.cpp b/cpp/src/tests/ClusterFailover.cpp
deleted file mode 100644
index bf5c147f19..0000000000
--- a/cpp/src/tests/ClusterFailover.cpp
+++ /dev/null
@@ -1,115 +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.
- *
- */
-
-/**@file Tests for partial failure in a cluster.
- * Partial failure means some nodes experience a failure while others do not.
- * In this case the failed nodes must shut down.
- */
-
-#include "test_tools.h"
-#include "unit_test.h"
-#include "ClusterFixture.h"
-#include "qpid/client/FailoverManager.h"
-#include <boost/assign.hpp>
-#include <boost/algorithm/string.hpp>
-#include <boost/bind.hpp>
-
-namespace qpid {
-namespace tests {
-
-QPID_AUTO_TEST_SUITE(ClusterFailoverTestSuite)
-
-using namespace std;
-using namespace qpid;
-using namespace qpid::cluster;
-using namespace qpid::framing;
-using namespace qpid::client;
-using namespace qpid::client::arg;
-using namespace boost::assign;
-using broker::Broker;
-using boost::shared_ptr;
-
-// Timeout for tests that wait for messages
-const sys::Duration TIMEOUT=sys::TIME_SEC/4;
-
-ClusterFixture::Args getArgs(bool durable=std::getenv("STORE_LIB"))
-{
- ClusterFixture::Args args;
- args += "--auth", "no", "--no-module-dir",
- "--load-module", getLibPath("CLUSTER_LIB");
- if (durable)
- args += "--load-module", getLibPath("STORE_LIB"), "TMP_DATA_DIR";
- else
- args += "--no-data-dir";
- return args;
-}
-
-// Test re-connecting with same session name after a failure.
-QPID_AUTO_TEST_CASE(testReconnectSameSessionName) {
- ClusterFixture cluster(2, getArgs(), -1);
- // Specify a timeout to make sure it is ignored, session resume is
- // not implemented so sessions belonging to dead brokers should
- // not be kept.
- Client c0(cluster[0], "foo", 5);
- BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c0.connection, 2).size()); // wait for both.
- c0.session.queueDeclare("q");
- c0.session.messageTransfer(arg::content=Message("sendme", "q"));
- BOOST_CHECK_EQUAL(c0.subs.get("q").getData(), "sendme");
- cluster.killWithSilencer(0, c0.connection, 9);
- Client c1(cluster[1], "foo", 5);
- c1.session.queueQuery(); // Try to use the session.
-}
-
-QPID_AUTO_TEST_CASE(testReconnectExclusiveQueue) {
- // Regresion test. Session timeouts should be ignored
- // by the broker as session resume is not implemented.
- ClusterFixture cluster(2, getArgs(), -1);
- Client c0(cluster[0], "foo", 5);
- c0.session.queueDeclare("exq", arg::exclusive=true);
- SubscriptionSettings settings;
- settings.exclusive = true;
- settings.autoAck = 0;
- Subscription s0 = c0.subs.subscribe(c0.lq, "exq", settings, "exsub");
- c0.session.messageTransfer(arg::content=Message("sendme", "exq"));
- BOOST_CHECK_EQUAL(c0.lq.get().getData(), "sendme");
-
- // Regression: core dump on exit if unacked messages were left in
- // a session with a timeout.
- cluster.killWithSilencer(0, c0.connection);
-
- // Regression: session timeouts prevented re-connecting to
- // exclusive queue.
- Client c1(cluster[1]);
- c1.session.queueDeclare("exq", arg::exclusive=true);
- Subscription s1 = c1.subs.subscribe(c1.lq, "exq", settings, "exsub");
- s1.cancel();
-
- // Regression: session timeouts prevented new member joining
- // cluster with exclusive queues.
- cluster.add();
- Client c2(cluster[2]);
- c2.session.queueQuery();
-}
-
-
-QPID_AUTO_TEST_SUITE_END()
-
-}} // namespace qpid::tests
diff --git a/cpp/src/tests/ClusterFixture.cpp b/cpp/src/tests/ClusterFixture.cpp
deleted file mode 100644
index 6b62cb6fc7..0000000000
--- a/cpp/src/tests/ClusterFixture.cpp
+++ /dev/null
@@ -1,160 +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 "test_tools.h"
-#include "unit_test.h"
-#include "ForkedBroker.h"
-#include "BrokerFixture.h"
-
-#include "qpid/client/Connection.h"
-#include "qpid/client/ConnectionAccess.h"
-#include "qpid/client/Session.h"
-#include "qpid/client/FailoverListener.h"
-#include "qpid/cluster/Cluster.h"
-#include "qpid/cluster/Cpg.h"
-#include "qpid/cluster/UpdateClient.h"
-#include "qpid/framing/AMQBody.h"
-#include "qpid/framing/Uuid.h"
-#include "qpid/framing/reply_exceptions.h"
-#include "qpid/framing/enum.h"
-#include "qpid/log/Logger.h"
-
-#include <boost/bind.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/assign.hpp>
-
-#include <string>
-#include <iostream>
-#include <iterator>
-#include <vector>
-#include <set>
-#include <algorithm>
-#include <iterator>
-
-
-using namespace std;
-using namespace qpid;
-using namespace qpid::cluster;
-using namespace qpid::framing;
-using namespace qpid::client;
-using qpid::sys::TIME_SEC;
-using qpid::broker::Broker;
-using boost::shared_ptr;
-using qpid::cluster::Cluster;
-using boost::assign::list_of;
-
-
-#include "ClusterFixture.h"
-
-namespace qpid {
-namespace tests {
-
-ClusterFixture::ClusterFixture(size_t n, const Args& args_, int localIndex_)
- : name(Uuid(true).str()), localIndex(localIndex_), userArgs(args_)
-{
- add(n);
-}
-
-ClusterFixture::ClusterFixture(size_t n, boost::function<void (Args&, size_t)> updateArgs_, int localIndex_)
- : name(Uuid(true).str()), localIndex(localIndex_), updateArgs(updateArgs_)
-{
- add(n);
-}
-
-ClusterFixture::Args ClusterFixture::makeArgs(const std::string& prefix, size_t index) {
- Args args = list_of<string>("qpidd ")
- ("--cluster-name")(name)
- ("--log-prefix")(prefix);
- args.insert(args.end(), userArgs.begin(), userArgs.end());
- if (updateArgs) updateArgs(args, index);
- return args;
-}
-
-void ClusterFixture::add() {
- if (size() != size_t(localIndex)) { // fork a broker process.
- std::ostringstream os; os << "fork" << size();
- std::string prefix = os.str();
- forkedBrokers.push_back(shared_ptr<ForkedBroker>(new ForkedBroker(makeArgs(prefix, size()))));
- push_back(forkedBrokers.back()->getPort());
- }
- else { // Run in this process
- addLocal();
- }
-}
-
-namespace {
-/** Parse broker & cluster options */
-Broker::Options parseOpts(size_t argc, const char* argv[]) {
- Broker::Options opts;
- Plugin::addOptions(opts); // Pick up cluster options.
- opts.parse(argc, argv, "", true); // Allow-unknown for --load-module
- return opts;
-}
-}
-
-void ClusterFixture::addLocal() {
- assert(int(size()) == localIndex);
- ostringstream os; os << "local" << localIndex;
- string prefix = os.str();
- Args args(makeArgs(prefix, localIndex));
- vector<const char*> argv(args.size());
- transform(args.begin(), args.end(), argv.begin(), boost::bind(&string::c_str, _1));
- qpid::log::Logger::instance().setPrefix(prefix);
- localBroker.reset(new BrokerFixture(parseOpts(argv.size(), &argv[0])));
- push_back(localBroker->getPort());
- forkedBrokers.push_back(shared_ptr<ForkedBroker>());
-}
-
-bool ClusterFixture::hasLocal() const { return localIndex >= 0 && size_t(localIndex) < size(); }
-
-/** Kill a forked broker with sig, or shutdown localBroker if n==0. */
-void ClusterFixture::kill(size_t n, int sig) {
- if (n == size_t(localIndex))
- localBroker->broker->shutdown();
- else
- forkedBrokers[n]->kill(sig);
-}
-
-/** Kill a broker and suppress errors from closing connection c. */
-void ClusterFixture::killWithSilencer(size_t n, client::Connection& c, int sig) {
- ScopedSuppressLogging sl;
- try { c.close(); } catch(...) {}
- kill(n,sig);
-}
-
-/**
- * Get the known broker ports from a Connection.
- *@param n if specified wait for the cluster size to be n, up to a timeout.
- */
-std::set<int> knownBrokerPorts(qpid::client::Connection& c, int n) {
- FailoverListener fl(c, false);
- std::vector<qpid::Url> urls = fl.getKnownBrokers();
- if (n >= 0 && unsigned(n) != urls.size()) {
- // Retry up to 10 secs in .1 second intervals.
- for (size_t retry=100; urls.size() != unsigned(n) && retry != 0; --retry) {
- qpid::sys::usleep(1000*100); // 0.1 secs
- urls = fl.getKnownBrokers();
- }
- }
- std::set<int> s;
- for (std::vector<qpid::Url>::const_iterator i = urls.begin(); i != urls.end(); ++i)
- s.insert((*i)[0].port);
- return s;
-}
-
-}} // namespace qpid::tests
diff --git a/cpp/src/tests/ClusterFixture.h b/cpp/src/tests/ClusterFixture.h
deleted file mode 100644
index f548ff9376..0000000000
--- a/cpp/src/tests/ClusterFixture.h
+++ /dev/null
@@ -1,115 +0,0 @@
-#ifndef CLUSTER_FIXTURE_H
-#define CLUSTER_FIXTURE_H
-
-/*
- *
- * Copyright (c) 2006 The Apache Software Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#include "test_tools.h"
-#include "unit_test.h"
-#include "ForkedBroker.h"
-#include "BrokerFixture.h"
-
-#include "qpid/client/Connection.h"
-#include "qpid/client/ConnectionAccess.h"
-#include "qpid/client/Session.h"
-#include "qpid/client/FailoverListener.h"
-#include "qpid/cluster/Cluster.h"
-#include "qpid/cluster/Cpg.h"
-#include "qpid/cluster/UpdateClient.h"
-#include "qpid/framing/AMQBody.h"
-#include "qpid/framing/Uuid.h"
-#include "qpid/framing/reply_exceptions.h"
-#include "qpid/framing/enum.h"
-#include "qpid/log/Logger.h"
-
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <string>
-#include <iostream>
-#include <iterator>
-#include <vector>
-#include <set>
-#include <algorithm>
-#include <iterator>
-
-
-using namespace std;
-using namespace qpid;
-using namespace qpid::cluster;
-using namespace qpid::framing;
-using namespace qpid::client;
-using qpid::sys::TIME_SEC;
-using qpid::broker::Broker;
-using boost::shared_ptr;
-using qpid::cluster::Cluster;
-
-namespace qpid {
-namespace tests {
-
-/** Cluster fixture is a vector of ports for the replicas.
- *
- * At most one replica (by default replica 0) is in the current
- * process, all others are forked as children.
- */
-class ClusterFixture : public vector<uint16_t> {
- public:
- typedef std::vector<std::string> Args;
-
- /** @param localIndex can be -1 meaning don't automatically start a local broker.
- * A local broker can be started with addLocal().
- */
- ClusterFixture(size_t n, const Args& args, int localIndex=-1);
-
- /**@param updateArgs function is passed the index of the cluster member and can update the arguments. */
- ClusterFixture(size_t n, boost::function<void (Args&, size_t)> updateArgs, int localIndex=-1);
-
- void add(size_t n) { for (size_t i=0; i < n; ++i) add(); }
- void add(); // Add a broker.
- void setup();
-
- bool hasLocal() const;
-
- /** Kill a forked broker with sig, or shutdown localBroker. */
- void kill(size_t n, int sig=SIGINT);
-
- /** Kill a broker and suppress errors from closing connection c. */
- void killWithSilencer(size_t n, client::Connection& c, int sig=SIGINT);
-
- private:
-
- void addLocal(); // Add a local broker.
- Args makeArgs(const std::string& prefix, size_t index);
- string name;
- std::auto_ptr<BrokerFixture> localBroker;
- int localIndex;
- std::vector<shared_ptr<ForkedBroker> > forkedBrokers;
- Args userArgs;
- boost::function<void (Args&, size_t)> updateArgs;
-};
-
-/**
- * Get the known broker ports from a Connection.
- *@param n if specified wait for the cluster size to be n, up to a timeout.
- */
-std::set<int> knownBrokerPorts(qpid::client::Connection& source, int n=-1);
-
-}} // namespace qpid::tests
-
-#endif /*!CLUSTER_FIXTURE_H*/
diff --git a/cpp/src/tests/DispatcherTest.cpp b/cpp/src/tests/DispatcherTest.cpp
index e1691db584..7312fe8d2e 100644
--- a/cpp/src/tests/DispatcherTest.cpp
+++ b/cpp/src/tests/DispatcherTest.cpp
@@ -20,7 +20,6 @@
*/
#include "qpid/sys/Poller.h"
-#include "qpid/sys/IOHandle.h"
#include "qpid/sys/Dispatcher.h"
#include "qpid/sys/DispatchHandle.h"
#include "qpid/sys/posix/PrivatePosix.h"
@@ -147,8 +146,8 @@ int main(int /*argc*/, char** /*argv*/)
for (int i = 0; i < 8; i++)
testString += testString;
- PosixIOHandle f0(sv[0]);
- PosixIOHandle f1(sv[1]);
+ IOHandle f0(sv[0]);
+ IOHandle f1(sv[1]);
rh = new DispatchHandleRef(f0, boost::bind(reader, _1, sv[0]), 0, 0);
wh = new DispatchHandleRef(f1, 0, boost::bind(writer, _1, sv[1], testString), 0);
diff --git a/cpp/src/tests/FieldTable.cpp b/cpp/src/tests/FieldTable.cpp
index 8aeeb031b3..81372d87c5 100644
--- a/cpp/src/tests/FieldTable.cpp
+++ b/cpp/src/tests/FieldTable.cpp
@@ -20,7 +20,6 @@
*/
#include <iostream>
#include <algorithm>
-#include "qpid/sys/alloca.h"
#include "qpid/framing/Array.h"
#include "qpid/framing/FieldTable.h"
#include "qpid/framing/FieldValue.h"
diff --git a/cpp/src/tests/ForkedBroker.cpp b/cpp/src/tests/ForkedBroker.cpp
deleted file mode 100644
index de1b42d40f..0000000000
--- a/cpp/src/tests/ForkedBroker.cpp
+++ /dev/null
@@ -1,157 +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 "ForkedBroker.h"
-#include "qpid/log/Statement.h"
-#include <boost/bind.hpp>
-#include <algorithm>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <signal.h>
-
-using namespace std;
-using qpid::ErrnoException;
-
-namespace std {
-static ostream& operator<<(ostream& o, const qpid::tests::ForkedBroker::Args& a) {
-copy(a.begin(), a.end(), ostream_iterator<string>(o, " "));
-return o;
-}
-}
-
-namespace qpid {
-namespace tests {
-
-ForkedBroker::ForkedBroker(const Args& constArgs) : running(false), exitStatus(0) {
- Args args(constArgs);
- // Substitute the special value "TMP_DATA_DIR" with a temporary data dir.
- Args::iterator i = find(args.begin(), args.end(), string("TMP_DATA_DIR"));
- if (i != args.end()) {
- args.erase(i);
- char dd[] = "/tmp/ForkedBroker.XXXXXX";
- if (!mkdtemp(dd))
- throw qpid::ErrnoException("Can't create data dir");
- dataDir = dd;
- args.push_back("--data-dir");
- args.push_back(dataDir);
- }
- // Never use the default data directory, set --no-data-dir if no other data-dir arg.
- Args::iterator j = find(args.begin(), args.end(), string("--data-dir"));
- Args::iterator k = find(args.begin(), args.end(), string("--no-data-dir"));
- if (j == args.end() && k == args.end())
- args.push_back("--no-data-dir");
- init(args);
-}
-
-ForkedBroker::~ForkedBroker() {
- try { kill(); }
- catch (const std::exception& e) {
- QPID_LOG(error, QPID_MSG("Killing forked broker: " << e.what()));
- }
- if (!dataDir.empty())
- {
- if(::system(("rm -rf "+dataDir).c_str())) {}
- }
-}
-
-void ForkedBroker::kill(int sig) {
- if (pid == 0) return;
- int savePid = pid;
- pid = 0; // Reset pid here in case of an exception.
- using qpid::ErrnoException;
- if (::kill(savePid, sig) < 0)
- throw ErrnoException("kill failed");
- int status;
- if (::waitpid(savePid, &status, 0) < 0 && sig != 9)
- throw ErrnoException("wait for forked process failed");
- if (WEXITSTATUS(status) != 0 && sig != 9)
- throw qpid::Exception(QPID_MSG("Forked broker exited with: " << WEXITSTATUS(status)));
- running = false;
- exitStatus = status;
-}
-
-bool isLogOption(const std::string& s) {
- const char * log_enable = "--log-enable",
- * trace = "--trace";
- return( (! strncmp(s.c_str(), log_enable, strlen(log_enable))) ||
- (! strncmp(s.c_str(), trace, strlen(trace)))
- );
-}
-
-namespace {
- void ignore_signal(int)
- {
- }
-}
-
-void ForkedBroker::init(const Args& userArgs) {
- using qpid::ErrnoException;
- port = 0;
- int pipeFds[2];
- if(::pipe(pipeFds) < 0) throw ErrnoException("Can't create pipe");
-
- // Ignore the SIGCHLD signal generated by an exitting child
- // We will clean up any exitting children in the waitpid above
- // This should really be neater (like only once not per fork)
- struct ::sigaction sa;
- sa.sa_handler = ignore_signal;
- ::sigemptyset(&sa.sa_mask);
- ::sigaddset(&sa.sa_mask, SIGCHLD);
- sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
- ::sigaction(SIGCHLD, &sa, 0);
-
- pid = ::fork();
- if (pid < 0) throw ErrnoException("Fork failed");
- if (pid) { // parent
- ::close(pipeFds[1]);
- FILE* f = ::fdopen(pipeFds[0], "r");
- if (!f) throw ErrnoException("fopen failed");
- if (::fscanf(f, "%d", &port) != 1) {
- if (ferror(f)) throw ErrnoException("Error reading port number from child.");
- else throw qpid::Exception("EOF reading port number from child.");
- }
- ::fclose(f);
- running = true;
- }
- else { // child
- ::close(pipeFds[0]);
- int fd = ::dup2(pipeFds[1], 1); // pipe stdout to the parent.
- if (fd < 0) throw ErrnoException("dup2 failed");
- const char* prog = ::getenv("QPIDD_EXEC");
- if (!prog) prog = "../qpidd"; // This only works from within svn checkout
- Args args(userArgs);
- args.push_back("--port=0");
- // Keep quiet except for errors.
- if (!::getenv("QPID_TRACE") && !::getenv("QPID_LOG_ENABLE")
- && find_if(userArgs.begin(), userArgs.end(), isLogOption) == userArgs.end())
- args.push_back("--log-enable=error+");
- std::vector<const char*> argv(args.size());
- std::transform(args.begin(), args.end(), argv.begin(), boost::bind(&std::string::c_str, _1));
- argv.push_back(0);
- QPID_LOG(debug, "ForkedBroker exec " << prog << ": " << args);
-
- execv(prog, const_cast<char* const*>(&argv[0]));
- QPID_LOG(critical, "execv failed to start broker: prog=\"" << prog << "\"; args=\"" << args << "\"; errno=" << errno << " (" << std::strerror(errno) << ")");
- ::exit(1);
- }
-}
-
-}} // namespace qpid::tests
diff --git a/cpp/src/tests/ForkedBroker.h b/cpp/src/tests/ForkedBroker.h
deleted file mode 100644
index 87e141a425..0000000000
--- a/cpp/src/tests/ForkedBroker.h
+++ /dev/null
@@ -1,82 +0,0 @@
-#ifndef TESTS_FORKEDBROKER_H
-#define TESTS_FORKEDBROKER_H
-
-
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#include "qpid/Exception.h"
-#include "qpid/log/Statement.h"
-#include "qpid/broker/Broker.h"
-#include <boost/lexical_cast.hpp>
-#include <string>
-#include <stdio.h>
-#include <sys/wait.h>
-
-namespace qpid {
-namespace tests {
-
-/**
- * Class to fork a broker child process.
- *
- * For most tests a BrokerFixture may be more convenient as it starts
- * a broker in the same process which allows you to easily debug into
- * the broker.
- *
- * This useful for tests that need to start multiple brokers where
- * those brokers can't coexist in the same process (e.g. for cluster
- * tests where CPG doesn't allow multiple group members in a single
- * process.)
- *
- */
-class ForkedBroker {
- public:
- typedef std::vector<std::string> Args;
-
- // argv args are passed to broker.
- //
- // Special value "TMP_DATA_DIR" is substituted with a temporary
- // data directory for the broker.
- //
- ForkedBroker(const Args& argv);
- ~ForkedBroker();
-
- void kill(int sig=SIGINT);
- int wait(); // Wait for exit, return exit status.
- uint16_t getPort() { return port; }
- pid_t getPID() { return pid; }
- bool isRunning() { return running; }
-
- private:
-
- void init(const Args& args);
-
- bool running;
- int exitStatus;
-
- pid_t pid;
- int port;
- std::string dataDir;
-};
-
-}} // namespace qpid::tests
-
-#endif /*!TESTS_FORKEDBROKER_H*/
diff --git a/cpp/src/tests/InitialStatusMap.cpp b/cpp/src/tests/InitialStatusMap.cpp
deleted file mode 100644
index 95806737e3..0000000000
--- a/cpp/src/tests/InitialStatusMap.cpp
+++ /dev/null
@@ -1,239 +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 "unit_test.h"
-#include "test_tools.h"
-#include "qpid/cluster/InitialStatusMap.h"
-#include "qpid/framing/Uuid.h"
-#include <boost/assign.hpp>
-
-using namespace std;
-using namespace qpid::cluster;
-using namespace qpid::framing;
-using namespace qpid::framing::cluster;
-using namespace boost::assign;
-
-namespace qpid {
-namespace tests {
-
-QPID_AUTO_TEST_SUITE(InitialStatusMapTestSuite)
-
-typedef InitialStatusMap::Status Status;
-
-Status activeStatus(const Uuid& id=Uuid(), const MemberSet& ms=MemberSet(),
- const framing::Array& urls=framing::Array())
-{
- return Status(ProtocolVersion(), 0, true, id, STORE_STATE_NO_STORE, Uuid(),
- encodeMemberSet(ms), urls);
-}
-
-Status newcomerStatus(const Uuid& id=Uuid(), const MemberSet& ms=MemberSet(),
- const framing::Array& urls=framing::Array())
-{
- return Status(ProtocolVersion(), 0, false, id, STORE_STATE_NO_STORE, Uuid(),
- encodeMemberSet(ms), urls);
-}
-
-Status storeStatus(bool active, StoreState state, Uuid start=Uuid(), Uuid stop=Uuid(),
- const MemberSet& ms=MemberSet(), const framing::Array& urls=framing::Array())
-{
- return Status(ProtocolVersion(), 0, active, start, state, stop,
- encodeMemberSet(ms), urls);
-}
-
-QPID_AUTO_TEST_CASE(testFirstInCluster) {
- // Single member is first in cluster.
- InitialStatusMap map(MemberId(0), 1);
- Uuid id(true);
- BOOST_CHECK(!map.isComplete());
- MemberSet members = list_of(MemberId(0));
- map.configChange(members);
- BOOST_CHECK(!map.isComplete());
- map.received(MemberId(0), newcomerStatus(id, list_of<MemberId>(0)));
- BOOST_CHECK(map.isComplete());
- BOOST_CHECK(map.transitionToComplete());
- BOOST_CHECK(map.getElders().empty());
- BOOST_CHECK(!map.isUpdateNeeded());
- BOOST_CHECK_EQUAL(id, map.getClusterId());
-}
-
-QPID_AUTO_TEST_CASE(testJoinExistingCluster) {
- // Single member 0 joins existing cluster 1,2
- InitialStatusMap map(MemberId(0), 1);
- Uuid id(true);
- MemberSet members = list_of(MemberId(0))(MemberId(1))(MemberId(2));
- map.configChange(members);
- BOOST_CHECK(map.isResendNeeded());
- BOOST_CHECK(!map.isComplete());
- map.received(MemberId(0), newcomerStatus());
- map.received(MemberId(1), activeStatus(id));
- BOOST_CHECK(!map.isComplete());
- map.received(MemberId(2), activeStatus(id));
- BOOST_CHECK(map.isComplete());
- BOOST_CHECK(map.transitionToComplete());
- BOOST_CHECK_EQUAL(map.getElders(), list_of<MemberId>(1)(2));
- BOOST_CHECK(map.isUpdateNeeded());
- BOOST_CHECK_EQUAL(map.getClusterId(), id);
-
- // Check that transitionToComplete is reset.
- map.configChange(list_of<MemberId>(0)(1));
- BOOST_CHECK(!map.transitionToComplete());
-}
-
-QPID_AUTO_TEST_CASE(testMultipleFirstInCluster) {
- // Multiple members 0,1,2 join at same time.
- InitialStatusMap map(MemberId(1), 1); // self is 1
- Uuid id(true);
- MemberSet members = list_of(MemberId(0))(MemberId(1))(MemberId(2));
- map.configChange(members);
- BOOST_CHECK(map.isResendNeeded());
-
- // All new members
- map.received(MemberId(0), newcomerStatus(id, list_of<MemberId>(0)(1)(2)));
- map.received(MemberId(1), newcomerStatus(id, list_of<MemberId>(0)(1)(2)));
- map.received(MemberId(2), newcomerStatus(id, list_of<MemberId>(0)(1)(2)));
- BOOST_CHECK(!map.isResendNeeded());
- BOOST_CHECK(map.isComplete());
- BOOST_CHECK(map.transitionToComplete());
- BOOST_CHECK_EQUAL(map.getElders(), list_of(MemberId(2)));
- BOOST_CHECK(!map.isUpdateNeeded());
- BOOST_CHECK_EQUAL(map.getClusterId(), id);
-}
-
-QPID_AUTO_TEST_CASE(testMultipleJoinExisting) {
- // Multiple members 2,3 join simultaneously a cluster containing 0,1.
- InitialStatusMap map(MemberId(2), 1); // self is 2
- Uuid id(true);
- MemberSet members = list_of(MemberId(0))(MemberId(1))(MemberId(2))(MemberId(3));
- map.configChange(members);
- BOOST_CHECK(map.isResendNeeded());
- map.received(MemberId(0), activeStatus(id, list_of<MemberId>(0)));
- map.received(MemberId(1), newcomerStatus(id, list_of<MemberId>(0)(1)));
- map.received(MemberId(2), newcomerStatus(id, list_of<MemberId>(0)(1)(2)(3)));
- map.received(MemberId(3), newcomerStatus(id, list_of<MemberId>(0)(1)(2)(3)));
- BOOST_CHECK(!map.isResendNeeded());
- BOOST_CHECK(map.isComplete());
- BOOST_CHECK(map.transitionToComplete());
- BOOST_CHECK_EQUAL(map.getElders(), list_of<MemberId>(0)(1)(3));
- BOOST_CHECK(map.isUpdateNeeded());
- BOOST_CHECK_EQUAL(map.getClusterId(), id);
-}
-
-QPID_AUTO_TEST_CASE(testMembersLeave) {
- // Test that map completes if members leave rather than send status.
- InitialStatusMap map(MemberId(0), 1);
- Uuid id(true);
- map.configChange(list_of(MemberId(0))(MemberId(1))(MemberId(2)));
- map.received(MemberId(0), newcomerStatus());
- map.received(MemberId(1), activeStatus(id));
- BOOST_CHECK(!map.isComplete());
- map.configChange(list_of(MemberId(0))(MemberId(1))); // 2 left
- BOOST_CHECK(map.isComplete());
- BOOST_CHECK(map.transitionToComplete());
- BOOST_CHECK_EQUAL(map.getElders(), list_of(MemberId(1)));
- BOOST_CHECK_EQUAL(map.getClusterId(), id);
-}
-
-QPID_AUTO_TEST_CASE(testInteveningConfig) {
- // Multiple config changes arrives before we complete the map.
- InitialStatusMap map(MemberId(0), 1);
- Uuid id(true);
-
- map.configChange(list_of<MemberId>(0)(1));
- BOOST_CHECK(map.isResendNeeded());
- map.received(MemberId(0), newcomerStatus());
- BOOST_CHECK(!map.isComplete());
- BOOST_CHECK(!map.isResendNeeded());
- // New member 2 joins before we receive 1
- map.configChange(list_of<MemberId>(0)(1)(2));
- BOOST_CHECK(!map.isComplete());
- BOOST_CHECK(map.isResendNeeded());
- map.received(1, activeStatus(id));
- map.received(2, newcomerStatus());
- // We should not be complete as we haven't received 0 since new member joined
- BOOST_CHECK(!map.isComplete());
- BOOST_CHECK(!map.isResendNeeded());
-
- map.received(0, newcomerStatus());
- BOOST_CHECK(map.isComplete());
- BOOST_CHECK(map.transitionToComplete());
- BOOST_CHECK_EQUAL(map.getElders(), list_of<MemberId>(1));
- BOOST_CHECK_EQUAL(map.getClusterId(), id);
-}
-
-QPID_AUTO_TEST_CASE(testAllCleanNoUpdate) {
- InitialStatusMap map(MemberId(0), 3);
- map.configChange(list_of<MemberId>(0)(1)(2));
- map.received(MemberId(0), storeStatus(false, STORE_STATE_CLEAN_STORE));
- map.received(MemberId(1), storeStatus(false, STORE_STATE_CLEAN_STORE));
- map.received(MemberId(2), storeStatus(false, STORE_STATE_CLEAN_STORE));
- BOOST_CHECK(!map.isUpdateNeeded());
-}
-
-QPID_AUTO_TEST_CASE(testAllEmptyNoUpdate) {
- InitialStatusMap map(MemberId(0), 3);
- map.configChange(list_of<MemberId>(0)(1)(2));
- map.received(MemberId(0), storeStatus(false, STORE_STATE_EMPTY_STORE));
- map.received(MemberId(1), storeStatus(false, STORE_STATE_EMPTY_STORE));
- map.received(MemberId(2), storeStatus(false, STORE_STATE_EMPTY_STORE));
- BOOST_CHECK(map.isComplete());
- BOOST_CHECK(!map.isUpdateNeeded());
-}
-
-QPID_AUTO_TEST_CASE(testAllNoStoreNoUpdate) {
- InitialStatusMap map(MemberId(0), 3);
- map.configChange(list_of<MemberId>(0)(1)(2));
- map.received(MemberId(0), storeStatus(false, STORE_STATE_NO_STORE));
- map.received(MemberId(1), storeStatus(false, STORE_STATE_NO_STORE));
- map.received(MemberId(2), storeStatus(false, STORE_STATE_NO_STORE));
- BOOST_CHECK(map.isComplete());
- BOOST_CHECK(!map.isUpdateNeeded());
-}
-
-QPID_AUTO_TEST_CASE(testDirtyNeedUpdate) {
- InitialStatusMap map(MemberId(0), 3);
- map.configChange(list_of<MemberId>(0)(1)(2));
- map.received(MemberId(0), storeStatus(false, STORE_STATE_DIRTY_STORE));
- map.received(MemberId(1), storeStatus(false, STORE_STATE_CLEAN_STORE));
- map.received(MemberId(2), storeStatus(false, STORE_STATE_CLEAN_STORE));
- BOOST_CHECK(map.transitionToComplete());
- BOOST_CHECK(map.isUpdateNeeded());
-}
-
-QPID_AUTO_TEST_CASE(testEmptyNeedUpdate) {
- InitialStatusMap map(MemberId(0), 3);
- map.configChange(list_of<MemberId>(0)(1)(2));
- map.received(MemberId(0), storeStatus(false, STORE_STATE_EMPTY_STORE));
- map.received(MemberId(1), storeStatus(false, STORE_STATE_CLEAN_STORE));
- map.received(MemberId(2), storeStatus(false, STORE_STATE_CLEAN_STORE));
- BOOST_CHECK(map.transitionToComplete());
- BOOST_CHECK(map.isUpdateNeeded());
-}
-
-QPID_AUTO_TEST_CASE(testEmptyAlone) {
- InitialStatusMap map(MemberId(0), 1);
- map.configChange(list_of<MemberId>(0));
- map.received(MemberId(0), storeStatus(false, STORE_STATE_EMPTY_STORE));
- BOOST_CHECK(map.transitionToComplete());
- BOOST_CHECK(!map.isUpdateNeeded());
-}
-
-QPID_AUTO_TEST_SUITE_END()
-
-}} // namespace qpid::tests
diff --git a/cpp/src/tests/Makefile.am b/cpp/src/tests/Makefile.am
index f9eed9270b..3513e0a6d6 100644
--- a/cpp/src/tests/Makefile.am
+++ b/cpp/src/tests/Makefile.am
@@ -17,7 +17,7 @@
# under the License.
#
-AM_CXXFLAGS = $(WARNING_CFLAGS) -DBOOST_TEST_DYN_LINK
+AM_CXXFLAGS = $(WARNING_CFLAGS) -DBOOST_TEST_DYN_LINK -D_IN_QPID_BROKER
INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src -I$(top_builddir)/src
PUBLIC_INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include # Use public API only
QMF_GEN=$(top_srcdir)/managementgen/qmf-gen
@@ -28,6 +28,7 @@ abs_srcdir=@abs_srcdir@
extra_libs =
lib_client = $(abs_builddir)/../libqpidclient.la
lib_messaging = $(abs_builddir)/../libqpidmessaging.la
+lib_types = $(abs_builddir)/../libqpidtypes.la
lib_common = $(abs_builddir)/../libqpidcommon.la
lib_broker = $(abs_builddir)/../libqpidbroker.la
lib_console = $(abs_builddir)/../libqmfconsole.la
@@ -107,8 +108,6 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \
TopicExchangeTest.cpp \
TxBufferTest.cpp \
ConnectionOptions.h \
- ForkedBroker.h \
- ForkedBroker.cpp \
ManagementTest.cpp \
MessageReplayTracker.cpp \
ConsoleTest.cpp \
@@ -154,7 +153,7 @@ receiver_SOURCES = \
receiver.cpp \
TestOptions.h \
ConnectionOptions.h
-receiver_LDADD = $(lib_client)
+receiver_LDADD = $(lib_client) -lboost_program_options $(lib_common)
qpidexectest_PROGRAMS += sender
sender_SOURCES = \
@@ -162,7 +161,7 @@ sender_SOURCES = \
TestOptions.h \
ConnectionOptions.h \
Statistics.cpp
-sender_LDADD = $(lib_messaging)
+sender_LDADD = $(lib_messaging) -lboost_program_options $(lib_common) $(lib_types) $(lib_client)
qpidexectest_PROGRAMS += qpid-receive
qpid_receive_SOURCES = \
@@ -171,7 +170,7 @@ qpid_receive_SOURCES = \
ConnectionOptions.h \
Statistics.h \
Statistics.cpp
-qpid_receive_LDADD = $(lib_messaging)
+qpid_receive_LDADD = $(lib_messaging) -lboost_program_options $(lib_common) $(lib_types)
qpidexectest_PROGRAMS += qpid-send
qpid_send_SOURCES = \
@@ -180,42 +179,42 @@ qpid_send_SOURCES = \
ConnectionOptions.h \
Statistics.h \
Statistics.cpp
-qpid_send_LDADD = $(lib_messaging)
+qpid_send_LDADD = $(lib_messaging) -lboost_program_options $(lib_common) $(lib_types)
qpidexectest_PROGRAMS+=qpid-perftest
qpid_perftest_SOURCES=qpid-perftest.cpp test_tools.h TestOptions.h ConnectionOptions.h
qpid_perftest_INCLUDES=$(PUBLIC_INCLUDES)
-qpid_perftest_LDADD=$(lib_client)
+qpid_perftest_LDADD=$(lib_client) -lboost_program_options $(lib_common)
qpidexectest_PROGRAMS+=qpid-txtest
qpid_txtest_INCLUDES=$(PUBLIC_INCLUDES)
qpid_txtest_SOURCES=qpid-txtest.cpp TestOptions.h ConnectionOptions.h
-qpid_txtest_LDADD=$(lib_client)
+qpid_txtest_LDADD=$(lib_client) -lboost_program_options $(lib_common)
qpidexectest_PROGRAMS+=qpid-latency-test
qpid_latency_test_INCLUDES=$(PUBLIC_INCLUDES)
qpid_latency_test_SOURCES=qpid-latency-test.cpp TestOptions.h ConnectionOptions.h
-qpid_latency_test_LDADD=$(lib_client)
+qpid_latency_test_LDADD=$(lib_client) -lboost_program_options $(lib_common)
qpidexectest_PROGRAMS+=qpid-client-test
qpid_client_test_INCLUDES=$(PUBLIC_INCLUDES)
qpid_client_test_SOURCES=qpid-client-test.cpp TestOptions.h ConnectionOptions.h
-qpid_client_test_LDADD=$(lib_client)
+qpid_client_test_LDADD=$(lib_client) -lboost_program_options $(lib_common)
qpidexectest_PROGRAMS+=qpid-topic-listener
qpid_topic_listener_INCLUDES=$(PUBLIC_INCLUDES)
qpid_topic_listener_SOURCES=qpid-topic-listener.cpp TestOptions.h ConnectionOptions.h
-qpid_topic_listener_LDADD=$(lib_client)
+qpid_topic_listener_LDADD=$(lib_client) -lboost_program_options $(lib_common)
qpidexectest_PROGRAMS+=qpid-topic-publisher
qpid_topic_publisher_INCLUDES=$(PUBLIC_INCLUDES)
qpid_topic_publisher_SOURCES=qpid-topic-publisher.cpp TestOptions.h ConnectionOptions.h
-qpid_topic_publisher_LDADD=$(lib_client)
+qpid_topic_publisher_LDADD=$(lib_client) -lboost_program_options $(lib_common)
qpidexectest_PROGRAMS+=qpid-ping
qpid_ping_INCLUDES=$(PUBLIC_INCLUDES)
qpid_ping_SOURCES=qpid-ping.cpp test_tools.h TestOptions.h ConnectionOptions.h
-qpid_ping_LDADD=$(lib_client)
+qpid_ping_LDADD=$(lib_client) -lboost_program_options $(lib_common)
#
# Other test programs
@@ -241,11 +240,6 @@ header_test_INCLUDES=$(PUBLIC_INCLUDES)
header_test_SOURCES=header_test.cpp TestOptions.h ConnectionOptions.h
header_test_LDADD=$(lib_client)
-check_PROGRAMS+=failover_soak
-failover_soak_INCLUDES=$(PUBLIC_INCLUDES)
-failover_soak_SOURCES=failover_soak.cpp ForkedBroker.h ForkedBroker.cpp
-failover_soak_LDADD=$(lib_client) $(lib_broker)
-
check_PROGRAMS+=declare_queues
declare_queues_INCLUDES=$(PUBLIC_INCLUDES)
declare_queues_SOURCES=declare_queues.cpp
@@ -303,8 +297,8 @@ system_tests = qpid-client-test quick_perftest quick_topictest run_header_test q
run_msg_group_tests
TESTS += start_broker $(system_tests) python_tests stop_broker \
run_ha_tests run_federation_tests run_federation_sys_tests \
- run_acl_tests run_cli_tests replication_test dynamic_log_level_test \
- run_queue_flow_limit_tests ipv6_test
+ run_acl_tests run_cli_tests dynamic_log_level_test \
+ dynamic_log_hires_timestamp run_queue_flow_limit_tests ipv6_test
EXTRA_DIST += \
run_test vg_check \
@@ -316,8 +310,8 @@ EXTRA_DIST += \
run_header_test \
header_test.py \
ssl_test \
+ ping_broker \
config.null \
- cpg_check.sh.in \
run_federation_tests \
run_federation_sys_tests \
run_long_federation_sys_tests \
@@ -327,11 +321,11 @@ EXTRA_DIST += \
MessageUtils.h \
TestMessageStore.h \
TxMocks.h \
- replication_test \
run_perftest \
ring_queue_test \
run_ring_queue_test \
dynamic_log_level_test \
+ dynamic_log_hires_timestamp \
qpid-ctrl \
CMakeLists.txt \
windows/DisableWin32ErrorWindows.cpp \
@@ -349,7 +343,10 @@ EXTRA_DIST += \
run_msg_group_tests \
ipv6_test \
run_ha_tests \
+ ha_test.py \
ha_tests.py \
+ brokertest.py \
+ ha_store_tests.py \
test_env.ps1.in
check_LTLIBRARIES += libdlclose_noop.la
@@ -372,9 +369,6 @@ EXTRA_DIST+= \
shared_perftest \
multiq_perftest \
topic_perftest \
- run_failover_soak \
- reliable_replication_test \
- federated_cluster_test_with_node_failure \
sasl_test_setup.sh \
run_msg_group_tests_soak \
qpidd-empty.conf
diff --git a/cpp/src/tests/MessageUtils.h b/cpp/src/tests/MessageUtils.h
index c2eabd804d..5024f5b77c 100644
--- a/cpp/src/tests/MessageUtils.h
+++ b/cpp/src/tests/MessageUtils.h
@@ -1,3 +1,6 @@
+#ifndef TESTS_MESSAGEUTILS_H
+#define TESTS_MESSAGEUTILS_H
+
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -98,3 +101,5 @@ struct MessageUtils
};
}} // namespace qpid::tests
+
+#endif /*!TESTS_MESSAGEUTILS_H*/
diff --git a/cpp/src/tests/MessagingSessionTests.cpp b/cpp/src/tests/MessagingSessionTests.cpp
index c8ee3aa401..55cff046e2 100644
--- a/cpp/src/tests/MessagingSessionTests.cpp
+++ b/cpp/src/tests/MessagingSessionTests.cpp
@@ -1164,6 +1164,59 @@ QPID_AUTO_TEST_CASE(testAlternateExchangeInLinkDeclare)
}
}
+QPID_AUTO_TEST_CASE(testBrowseOnly)
+{
+ /* Set up a queue browse-only, and try to receive
+ the same messages twice with two different receivers.
+ This works because the browse-only queue does not
+ allow message acquisition. */
+
+ QueueFixture fix;
+ std::string addr = "q; {create:always, node:{type:queue, durable:false, x-declare:{arguments:{qpid.browse-only:1}}}}";
+ Sender sender = fix.session.createSender(addr);
+ Message out("test-message");
+
+ int count = 10;
+ for ( int i = 0; i < count; ++ i ) {
+ sender.send(out);
+ }
+
+ Message m;
+
+ Receiver receiver_1 = fix.session.createReceiver(addr);
+ for ( int i = 0; i < count; ++ i ) {
+ BOOST_CHECK(receiver_1.fetch(m, Duration::SECOND));
+ }
+
+ Receiver receiver_2 = fix.session.createReceiver(addr);
+ for ( int i = 0; i < count; ++ i ) {
+ BOOST_CHECK(receiver_2.fetch(m, Duration::SECOND));
+ }
+
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testLinkBindingCleanup)
+{
+ MessagingFixture fix;
+
+ Sender sender = fix.session.createSender("test.ex;{create:always,node:{type:topic}}");
+
+ Connection connection = fix.newConnection();
+ connection.open();
+
+ Session session(connection.createSession());
+ Receiver receiver1 = session.createReceiver("test.q;{create:always, node:{type:queue, x-bindings:[{exchange:test.ex,queue:test.q,key:#,arguments:{x-scope:session}}]}}");
+ Receiver receiver2 = fix.session.createReceiver("test.q;{create:never, delete:always}");
+ connection.close();
+
+ sender.send(Message("test-message"), true);
+
+ // The session-scoped binding should be removed when receiver1's network connection is lost
+ Message in;
+ BOOST_CHECK(!receiver2.fetch(in, Duration::IMMEDIATE));
+}
+
QPID_AUTO_TEST_SUITE_END()
}} // namespace qpid::tests
diff --git a/cpp/src/tests/PartialFailure.cpp b/cpp/src/tests/PartialFailure.cpp
deleted file mode 100644
index 63ee28017a..0000000000
--- a/cpp/src/tests/PartialFailure.cpp
+++ /dev/null
@@ -1,291 +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.
- *
- */
-
-/**@file Tests for partial failure in a cluster.
- * Partial failure means some nodes experience a failure while others do not.
- * In this case the failed nodes must shut down.
- */
-
-#include "test_tools.h"
-#include "unit_test.h"
-#include "ClusterFixture.h"
-#include <boost/assign.hpp>
-#include <boost/algorithm/string.hpp>
-#include <boost/bind.hpp>
-
-namespace qpid {
-namespace tests {
-
-QPID_AUTO_TEST_SUITE(PartialFailureTestSuite)
-
-using namespace std;
-using namespace qpid;
-using namespace qpid::cluster;
-using namespace qpid::framing;
-using namespace qpid::client;
-using namespace qpid::client::arg;
-using namespace boost::assign;
-using broker::Broker;
-using boost::shared_ptr;
-
-// Timeout for tests that wait for messages
-const sys::Duration TIMEOUT=sys::TIME_SEC/4;
-
-static bool isLogOption(const std::string& s) { return boost::starts_with(s, "--log-enable"); }
-
-void updateArgs(ClusterFixture::Args& args, size_t index) {
- ostringstream clusterLib, testStoreLib, storeName;
- clusterLib << getLibPath("CLUSTER_LIB");
- testStoreLib << getLibPath("TEST_STORE_LIB");
- storeName << "s" << index;
- args.push_back("--auth");
- args.push_back("no");
- args.push_back("--no-module-dir");
- args.push_back("--load-module");
- args.push_back(clusterLib.str());
- args.push_back("--load-module");
- args.push_back(testStoreLib.str());
- args.push_back("--test-store-name");
- args.push_back(storeName.str());
- args.push_back("TMP_DATA_DIR");
-
- // These tests generate errors deliberately, disable error logging unless a log env var is set.
- if (!::getenv("QPID_TRACE") && !::getenv("QPID_LOG_ENABLE")) {
- remove_if(args.begin(), args.end(), isLogOption);
- args.push_back("--log-enable=critical+:DISABLED"); // hacky way to disable logs.
- }
-}
-
-Message pMessage(string data, string q) {
- Message msg(data, q);
- msg.getDeliveryProperties().setDeliveryMode(PERSISTENT);
- return msg;
-}
-
-void queueAndSub(Client& c) {
- c.session.queueDeclare(c.name, durable=true);
- c.subs.subscribe(c.lq, c.name);
-}
-
-// Handle near-simultaneous errors
-QPID_AUTO_TEST_CASE(testCoincidentErrors) {
- ClusterFixture cluster(2, updateArgs, -1);
- Client c0(cluster[0], "c0");
- Client c1(cluster[1], "c1");
-
- c0.session.queueDeclare("q", durable=true);
- {
- ScopedSuppressLogging allQuiet;
- async(c0.session).messageTransfer(content=pMessage("TEST_STORE_DO: s0[exception]", "q"));
- async(c1.session).messageTransfer(content=pMessage("TEST_STORE_DO: s1[exception]", "q"));
-
- int alive=0;
- try { Client c00(cluster[0], "c00"); ++alive; c00.close(); } catch (...) {}
- try { Client c11(cluster[1], "c11"); ++alive; c11.close(); } catch (...) {}
-
- BOOST_CHECK_EQUAL(alive, 1);
-
- // Close inside ScopedSuppressLogging to avoid warnings
- c0.close();
- c1.close();
- }
-}
-
-// Verify normal cluster-wide errors.
-QPID_AUTO_TEST_CASE(testNormalErrors) {
- // FIXME aconway 2009-04-10: Would like to put a scope just around
- // the statements expected to fail (in BOOST_CHECK_yTHROW) but that
- // sproadically lets out messages, possibly because they're in
- // Connection thread.
-
- ClusterFixture cluster(3, updateArgs, -1);
- Client c0(cluster[0], "c0");
- Client c1(cluster[1], "c1");
- Client c2(cluster[2], "c2");
-
- {
- ScopedSuppressLogging allQuiet;
- queueAndSub(c0);
- c0.session.messageTransfer(content=Message("x", "c0"));
- BOOST_CHECK_EQUAL(c0.lq.get(TIMEOUT).getData(), "x");
-
- // Session error.
- BOOST_CHECK_THROW(c0.session.exchangeBind(), SessionException);
- c1.session.messageTransfer(content=Message("stay", "c0")); // Will stay on queue, session c0 is dead.
-
- // Connection error, kill c1 on all members.
- queueAndSub(c1);
- BOOST_CHECK_THROW(
- c1.session.messageTransfer(
- content=pMessage("TEST_STORE_DO: s0[exception] s1[exception] s2[exception] testNormalErrors", "c1")),
- ConnectionException);
- c2.session.messageTransfer(content=Message("stay", "c1")); // Will stay on queue, session/connection c1 is dead.
-
- BOOST_CHECK_EQUAL(3u, knownBrokerPorts(c2.connection, 3).size());
- BOOST_CHECK_EQUAL(c2.subs.get("c0", TIMEOUT).getData(), "stay");
- BOOST_CHECK_EQUAL(c2.subs.get("c1", TIMEOUT).getData(), "stay");
-
- // Close inside ScopedSuppressLogging to avoid warnings
- c0.close();
- c1.close();
- c2.close();
- }
-}
-
-
-// Test errors after a new member joins to verify frame-sequence-numbers are ok in update.
-QPID_AUTO_TEST_CASE(testErrorAfterJoin) {
- ClusterFixture cluster(1, updateArgs, -1);
- Client c0(cluster[0]);
- {
- ScopedSuppressLogging allQuiet;
-
- c0.session.queueDeclare("q", durable=true);
- c0.session.messageTransfer(content=pMessage("a", "q"));
-
- // Kill the new guy
- cluster.add();
- Client c1(cluster[1]);
- c0.session.messageTransfer(content=pMessage("TEST_STORE_DO: s1[exception] testErrorAfterJoin", "q"));
- BOOST_CHECK_THROW(c1.session.messageTransfer(content=pMessage("xxx", "q")), TransportFailure);
- BOOST_CHECK_EQUAL(1u, knownBrokerPorts(c0.connection, 1).size());
-
- // Kill the old guy
- cluster.add();
- Client c2(cluster[2]);
- c2.session.messageTransfer(content=pMessage("TEST_STORE_DO: s0[exception] testErrorAfterJoin2", "q"));
- BOOST_CHECK_THROW(c0.session.messageTransfer(content=pMessage("xxx", "q")), TransportFailure);
-
- BOOST_CHECK_EQUAL(1u, knownBrokerPorts(c2.connection, 1).size());
-
- // Close inside ScopedSuppressLogging to avoid warnings
- c0.close();
- c1.close();
- c2.close();
- }
-}
-
-// Test that if one member fails and others do not, the failure leaves the cluster.
-QPID_AUTO_TEST_CASE(testSinglePartialFailure) {
- ClusterFixture cluster(3, updateArgs, -1);
- Client c0(cluster[0], "c0");
- Client c1(cluster[1], "c1");
- Client c2(cluster[2], "c2");
-
- {
- ScopedSuppressLogging allQuiet;
-
- c0.session.queueDeclare("q", durable=true);
- c0.session.messageTransfer(content=pMessage("a", "q"));
- // Cause partial failure on c1
- c0.session.messageTransfer(content=pMessage("TEST_STORE_DO: s1[exception] testSinglePartialFailure", "q"));
- BOOST_CHECK_THROW(c1.session.queueQuery("q"), TransportFailure);
-
- c0.session.messageTransfer(content=pMessage("b", "q"));
- BOOST_CHECK_EQUAL(c0.session.queueQuery("q").getMessageCount(), 3u);
- BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c0.connection, 2).size());
-
- // Cause partial failure on c2
- c0.session.messageTransfer(content=pMessage("TEST_STORE_DO: s2[exception] testSinglePartialFailure2", "q"));
- BOOST_CHECK_THROW(c2.session.queueQuery("q"), TransportFailure);
-
- c0.session.messageTransfer(content=pMessage("c", "q"));
- BOOST_CHECK_EQUAL(c0.session.queueQuery("q").getMessageCount(), 5u);
- BOOST_CHECK_EQUAL(1u, knownBrokerPorts(c0.connection, 1).size());
-
- // Close inside ScopedSuppressLogging to avoid warnings
- c0.close();
- c1.close();
- c2.close();
- }
-}
-
-// Test multiple partial falures: 2 fail 2 pass
-QPID_AUTO_TEST_CASE(testMultiPartialFailure) {
- ClusterFixture cluster(4, updateArgs, -1);
- Client c0(cluster[0], "c0");
- Client c1(cluster[1], "c1");
- Client c2(cluster[2], "c2");
- Client c3(cluster[3], "c3");
-
- {
- ScopedSuppressLogging allQuiet;
-
- c0.session.queueDeclare("q", durable=true);
- c0.session.messageTransfer(content=pMessage("a", "q"));
-
- // Cause partial failure on c1, c2
- c0.session.messageTransfer(content=pMessage("TEST_STORE_DO: s1[exception] s2[exception] testMultiPartialFailure", "q"));
- BOOST_CHECK_THROW(c1.session.queueQuery("q"), TransportFailure);
- BOOST_CHECK_THROW(c2.session.queueQuery("q"), TransportFailure);
-
- c0.session.messageTransfer(content=pMessage("b", "q"));
- c3.session.messageTransfer(content=pMessage("c", "q"));
- BOOST_CHECK_EQUAL(c3.session.queueQuery("q").getMessageCount(), 4u);
- // FIXME aconway 2009-06-30: This check fails sporadically with 2 != 3.
- // It should pass reliably.
- // BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c0.connection, 2).size());
-
- // Close inside ScopedSuppressLogging to avoid warnings
- c0.close();
- c1.close();
- c2.close();
- c3.close();
- }
-}
-
-/** FIXME aconway 2009-04-10:
- * The current approach to shutting down a process in test_store
- * sometimes leads to assertion failures and errors in the shut-down
- * process. Need a cleaner solution
- */
-#if 0
-QPID_AUTO_TEST_CASE(testPartialFailureMemberLeaves) {
- ClusterFixture cluster(2, updateArgs, -1);
- Client c0(cluster[0], "c0");
- Client c1(cluster[1], "c1");
-
- {
- ScopedSuppressLogging allQuiet;
-
- c0.session.queueDeclare("q", durable=true);
- c0.session.messageTransfer(content=pMessage("a", "q"));
-
- // Cause failure on member 0 and simultaneous crash on member 1.
- BOOST_CHECK_THROW(
- c0.session.messageTransfer(
- content=pMessage("TEST_STORE_DO: s0[exception] s1[exit_process] testPartialFailureMemberLeaves", "q")),
- ConnectionException);
- cluster.wait(1);
-
- Client c00(cluster[0], "c00"); // Old connection is dead.
- BOOST_CHECK_EQUAL(c00.session.queueQuery("q").getMessageCount(), 1u);
- BOOST_CHECK_EQUAL(1u, knownBrokerPorts(c00.connection, 1).size());
-
- // Close inside ScopedSuppressLogging to avoid warnings
- c0.close();
- }
-}
-#endif
-
-QPID_AUTO_TEST_SUITE_END()
-
-}} // namespace qpid::tests
diff --git a/cpp/src/tests/PollerTest.cpp b/cpp/src/tests/PollerTest.cpp
index 9fa5689c5f..5a1d02964c 100644
--- a/cpp/src/tests/PollerTest.cpp
+++ b/cpp/src/tests/PollerTest.cpp
@@ -23,7 +23,6 @@
* Use socketpair to test the poller
*/
-#include "qpid/sys/IOHandle.h"
#include "qpid/sys/Poller.h"
#include "qpid/sys/posix/PrivatePosix.h"
@@ -106,8 +105,8 @@ int main(int /*argc*/, char** /*argv*/)
auto_ptr<Poller> poller(new Poller);
- PosixIOHandle f0(sv[0]);
- PosixIOHandle f1(sv[1]);
+ IOHandle f0(sv[0]);
+ IOHandle f1(sv[1]);
PollerHandle h0(f0);
PollerHandle h1(f1);
@@ -225,8 +224,8 @@ int main(int /*argc*/, char** /*argv*/)
auto_ptr<Poller> poller1(new Poller);
- PosixIOHandle f2(sv[0]);
- PosixIOHandle f3(sv[1]);
+ IOHandle f2(sv[0]);
+ IOHandle f3(sv[1]);
PollerHandle h2(f2);
PollerHandle h3(f3);
diff --git a/cpp/src/tests/QueueTest.cpp b/cpp/src/tests/QueueTest.cpp
index d86c18c38d..60ba036a7e 100644
--- a/cpp/src/tests/QueueTest.cpp
+++ b/cpp/src/tests/QueueTest.cpp
@@ -40,6 +40,7 @@
#include "qpid/framing/reply_exceptions.h"
#include "qpid/broker/QueueFlowLimit.h"
#include "qpid/broker/QueueSettings.h"
+#include "qpid/sys/Timer.h"
#include <iostream>
#include <vector>
diff --git a/cpp/src/tests/StoreStatus.cpp b/cpp/src/tests/StoreStatus.cpp
deleted file mode 100644
index 43d4cfd920..0000000000
--- a/cpp/src/tests/StoreStatus.cpp
+++ /dev/null
@@ -1,117 +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 "unit_test.h"
-#include "test_tools.h"
-#include "qpid/cluster/StoreStatus.h"
-#include "qpid/framing/Uuid.h"
-#include <boost/assign.hpp>
-#include <boost/filesystem/operations.hpp>
-
-using namespace std;
-using namespace qpid::cluster;
-using namespace qpid::framing;
-using namespace qpid::framing::cluster;
-using namespace boost::assign;
-using namespace boost::filesystem;
-
-
-namespace qpid {
-namespace tests {
-
-QPID_AUTO_TEST_SUITE(StoreStatusTestSuite)
-
-const char* TEST_DIR = "StoreStatus.tmp";
-
-struct TestDir {
- TestDir() {
- remove_all(TEST_DIR);
- create_directory(TEST_DIR);
- }
- ~TestDir() {
- remove_all(TEST_DIR);
- }
-};
-
-QPID_AUTO_TEST_CASE(testLoadEmpty) {
- TestDir td;
- StoreStatus ss(TEST_DIR);
- BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_NO_STORE);
- BOOST_CHECK(!ss.getClusterId());
- BOOST_CHECK(!ss.getShutdownId());
- ss.load();
- BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_EMPTY_STORE);
- BOOST_CHECK(!ss.getShutdownId());
-}
-
-QPID_AUTO_TEST_CASE(testSaveLoadDirty) {
- TestDir td;
- Uuid clusterId = Uuid(true);
- StoreStatus ss(TEST_DIR);
- ss.load();
- ss.setClusterId(clusterId);
- ss.dirty();
- BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_DIRTY_STORE);
-
- StoreStatus ss2(TEST_DIR);
- ss2.load();
- BOOST_CHECK_EQUAL(ss2.getState(), STORE_STATE_DIRTY_STORE);
- BOOST_CHECK_EQUAL(ss2.getClusterId(), clusterId);
- BOOST_CHECK(!ss2.getShutdownId());
-}
-
-QPID_AUTO_TEST_CASE(testSaveLoadClean) {
- TestDir td;
- Uuid clusterId = Uuid(true);
- Uuid shutdownId = Uuid(true);
- StoreStatus ss(TEST_DIR);
- ss.load();
- ss.setClusterId(clusterId);
- ss.clean(shutdownId);
- BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_CLEAN_STORE);
-
- StoreStatus ss2(TEST_DIR);
- ss2.load();
- BOOST_CHECK_EQUAL(ss2.getState(), STORE_STATE_CLEAN_STORE);
- BOOST_CHECK_EQUAL(ss2.getClusterId(), clusterId);
- BOOST_CHECK_EQUAL(ss2.getShutdownId(), shutdownId);
-}
-
-QPID_AUTO_TEST_CASE(testMarkDirty) {
- // Save clean then mark to dirty.
- TestDir td;
- Uuid clusterId = Uuid(true);
- Uuid shutdownId = Uuid(true);
- StoreStatus ss(TEST_DIR);
- ss.load();
- ss.setClusterId(clusterId);
- ss.dirty();
- ss.clean(shutdownId);
- ss.dirty();
-
- StoreStatus ss2(TEST_DIR);
- ss2.load();
- BOOST_CHECK_EQUAL(ss2.getState(), STORE_STATE_DIRTY_STORE);
- BOOST_CHECK_EQUAL(ss2.getClusterId(), clusterId);
- BOOST_CHECK(!ss2.getShutdownId());
-}
-
-QPID_AUTO_TEST_SUITE_END()
-
- }} // namespace qpid::tests
diff --git a/cpp/src/tests/SystemInfo.cpp b/cpp/src/tests/SystemInfo.cpp
index 12d8d3dba8..34f1ac408e 100644
--- a/cpp/src/tests/SystemInfo.cpp
+++ b/cpp/src/tests/SystemInfo.cpp
@@ -31,22 +31,6 @@ namespace tests {
QPID_AUTO_TEST_SUITE(SystemInfoTestSuite)
-QPID_AUTO_TEST_CASE(TestIsLocalHost) {
- // Test that local hostname and addresses are considered local
- Address a;
- BOOST_ASSERT(SystemInfo::getLocalHostname(a));
- BOOST_ASSERT(SystemInfo::isLocalHost(a.host));
- std::vector<Address> addrs;
- SystemInfo::getLocalIpAddresses(0, addrs);
- for (std::vector<Address>::iterator i = addrs.begin(); i != addrs.end(); ++i)
- BOOST_ASSERT(SystemInfo::isLocalHost(i->host));
- // Check some non-local addresses
- BOOST_ASSERT(!SystemInfo::isLocalHost("123.4.5.6"));
- BOOST_ASSERT(!SystemInfo::isLocalHost("nosuchhost"));
- BOOST_ASSERT(SystemInfo::isLocalHost("127.0.0.1"));
- BOOST_ASSERT(SystemInfo::isLocalHost("::1"));
-}
-
QPID_AUTO_TEST_SUITE_END()
}} // namespace qpid::tests
diff --git a/cpp/src/tests/Variant.cpp b/cpp/src/tests/Variant.cpp
index 40f1c0cf75..6d629bbb4a 100644
--- a/cpp/src/tests/Variant.cpp
+++ b/cpp/src/tests/Variant.cpp
@@ -135,6 +135,16 @@ QPID_AUTO_TEST_CASE(testConversionsFromString)
BOOST_CHECK_EQUAL(0, value.asInt16());
BOOST_CHECK_EQUAL(0u, value.asUint16());
+ value = "-Blah";
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asFloat(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asDouble(), InvalidConversion);
+
value = "-000";
BOOST_CHECK_EQUAL(0, value.asInt16());
BOOST_CHECK_EQUAL(0u, value.asUint16());
diff --git a/cpp/src/tests/acl.py b/cpp/src/tests/acl.py
index 102796cba6..48723bfde9 100755
--- a/cpp/src/tests/acl.py
+++ b/cpp/src/tests/acl.py
@@ -53,6 +53,9 @@ class ACLTests(TestBase010):
def port_u(self):
return int(self.defines["port-u"])
+ def port_q(self):
+ return int(self.defines["port-q"])
+
def get_session_by_port(self, user, passwd, byPort):
socket = connect(self.broker.host, byPort)
connection = Connection (sock=socket, username=user, password=passwd,
@@ -542,6 +545,123 @@ class ACLTests(TestBase010):
self.fail(result)
+ def test_illegal_filemaxsize_upper_limit_spec(self):
+ """
+ Test illegal file policy
+ """
+ #
+ # Use filemaxsizeupperlimit
+ #
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxsizeupperlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'filemaxsizeupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxsizeupperlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'filemaxsizeupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
+
+ def test_illegal_filemaxcount_upper_limit_spec(self):
+ """
+ Test illegal file policy
+ """
+ #
+ # use maxfilecountupperlimit
+ #
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxcountupperlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'filemaxcountupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxcountupperlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'filemaxcountupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
+ def test_illegal_filemaxsize_lower_limit_spec(self):
+ """
+ Test illegal file policy
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxsizelowerlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'filemaxsizelowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxsizelowerlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'filemaxsizelowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
+
+ def test_illegal_filemaxcount_lower_limit_spec(self):
+ """
+ Test illegal file policy
+ """
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxcountlowerlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'filemaxcountlowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxcountlowerlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'filemaxcountlowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
#=====================================
# ACL queue tests
#=====================================
@@ -831,6 +951,171 @@ class ACLTests(TestBase010):
self.fail("ACL should allow queue delete request for q4");
#=====================================
+ # ACL file tests
+ #=====================================
+
+ def test_file_allow_mode(self):
+ """
+ Test cases for file acl in allow mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID access queue name=qf1\n')
+ aclf.write('acl deny bob@QPID create queue name=qf1 durable=true\n')
+ aclf.write('acl deny bob@QPID create queue name=qf2 exclusive=true policytype=ring\n')
+ aclf.write('acl deny bob@QPID access queue name=qf3\n')
+ aclf.write('acl deny bob@QPID purge queue name=qf3\n')
+ aclf.write('acl deny bob@QPID delete queue name=qf4\n')
+ aclf.write('acl deny bob@QPID create queue name=qf5 filemaxsizeupperlimit=1000 filemaxcountupperlimit=100\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 200
+ queue_options["qpid.file_size"] = 500
+ session.queue_declare(queue="qf5", exclusive=True, arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=qf5, qpid.file_size=500 and qpid.file_count=200");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 200
+ queue_options["qpid.file_size"] = 100
+ session.queue_declare(queue="qf2", exclusive=True, arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qf2, qpid.file_size=100 and qpid.file_count=200 ");
+
+
+ def test_file_deny_mode(self):
+ """
+ Test cases for queue acl in deny mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID access queue name=qfd1\n')
+ aclf.write('acl allow bob@QPID create queue name=qfd1 durable=true\n')
+ aclf.write('acl allow bob@QPID create queue name=qfd2 exclusive=true policytype=ring\n')
+ aclf.write('acl allow bob@QPID access queue name=qfd3\n')
+ aclf.write('acl allow bob@QPID purge queue name=qfd3\n')
+ aclf.write('acl allow bob@QPID create queue name=qfd3\n')
+ aclf.write('acl allow bob@QPID create queue name=qfd4\n')
+ aclf.write('acl allow bob@QPID delete queue name=qfd4\n')
+ aclf.write('acl allow bob@QPID create queue name=qfd5 filemaxsizeupperlimit=1000 filemaxcountupperlimit=100\n')
+ aclf.write('acl allow bob@QPID create queue name=qfd6 filemaxsizelowerlimit=50 filemaxsizeupperlimit=100 filemaxcountlowerlimit=50 filemaxcountupperlimit=100\n')
+ aclf.write('acl allow anonymous all all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="qfd1", durable=True)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qfd1 durable=true");
+
+ try:
+ session.queue_declare(queue="qfd1", durable=True, passive=True)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue passive declare request with name=qfd1 durable=true passive=true");
+
+ try:
+ session.queue_declare(queue="qfd1", durable=False, passive=False)
+ self.fail("ACL should deny queue create request with name=qfd1 durable=true passive=false");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="qfd2", exclusive=False)
+ self.fail("ACL should deny queue create request with name=qfd2 exclusive=false");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 200
+ queue_options["qpid.file_size"] = 500
+ session.queue_declare(queue="qfd5", arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=qfd5 filemaxsizeupperlimit=500 filemaxcountupperlimit=200");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 100
+ queue_options["qpid.file_size"] = 500
+ session.queue_declare(queue="qfd5", arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qfd5 filemaxsizeupperlimit=500 filemaxcountupperlimit=200");
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 49
+ queue_options["qpid.file_size"] = 100
+ session.queue_declare(queue="qfd6", arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=qfd6 filemaxsizeupperlimit=100 filemaxcountupperlimit=49");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 101
+ queue_options["qpid.file_size"] = 100
+ session.queue_declare(queue="qfd6", arguments=queue_options)
+ self.fail("ACL should allow queue create request with name=qfd6 filemaxsizeupperlimit=100 filemaxcountupperlimit=101");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 100
+ queue_options["qpid.file_size"] = 49
+ session.queue_declare(queue="qfd6", arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=qfd6 filemaxsizeupperlimit=49 filemaxcountupperlimit=100");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 100
+ queue_options["qpid.file_size"] =101
+ session.queue_declare(queue="qfd6", arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=qfd6 filemaxsizeupperlimit=101 filemaxcountupperlimit=100");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 50
+ queue_options["qpid.file_size"] = 50
+ session.queue_declare(queue="qfd6", arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qfd6 filemaxsizeupperlimit=50 filemaxcountupperlimit=50");
+
+
+ #=====================================
# ACL exchange tests
#=====================================
@@ -1538,10 +1823,10 @@ class ACLTests(TestBase010):
#=====================================
- # QMF Topic Exchange tests
+ # Routingkey lookup using Topic Exchange tests
#=====================================
- def test_qmf_topic_exchange_tests(self):
+ def test_topic_exchange_publish_tests(self):
"""
Test using QMF method hooks into ACL logic
"""
@@ -1655,40 +1940,367 @@ class ACLTests(TestBase010):
self.LookupPublish("dev@QPID", "X", "a.M.N", "allow-log")
self.LookupPublish("dev@QPID", "X", "a.M.p.qq.N", "allow-log")
+ def test_topic_exchange_other_tests(self):
+ """
+ Test using QMF method hooks into ACL logic
+ """
+ action_list = ['access','bind','unbind']
+
+ aclf = self.get_acl_file()
+ aclf.write('# begin hack alert: allow anonymous to access the lookup debug functions\n')
+ aclf.write('acl allow-log anonymous create queue\n')
+ aclf.write('acl allow-log anonymous all exchange name=qmf.*\n')
+ aclf.write('acl allow-log anonymous all exchange name=amq.direct\n')
+ aclf.write('acl allow-log anonymous all exchange name=qpid.management\n')
+ aclf.write('acl allow-log anonymous access method name=*\n')
+ aclf.write('# end hack alert\n')
+ for action in action_list:
+ aclf.write('acl allow-log uPlain1@COMPANY ' + action + ' exchange name=X routingkey=ab.cd.e\n')
+ aclf.write('acl allow-log uPlain2@COMPANY ' + action + ' exchange name=X routingkey=.\n')
+ aclf.write('acl allow-log uStar1@COMPANY ' + action + ' exchange name=X routingkey=a.*.b\n')
+ aclf.write('acl allow-log uStar2@COMPANY ' + action + ' exchange name=X routingkey=*.x\n')
+ aclf.write('acl allow-log uStar3@COMPANY ' + action + ' exchange name=X routingkey=x.x.*\n')
+ aclf.write('acl allow-log uHash1@COMPANY ' + action + ' exchange name=X routingkey=a.#.b\n')
+ aclf.write('acl allow-log uHash2@COMPANY ' + action + ' exchange name=X routingkey=a.#\n')
+ aclf.write('acl allow-log uHash3@COMPANY ' + action + ' exchange name=X routingkey=#.a\n')
+ aclf.write('acl allow-log uHash4@COMPANY ' + action + ' exchange name=X routingkey=a.#.b.#.c\n')
+ aclf.write('acl allow-log uMixed1@COMPANY ' + action + ' exchange name=X routingkey=*.x.#.y\n')
+ aclf.write('acl allow-log uMixed2@COMPANY ' + action + ' exchange name=X routingkey=a.#.b.*\n')
+ aclf.write('acl allow-log uMixed3@COMPANY ' + action + ' exchange name=X routingkey=*.*.*.#\n')
+
+ aclf.write('acl allow-log all ' + action + ' exchange name=X routingkey=MN.OP.Q\n')
+ aclf.write('acl allow-log all ' + action + ' exchange name=X routingkey=M.*.N\n')
+ aclf.write('acl allow-log all ' + action + ' exchange name=X routingkey=M.#.N\n')
+ aclf.write('acl allow-log all ' + action + ' exchange name=X routingkey=*.M.#.N\n')
+
+ aclf.write('acl deny-log all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ for action in action_list:
+ # aclKey: "ab.cd.e"
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"ab.cd.e"}, "allow-log")
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"ab.cd.e"}, "allow-log")
+
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"ab.cd.e"}, "allow-log")
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"abx.cd.e"}, "deny-log")
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"ab.cd"}, "deny-log")
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"ab.cd..e."}, "deny-log")
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"ab.cd.e."}, "deny-log")
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":".ab.cd.e"}, "deny-log")
+ # aclKey: "."
+ self.Lookup("uPlain2@COMPANY", action, "exchange", "X", {"routingkey":"."}, "allow-log")
+
+ # aclKey: "a.*.b"
+ self.Lookup("uStar1@COMPANY", action, "exchange", "X", {"routingkey":"a.xx.b"}, "allow-log")
+ self.Lookup("uStar1@COMPANY", action, "exchange", "X", {"routingkey":"a.b"}, "deny-log")
+ # aclKey: "*.x"
+ self.Lookup("uStar2@COMPANY", action, "exchange", "X", {"routingkey":"y.x"}, "allow-log")
+ self.Lookup("uStar2@COMPANY", action, "exchange", "X", {"routingkey":".x"}, "allow-log")
+ self.Lookup("uStar2@COMPANY", action, "exchange", "X", {"routingkey":"x"}, "deny-log")
+ # aclKey: "x.x.*"
+ self.Lookup("uStar3@COMPANY", action, "exchange", "X", {"routingkey":"x.x.y"}, "allow-log")
+ self.Lookup("uStar3@COMPANY", action, "exchange", "X", {"routingkey":"x.x."}, "allow-log")
+ self.Lookup("uStar3@COMPANY", action, "exchange", "X", {"routingkey":"x.x"}, "deny-log")
+ self.Lookup("uStar3@COMPANY", action, "exchange", "X", {"routingkey":"q.x.y"}, "deny-log")
+
+ # aclKey: "a.#.b"
+ self.Lookup("uHash1@COMPANY", action, "exchange", "X", {"routingkey":"a.b"}, "allow-log")
+ self.Lookup("uHash1@COMPANY", action, "exchange", "X", {"routingkey":"a.x.b"}, "allow-log")
+ self.Lookup("uHash1@COMPANY", action, "exchange", "X", {"routingkey":"a..x.y.zz.b"}, "allow-log")
+ self.Lookup("uHash1@COMPANY", action, "exchange", "X", {"routingkey":"a.b."}, "deny-log")
+ self.Lookup("uHash1@COMPANY", action, "exchange", "X", {"routingkey":"q.x.b"}, "deny-log")
+
+ # aclKey: "a.#"
+ self.Lookup("uHash2@COMPANY", action, "exchange", "X", {"routingkey":"a"}, "allow-log")
+ self.Lookup("uHash2@COMPANY", action, "exchange", "X", {"routingkey":"a.b"}, "allow-log")
+ self.Lookup("uHash2@COMPANY", action, "exchange", "X", {"routingkey":"a.b.c"}, "allow-log")
+
+ # aclKey: "#.a"
+ self.Lookup("uHash3@COMPANY", action, "exchange", "X", {"routingkey":"a"}, "allow-log")
+ self.Lookup("uHash3@COMPANY", action, "exchange", "X", {"routingkey":"x.y.a"}, "allow-log")
+
+ # aclKey: "a.#.b.#.c"
+ self.Lookup("uHash4@COMPANY", action, "exchange", "X", {"routingkey":"a.b.c"}, "allow-log")
+ self.Lookup("uHash4@COMPANY", action, "exchange", "X", {"routingkey":"a.x.b.y.c"}, "allow-log")
+ self.Lookup("uHash4@COMPANY", action, "exchange", "X", {"routingkey":"a.x.x.b.y.y.c"}, "allow-log")
+
+ # aclKey: "*.x.#.y"
+ self.Lookup("uMixed1@COMPANY", action, "exchange", "X", {"routingkey":"a.x.y"}, "allow-log")
+ self.Lookup("uMixed1@COMPANY", action, "exchange", "X", {"routingkey":"a.x.p.qq.y"}, "allow-log")
+ self.Lookup("uMixed1@COMPANY", action, "exchange", "X", {"routingkey":"a.a.x.y"}, "deny-log")
+ self.Lookup("uMixed1@COMPANY", action, "exchange", "X", {"routingkey":"aa.x.b.c"}, "deny-log")
+
+ # aclKey: "a.#.b.*"
+ self.Lookup("uMixed2@COMPANY", action, "exchange", "X", {"routingkey":"a.b.x"}, "allow-log")
+ self.Lookup("uMixed2@COMPANY", action, "exchange", "X", {"routingkey":"a.x.x.x.b.x"}, "allow-log")
+
+ # aclKey: "*.*.*.#"
+ self.Lookup("uMixed3@COMPANY", action, "exchange", "X", {"routingkey":"x.y.z"}, "allow-log")
+ self.Lookup("uMixed3@COMPANY", action, "exchange", "X", {"routingkey":"x.y.z.a.b.c"}, "allow-log")
+ self.Lookup("uMixed3@COMPANY", action, "exchange", "X", {"routingkey":"x.y"}, "deny-log")
+ self.Lookup("uMixed3@COMPANY", action, "exchange", "X", {"routingkey":"x"}, "deny-log")
+
+ # Repeat the keys with wildcard user spec
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"MN.OP.Q"}, "allow-log")
+ self.Lookup("uStar1@COMPANY" , action, "exchange", "X", {"routingkey":"M.xx.N"}, "allow-log")
+ self.Lookup("uHash1@COMPANY" , action, "exchange", "X", {"routingkey":"M.N"}, "allow-log")
+ self.Lookup("uHash1@COMPANY" , action, "exchange", "X", {"routingkey":"M.x.N"}, "allow-log")
+ self.Lookup("uHash1@COMPANY" , action, "exchange", "X", {"routingkey":"M..x.y.zz.N"}, "allow-log")
+ self.Lookup("uMixed1@COMPANY", action, "exchange", "X", {"routingkey":"a.M.N"}, "allow-log")
+ self.Lookup("uMixed1@COMPANY", action, "exchange", "X", {"routingkey":"a.M.p.qq.N"}, "allow-log")
+
+ self.Lookup("dev@QPID", action, "exchange", "X", {"routingkey": "MN.OP.Q"}, "allow-log")
+ self.Lookup("dev@QPID", action, "exchange", "X", {"routingkey": "M.xx.N"}, "allow-log")
+ self.Lookup("dev@QPID", action, "exchange", "X", {"routingkey": "M.N"}, "allow-log")
+ self.Lookup("dev@QPID", action, "exchange", "X", {"routingkey": "M.x.N"}, "allow-log")
+ self.Lookup("dev@QPID", action, "exchange", "X", {"routingkey": "M..x.y.zz.N"}, "allow-log")
+ self.Lookup("dev@QPID", action, "exchange", "X", {"routingkey": "a.M.N"}, "allow-log")
+ self.Lookup("dev@QPID", action, "exchange", "X", {"routingkey": "a.M.p.qq.N"}, "allow-log")
+
#=====================================
# Connection limits
#=====================================
- def test_connection_limits(self):
+ def test_connection_limits_cli_sets_all(self):
+
+ try:
+ sessiona1 = self.get_session_by_port('alice','alice', self.port_u())
+ sessiona2 = self.get_session_by_port('alice','alice', self.port_u())
+ except Exception, e:
+ self.fail("Could not create two connections for user alice: " + str(e))
+
+ # Third session should fail
+ try:
+ sessiona3 = self.get_session_by_port('alice','alice', self.port_u())
+ self.fail("Should not be able to create third connection for user alice")
+ except Exception, e:
+ result = None
+
+
+
+ def test_connection_limits_by_named_user(self):
"""
Test ACL control connection limits
"""
+ aclf = self.get_acl_file()
+ aclf.write('quota connections 2 alice bob\n')
+ aclf.write('quota connections 0 evildude\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
# By username should be able to connect twice per user
try:
- sessiona1 = self.get_session_by_port('alice','alice', self.port_u())
- sessiona2 = self.get_session_by_port('alice','alice', self.port_u())
+ sessiona1 = self.get_session('alice','alice')
+ sessiona2 = self.get_session('alice','alice')
except Exception, e:
self.fail("Could not create two connections for user alice: " + str(e))
# Third session should fail
try:
- sessiona3 = self.get_session_by_port('alice','alice', self.port_u())
+ sessiona3 = self.get_session('alice','alice')
+ self.fail("Should not be able to create third connection for user alice")
+ except Exception, e:
+ result = None
+
+ # Disconnecting should allow another session.
+ sessiona1.close()
+ try:
+ sessiona3 = self.get_session('alice','alice')
+ except Exception, e:
+ self.fail("Could not recreate second connection for user alice: " + str(e))
+
+ # By username should be able to connect twice per user
+ try:
+ sessionb1 = self.get_session('bob','bob')
+ sessionb2 = self.get_session('bob','bob')
+ except Exception, e:
+ self.fail("Could not create two connections for user bob: " + str(e))
+
+ # Third session should fail
+ try:
+ sessionb3 = self.get_session('bob','bob')
+ self.fail("Should not be able to create third connection for user bob")
+ except Exception, e:
+ result = None
+
+
+ # User with quota of 0 is denied
+ try:
+ sessione1 = self.get_session('evildude','evildude')
+ self.fail("Should not be able to create a connection for user evildude")
+ except Exception, e:
+ result = None
+
+
+ # User not named in quotas is denied
+ try:
+ sessionc1 = self.get_session('charlie','charlie')
+ self.fail("Should not be able to create a connection for user charlie")
+ except Exception, e:
+ result = None
+
+ # Clean up the sessions
+ sessiona2.close()
+ sessiona3.close()
+ sessionb1.close()
+ sessionb2.close()
+
+
+
+ def test_connection_limits_by_unnamed_all(self):
+ """
+ Test ACL control connection limits
+ """
+ aclf = self.get_acl_file()
+ aclf.write('quota connections 2 alice bob\n')
+ aclf.write('quota connections 1 all\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ # By username should be able to connect twice per user
+ try:
+ sessiona1 = self.get_session('alice','alice')
+ sessiona2 = self.get_session('alice','alice')
+ except Exception, e:
+ self.fail("Could not create two connections for user alice: " + str(e))
+
+ # Third session should fail
+ try:
+ sessiona3 = self.get_session('alice','alice')
self.fail("Should not be able to create third connection for user alice")
except Exception, e:
result = None
+ # By username should be able to connect twice per user
try:
- sessionb1 = self.get_session_by_port('bob','bob', self.port_u())
- sessionb2 = self.get_session_by_port('bob','bob', self.port_u())
+ sessionb1 = self.get_session('bob','bob')
+ sessionb2 = self.get_session('bob','bob')
except Exception, e:
self.fail("Could not create two connections for user bob: " + str(e))
+ # Third session should fail
try:
- sessionb3 = self.get_session_by_port('bob','bob', self.port_u())
+ sessionb3 = self.get_session('bob','bob')
self.fail("Should not be able to create third connection for user bob")
except Exception, e:
result = None
+ # User not named in quotas gets 'all' quota
+ try:
+ sessionc1 = self.get_session('charlie','charlie')
+ except Exception, e:
+ self.fail("Could not create one connection for user charlie: " + str(e))
+
+ # Next session should fail
+ try:
+ sessionc2 = self.get_session('charlie','charlie')
+ self.fail("Should not be able to create second connection for user charlie")
+ except Exception, e:
+ result = None
+
+ # Clean up the sessions
+ sessiona1.close()
+ sessiona2.close()
+ sessionb1.close()
+ sessionb2.close()
+ sessionc1.close()
+
+
+ def test_connection_limits_by_group(self):
+ """
+ Test ACL control connection limits
+ """
+ aclf = self.get_acl_file()
+ aclf.write('group stooges moe@QPID larry@QPID curly@QPID\n')
+ aclf.write('quota connections 2 alice bob\n')
+ aclf.write('quota connections 2 stooges charlie\n')
+ aclf.write('# user and groups may be overwritten. Should use last value\n')
+ aclf.write('quota connections 3 bob stooges\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ # Alice gets 2
+ try:
+ sessiona1 = self.get_session('alice','alice')
+ sessiona2 = self.get_session('alice','alice')
+ except Exception, e:
+ self.fail("Could not create two connections for user alice: " + str(e))
+
+ # Third session should fail
+ try:
+ sessiona3 = self.get_session('alice','alice')
+ self.fail("Should not be able to create third connection for user alice")
+ except Exception, e:
+ result = None
+
+ # Bob gets 3
+ try:
+ sessionb1 = self.get_session('bob','bob')
+ sessionb2 = self.get_session('bob','bob')
+ sessionb3 = self.get_session('bob','bob')
+ except Exception, e:
+ self.fail("Could not create three connections for user bob: " + str(e))
+
+ # Fourth session should fail
+ try:
+ sessionb4 = self.get_session('bob','bob')
+ self.fail("Should not be able to create fourth connection for user bob")
+ except Exception, e:
+ result = None
+
+ # Moe gets 3
+ try:
+ sessionm1 = self.get_session('moe','moe')
+ sessionm2 = self.get_session('moe','moe')
+ sessionm3 = self.get_session('moe','moe')
+ except Exception, e:
+ self.fail("Could not create three connections for user moe: " + str(e))
+
+ # Fourth session should fail
+ try:
+ sessionb4 = self.get_session('moe','moe')
+ self.fail("Should not be able to create fourth connection for user ,pe")
+ except Exception, e:
+ result = None
+
+ # User not named in quotas is denied
+ try:
+ sessions1 = self.get_session('shemp','shemp')
+ self.fail("Should not be able to create a connection for user shemp")
+ except Exception, e:
+ result = None
+
+ # Clean up the sessions
+ sessiona1.close()
+ sessiona2.close()
+ sessionb1.close()
+ sessionb2.close()
+ sessionb3.close()
+ sessionm1.close()
+ sessionm2.close()
+ sessionm3.close()
+
+
+ def test_connection_limits_by_ip_address(self):
+ """
+ Test ACL control connection limits by ip address
+ """
# By IP address should be able to connect twice per client address
try:
sessionb1 = self.get_session_by_port('alice','alice', self.port_i())
@@ -1703,6 +2315,8 @@ class ACLTests(TestBase010):
except Exception, e:
result = None
+ sessionb1.close()
+ sessionb2.close()
#=====================================
# User name substitution
@@ -2243,6 +2857,62 @@ class ACLTests(TestBase010):
self.LookupPublish("joe@QPID", "QPID-work", "QPID", "allow")
self.LookupPublish("joe@QPID", "QPID-work2", "QPID", "allow")
+ #=====================================
+ # Queue per-user quota
+ #=====================================
+
+ def test_queue_per_user_quota(self):
+ """
+ Test ACL queue counting limits.
+ port_q has a limit of 2
+ """
+ # bob should be able to create two queues
+ session = self.get_session_by_port('bob','bob', self.port_q())
+
+ try:
+ session.queue_declare(queue="queue1")
+ session.queue_declare(queue="queue2")
+ except qpid.session.SessionException, e:
+ self.fail("Error during queue create request");
+
+ # third queue should fail
+ try:
+ session.queue_declare(queue="queue3")
+ self.fail("Should not be able to create third queue")
+ except Exception, e:
+ result = None
+ session = self.get_session_by_port('bob','bob', self.port_q())
+
+ # alice should be able to create two queues
+ session2 = self.get_session_by_port('alice','alice', self.port_q())
+
+ try:
+ session2.queue_declare(queue="queuea1")
+ session2.queue_declare(queue="queuea2")
+ except qpid.session.SessionException, e:
+ self.fail("Error during queue create request");
+
+ # third queue should fail
+ try:
+ session2.queue_declare(queue="queuea3")
+ self.fail("Should not be able to create third queue")
+ except Exception, e:
+ result = None
+ session2 = self.get_session_by_port('alice','alice', self.port_q())
+
+ # bob should be able to delete a queue and create another
+ try:
+ session.queue_delete(queue="queue1")
+ session.queue_declare(queue="queue3")
+ except qpid.session.SessionException, e:
+ self.fail("Error during queue create request");
+
+ # alice should be able to delete a queue and create another
+ try:
+ session2.queue_delete(queue="queuea1")
+ session2.queue_declare(queue="queuea3")
+ except qpid.session.SessionException, e:
+ self.fail("Error during queue create request");
class BrokerAdmin:
def __init__(self, broker, username=None, password=None):
diff --git a/cpp/src/tests/benchmark b/cpp/src/tests/benchmark
deleted file mode 100755
index c075837847..0000000000
--- a/cpp/src/tests/benchmark
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/bin/sh
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-# A basic "benchmark" to generate performacne samples of throughput
-# and latency against a single cluster member while they are replicating.
-#
-# Must be run in the qpid src/tests build directory.
-#
-
-usage() {
-cat <<EOF
-Usage: $0 [options] -- client hosts --- broker hosts
-Read the script for options.
-EOF
-}
-# Defaults
-TESTDIR=${TESTDIR:-$PWD} # Absolute path to test exes on all hosts.
-SCRIPTDIR=${SCRIPTDIR:-`dirname $0`} # Path to local test scripts directory.
-SAMPLES=10 # Runs of each test.
-COUNT=${COUNT:-10000} # Count for pub/sub tests.
-SIZE=${SIZE:-600} # Size of messages
-ECHO=${ECHO:-1000} # Count for echo test.
-NSUBS=${NSUBS:-4}
-NPUBS=${NPUBS:-4}
-
-collect() { eval $COLLECT=\""\$$COLLECT $*"\"; }
-COLLECT=ARGS
-while test $# -gt 0; do
- case $1 in
- --testdir) TESTDIR=$2 ; shift 2 ;;
- --samples) SAMPLES=$2 ; shift 2 ;;
- --count) COUNT=$2 ; shift 2 ;;
- --echos) ECHO=$2 ; shift 2 ;;
- --size) SIZE=$2 ; shift 2 ;;
- --nsubs) NSUBS=$2 ; shift 2 ;;
- --npubs) NPUBS=$2 ; shift 2 ;;
- --) COLLECT=CLIENTARG; shift ;;
- ---) COLLECT=BROKERARG; shift;;
- *) collect $1; shift ;;
- esac
-done
-
-CLIENTS=${CLIENTARG:-$CLIENTS}
-BROKERS=${BROKERARG:-$BROKERS}
-test -z "$CLIENTS" && { echo "Must specify at least one client host."; exit 1; }
-test -z "$BROKERS" && { echo "Must specify at least one broker host."; exit 1; }
-
-export TESTDIR # For perfdist
-CLIENTS=($CLIENTS) # Convert to array
-BROKERS=($BROKERS)
-trap "rm -f $FILES" EXIT
-
-dosamples() {
- FILE=`mktemp`
- FILES="$FILES $FILE"
- TABS=`echo "$HEADING" | sed s'/[^ ]//g'`
- {
- echo "\"$*\"$TABS"
- echo "$HEADING"
- for (( i=0; i<$SAMPLES; ++i)) ; do echo "`$*`" ; done
- echo
- } | tee $FILE
-}
-
-HEADING="pub sub total Mb"
-dosamples $SCRIPTDIR/perfdist --size $SIZE --count $COUNT --nsubs $NSUBS --npubs $NPUBS -s -- ${CLIENTS[*]} --- ${BROKERS[*]}
-HEADING="pub"
-dosamples ssh -A ${CLIENTS[0]} $TESTDIR/publish --routing-key perftest0 --size $SIZE --count $COUNT -s -b ${BROKERS[0]}
-HEADING="sub"
-dosamples ssh -A ${CLIENTS[0]} $TESTDIR/consume --queue perftest0 -s --count $COUNT -b ${BROKERS[0]}
-HEADING="min max avg"
-dosamples ssh -A ${CLIENTS[0]} $TESTDIR/echotest --count $ECHO -s -b ${BROKERS[0]}
-
-echo
-echo "Tab separated spreadsheet (also saved as benchmark.tab):"
-echo
-
-echo "benchmark -- ${CLIENTS[*]} --- ${BROKERS[*]} " | tee benchmark.tab
-paste $FILES | tee -a benchmark.tab
diff --git a/cpp/src/tests/brokertest.py b/cpp/src/tests/brokertest.py
index aea4460e5a..70c145a51b 100644
--- a/cpp/src/tests/brokertest.py
+++ b/cpp/src/tests/brokertest.py
@@ -17,8 +17,7 @@
# under the License.
#
-# Support library for tests that start multiple brokers, e.g. cluster
-# or federation
+# Support library for tests that start multiple brokers, e.g. HA or federation
import os, signal, string, tempfile, subprocess, socket, threading, time, imp, re
import qpid, traceback, signal
@@ -203,8 +202,10 @@ class Popen(subprocess.Popen):
self.wait()
def kill(self):
- try:
- subprocess.Popen.kill(self)
+ # Set to EXPECT_UNKNOWN, EXPECT_EXIT_FAIL creates a race condition
+ # if the process exits normally concurrent with the call to kill.
+ self.expect = EXPECT_UNKNOWN
+ try: subprocess.Popen.kill(self)
except AttributeError: # No terminate method
try:
os.kill( self.pid , signal.SIGKILL)
@@ -380,8 +381,7 @@ class Broker(Popen):
if not retry(self.log_ready, timeout=timeout):
raise Exception(
"Timed out waiting for broker %s%s"%(self.name, error_line(self.log,5)))
- # Create a connection and a session. For a cluster broker this will
- # return after cluster init has finished.
+ # Create a connection and a session.
try:
c = self.connect(**kwargs)
try: c.session()
@@ -389,54 +389,6 @@ class Broker(Popen):
except Exception,e: raise RethrownException(
"Broker %s not responding: (%s)%s"%(self.name,e,error_line(self.log, 5)))
- def store_state(self):
- f = open(os.path.join(self.datadir, "cluster", "store.status"))
- try: uuids = f.readlines()
- finally: f.close()
- null_uuid="00000000-0000-0000-0000-000000000000\n"
- if len(uuids) < 2: return "unknown" # we looked while the file was being updated.
- if uuids[0] == null_uuid: return "empty"
- if uuids[1] == null_uuid: return "dirty"
- return "clean"
-
-class Cluster:
- """A cluster of brokers in a test."""
- # Client connection options for use in failover tests.
- CONNECTION_OPTIONS = "reconnect:true,reconnect-timeout:10,reconnect-urls-replace:true"
-
- _cluster_count = 0
-
- def __init__(self, test, count=0, args=[], expect=EXPECT_RUNNING, wait=True, show_cmd=False):
- self.test = test
- self._brokers=[]
- self.name = "cluster%d" % Cluster._cluster_count
- Cluster._cluster_count += 1
- # Use unique cluster name
- self.args = copy(args)
- self.args += [ "--cluster-name", "%s-%s:%d" % (self.name, socket.gethostname(), os.getpid()) ]
- self.args += [ "--log-enable=info+", "--log-enable=debug+:cluster"]
- assert BrokerTest.cluster_lib, "Cannot locate cluster plug-in"
- self.args += [ "--load-module", BrokerTest.cluster_lib ]
- self.start_n(count, expect=expect, wait=wait, show_cmd=show_cmd)
-
- def start(self, name=None, expect=EXPECT_RUNNING, wait=True, args=[], port=0, show_cmd=False):
- """Add a broker to the cluster. Returns the index of the new broker."""
- if not name: name="%s-%d" % (self.name, len(self._brokers))
- self._brokers.append(self.test.broker(self.args+args, name, expect, wait, port=port, show_cmd=show_cmd))
- return self._brokers[-1]
-
- def ready(self):
- for b in self: b.ready()
-
- def start_n(self, count, expect=EXPECT_RUNNING, wait=True, args=[], show_cmd=False):
- for i in range(count): self.start(expect=expect, wait=wait, args=args, show_cmd=show_cmd)
-
- # Behave like a list of brokers.
- def __len__(self): return len(self._brokers)
- def __getitem__(self,index): return self._brokers[index]
- def __iter__(self): return self._brokers.__iter__()
-
-
def browse(session, queue, timeout=0, transform=lambda m: m.content):
"""Return a list with the contents of each message on queue."""
r = session.receiver("%s;{mode:browse}"%(queue))
@@ -473,7 +425,6 @@ class BrokerTest(TestCase):
# Environment settings.
qpidd_exec = os.path.abspath(checkenv("QPIDD_EXEC"))
- cluster_lib = os.getenv("CLUSTER_LIB")
ha_lib = os.getenv("HA_LIB")
xml_lib = os.getenv("XML_LIB")
qpid_config_exec = os.getenv("QPID_CONFIG_EXEC")
@@ -525,11 +476,6 @@ class BrokerTest(TestCase):
raise RethrownException("Failed to start broker %s(%s): %s" % (b.name, b.log, e))
return b
- def cluster(self, count=0, args=[], expect=EXPECT_RUNNING, wait=True, show_cmd=False):
- """Create and return a cluster ready for use"""
- cluster = Cluster(self, count, args, expect=expect, wait=wait, show_cmd=show_cmd)
- return cluster
-
def browse(self, *args, **kwargs): browse(*args, **kwargs)
def assert_browse(self, *args, **kwargs): assert_browse(*args, **kwargs)
def assert_browse_retry(self, *args, **kwargs): assert_browse_retry(*args, **kwargs)
@@ -558,14 +504,17 @@ class StoppableThread(Thread):
join(self)
if self.error: raise self.error
+# Options for a client that wants to reconnect automatically.
+RECONNECT_OPTIONS="reconnect:true,reconnect-timeout:10,reconnect-urls-replace:true"
+
class NumberedSender(Thread):
"""
Thread to run a sender client and send numbered messages until stopped.
"""
def __init__(self, broker, max_depth=None, queue="test-queue",
- connection_options=Cluster.CONNECTION_OPTIONS,
- failover_updates=True, url=None):
+ connection_options=RECONNECT_OPTIONS,
+ failover_updates=True, url=None, args=[]):
"""
max_depth: enable flow control, ensure sent - received <= max_depth.
Requires self.notify_received(n) to be called each time messages are received.
@@ -576,7 +525,7 @@ class NumberedSender(Thread):
"--address", "%s;{create:always}"%queue,
"--connection-options", "{%s}"%(connection_options),
"--content-stdin"
- ]
+ ] + args
if failover_updates: cmd += ["--failover-updates"]
self.sender = broker.test.popen(
cmd, expect=EXPECT_RUNNING, stdin=PIPE)
@@ -627,7 +576,7 @@ class NumberedReceiver(Thread):
sequentially numbered messages.
"""
def __init__(self, broker, sender=None, queue="test-queue",
- connection_options=Cluster.CONNECTION_OPTIONS,
+ connection_options=RECONNECT_OPTIONS,
failover_updates=True, url=None):
"""
sender: enable flow control. Call sender.received(n) for each message received.
@@ -676,31 +625,6 @@ class NumberedReceiver(Thread):
join(self)
self.check()
-class ErrorGenerator(StoppableThread):
- """
- Thread that continuously generates errors by trying to consume from
- a non-existent queue. For cluster regression tests, error handling
- caused issues in the past.
- """
-
- def __init__(self, broker):
- StoppableThread.__init__(self)
- self.broker=broker
- broker.test.cleanup_stop(self)
- self.start()
-
- def run(self):
- c = self.broker.connect_old()
- try:
- while not self.stopped:
- try:
- c.session(str(qpid.datatypes.uuid4())).message_subscribe(
- queue="non-existent-queue")
- assert(False)
- except qpid.session.SessionException: pass
- time.sleep(0.01)
- except: pass # Normal if broker is killed.
-
def import_script(path):
"""
Import executable script at path as a module.
diff --git a/cpp/src/tests/cluster_authentication_soak.cpp b/cpp/src/tests/cluster_authentication_soak.cpp
deleted file mode 100644
index a3271701c3..0000000000
--- a/cpp/src/tests/cluster_authentication_soak.cpp
+++ /dev/null
@@ -1,310 +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 <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <signal.h>
-#include <fcntl.h>
-
-#include <sys/wait.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#include <string>
-#include <iostream>
-#include <sstream>
-#include <vector>
-
-#include <boost/assign.hpp>
-
-#include "qpid/framing/Uuid.h"
-
-#include <ForkedBroker.h>
-#include <qpid/client/Connection.h>
-
-#include <sasl/sasl.h>
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-
-
-
-using namespace std;
-using boost::assign::list_of;
-using namespace qpid::framing;
-using namespace qpid::client;
-
-
-namespace qpid {
-namespace tests {
-
-vector<pid_t> brokerPids;
-
-typedef vector<ForkedBroker *> brokerVector;
-
-
-
-
-
-int runSilent = 1;
-int newbiePort = 0;
-
-
-void
-makeClusterName ( string & s ) {
- stringstream ss;
- ss << "authenticationSoakCluster_" << Uuid(true).str();
- s = ss.str();
-}
-
-
-
-void
-startBroker ( brokerVector & brokers , int brokerNumber, string const & clusterName ) {
- stringstream prefix, clusterArg;
- prefix << "soak-" << brokerNumber;
- clusterArg << "--cluster-name=" << clusterName;
-
- std::vector<std::string> argv;
-
- argv.push_back ("../qpidd");
- argv.push_back ("--no-module-dir");
- argv.push_back ("--load-module=../.libs/cluster.so");
- argv.push_back (clusterArg.str());
- argv.push_back ("--cluster-username=zig");
- argv.push_back ("--cluster-password=zig");
- argv.push_back ("--cluster-mechanism=PLAIN");
- argv.push_back ("--sasl-config=./sasl_config");
- argv.push_back ("--auth=yes");
- argv.push_back ("--mgmt-enable=yes");
- argv.push_back ("--log-prefix");
- argv.push_back (prefix.str());
- argv.push_back ("--log-to-file");
- argv.push_back (prefix.str()+".log");
- argv.push_back ("TMP_DATA_DIR");
-
- ForkedBroker * newbie = new ForkedBroker (argv);
- newbiePort = newbie->getPort();
- brokers.push_back ( newbie );
-}
-
-
-
-
-bool
-runPerftest ( bool hangTest ) {
- stringstream portSs;
- portSs << newbiePort;
- string portStr = portSs.str();
- char const * path = "./qpid-perftest";
-
- vector<char const *> argv;
- argv.push_back ( "./qpid-perftest" );
- argv.push_back ( "-p" );
- argv.push_back ( portStr.c_str() );
- argv.push_back ( "--username" );
- argv.push_back ( "zig" );
- argv.push_back ( "--password" );
- argv.push_back ( "zig" );
- argv.push_back ( "--mechanism" );
- argv.push_back ( "DIGEST-MD5" );
- argv.push_back ( "--count" );
- argv.push_back ( "20000" );
- argv.push_back ( 0 );
-
- pid_t pid = fork();
-
- if ( ! pid ) {
- int i=open("/dev/null",O_RDWR);
- dup2 ( i, fileno(stdout) );
- dup2 ( i, fileno(stderr) );
-
- execv ( path, const_cast<char * const *>(&argv[0]) );
- // The exec failed: we are still in parent process.
- perror ( "error running qpid-perftest: " );
- return false;
- }
- else {
- if ( hangTest ) {
- if ( ! runSilent )
- cerr << "Pausing perftest " << pid << endl;
- kill ( pid, 19 );
- }
-
- struct timeval startTime,
- currentTime,
- duration;
-
- gettimeofday ( & startTime, 0 );
-
- while ( 1 ) {
- sleep ( 2 );
- int status;
- int returned_pid = waitpid ( pid, &status, WNOHANG );
- if ( returned_pid == pid ) {
- int exit_status = WEXITSTATUS(status);
- if ( exit_status ) {
- cerr << "qpid-perftest failed. exit_status was: " << exit_status << endl;
- return false;
- }
- else {
- return true; // qpid-perftest succeeded.
- }
- }
- else { // qpid-perftest has not yet completed.
- gettimeofday ( & currentTime, 0 );
- timersub ( & currentTime, & startTime, & duration );
- if ( duration.tv_sec > 60 ) {
- kill ( pid, 9 );
- cerr << "qpid-perftest pid " << pid << " hanging: killed.\n";
- return false;
- }
- }
- }
-
- }
-}
-
-
-
-bool
-allBrokersAreAlive ( brokerVector & brokers ) {
- for ( unsigned int i = 0; i < brokers.size(); ++ i )
- if ( ! brokers[i]->isRunning() )
- return false;
-
- return true;
-}
-
-
-
-
-
-void
-killAllBrokers ( brokerVector & brokers ) {
- for ( unsigned int i = 0; i < brokers.size(); ++ i ) {
- brokers[i]->kill ( 9 );
- }
-}
-
-
-
-
-void
-killOneBroker ( brokerVector & brokers ) {
- int doomedBroker = getpid() % brokers.size();
- cout << "Killing broker " << brokers[doomedBroker]->getPID() << endl;
- brokers[doomedBroker]->kill ( 9 );
- sleep ( 2 );
-}
-
-
-
-
-}} // namespace qpid::tests
-
-using namespace qpid::tests;
-
-
-
-/*
- * Please note that this test has self-test capability.
- * It is intended to detect
- * 1. perftest hangs.
- * 2. broker deaths
- * Both of these condtions can be forced when running manually
- * to ensure that the test really does detect them.
- * See command-line arguments 3 and 4.
- */
-int
-main ( int argc, char ** argv )
-{
- // I need the SASL_PATH_TYPE_CONFIG feature, which did not appear until SASL 2.1.22
-#if (SASL_VERSION_FULL < ((2<<16)|(1<<8)|22))
- cout << "Skipping SASL test, SASL version too low." << endl;
- return 0;
-#endif
-
- int n_iterations = argc > 1 ? atoi(argv[1]) : 1;
- runSilent = argc > 2 ? atoi(argv[2]) : 1; // default to silent
- int killBroker = argc > 3 ? atoi(argv[3]) : 0; // Force the kill of one broker.
- int hangTest = argc > 4 ? atoi(argv[4]) : 0; // Force the first perftest to hang.
- int n_brokers = 3;
- brokerVector brokers;
-
- srand ( getpid() );
- string clusterName;
- makeClusterName ( clusterName );
- for ( int i = 0; i < n_brokers; ++ i ) {
- startBroker ( brokers, i, clusterName );
- }
-
- sleep ( 3 );
-
- /* Run all qpid-perftest iterations, and only then check for brokers
- * still being up. If you just want a quick check for the failure
- * mode in which a single iteration would kill all brokers except
- * the client-connected one, just run it with the iterations arg
- * set to 1.
- */
- for ( int iteration = 0; iteration < n_iterations; ++ iteration ) {
- if ( ! runPerftest ( hangTest ) ) {
- if ( ! runSilent )
- cerr << "qpid-perftest " << iteration << " failed.\n";
- return 1;
- }
- if ( ! ( iteration % 10 ) ) {
- if ( ! runSilent )
- cerr << "qpid-perftest " << iteration << " complete. -------------- \n";
- }
- }
- if ( ! runSilent )
- cerr << "\nqpid-perftest " << n_iterations << " iterations complete. -------------- \n\n";
-
- /* If the command-line tells us to kill a broker, do
- * it now. Use this option to prove that this test
- * really can detect broker-deaths.
- */
- if ( killBroker ) {
- killOneBroker ( brokers );
- }
-
- if ( ! allBrokersAreAlive ( brokers ) ) {
- if ( ! runSilent )
- cerr << "not all brokers are alive.\n";
- killAllBrokers ( brokers );
- return 2;
- }
-
- killAllBrokers ( brokers );
- if ( ! runSilent )
- cout << "success.\n";
-
- return 0;
-}
-
-
-
diff --git a/cpp/src/tests/cluster_failover b/cpp/src/tests/cluster_failover
deleted file mode 100755
index 43170c731a..0000000000
--- a/cpp/src/tests/cluster_failover
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/sh
-# A simple manual failover test, sends a stream of numbered messages.
-# You can kill the connected broker and verify that the clients reconnect
-# and no messages are lost.
-
-URL=$1
-test -n "$URL" || { echo Usage: $0 URL ; exit 1; }
-SEND=$(mktemp /tmp/send.XXXXXXXXXX)
-RECV=$(mktemp /tmp/recv.XXXXXXXXXX)
-echo $SEND $RECV
-
-seq 1000000 > $SEND
-
-qpid-send -a 'cluster_failover;{create:always}' -b $URL --connection-options "{reconnect:true}" --send-rate 10 --content-stdin < $SEND &
-
-while msg=$(qpid-receive -m1 -f -a 'cluster_failover;{create:always}' -b $URL --connection-options "{reconnect:true,heartbeat:1}"); do
- echo -n $msg; date
-done
-wait
diff --git a/cpp/src/tests/cluster_python_tests b/cpp/src/tests/cluster_python_tests
deleted file mode 100755
index 25c7889246..0000000000
--- a/cpp/src/tests/cluster_python_tests
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Skip if cluster services not running.
-. cpg_check.sh
-cpg_enabled || exit 0
-
-FAILING=`dirname $0`/cluster_python_tests_failing.txt
-source `dirname $0`/python_tests
-
diff --git a/cpp/src/tests/cluster_python_tests_failing.txt b/cpp/src/tests/cluster_python_tests_failing.txt
deleted file mode 100644
index f8639d7b59..0000000000
--- a/cpp/src/tests/cluster_python_tests_failing.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-qpid_tests.broker_0_10.management.ManagementTest.test_purge_queue
-qpid_tests.broker_0_10.management.ManagementTest.test_connection_close
-qpid_tests.broker_0_10.message.MessageTests.test_ttl
-qpid_tests.broker_0_10.management.ManagementTest.test_broker_connectivity_oldAPI
diff --git a/cpp/src/tests/cluster_read_credit b/cpp/src/tests/cluster_read_credit
deleted file mode 100755
index 552ffee53b..0000000000
--- a/cpp/src/tests/cluster_read_credit
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Regression test for http://issues.apache.org/jira/browse/QPID-2086
-
-srcdir=`dirname $0`
-source cpg_check.sh
-cpg_enabled || exit 0
-
-$srcdir/start_cluster 1 --cluster-read-max=2 || exit 1
-trap $srcdir/stop_cluster EXIT
-seq 1 10000 | ./sender --port `cat cluster.ports` --routing-key no-such-queue
diff --git a/cpp/src/tests/cluster_test.cpp b/cpp/src/tests/cluster_test.cpp
deleted file mode 100644
index f2ccd0ba84..0000000000
--- a/cpp/src/tests/cluster_test.cpp
+++ /dev/null
@@ -1,1231 +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 "test_tools.h"
-#include "unit_test.h"
-#include "ForkedBroker.h"
-#include "BrokerFixture.h"
-#include "ClusterFixture.h"
-
-#include "qpid/client/Connection.h"
-#include "qpid/client/ConnectionSettings.h"
-#include "qpid/client/ConnectionAccess.h"
-#include "qpid/client/Session.h"
-#include "qpid/client/FailoverListener.h"
-#include "qpid/client/FailoverManager.h"
-#include "qpid/client/QueueOptions.h"
-#include "qpid/cluster/Cluster.h"
-#include "qpid/cluster/Cpg.h"
-#include "qpid/cluster/UpdateClient.h"
-#include "qpid/framing/AMQBody.h"
-#include "qpid/framing/Uuid.h"
-#include "qpid/framing/reply_exceptions.h"
-#include "qpid/framing/enum.h"
-#include "qpid/framing/MessageTransferBody.h"
-#include "qpid/log/Logger.h"
-#include "qpid/sys/Monitor.h"
-#include "qpid/sys/Thread.h"
-
-#include <boost/bind.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/assign.hpp>
-
-#include <string>
-#include <iostream>
-#include <fstream>
-#include <iterator>
-#include <vector>
-#include <set>
-#include <algorithm>
-#include <iterator>
-
-
-using namespace std;
-using namespace qpid;
-using namespace qpid::cluster;
-using namespace qpid::framing;
-using namespace qpid::client;
-using namespace boost::assign;
-using broker::Broker;
-using boost::shared_ptr;
-
-namespace qpid {
-namespace tests {
-
-QPID_AUTO_TEST_SUITE(cluster_test)
-
-bool durableFlag = std::getenv("STORE_LIB") != 0;
-
-void prepareArgs(ClusterFixture::Args& args, const bool durableFlag = false) {
- ostringstream clusterLib;
- clusterLib << getLibPath("CLUSTER_LIB");
- args += "--auth", "no", "--no-module-dir", "--load-module", clusterLib.str();
- if (durableFlag)
- args += "--load-module", getLibPath("STORE_LIB"), "TMP_DATA_DIR";
- else
- args += "--no-data-dir";
-}
-
-ClusterFixture::Args prepareArgs(const bool durableFlag = false) {
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- return args;
-}
-
-// Timeout for tests that wait for messages
-const sys::Duration TIMEOUT=2*sys::TIME_SEC;
-
-
-ostream& operator<<(ostream& o, const cpg_name* n) {
- return o << Cpg::str(*n);
-}
-
-ostream& operator<<(ostream& o, const cpg_address& a) {
- return o << "(" << a.nodeid <<","<<a.pid<<","<<a.reason<<")";
-}
-
-template <class T>
-ostream& operator<<(ostream& o, const pair<T*, int>& array) {
- o << "{ ";
- ostream_iterator<cpg_address> i(o, " ");
- copy(array.first, array.first+array.second, i);
- o << "}";
- return o;
-}
-
-template <class C> set<int> makeSet(const C& c) {
- set<int> s;
- copy(c.begin(), c.end(), inserter(s, s.begin()));
- return s;
-}
-
-class Sender {
- public:
- Sender(boost::shared_ptr<ConnectionImpl> ci, uint16_t ch) : connection(ci), channel(ch) {}
- void send(const AMQBody& body, bool firstSeg, bool lastSeg, bool firstFrame, bool lastFrame) {
- AMQFrame f(body);
- f.setChannel(channel);
- f.setFirstSegment(firstSeg);
- f.setLastSegment(lastSeg);
- f.setFirstFrame(firstFrame);
- f.setLastFrame(lastFrame);
- connection->expand(f.encodedSize(), false);
- connection->handle(f);
- }
-
- private:
- boost::shared_ptr<ConnectionImpl> connection;
- uint16_t channel;
-};
-
-int64_t getMsgSequence(const Message& m) {
- return m.getMessageProperties().getApplicationHeaders().getAsInt64("qpid.msg_sequence");
-}
-
-Message ttlMessage(const string& data, const string& key, uint64_t ttl, bool durable = false) {
- Message m(data, key);
- m.getDeliveryProperties().setTtl(ttl);
- if (durable) m.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
- return m;
-}
-
-Message makeMessage(const string& data, const string& key, bool durable = false) {
- Message m(data, key);
- if (durable) m.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
- return m;
-}
-
-vector<string> browse(Client& c, const string& q, int n) {
- SubscriptionSettings browseSettings(
- FlowControl::messageCredit(n),
- ACCEPT_MODE_NONE,
- ACQUIRE_MODE_NOT_ACQUIRED,
- 0 // No auto-ack.
- );
- LocalQueue lq;
- c.subs.subscribe(lq, q, browseSettings);
- c.session.messageFlush(q);
- vector<string> result;
- for (int i = 0; i < n; ++i) {
- Message m;
- if (!lq.get(m, TIMEOUT))
- break;
- result.push_back(m.getData());
- }
- c.subs.getSubscription(q).cancel();
- return result;
-}
-
-ConnectionSettings aclSettings(int port, const std::string& id) {
- ConnectionSettings settings;
- settings.port = port;
- settings.mechanism = "PLAIN";
- settings.username = id;
- settings.password = id;
- return settings;
-}
-
-// An illegal frame body
-struct PoisonPill : public AMQBody {
- virtual uint8_t type() const { return 0xFF; }
- virtual void encode(Buffer& ) const {}
- virtual void decode(Buffer& , uint32_t=0) {}
- virtual uint32_t encodedSize() const { return 0; }
-
- virtual void print(std::ostream&) const {};
- virtual void accept(AMQBodyConstVisitor&) const {};
-
- virtual AMQMethodBody* getMethod() { return 0; }
- virtual const AMQMethodBody* getMethod() const { return 0; }
-
- /** Match if same type and same class/method ID for methods */
- static bool match(const AMQBody& , const AMQBody& ) { return false; }
- virtual boost::intrusive_ptr<AMQBody> clone() const { return new PoisonPill; }
-};
-
-QPID_AUTO_TEST_CASE(testBadClientData) {
- // Ensure that bad data on a client connection closes the
- // connection but does not stop the broker.
- ClusterFixture::Args args;
- prepareArgs(args, false);
- args += "--log-enable=critical"; // Supress expected errors
- ClusterFixture cluster(2, args, -1);
- Client c0(cluster[0]);
- Client c1(cluster[1]);
- boost::shared_ptr<client::ConnectionImpl> ci =
- client::ConnectionAccess::getImpl(c0.connection);
- AMQFrame poison(boost::intrusive_ptr<AMQBody>(new PoisonPill));
- ci->expand(poison.encodedSize(), false);
- ci->handle(poison);
- {
- ScopedSuppressLogging sl;
- BOOST_CHECK_THROW(c0.session.queueQuery("q0"), Exception);
- }
- Client c00(cluster[0]);
- BOOST_CHECK_EQUAL(c00.session.queueQuery("q00").getQueue(), "");
- BOOST_CHECK_EQUAL(c1.session.queueQuery("q1").getQueue(), "");
-}
-
-QPID_AUTO_TEST_CASE(testAcl) {
- ofstream policyFile("cluster_test.acl");
- policyFile << "acl allow foo@QPID create queue name=foo" << endl
- << "acl allow foo@QPID create queue name=foo2" << endl
- << "acl deny foo@QPID create queue name=bar" << endl
- << "acl allow all all" << endl;
- policyFile.close();
- char cwd[1024];
- BOOST_CHECK(::getcwd(cwd, sizeof(cwd)));
- ostringstream aclLib;
- aclLib << getLibPath("ACL_LIB");
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- args += "--log-enable=critical"; // Supress expected errors
- args += "--acl-file", string(cwd) + "/cluster_test.acl",
- "--cluster-mechanism", "PLAIN",
- "--cluster-username", "cluster",
- "--cluster-password", "cluster",
- "--load-module", aclLib.str();
- ClusterFixture cluster(2, args, -1);
-
- Client c0(aclSettings(cluster[0], "c0"), "c0");
- Client c1(aclSettings(cluster[1], "c1"), "c1");
- Client foo(aclSettings(cluster[1], "foo"), "foo");
-
- foo.session.queueDeclare("foo", arg::durable=durableFlag);
- BOOST_CHECK_EQUAL(c0.session.queueQuery("foo").getQueue(), "foo");
-
- {
- ScopedSuppressLogging sl;
- BOOST_CHECK_THROW(foo.session.queueDeclare("bar", arg::durable=durableFlag), framing::UnauthorizedAccessException);
- }
- BOOST_CHECK(c0.session.queueQuery("bar").getQueue().empty());
- BOOST_CHECK(c1.session.queueQuery("bar").getQueue().empty());
-
- cluster.add();
- Client c2(aclSettings(cluster[2], "c2"), "c2");
- {
- ScopedSuppressLogging sl;
- BOOST_CHECK_THROW(foo.session.queueDeclare("bar", arg::durable=durableFlag), framing::UnauthorizedAccessException);
- }
- BOOST_CHECK(c2.session.queueQuery("bar").getQueue().empty());
-}
-
-QPID_AUTO_TEST_CASE(testMessageTimeToLive) {
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(2, args, -1);
- Client c0(cluster[0], "c0");
- Client c1(cluster[1], "c1");
- c0.session.queueDeclare("p", arg::durable=durableFlag);
- c0.session.queueDeclare("q", arg::durable=durableFlag);
- c0.session.messageTransfer(arg::content=ttlMessage("a", "q", 200, durableFlag));
- c0.session.messageTransfer(arg::content=makeMessage("b", "q", durableFlag));
- c0.session.messageTransfer(arg::content=ttlMessage("x", "p", 100000, durableFlag));
- c0.session.messageTransfer(arg::content=makeMessage("y", "p", durableFlag));
- cluster.add();
- Client c2(cluster[1], "c2");
-
- BOOST_CHECK_EQUAL(browse(c0, "p", 1), list_of<string>("x"));
- BOOST_CHECK_EQUAL(browse(c1, "p", 1), list_of<string>("x"));
- BOOST_CHECK_EQUAL(browse(c2, "p", 1), list_of<string>("x"));
-
- sys::usleep(200*1000);
- BOOST_CHECK_EQUAL(browse(c0, "q", 1), list_of<string>("b"));
- BOOST_CHECK_EQUAL(browse(c1, "q", 1), list_of<string>("b"));
- BOOST_CHECK_EQUAL(browse(c2, "q", 1), list_of<string>("b"));
-}
-
-QPID_AUTO_TEST_CASE(testSequenceOptions) {
- // Make sure the exchange qpid.msg_sequence property is properly replicated.
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c0(cluster[0], "c0");
- FieldTable ftargs;
- ftargs.setInt("qpid.msg_sequence", 1);
- c0.session.queueDeclare(arg::queue="q", arg::durable=durableFlag);
- c0.session.exchangeDeclare(arg::exchange="ex", arg::type="direct", arg::arguments=ftargs);
- c0.session.exchangeBind(arg::exchange="ex", arg::queue="q", arg::bindingKey="k");
- c0.session.messageTransfer(arg::content=makeMessage("1", "k", durableFlag), arg::destination="ex");
- c0.session.messageTransfer(arg::content=makeMessage("2", "k", durableFlag), arg::destination="ex");
- BOOST_CHECK_EQUAL(1, getMsgSequence(c0.subs.get("q", TIMEOUT)));
- BOOST_CHECK_EQUAL(2, getMsgSequence(c0.subs.get("q", TIMEOUT)));
-
- cluster.add();
- Client c1(cluster[1]);
- c1.session.messageTransfer(arg::content=makeMessage("3", "k", durableFlag), arg::destination="ex");
- BOOST_CHECK_EQUAL(3, getMsgSequence(c1.subs.get("q", TIMEOUT)));
-}
-
-QPID_AUTO_TEST_CASE(testTxTransaction) {
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c0(cluster[0], "c0");
- c0.session.queueDeclare(arg::queue="q", arg::durable=durableFlag);
- c0.session.messageTransfer(arg::content=makeMessage("A", "q", durableFlag));
- c0.session.messageTransfer(arg::content=makeMessage("B", "q", durableFlag));
-
- // Start a transaction that will commit.
- Session commitSession = c0.connection.newSession("commit");
- SubscriptionManager commitSubs(commitSession);
- commitSession.txSelect();
- commitSession.messageTransfer(arg::content=makeMessage("a", "q", durableFlag));
- commitSession.messageTransfer(arg::content=makeMessage("b", "q", durableFlag));
- BOOST_CHECK_EQUAL(commitSubs.get("q", TIMEOUT).getData(), "A");
-
- // Start a transaction that will roll back.
- Session rollbackSession = c0.connection.newSession("rollback");
- SubscriptionManager rollbackSubs(rollbackSession);
- rollbackSession.txSelect();
- rollbackSession.messageTransfer(arg::content=makeMessage("1", "q", durableFlag));
- Message rollbackMessage = rollbackSubs.get("q", TIMEOUT);
- BOOST_CHECK_EQUAL(rollbackMessage.getData(), "B");
-
- BOOST_CHECK_EQUAL(c0.session.queueQuery("q").getMessageCount(), 0u);
- // Add new member mid transaction.
- cluster.add();
- Client c1(cluster[1], "c1");
-
- // More transactional work
- BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u);
- rollbackSession.messageTransfer(arg::content=makeMessage("2", "q", durableFlag));
- commitSession.messageTransfer(arg::content=makeMessage("c", "q", durableFlag));
- rollbackSession.messageTransfer(arg::content=makeMessage("3", "q", durableFlag));
-
- BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u);
-
- // Commit/roll back.
- commitSession.txCommit();
- rollbackSession.txRollback();
- rollbackSession.messageRelease(rollbackMessage.getId());
-
- // Verify queue status: just the comitted messages and dequeues should remain.
- BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 4u);
- BOOST_CHECK_EQUAL(c1.subs.get("q", TIMEOUT).getData(), "B");
- BOOST_CHECK_EQUAL(c1.subs.get("q", TIMEOUT).getData(), "a");
- BOOST_CHECK_EQUAL(c1.subs.get("q", TIMEOUT).getData(), "b");
- BOOST_CHECK_EQUAL(c1.subs.get("q", TIMEOUT).getData(), "c");
-
- commitSession.close();
- rollbackSession.close();
-}
-
-QPID_AUTO_TEST_CASE(testUnacked) {
- // Verify replication of unacknowledged messages.
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c0(cluster[0], "c0");
-
- Message m;
-
- // Create unacked message: acquired but not accepted.
- SubscriptionSettings manualAccept(FlowControl::unlimited(), ACCEPT_MODE_EXPLICIT, ACQUIRE_MODE_PRE_ACQUIRED, 0);
- c0.session.queueDeclare("q1", arg::durable=durableFlag);
- c0.session.messageTransfer(arg::content=makeMessage("11","q1", durableFlag));
- LocalQueue q1;
- c0.subs.subscribe(q1, "q1", manualAccept);
- BOOST_CHECK_EQUAL(q1.get(TIMEOUT).getData(), "11"); // Acquired but not accepted
- BOOST_CHECK_EQUAL(c0.session.queueQuery("q1").getMessageCount(), 0u); // Gone from queue
-
- // Create unacked message: not acquired, accepted or completeed.
- SubscriptionSettings manualAcquire(FlowControl::unlimited(), ACCEPT_MODE_EXPLICIT, ACQUIRE_MODE_NOT_ACQUIRED, 0);
- c0.session.queueDeclare("q2", arg::durable=durableFlag);
- c0.session.messageTransfer(arg::content=makeMessage("21","q2", durableFlag));
- c0.session.messageTransfer(arg::content=makeMessage("22","q2", durableFlag));
- LocalQueue q2;
- c0.subs.subscribe(q2, "q2", manualAcquire);
- m = q2.get(TIMEOUT); // Not acquired or accepted, still on queue
- BOOST_CHECK_EQUAL(m.getData(), "21");
- BOOST_CHECK_EQUAL(c0.session.queueQuery("q2").getMessageCount(), 2u); // Not removed
- c0.subs.getSubscription("q2").acquire(m); // Acquire manually
- BOOST_CHECK_EQUAL(c0.session.queueQuery("q2").getMessageCount(), 1u); // Removed
- BOOST_CHECK_EQUAL(q2.get(TIMEOUT).getData(), "22"); // Not acquired or accepted, still on queue
- BOOST_CHECK_EQUAL(c0.session.queueQuery("q2").getMessageCount(), 1u); // 1 not acquired.
-
- // Create empty credit record: acquire and accept but don't complete.
- SubscriptionSettings manualComplete(FlowControl::messageWindow(1), ACCEPT_MODE_EXPLICIT, ACQUIRE_MODE_PRE_ACQUIRED, 1, MANUAL_COMPLETION);
- c0.session.queueDeclare("q3", arg::durable=durableFlag);
- c0.session.messageTransfer(arg::content=makeMessage("31", "q3", durableFlag));
- c0.session.messageTransfer(arg::content=makeMessage("32", "q3", durableFlag));
- LocalQueue q3;
- c0.subs.subscribe(q3, "q3", manualComplete);
- Message m31=q3.get(TIMEOUT);
- BOOST_CHECK_EQUAL(m31.getData(), "31"); // Automatically acquired & accepted but not completed.
- BOOST_CHECK_EQUAL(c0.session.queueQuery("q3").getMessageCount(), 1u);
-
- // Add new member while there are unacked messages.
- cluster.add();
- Client c1(cluster[1], "c1");
-
- // Check queue counts
- BOOST_CHECK_EQUAL(c1.session.queueQuery("q1").getMessageCount(), 0u);
- BOOST_CHECK_EQUAL(c1.session.queueQuery("q2").getMessageCount(), 1u);
- BOOST_CHECK_EQUAL(c1.session.queueQuery("q3").getMessageCount(), 1u);
-
- // Complete the empty credit message, should unblock the message behind it.
- BOOST_CHECK_THROW(q3.get(0), Exception);
- c0.session.markCompleted(SequenceSet(m31.getId()), true);
- BOOST_CHECK_EQUAL(q3.get(TIMEOUT).getData(), "32");
- BOOST_CHECK_EQUAL(c0.session.queueQuery("q3").getMessageCount(), 0u);
- BOOST_CHECK_EQUAL(c1.session.queueQuery("q3").getMessageCount(), 0u);
-
- // Close the original session - unacked messages should be requeued.
- c0.session.close();
- BOOST_CHECK_EQUAL(c1.session.queueQuery("q1").getMessageCount(), 1u);
- BOOST_CHECK_EQUAL(c1.session.queueQuery("q2").getMessageCount(), 2u);
-
- BOOST_CHECK_EQUAL(c1.subs.get("q1", TIMEOUT).getData(), "11");
- BOOST_CHECK_EQUAL(c1.subs.get("q2", TIMEOUT).getData(), "21");
- BOOST_CHECK_EQUAL(c1.subs.get("q2", TIMEOUT).getData(), "22");
-}
-
-// FIXME aconway 2009-06-17: test for unimplemented feature, enable when implemented.
-void testUpdateTxState() {
- // Verify that we update transaction state correctly to new members.
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c0(cluster[0], "c0");
-
- // Do work in a transaction.
- c0.session.txSelect();
- c0.session.queueDeclare("q", arg::durable=durableFlag);
- c0.session.messageTransfer(arg::content=makeMessage("1","q", durableFlag));
- c0.session.messageTransfer(arg::content=makeMessage("2","q", durableFlag));
- Message m;
- BOOST_CHECK(c0.subs.get(m, "q", TIMEOUT));
- BOOST_CHECK_EQUAL(m.getData(), "1");
-
- // New member, TX not comitted, c1 should see nothing.
- cluster.add();
- Client c1(cluster[1], "c1");
- BOOST_CHECK_EQUAL(c1.session.queueQuery(arg::queue="q").getMessageCount(), 0u);
-
- // After commit c1 shoudl see results of tx.
- c0.session.txCommit();
- BOOST_CHECK_EQUAL(c1.session.queueQuery(arg::queue="q").getMessageCount(), 1u);
- BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT));
- BOOST_CHECK_EQUAL(m.getData(), "2");
-
- // Another transaction with both members active.
- c0.session.messageTransfer(arg::content=makeMessage("3","q", durableFlag));
- BOOST_CHECK_EQUAL(c1.session.queueQuery(arg::queue="q").getMessageCount(), 0u);
- c0.session.txCommit();
- BOOST_CHECK_EQUAL(c1.session.queueQuery(arg::queue="q").getMessageCount(), 1u);
- BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT));
- BOOST_CHECK_EQUAL(m.getData(), "3");
-}
-
-QPID_AUTO_TEST_CASE(testUpdateMessageBuilder) {
- // Verify that we update a partially recieved message to a new member.
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c0(cluster[0], "c0");
- c0.session.queueDeclare("q", arg::durable=durableFlag);
- Sender sender(ConnectionAccess::getImpl(c0.connection), c0.session.getChannel());
-
- // Send first 2 frames of message.
- MessageTransferBody transfer(
- ProtocolVersion(), string(), // default exchange.
- framing::message::ACCEPT_MODE_NONE,
- framing::message::ACQUIRE_MODE_PRE_ACQUIRED);
- sender.send(transfer, true, false, true, true);
- AMQHeaderBody header;
- header.get<DeliveryProperties>(true)->setRoutingKey("q");
- if (durableFlag)
- header.get<DeliveryProperties>(true)->setDeliveryMode(DELIVERY_MODE_PERSISTENT);
- else
- header.get<DeliveryProperties>(true)->setDeliveryMode(DELIVERY_MODE_NON_PERSISTENT);
- sender.send(header, false, false, true, true);
-
- // No reliable way to ensure the partial message has arrived
- // before we start the new broker, so we sleep.
- sys::usleep(2500);
- cluster.add();
-
- // Send final 2 frames of message.
- sender.send(AMQContentBody("ab"), false, true, true, false);
- sender.send(AMQContentBody("cd"), false, true, false, true);
-
- // Verify message is enqued correctly on second member.
- Message m;
- Client c1(cluster[1], "c1");
- BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT));
- BOOST_CHECK_EQUAL(m.getData(), "abcd");
- BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection, 2).size());
-}
-
-QPID_AUTO_TEST_CASE(testConnectionKnownHosts) {
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c0(cluster[0], "c0");
- set<int> kb0 = knownBrokerPorts(c0.connection, 1);
- BOOST_CHECK_EQUAL(kb0.size(), 1u);
- BOOST_CHECK_EQUAL(kb0, makeSet(cluster));
-
- cluster.add();
- Client c1(cluster[1], "c1");
- set<int> kb1 = knownBrokerPorts(c1.connection, 2);
- kb0 = knownBrokerPorts(c0.connection, 2);
- BOOST_CHECK_EQUAL(kb1.size(), 2u);
- BOOST_CHECK_EQUAL(kb1, makeSet(cluster));
- BOOST_CHECK_EQUAL(kb1,kb0);
-
- cluster.add();
- Client c2(cluster[2], "c2");
- set<int> kb2 = knownBrokerPorts(c2.connection, 3);
- kb1 = knownBrokerPorts(c1.connection, 3);
- kb0 = knownBrokerPorts(c0.connection, 3);
- BOOST_CHECK_EQUAL(kb2.size(), 3u);
- BOOST_CHECK_EQUAL(kb2, makeSet(cluster));
- BOOST_CHECK_EQUAL(kb2,kb0);
- BOOST_CHECK_EQUAL(kb2,kb1);
-
- cluster.killWithSilencer(1,c1.connection,9);
- kb0 = knownBrokerPorts(c0.connection, 2);
- kb2 = knownBrokerPorts(c2.connection, 2);
- BOOST_CHECK_EQUAL(kb0.size(), 2u);
- BOOST_CHECK_EQUAL(kb0, kb2);
-}
-
-QPID_AUTO_TEST_CASE(testUpdateConsumers) {
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
-
- Client c0(cluster[0], "c0");
- c0.session.queueDeclare("p", arg::durable=durableFlag);
- c0.session.queueDeclare("q", arg::durable=durableFlag);
- c0.subs.subscribe(c0.lq, "q", FlowControl::zero());
- LocalQueue lp;
- c0.subs.subscribe(lp, "p", FlowControl::messageCredit(1));
- c0.session.sync();
-
- // Start new members
- cluster.add(); // Local
- Client c1(cluster[1], "c1");
- cluster.add();
- Client c2(cluster[2], "c2");
-
- // Transfer messages
- c0.session.messageTransfer(arg::content=makeMessage("aaa", "q", durableFlag));
-
- c0.session.messageTransfer(arg::content=makeMessage("bbb", "p", durableFlag));
- c0.session.messageTransfer(arg::content=makeMessage("ccc", "p", durableFlag));
-
- // Activate the subscription, ensure message removed on all queues.
- c0.subs.setFlowControl("q", FlowControl::unlimited());
- Message m;
- BOOST_CHECK(c0.lq.get(m, TIMEOUT));
- BOOST_CHECK_EQUAL(m.getData(), "aaa");
- BOOST_CHECK_EQUAL(c0.session.queueQuery("q").getMessageCount(), 0u);
- BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u);
- BOOST_CHECK_EQUAL(c2.session.queueQuery("q").getMessageCount(), 0u);
-
- // Check second subscription's flow control: gets first message, not second.
- BOOST_CHECK(lp.get(m, TIMEOUT));
- BOOST_CHECK_EQUAL(m.getData(), "bbb");
- BOOST_CHECK_EQUAL(c0.session.queueQuery("p").getMessageCount(), 1u);
- BOOST_CHECK_EQUAL(c1.session.queueQuery("p").getMessageCount(), 1u);
- BOOST_CHECK_EQUAL(c2.session.queueQuery("p").getMessageCount(), 1u);
-
- BOOST_CHECK(c0.subs.get(m, "p", TIMEOUT));
- BOOST_CHECK_EQUAL(m.getData(), "ccc");
-
- // Kill the subscribing member, ensure further messages are not removed.
- cluster.killWithSilencer(0,c0.connection,9);
- BOOST_REQUIRE_EQUAL(knownBrokerPorts(c1.connection, 2).size(), 2u);
- for (int i = 0; i < 10; ++i) {
- c1.session.messageTransfer(arg::content=makeMessage("xxx", "q", durableFlag));
- BOOST_REQUIRE(c1.subs.get(m, "q", TIMEOUT));
- BOOST_REQUIRE_EQUAL(m.getData(), "xxx");
- }
-}
-
-// Test that message data and delivery properties are updated properly.
-QPID_AUTO_TEST_CASE(testUpdateMessages) {
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c0(cluster[0], "c0");
-
- // Create messages with different delivery properties
- c0.session.queueDeclare("q", arg::durable=durableFlag);
- c0.session.exchangeBind(arg::exchange="amq.fanout", arg::queue="q");
- c0.session.messageTransfer(arg::content=makeMessage("foo","q", durableFlag));
- c0.session.messageTransfer(arg::content=makeMessage("bar","q", durableFlag),
- arg::destination="amq.fanout");
-
- while (c0.session.queueQuery("q").getMessageCount() != 2)
- sys::usleep(1000); // Wait for message to show up on broker 0.
-
- // Add a new broker, it will catch up.
- cluster.add();
-
- // Do some work post-add
- c0.session.queueDeclare("p", arg::durable=durableFlag);
- c0.session.messageTransfer(arg::content=makeMessage("pfoo","p", durableFlag));
-
- // Do some work post-join
- BOOST_REQUIRE_EQUAL(knownBrokerPorts(c0.connection, 2).size(), 2u);
- c0.session.messageTransfer(arg::content=makeMessage("pbar","p", durableFlag));
-
- // Verify new brokers have state.
- Message m;
-
- Client c1(cluster[1], "c1");
-
- BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT));
- BOOST_CHECK_EQUAL(m.getData(), "foo");
- BOOST_CHECK(m.getDeliveryProperties().hasExchange());
- BOOST_CHECK_EQUAL(m.getDeliveryProperties().getExchange(), "");
- BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT));
- BOOST_CHECK_EQUAL(m.getData(), "bar");
- BOOST_CHECK(m.getDeliveryProperties().hasExchange());
- BOOST_CHECK_EQUAL(m.getDeliveryProperties().getExchange(), "amq.fanout");
- BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u);
-
- // Add another broker, don't wait for join - should be stalled till ready.
- cluster.add();
- Client c2(cluster[2], "c2");
- BOOST_CHECK(c2.subs.get(m, "p", TIMEOUT));
- BOOST_CHECK_EQUAL(m.getData(), "pfoo");
- BOOST_CHECK(c2.subs.get(m, "p", TIMEOUT));
- BOOST_CHECK_EQUAL(m.getData(), "pbar");
- BOOST_CHECK_EQUAL(c2.session.queueQuery("p").getMessageCount(), 0u);
-}
-
-QPID_AUTO_TEST_CASE(testWiringReplication) {
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(3, args, -1);
- Client c0(cluster[0]);
- BOOST_CHECK(c0.session.queueQuery("q").getQueue().empty());
- BOOST_CHECK(c0.session.exchangeQuery("ex").getType().empty());
- c0.session.queueDeclare("q", arg::durable=durableFlag);
- c0.session.exchangeDeclare("ex", arg::type="direct");
- c0.session.close();
- c0.connection.close();
- // Verify all brokers get wiring update.
- for (size_t i = 0; i < cluster.size(); ++i) {
- BOOST_MESSAGE("i == "<< i);
- Client c(cluster[i]);
- BOOST_CHECK_EQUAL("q", c.session.queueQuery("q").getQueue());
- BOOST_CHECK_EQUAL("direct", c.session.exchangeQuery("ex").getType());
- }
-}
-
-QPID_AUTO_TEST_CASE(testMessageEnqueue) {
- // Enqueue on one broker, dequeue on another.
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(2, args, -1);
- Client c0(cluster[0]);
- c0.session.queueDeclare("q", arg::durable=durableFlag);
- c0.session.messageTransfer(arg::content=makeMessage("foo", "q", durableFlag));
- c0.session.messageTransfer(arg::content=makeMessage("bar", "q", durableFlag));
- c0.session.close();
- Client c1(cluster[1]);
- Message msg;
- BOOST_CHECK(c1.subs.get(msg, "q", TIMEOUT));
- BOOST_CHECK_EQUAL(string("foo"), msg.getData());
- BOOST_CHECK(c1.subs.get(msg, "q", TIMEOUT));
- BOOST_CHECK_EQUAL(string("bar"), msg.getData());
-}
-
-QPID_AUTO_TEST_CASE(testMessageDequeue) {
- // Enqueue on one broker, dequeue on two others.
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(3, args, -1);
- Client c0(cluster[0], "c0");
- c0.session.queueDeclare("q", arg::durable=durableFlag);
- c0.session.messageTransfer(arg::content=makeMessage("foo", "q", durableFlag));
- c0.session.messageTransfer(arg::content=makeMessage("bar", "q", durableFlag));
-
- Message msg;
-
- // Dequeue on 2 others, ensure correct order.
- Client c1(cluster[1], "c1");
- BOOST_CHECK(c1.subs.get(msg, "q"));
- BOOST_CHECK_EQUAL("foo", msg.getData());
-
- Client c2(cluster[2], "c2");
- BOOST_CHECK(c1.subs.get(msg, "q"));
- BOOST_CHECK_EQUAL("bar", msg.getData());
-
- // Queue should be empty on all cluster members.
- BOOST_CHECK_EQUAL(0u, c0.session.queueQuery("q").getMessageCount());
- BOOST_CHECK_EQUAL(0u, c1.session.queueQuery("q").getMessageCount());
- BOOST_CHECK_EQUAL(0u, c2.session.queueQuery("q").getMessageCount());
-}
-
-QPID_AUTO_TEST_CASE(testDequeueWaitingSubscription) {
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(3, args, -1);
- Client c0(cluster[0]);
- BOOST_REQUIRE_EQUAL(knownBrokerPorts(c0.connection, 3).size(), 3u); // Wait for brokers.
-
- // First start a subscription.
- c0.session.queueDeclare("q", arg::durable=durableFlag);
- c0.subs.subscribe(c0.lq, "q", FlowControl::messageCredit(2));
-
- // Now send messages
- Client c1(cluster[1]);
- c1.session.messageTransfer(arg::content=makeMessage("foo", "q", durableFlag));
- c1.session.messageTransfer(arg::content=makeMessage("bar", "q", durableFlag));
-
- // Check they arrived
- Message m;
- BOOST_CHECK(c0.lq.get(m, TIMEOUT));
- BOOST_CHECK_EQUAL("foo", m.getData());
- BOOST_CHECK(c0.lq.get(m, TIMEOUT));
- BOOST_CHECK_EQUAL("bar", m.getData());
-
- // Queue should be empty on all cluster members.
- Client c2(cluster[2]);
- BOOST_CHECK_EQUAL(0u, c0.session.queueQuery("q").getMessageCount());
- BOOST_CHECK_EQUAL(0u, c1.session.queueQuery("q").getMessageCount());
- BOOST_CHECK_EQUAL(0u, c2.session.queueQuery("q").getMessageCount());
-}
-
-QPID_AUTO_TEST_CASE(queueDurabilityPropagationToNewbie)
-{
- /*
- Start with a single broker.
- Set up two queues: one durable, and one not.
- Add a new broker to the cluster.
- Make sure it has one durable and one non-durable queue.
- */
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c0(cluster[0]);
- c0.session.queueDeclare("durable_queue", arg::durable=true);
- c0.session.queueDeclare("non_durable_queue", arg::durable=false);
- cluster.add();
- Client c1(cluster[1]);
- QueueQueryResult durable_query = c1.session.queueQuery ( "durable_queue" );
- QueueQueryResult non_durable_query = c1.session.queueQuery ( "non_durable_queue" );
- BOOST_CHECK_EQUAL(durable_query.getQueue(), std::string("durable_queue"));
- BOOST_CHECK_EQUAL(non_durable_query.getQueue(), std::string("non_durable_queue"));
-
- BOOST_CHECK_EQUAL ( durable_query.getDurable(), true );
- BOOST_CHECK_EQUAL ( non_durable_query.getDurable(), false );
-}
-
-
-QPID_AUTO_TEST_CASE(testHeartbeatCancelledOnFailover)
-{
-
- struct Sender : FailoverManager::Command
- {
- std::string queue;
- std::string content;
-
- Sender(const std::string& q, const std::string& c) : queue(q), content(c) {}
-
- void execute(AsyncSession& session, bool)
- {
- session.messageTransfer(arg::content=makeMessage(content, queue, durableFlag));
- }
- };
-
- struct Receiver : FailoverManager::Command, MessageListener, qpid::sys::Runnable
- {
- FailoverManager& mgr;
- std::string queue;
- std::string expectedContent;
- qpid::client::Subscription subscription;
- qpid::sys::Monitor lock;
- bool ready, failed;
-
- Receiver(FailoverManager& m, const std::string& q, const std::string& c) : mgr(m), queue(q), expectedContent(c), ready(false), failed(false) {}
-
- void received(Message& message)
- {
- BOOST_CHECK_EQUAL(expectedContent, message.getData());
- subscription.cancel();
- }
-
- void execute(AsyncSession& session, bool)
- {
- session.queueDeclare(arg::queue=queue, arg::durable=durableFlag);
- SubscriptionManager subs(session);
- subscription = subs.subscribe(*this, queue);
- session.sync();
- setReady();
- subs.run();
- //cleanup:
- session.queueDelete(arg::queue=queue);
- }
-
- void run()
- {
- try {
- mgr.execute(*this);
- }
- catch (const std::exception& e) {
- BOOST_MESSAGE("Exception in mgr.execute: " << e.what());
- failed = true;
- }
- }
-
- void waitForReady()
- {
- qpid::sys::Monitor::ScopedLock l(lock);
- while (!ready) {
- lock.wait();
- }
- }
-
- void setReady()
- {
- qpid::sys::Monitor::ScopedLock l(lock);
- ready = true;
- lock.notify();
- }
- };
-
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(2, args, -1);
- ConnectionSettings settings;
- settings.port = cluster[1];
- settings.heartbeat = 1;
- FailoverManager fmgr(settings);
- Sender sender("my-queue", "my-data");
- Receiver receiver(fmgr, "my-queue", "my-data");
- qpid::sys::Thread runner(receiver);
- receiver.waitForReady();
- {
- ScopedSuppressLogging allQuiet; // suppress connection closed messages
- cluster.kill(1);
- //sleep for 2 secs to allow the heartbeat task to fire on the now dead connection:
- ::usleep(2*1000*1000);
- }
- fmgr.execute(sender);
- runner.join();
- BOOST_CHECK(!receiver.failed);
- fmgr.close();
-}
-
-QPID_AUTO_TEST_CASE(testPolicyUpdate) {
- //tests that the policys internal state is accurate on newly
- //joined nodes
- ClusterFixture::Args args;
- args += "--log-enable", "critical";
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c1(cluster[0], "c1");
- {
- ScopedSuppressLogging allQuiet;
- QueueOptions options;
- options.setSizePolicy(REJECT, 0, 2);
- c1.session.queueDeclare("q", arg::arguments=options, arg::durable=durableFlag);
- c1.session.messageTransfer(arg::content=makeMessage("one", "q", durableFlag));
- cluster.add();
- Client c2(cluster[1], "c2");
- c2.session.messageTransfer(arg::content=makeMessage("two", "q", durableFlag));
-
- BOOST_CHECK_THROW(c2.session.messageTransfer(arg::content=makeMessage("three", "q", durableFlag)), framing::ResourceLimitExceededException);
-
- Message received;
- BOOST_CHECK(c1.subs.get(received, "q"));
- BOOST_CHECK_EQUAL(received.getData(), std::string("one"));
- BOOST_CHECK(c1.subs.get(received, "q"));
- BOOST_CHECK_EQUAL(received.getData(), std::string("two"));
- BOOST_CHECK(!c1.subs.get(received, "q"));
- }
-}
-
-QPID_AUTO_TEST_CASE(testExclusiveQueueUpdate) {
- //tests that exclusive queues are accurately replicated on newly
- //joined nodes
- ClusterFixture::Args args;
- args += "--log-enable", "critical";
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c1(cluster[0], "c1");
- {
- ScopedSuppressLogging allQuiet;
- c1.session.queueDeclare("q", arg::exclusive=true, arg::autoDelete=true, arg::alternateExchange="amq.fanout");
- cluster.add();
- Client c2(cluster[1], "c2");
- QueueQueryResult result = c2.session.queueQuery("q");
- BOOST_CHECK_EQUAL(result.getQueue(), std::string("q"));
- BOOST_CHECK(result.getExclusive());
- BOOST_CHECK(result.getAutoDelete());
- BOOST_CHECK(!result.getDurable());
- BOOST_CHECK_EQUAL(result.getAlternateExchange(), std::string("amq.fanout"));
- BOOST_CHECK_THROW(c2.session.queueDeclare(arg::queue="q", arg::exclusive=true, arg::passive=true), framing::ResourceLockedException);
- c1.session.close();
- c1.connection.close();
- c2.session = c2.connection.newSession();
- BOOST_CHECK_THROW(c2.session.queueDeclare(arg::queue="q", arg::passive=true), framing::NotFoundException);
- }
-}
-
-/**
- * Subscribes to specified queue and acquires up to the specified
- * number of message but does not accept or release them. These
- * message are therefore 'locked' by the clients session.
- */
-Subscription lockMessages(Client& client, const std::string& queue, int count)
-{
- LocalQueue q;
- SubscriptionSettings settings(FlowControl::messageCredit(count));
- settings.autoAck = 0;
- Subscription sub = client.subs.subscribe(q, queue, settings);
- client.session.messageFlush(sub.getName());
- return sub;
-}
-
-/**
- * check that the specified queue contains the expected set of
- * messages (matched on content) for all nodes in the cluster
- */
-void checkQueue(ClusterFixture& cluster, const std::string& queue, const std::vector<std::string>& messages)
-{
- for (size_t i = 0; i < cluster.size(); i++) {
- Client client(cluster[i], (boost::format("%1%_%2%") % "c" % (i+1)).str());
- BOOST_CHECK_EQUAL(browse(client, queue, messages.size()), messages);
- client.close();
- }
-}
-
-void send(Client& client, const std::string& queue, int count, int start=1, const std::string& base="m",
- const std::string& lvqKey="")
-{
- for (int i = 0; i < count; i++) {
- Message message = makeMessage((boost::format("%1%_%2%") % base % (i+start)).str(), queue, durableFlag);
- if (!lvqKey.empty()) message.getHeaders().setString(QueueOptions::strLVQMatchProperty, lvqKey);
- client.session.messageTransfer(arg::content=message);
- }
-}
-
-QPID_AUTO_TEST_CASE(testRingQueueUpdate) {
- //tests that ring queues are accurately replicated on newly
- //joined nodes
- ClusterFixture::Args args;
- args += "--log-enable", "critical";
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c1(cluster[0], "c1");
- {
- ScopedSuppressLogging allQuiet;
- QueueOptions options;
- options.setSizePolicy(RING, 0, 5);
- c1.session.queueDeclare("q", arg::arguments=options, arg::durable=durableFlag);
- send(c1, "q", 5);
- lockMessages(c1, "q", 1);
- //add new node
- cluster.add();
- BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection, 2).size());//wait till joined
- //send one more message
- send(c1, "q", 1, 6);
- //release locked message
- c1.close();
- //check state of queue on both nodes
- checkQueue(cluster, "q", list_of<string>("m_2")("m_3")("m_4")("m_5")("m_6"));
- }
-}
-
-QPID_AUTO_TEST_CASE(testRingQueueUpdate2) {
- //tests that ring queues are accurately replicated on newly joined
- //nodes; just like testRingQueueUpdate, but new node joins after
- //the sixth message has been sent.
- ClusterFixture::Args args;
- args += "--log-enable", "critical";
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c1(cluster[0], "c1");
- {
- ScopedSuppressLogging allQuiet;
- QueueOptions options;
- options.setSizePolicy(RING, 0, 5);
- c1.session.queueDeclare("q", arg::arguments=options, arg::durable=durableFlag);
- send(c1, "q", 5);
- lockMessages(c1, "q", 1);
- //send sixth message
- send(c1, "q", 1, 6);
- //add new node
- cluster.add();
- BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection, 2).size());//wait till joined
- //release locked message
- c1.close();
- //check state of queue on both nodes
- checkQueue(cluster, "q", list_of<string>("m_2")("m_3")("m_4")("m_5")("m_6"));
- }
-}
-
-QPID_AUTO_TEST_CASE(testLvqUpdate) {
- //tests that lvqs are accurately replicated on newly joined nodes
- ClusterFixture::Args args;
- args += "--log-enable", "critical";
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c1(cluster[0], "c1");
- {
- ScopedSuppressLogging allQuiet;
- QueueOptions options;
- options.setOrdering(LVQ);
- c1.session.queueDeclare("q", arg::arguments=options, arg::durable=durableFlag);
-
- send(c1, "q", 5, 1, "a", "a");
- send(c1, "q", 2, 1, "b", "b");
- send(c1, "q", 1, 1, "c", "c");
- send(c1, "q", 1, 3, "b", "b");
-
- //add new node
- cluster.add();
- BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection, 2).size());//wait till joined
-
- //check state of queue on both nodes
- checkQueue(cluster, "q", list_of<string>("a_5")("b_3")("c_1"));
- }
-}
-
-
-QPID_AUTO_TEST_CASE(testBrowsedLvqUpdate) {
- //tests that lvqs are accurately replicated on newly joined nodes
- //if the lvq state has been affected by browsers
- ClusterFixture::Args args;
- args += "--log-enable", "critical";
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c1(cluster[0], "c1");
- {
- ScopedSuppressLogging allQuiet;
- QueueOptions options;
- options.setOrdering(LVQ);
- c1.session.queueDeclare("q", arg::arguments=options, arg::durable=durableFlag);
-
- send(c1, "q", 1, 1, "a", "a");
- send(c1, "q", 2, 1, "b", "b");
- send(c1, "q", 1, 1, "c", "c");
- checkQueue(cluster, "q", list_of<string>("a_1")("b_2")("c_1"));
- send(c1, "q", 4, 2, "a", "a");
- send(c1, "q", 1, 3, "b", "b");
-
- //add new node
- cluster.add();
- BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection, 2).size());//wait till joined
-
- //check state of queue on both nodes
- checkQueue(cluster, "q", list_of<string>("a_1")("b_2")("c_1")("a_5")("b_3"));
- }
-}
-
-QPID_AUTO_TEST_CASE(testRelease) {
- //tests that releasing a messages that was unacked when one node
- //joined works correctly
- ClusterFixture::Args args;
- args += "--log-enable", "critical";
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c1(cluster[0], "c1");
- {
- ScopedSuppressLogging allQuiet;
- c1.session.queueDeclare("q", arg::durable=durableFlag);
- for (int i = 0; i < 5; i++) {
- c1.session.messageTransfer(arg::content=makeMessage((boost::format("%1%_%2%") % "m" % (i+1)).str(), "q", durableFlag));
- }
- //receive but don't ack a message
- LocalQueue lq;
- SubscriptionSettings lqSettings(FlowControl::messageCredit(1));
- lqSettings.autoAck = 0;
- Subscription lqSub = c1.subs.subscribe(lq, "q", lqSettings);
- c1.session.messageFlush("q");
- Message received;
- BOOST_CHECK(lq.get(received));
- BOOST_CHECK_EQUAL(received.getData(), std::string("m_1"));
-
- //add new node
- cluster.add();
-
- lqSub.release(lqSub.getUnaccepted());
-
- //check state of queue on both nodes
- vector<string> expected = list_of<string>("m_1")("m_2")("m_3")("m_4")("m_5");
- Client c3(cluster[0], "c3");
- BOOST_CHECK_EQUAL(browse(c3, "q", 5), expected);
- Client c2(cluster[1], "c2");
- BOOST_CHECK_EQUAL(browse(c2, "q", 5), expected);
- }
-}
-
-
-// Browse for 1 message with byte credit, return true if a message was
-// received false if not.
-bool browseByteCredit(Client& c, const string& q, int n, Message& m) {
- SubscriptionSettings browseSettings(
- FlowControl(1, n, false), // 1 message, n bytes credit, no window
- ACCEPT_MODE_NONE,
- ACQUIRE_MODE_NOT_ACQUIRED,
- 0 // No auto-ack.
- );
- LocalQueue lq;
- Subscription s = c.subs.subscribe(lq, q, browseSettings);
- c.session.messageFlush(arg::destination=q, arg::sync=true);
- c.session.sync();
- c.subs.getSubscription(q).cancel();
- return lq.get(m, 0); // No timeout, flush should push message thru.
-}
-
-// Ensure cluster update preserves exact message size, use byte credt as test.
-QPID_AUTO_TEST_CASE(testExactByteCredit) {
- ClusterFixture cluster(1, prepareArgs(), -1);
- Client c0(cluster[0], "c0");
- c0.session.queueDeclare("q");
- c0.session.messageTransfer(arg::content=Message("MyMessage", "q"));
- cluster.add();
-
- int size=36; // Size of message on broker: headers+body
- Client c1(cluster[1], "c1");
- Message m;
-
- // Ensure we get the message with exact credit.
- BOOST_CHECK(browseByteCredit(c0, "q", size, m));
- BOOST_CHECK(browseByteCredit(c1, "q", size, m));
- // and not with one byte less.
- BOOST_CHECK(!browseByteCredit(c0, "q", size-1, m));
- BOOST_CHECK(!browseByteCredit(c1, "q", size-1, m));
-}
-
-// Test that consumer positions are updated correctly.
-// Regression test for https://bugzilla.redhat.com/show_bug.cgi?id=541927
-//
-QPID_AUTO_TEST_CASE(testUpdateConsumerPosition) {
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c0(cluster[0], "c0");
-
- c0.session.queueDeclare("q", arg::durable=durableFlag);
- SubscriptionSettings settings;
- settings.autoAck = 0;
- // Set the acquire mode to 'not-acquired' the consumer moves along the queue
- // but does not acquire (remove) messages.
- settings.acquireMode = ACQUIRE_MODE_NOT_ACQUIRED;
- Subscription s = c0.subs.subscribe(c0.lq, "q", settings);
- c0.session.messageTransfer(arg::content=makeMessage("1", "q", durableFlag));
- BOOST_CHECK_EQUAL("1", c0.lq.get(TIMEOUT).getData());
-
- // Add another member, send/receive another message and acquire
- // the messages. With the bug, this creates an inconsistency
- // because the browse position was not updated to the new member.
- cluster.add();
- c0.session.messageTransfer(arg::content=makeMessage("2", "q", durableFlag));
- BOOST_CHECK_EQUAL("2", c0.lq.get(TIMEOUT).getData());
- s.acquire(s.getUnacquired());
- s.accept(s.getUnaccepted());
-
- // In the bug we now have 0 messages on cluster[0] and 1 message on cluster[1]
- // Subscribing on cluster[1] provokes an error that shuts down cluster[0]
- Client c1(cluster[1], "c1");
- Subscription s1 = c1.subs.subscribe(c1.lq, "q"); // Default auto-ack=1
- Message m;
- BOOST_CHECK(!c1.lq.get(m, TIMEOUT/10));
- BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u);
- BOOST_CHECK_EQUAL(c0.session.queueQuery("q").getMessageCount(), 0u);
-}
-
-QPID_AUTO_TEST_CASE(testFairsharePriorityDelivery) {
- ClusterFixture::Args args;
- prepareArgs(args, durableFlag);
- ClusterFixture cluster(1, args, -1);
- Client c0(cluster[0], "c0");
-
- FieldTable arguments;
- arguments.setInt("x-qpid-priorities", 10);
- arguments.setInt("x-qpid-fairshare", 5);
- c0.session.queueDeclare("q", arg::durable=durableFlag, arg::arguments=arguments);
-
- //send messages of different priorities
- for (int i = 0; i < 20; i++) {
- Message msg = makeMessage((boost::format("msg-%1%") % i).str(), "q", durableFlag);
- msg.getDeliveryProperties().setPriority(i % 2 ? 9 : 5);
- c0.session.messageTransfer(arg::content=msg);
- }
-
- //pull off a couple of the messages (first four should be the top priority messages
- for (int i = 0; i < 4; i++) {
- BOOST_CHECK_EQUAL((boost::format("msg-%1%") % ((i*2)+1)).str(), c0.subs.get("q", TIMEOUT).getData());
- }
-
- // Add another member
- cluster.add();
- Client c1(cluster[1], "c1");
-
- //pull off some more messages
- BOOST_CHECK_EQUAL((boost::format("msg-%1%") % 9).str(), c0.subs.get("q", TIMEOUT).getData());
- BOOST_CHECK_EQUAL((boost::format("msg-%1%") % 0).str(), c1.subs.get("q", TIMEOUT).getData());
- BOOST_CHECK_EQUAL((boost::format("msg-%1%") % 2).str(), c0.subs.get("q", TIMEOUT).getData());
-
- //check queue has same content on both nodes
- BOOST_CHECK_EQUAL(browse(c0, "q", 12), browse(c1, "q", 12));
-}
-
-QPID_AUTO_TEST_SUITE_END()
-}} // namespace qpid::tests
diff --git a/cpp/src/tests/cluster_test_logs.py b/cpp/src/tests/cluster_test_logs.py
deleted file mode 100755
index 22f2470590..0000000000
--- a/cpp/src/tests/cluster_test_logs.py
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/usr/bin/env python
-
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Functions for comparing broker log files, used by cluster_tests.py.
-
-import os, os.path, re, glob
-from itertools import izip
-
-def split_log(log):
- """Split a broker log at checkpoints where a member joins.
- Return the set of checkpoints discovered."""
- checkpoint_re = re.compile("Member joined, frameSeq=([0-9]+), queue snapshot:")
- outfile = None
- checkpoints = []
- for l in open(log):
- match = checkpoint_re.search(l)
- if match:
- checkpoint = match.groups()[0]
- checkpoints.append(checkpoint)
- if outfile: outfile.close()
- outfile = open("%s.%s"%(log, checkpoint), 'w')
-
- if outfile: outfile.write(l)
- if outfile: outfile.close()
- return checkpoints
-
-def filter_log(log):
- """Filter the contents of a log file to remove data that is expected
- to differ between brokers in a cluster. Filtered log contents between
- the same checkpoints should match across the cluster."""
- out = open("%s.filter"%(log), 'w')
- # Lines to skip entirely, expected differences
- skip = "|".join([
- 'local connection', # Only on local broker
- 'UPDATER|UPDATEE', # Ignore update process
- 'stall for update|unstall, ignore update|cancelled offer .* unstall',
- 'caught up',
- 'active for links|Passivating links|Activating links',
- 'info Connecting: .*', # UpdateClient connection
- 'info Connection.* connected to', # UpdateClient connection
- 'warning Connection \\[[-0-9.: ]+\\] closed', # UpdateClient connection
- 'warning Broker closed connection: 200, OK',
- 'task late',
- 'task overran',
- 'warning CLOSING .* unsent data',
- 'Inter-broker link ', # ignore link state changes
- 'Updated link key from ', # ignore link state changes
- 'Running in a cluster, marking store',
- 'debug Sending keepalive signal to watchdog', # Watchdog timer thread
- 'last broker standing joined by 1 replicas, updating queue policies.',
- 'Connection .* timed out: closing', # heartbeat connection close
- "org.apache.qpid.broker:bridge:", # ignore bridge index
- "closed connection"
- ])
- # Regex to match a UUID
- uuid='\w\w\w\w\w\w\w\w-\w\w\w\w-\w\w\w\w-\w\w\w\w-\w\w\w\w\w\w\w\w\w\w\w\w'
- # Substitutions to remove expected differences
- subs = [
- (r'\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d ', ''), # Remove timestamp
- (r'cluster\([0-9.: ]*', 'cluster('), # Remove cluster node id
- (r' local\)| shadow\)', ')'), # Remove local/shadow indication
- (r'CATCHUP', 'READY'), # Treat catchup as equivalent to ready.
- (r'OFFER', 'READY'), # Treat offer as equivalent to ready.
- # System UUID expected to be different
- (r'(org.apache.qpid.broker:system[:(])%s(\)?)'%(uuid), r'\1UUID\2'),
-
- # TODO aconway 2010-12-20: review if these should be expected:
- (r' len=\d+', ' len=NN'), # buffer lengths
- (r' map={.*_object_name:([^,}]*)[,}].*', r' \1'), # V2 map - just keep name
- (r'\d+-\d+-\d+--\d+', 'X-X-X--X'), # V1 Object IDs
- ]
- # Substitutions to mask known issue: durable test shows inconsistent "changed stats for com.redhat.rhm.store:journal" messages.
- skip += '|Changed V[12] statistics com.redhat.rhm.store:journal'
- subs += [(r'to=console.obj.1.0.com.redhat.rhm.store.journal props=\d+ stats=\d+',
- 'to=console.obj.1.0.com.redhat.rhm.store.journal props=NN stats=NN')]
-
- skip_re = re.compile(skip)
- subs = [(re.compile(pattern), subst) for pattern, subst in subs]
- for l in open(log):
- if skip_re.search(l): continue
- for pattern,subst in subs: l = re.sub(pattern,subst,l)
- out.write(l)
- out.close()
-
-def verify_logs():
- """Compare log files from cluster brokers, verify that they correspond correctly."""
- for l in glob.glob("*.log"): filter_log(l)
- checkpoints = set()
- for l in glob.glob("*.filter"): checkpoints = checkpoints.union(set(split_log(l)))
- errors=[]
- for c in checkpoints:
- fragments = glob.glob("*.filter.%s"%(c))
- fragments.sort(reverse=True, key=os.path.getsize)
- while len(fragments) >= 2:
- a = fragments.pop(0)
- b = fragments[0]
- for ab in izip(open(a), open(b)):
- if ab[0] != ab[1]:
- errors.append("\n %s %s"%(a, b))
- break
- if errors:
- raise Exception("Files differ in %s"%(os.getcwd())+"".join(errors))
-
-# Can be run as a script.
-if __name__ == "__main__":
- verify_logs()
diff --git a/cpp/src/tests/cluster_test_scripts/README.txt b/cpp/src/tests/cluster_test_scripts/README.txt
deleted file mode 100644
index e861a2f397..0000000000
--- a/cpp/src/tests/cluster_test_scripts/README.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-Cluster test scripts.
-
-A set of scripts to start and stop cluster and test clients on
-multiple hosts using ssh.
-
-Pre-requisites: You must be
- - set up for password-free ssh access to the test hosts.
- - a member of the ais group on all the test hosts.
-
-Configuration:
-
-Copy defaults.sh to config.sh and edit the values as necessary.
-
-Test scripts:
-
-Test scripts use the functions in functions.sh to start & monitor
-cluster and clients.
-A test script can collect other scripts.
-
-
diff --git a/cpp/src/tests/cluster_test_scripts/cluster_check b/cpp/src/tests/cluster_test_scripts/cluster_check
deleted file mode 100755
index 05fcc1bcd2..0000000000
--- a/cpp/src/tests/cluster_test_scripts/cluster_check
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/sh
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Check that all members of a cluster are running
-
-source config.sh
-
-HOSTS=(`cat $CLUSTER_HOME/hosts`)
-PORTS=(`cat $CLUSTER_HOME/ports`)
-
-for ((i=0; i<${#HOSTS[*]}; ++i)); do
- host=${HOSTS[$i]}
- port=${PORTS[$i]}
- ssh $host "$QPIDD -cp $port" > /dev/null || {
- ret=1
- echo "ERROR: broker not running $host:$port"
- }
-done
-exit $ret
diff --git a/cpp/src/tests/cluster_test_scripts/cluster_start b/cpp/src/tests/cluster_test_scripts/cluster_start
deleted file mode 100755
index 8911358f7e..0000000000
--- a/cpp/src/tests/cluster_test_scripts/cluster_start
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/sh
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Start a cluster
-#
-# Arguments: NAME HOST [host...]
-# Start a cluster called NAME with N nodes running on the given HOSTs
-# repeat the host name to run multiple brokers on one host. Use dynamic
-# ports.
-#
-# Log files, data directories and hosts/ports files are all stored under
-# $HOME/cluster_test/$NAME
-#
-
-source config.sh
-
-CLUSTER_NAME=`date +"${USER}_%F_%T"`
-HOSTS=($BROKER_HOSTS)
-for ((i = 0; i < ${#HOSTS[*]}; ++i)) ; do
- host=${HOSTS[$i]}
- datadir=$CLUSTER_HOME/broker$i
- log=$datadir/qpidd.log
- ssh $host "rm -rf $datadir; mkdir -p $datadir" || {
- echo "ERROR: can't make data dir $datadir"; exit 1
- }
- port=`ssh $host "echo $QPIDD -dp0 --cluster-name=$CLUSTER_NAME \
- --data-dir=$datadir \
- --log-to-file=$log --log-prefix=broker$i \
- $QPIDD_OPTS | newgrp ais"` || {
- error "ERROR: can't start broker $i on $host"; exit 1;
- }
- PORTS="$PORTS $port"
-done
-
-echo "$BROKER_HOSTS" > $CLUSTER_HOME/hosts
-echo "$PORTS" > $CLUSTER_HOME/ports
-
-`dirname $0`/cluster_check $NAME
diff --git a/cpp/src/tests/cluster_test_scripts/cluster_stop b/cpp/src/tests/cluster_test_scripts/cluster_stop
deleted file mode 100755
index 09aa8f3b21..0000000000
--- a/cpp/src/tests/cluster_test_scripts/cluster_stop
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/sh
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Stop the cluster.
-
-source config.sh
-
-HOSTS=(`cat $CLUSTER_HOME/hosts`)
-PORTS=(`cat $CLUSTER_HOME/ports`)
-
-for ((i=0; i<${#HOSTS[*]}; ++i)); do
- host=${HOSTS[$i]}
- port=${PORTS[$i]}
- ssh $host "$QPIDD -qp $port" > /dev/null || {
- ret=1
- echo "ERROR: stopping broker at $host:$port"
- }
-done
-
-exit $ret
diff --git a/cpp/src/tests/cluster_test_scripts/config_example.sh b/cpp/src/tests/cluster_test_scripts/config_example.sh
deleted file mode 100755
index d47c9a9c77..0000000000
--- a/cpp/src/tests/cluster_test_scripts/config_example.sh
+++ /dev/null
@@ -1,44 +0,0 @@
-# Cluster configuration.
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# All output stored under $HOME/$CLUSTER_HOME.
-CLUSTER_HOME=$HOME/cluster_test
-
-# Hosts where brokers will be run. Repeat hostname to run multiple brokers on 1 host.
-BROKER_HOSTS="mrg22 mrg23 mrg24 mrg25 mrg26"
-
-# Hosts where clients will be run.
-CLIENT_HOSTS="$BROKER_HOSTS"
-
-# Paths to executables
-QPIDD=qpidd
-PERFTEST=perftest
-
-# Directory containing tests
-TESTDIR=/usr/bin
-
-# Options for qpidd, must be sufficient to load the cluster plugin.
-# Scripts will add --cluster-name, --daemon, --port and --log-to-file options here.
-QPIDD_OPTS=" \
---auth=no \
---log-enable=notice+ \
---log-enable=debug+:cluster \
-"
diff --git a/cpp/src/tests/cluster_test_scripts/perftest b/cpp/src/tests/cluster_test_scripts/perftest
deleted file mode 100755
index 984761eb5f..0000000000
--- a/cpp/src/tests/cluster_test_scripts/perftest
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/bin/sh
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Run a distributed perftest against a cluster.
-# Args: npubs nsubs [perftest-options]
-
-source config.sh
-
-NPUBS=${1:-4} ; shift
-NSUBS=${1:-4} ; shift
-OPTS="--npubs $NPUBS --nsubs $NSUBS $*"
-
-CLIENTS=($CLIENT_HOSTS)
-BROKERS=(`cat $CLUSTER_HOME/hosts`)
-PORTS=(`cat $CLUSTER_HOME/ports`)
-
-start() {
- client=${CLIENTS[i % ${#CLIENTS[*]}]}
- broker=${BROKERS[i % ${#BROKERS[*]}]}
- port=${PORTS[i % ${#PORTS[*]}]}
- ssh -n $client $PERFTEST $OPTS $* -b $broker -p $port &
- PIDS="$PIDS $!"
-}
-
-ssh ${CLIENTS[0]} $PERFTEST $OPTS --setup -b ${BROKERS[0]} -p${PORTS[0]}
-for (( i=0 ; i < $NPUBS ; ++i)); do start --publish; done
-for (( ; i < $NPUBS+$NSUBS ; ++i)); do start --subscribe; done
-ssh ${CLIENTS[0]} $PERFTEST $OPTS --control -b ${BROKERS[0]} -p${PORTS[0]}
-
-for pid in $PIDS; do
- wait $pid || echo "ERROR: client process $pid failed"
-done
-
-`dirname $0`/cluster_check
-
-
diff --git a/cpp/src/tests/cluster_tests.fail b/cpp/src/tests/cluster_tests.fail
deleted file mode 100644
index b28b04f643..0000000000
--- a/cpp/src/tests/cluster_tests.fail
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/cpp/src/tests/cluster_tests.py b/cpp/src/tests/cluster_tests.py
deleted file mode 100755
index 3c96b252df..0000000000
--- a/cpp/src/tests/cluster_tests.py
+++ /dev/null
@@ -1,1834 +0,0 @@
-#!/usr/bin/env python
-
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-import os, signal, sys, time, imp, re, subprocess, glob, random, logging
-import cluster_test_logs
-from qpid import datatypes, messaging
-from brokertest import *
-from qpid.harness import Skipped
-from qpid.messaging import Message, Empty, Disposition, REJECTED, util
-from threading import Thread, Lock, Condition
-from logging import getLogger
-from itertools import chain
-from tempfile import NamedTemporaryFile
-
-log = getLogger("qpid.cluster_tests")
-
-# Note: brokers that shut themselves down due to critical error during
-# normal operation will still have an exit code of 0. Brokers that
-# shut down because of an error found during initialize will exit with
-# a non-0 code. Hence the apparently inconsistent use of EXPECT_EXIT_OK
-# and EXPECT_EXIT_FAIL in some of the tests below.
-
-# TODO aconway 2010-03-11: resolve this - ideally any exit due to an error
-# should give non-0 exit status.
-
-# Import scripts as modules
-qpid_cluster=import_script(checkenv("QPID_CLUSTER_EXEC"))
-
-def readfile(filename):
- """Returns te content of file named filename as a string"""
- f = file(filename)
- try: return f.read()
- finally: f.close()
-
-class ShortTests(BrokerTest):
- """Short cluster functionality tests."""
-
- def test_message_replication(self):
- """Test basic cluster message replication."""
- # Start a cluster, send some messages to member 0.
- cluster = self.cluster(2)
- s0 = cluster[0].connect().session()
- s0.sender("q; {create:always}").send(Message("x"))
- s0.sender("q; {create:always}").send(Message("y"))
- s0.connection.close()
-
- # Verify messages available on member 1.
- s1 = cluster[1].connect().session()
- m = s1.receiver("q", capacity=1).fetch(timeout=1)
- s1.acknowledge()
- self.assertEqual("x", m.content)
- s1.connection.close()
-
- # Start member 2 and verify messages available.
- s2 = cluster.start().connect().session()
- m = s2.receiver("q", capacity=1).fetch(timeout=1)
- s2.acknowledge()
- self.assertEqual("y", m.content)
- s2.connection.close()
-
- def test_store_direct_update_match(self):
- """Verify that brokers stores an identical message whether they receive it
- direct from clients or during an update, no header or other differences"""
- cluster = self.cluster(0, args=["--load-module", self.test_store_lib])
- cluster.start(args=["--test-store-dump", "direct.dump"])
- # Try messages with various headers
- cluster[0].send_message("q", Message(durable=True, content="foobar",
- subject="subject",
- reply_to="reply_to",
- properties={"n":10}))
- # Try messages of different sizes
- for size in range(0,10000,100):
- cluster[0].send_message("q", Message(content="x"*size, durable=True))
- # Try sending via named exchange
- c = cluster[0].connect_old()
- s = c.session(str(qpid.datatypes.uuid4()))
- s.exchange_bind(exchange="amq.direct", binding_key="foo", queue="q")
- props = s.delivery_properties(routing_key="foo", delivery_mode=2)
- s.message_transfer(
- destination="amq.direct",
- message=qpid.datatypes.Message(props, "content"))
-
- # Try message with TTL and differnet headers/properties
- cluster[0].send_message("q", Message(durable=True, ttl=100000))
- cluster[0].send_message("q", Message(durable=True, properties={}, ttl=100000))
- cluster[0].send_message("q", Message(durable=True, properties={"x":10}, ttl=100000))
-
- # Now update a new member and compare their dumps.
- cluster.start(args=["--test-store-dump", "updatee.dump"])
- assert readfile("direct.dump") == readfile("updatee.dump")
-
- os.remove("direct.dump")
- os.remove("updatee.dump")
-
- def test_sasl(self):
- """Test SASL authentication and encryption in a cluster"""
- sasl_config=os.path.join(self.rootdir, "sasl_config")
- acl=os.path.join(os.getcwd(), "policy.acl")
- aclf=file(acl,"w")
- # Must allow cluster-user (zag) access to credentials exchange.
- aclf.write("""
-acl allow zag@QPID publish exchange name=qpid.cluster-credentials
-acl allow zig@QPID all all
-acl deny all all
-""")
- aclf.close()
- cluster = self.cluster(1, args=["--auth", "yes",
- "--sasl-config", sasl_config,
- "--load-module", os.getenv("ACL_LIB"),
- "--acl-file", acl,
- "--cluster-username=zag",
- "--cluster-password=zag",
- "--cluster-mechanism=PLAIN"
- ])
-
- # Valid user/password, ensure queue is created.
- c = cluster[0].connect(username="zig", password="zig")
- c.session().sender("ziggy;{create:always,node:{x-declare:{exclusive:true}}}")
- c.close()
- cluster.start() # Start second node.
-
- # Check queue is created on second node.
- c = cluster[1].connect(username="zig", password="zig")
- c.session().receiver("ziggy;{assert:always}")
- c.close()
- for b in cluster: b.ready() # Make sure all brokers still running.
-
- # Valid user, bad password
- try:
- cluster[0].connect(username="zig", password="foo").close()
- self.fail("Expected exception")
- except messaging.exceptions.ConnectionError: pass
- for b in cluster: b.ready() # Make sure all brokers still running.
-
- # Bad user ID
- try:
- cluster[0].connect(username="foo", password="bar").close()
- self.fail("Expected exception")
- except messaging.exceptions.ConnectionError: pass
- for b in cluster: b.ready() # Make sure all brokers still running.
-
- # Action disallowed by ACL
- c = cluster[0].connect(username="zag", password="zag")
- try:
- s = c.session()
- s.sender("zaggy;{create:always}")
- s.close()
- self.fail("Expected exception")
- except messaging.exceptions.UnauthorizedAccess: pass
- # make sure the queue was not created at the other node.
- c = cluster[1].connect(username="zig", password="zig")
- try:
- s = c.session()
- s.sender("zaggy;{assert:always}")
- s.close()
- self.fail("Expected exception")
- except messaging.exceptions.NotFound: pass
-
- def test_sasl_join_good(self):
- """Verify SASL authentication between brokers when joining a cluster."""
- sasl_config=os.path.join(self.rootdir, "sasl_config")
- # Test with a valid username/password
- cluster = self.cluster(1, args=["--auth", "yes",
- "--sasl-config", sasl_config,
- "--cluster-username=zig",
- "--cluster-password=zig",
- "--cluster-mechanism=PLAIN"
- ])
- cluster.start()
- c = cluster[1].connect(username="zag", password="zag", mechanism="PLAIN")
-
- def test_sasl_join_bad_password(self):
- # Test with an invalid password
- cluster = self.cluster(1, args=["--auth", "yes",
- "--sasl-config", os.path.join(self.rootdir, "sasl_config"),
- "--cluster-username=zig",
- "--cluster-password=bad",
- "--cluster-mechanism=PLAIN"
- ])
- cluster.start(wait=False, expect=EXPECT_EXIT_FAIL)
- assert cluster[1].log_contains("critical Unexpected error: connection-forced: Authentication failed")
-
- def test_sasl_join_wrong_user(self):
- # Test with a valid user that is not the cluster user.
- cluster = self.cluster(0, args=["--auth", "yes",
- "--sasl-config", os.path.join(self.rootdir, "sasl_config")])
- cluster.start(args=["--cluster-username=zig",
- "--cluster-password=zig",
- "--cluster-mechanism=PLAIN"
- ])
-
- cluster.start(wait=False, expect=EXPECT_EXIT_FAIL,
- args=["--cluster-username=zag",
- "--cluster-password=zag",
- "--cluster-mechanism=PLAIN"
- ])
- assert cluster[1].log_contains("critical Unexpected error: unauthorized-access: unauthorized-access: Unauthorized user zag@QPID for qpid.cluster-credentials, should be zig")
-
- def test_user_id_update(self):
- """Ensure that user-id of an open session is updated to new cluster members"""
- sasl_config=os.path.join(self.rootdir, "sasl_config")
- cluster = self.cluster(1, args=["--auth", "yes", "--sasl-config", sasl_config,
- "--cluster-mechanism=ANONYMOUS"])
- c = cluster[0].connect(username="zig", password="zig")
- s = c.session().sender("q;{create:always}")
- s.send(Message("x", user_id="zig")) # Message sent before start new broker
- cluster.start()
- s.send(Message("y", user_id="zig")) # Messsage sent after start of new broker
- # Verify brokers are healthy and messages are on the queue.
- self.assertEqual("x", cluster[0].get_message("q").content)
- self.assertEqual("y", cluster[1].get_message("q").content)
-
- def test_other_mech(self):
- """Test using a mechanism other than PLAIN/ANONYMOUS for cluster update authentication.
- Regression test for https://issues.apache.org/jira/browse/QPID-3849"""
- sasl_config=os.path.join(self.rootdir, "sasl_config")
- cluster = self.cluster(2, args=["--auth", "yes", "--sasl-config", sasl_config,
- "--cluster-username=zig",
- "--cluster-password=zig",
- "--cluster-mechanism=DIGEST-MD5"])
- cluster[0].connect()
- cluster.start() # Before the fix this broker falied to join the cluster.
- cluster[2].connect()
-
- def test_link_events(self):
- """Regression test for https://bugzilla.redhat.com/show_bug.cgi?id=611543"""
- args = ["--mgmt-pub-interval", 1] # Publish management information every second.
- broker1 = self.cluster(1, args)[0]
- broker2 = self.cluster(1, args)[0]
- qp = self.popen(["qpid-printevents", broker1.host_port()], EXPECT_RUNNING)
- qr = self.popen(["qpid-route", "route", "add",
- broker1.host_port(), broker2.host_port(),
- "amq.fanout", "key"
- ], EXPECT_EXIT_OK)
- # Look for link event in printevents output.
- retry(lambda: find_in_file("brokerLinkUp", qp.outfile("out")))
- broker1.ready()
- broker2.ready()
- qr.wait()
-
- def test_queue_cleaner(self):
- """ Regression test to ensure that cleanup of expired messages works correctly """
- cluster = self.cluster(2, args=["--queue-purge-interval", 3])
-
- s0 = cluster[0].connect().session()
- sender = s0.sender("my-lvq; {create: always, node:{x-declare:{arguments:{'qpid.last_value_queue':1}}}}")
- #send 10 messages that will all expire and be cleaned up
- for i in range(1, 10):
- msg = Message("message-%s" % i)
- msg.properties["qpid.LVQ_key"] = "a"
- msg.ttl = 0.1
- sender.send(msg)
- #wait for queue cleaner to run
- time.sleep(3)
-
- #test all is ok by sending and receiving a message
- msg = Message("non-expiring")
- msg.properties["qpid.LVQ_key"] = "b"
- sender.send(msg)
- s0.connection.close()
- s1 = cluster[1].connect().session()
- m = s1.receiver("my-lvq", capacity=1).fetch(timeout=1)
- s1.acknowledge()
- self.assertEqual("non-expiring", m.content)
- s1.connection.close()
-
- for b in cluster: b.ready() # Make sure all brokers still running.
-
-
- def test_amqfailover_visible(self):
- """Verify that the amq.failover exchange can be seen by
- QMF-based tools - regression test for BZ615300."""
- broker1 = self.cluster(1)[0]
- broker2 = self.cluster(1)[0]
- qs = subprocess.Popen(["qpid-stat", "-e", "-b", broker1.host_port()], stdout=subprocess.PIPE)
- out = qs.communicate()[0]
- assert out.find("amq.failover") > 0
-
- def evaluate_address(self, session, address):
- """Create a receiver just to evaluate an address for its side effects"""
- r = session.receiver(address)
- r.close()
-
- def test_expire_fanout(self):
- """Regression test for QPID-2874: Clustered broker crashes in assertion in
- cluster/ExpiryPolicy.cpp.
- Caused by a fan-out message being updated as separate messages"""
- cluster = self.cluster(1)
- session0 = cluster[0].connect().session()
- # Create 2 queues bound to fanout exchange.
- self.evaluate_address(session0, "q1;{create:always,node:{x-bindings:[{exchange:'amq.fanout',queue:q1}]}}")
- self.evaluate_address(session0, "q2;{create:always,node:{x-bindings:[{exchange:'amq.fanout',queue:q2}]}}")
- queues = ["q1", "q2"]
- # Send a fanout message with a long timeout
- s = session0.sender("amq.fanout")
- s.send(Message("foo", ttl=100), sync=False)
- # Start a new member, check the messages
- cluster.start()
- session1 = cluster[1].connect().session()
- for q in queues: self.assert_browse(session1, "q1", ["foo"])
-
- def test_route_update(self):
- """Regression test for https://issues.apache.org/jira/browse/QPID-2982
- Links and bridges associated with routes were not replicated on update.
- This meant extra management objects and caused an exit if a management
- client was attached.
- """
- args=["--mgmt-pub-interval=1","--log-enable=trace+:management"]
-
- # First broker will be killed.
- cluster0 = self.cluster(1, args=args)
- cluster1 = self.cluster(1, args=args)
- assert 0 == subprocess.call(
- ["qpid-route", "route", "add", cluster0[0].host_port(),
- cluster1[0].host_port(), "dummy-exchange", "dummy-key", "-d"])
- cluster0.start()
-
- # Wait for qpid-tool:list on cluster0[0] to generate expected output.
- pattern = re.compile("org.apache.qpid.broker.*link")
- qpid_tool = subprocess.Popen(["qpid-tool", cluster0[0].host_port()],
- stdin=subprocess.PIPE, stdout=subprocess.PIPE)
- class Scanner(Thread):
- def __init__(self): self.found = False; Thread.__init__(self)
- def run(self):
- for l in qpid_tool.stdout:
- if pattern.search(l): self.found = True; return
- scanner = Scanner()
- scanner.start()
- start = time.time()
- try:
- # Wait up to 5 second timeout for scanner to find expected output
- while not scanner.found and time.time() < start + 5:
- qpid_tool.stdin.write("list\n") # Ask qpid-tool to list
- for b in cluster0: b.ready() # Raise if any brokers are down
- finally:
- qpid_tool.stdin.write("quit\n")
- qpid_tool.wait()
- scanner.join()
- assert scanner.found
- # Regression test for https://issues.apache.org/jira/browse/QPID-3235
- # Inconsistent stats when changing elder.
-
- # Force a change of elder
- cluster0.start()
- for b in cluster0: b.ready()
- cluster0[0].expect=EXPECT_EXIT_FAIL # About to die.
- cluster0[0].kill()
- time.sleep(2) # Allow a management interval to pass.
- for b in cluster0[1:]: b.ready()
- # Verify logs are consistent
- cluster_test_logs.verify_logs()
-
- def test_redelivered(self):
- """Verify that redelivered flag is set correctly on replayed messages"""
- cluster = self.cluster(2, expect=EXPECT_EXIT_FAIL)
- url = "amqp:tcp:%s,tcp:%s" % (cluster[0].host_port(), cluster[1].host_port())
- queue = "my-queue"
- cluster[0].declare_queue(queue)
- self.sender = self.popen(
- ["qpid-send",
- "--broker", url,
- "--address", queue,
- "--sequence=true",
- "--send-eos=1",
- "--messages=100000",
- "--connection-options={%s}"%(Cluster.CONNECTION_OPTIONS)
- ])
- self.receiver = self.popen(
- ["qpid-receive",
- "--broker", url,
- "--address", queue,
- "--ignore-duplicates",
- "--check-redelivered",
- "--connection-options={%s}"%(Cluster.CONNECTION_OPTIONS),
- "--forever"
- ])
- time.sleep(1)#give sender enough time to have some messages to replay
- cluster[0].kill()
- self.sender.wait()
- self.receiver.wait()
- cluster[1].kill()
-
- class BlockedSend(Thread):
- """Send a message, send is expected to block.
- Verify that it does block (for a given timeout), then allow
- waiting till it unblocks when it is expected to do so."""
- def __init__(self, sender, msg):
- self.sender, self.msg = sender, msg
- self.blocked = True
- self.condition = Condition()
- self.timeout = 0.1 # Time to wait for expected results.
- Thread.__init__(self)
- def run(self):
- try:
- self.sender.send(self.msg, sync=True)
- self.condition.acquire()
- try:
- self.blocked = False
- self.condition.notify()
- finally: self.condition.release()
- except Exception,e: print "BlockedSend exception: %s"%e
- def start(self):
- Thread.start(self)
- time.sleep(self.timeout)
- assert self.blocked # Expected to block
- def assert_blocked(self): assert self.blocked
- def wait(self): # Now expecting to unblock
- self.condition.acquire()
- try:
- while self.blocked:
- self.condition.wait(self.timeout)
- if self.blocked: raise Exception("Timed out waiting for send to unblock")
- finally: self.condition.release()
- self.join()
-
- def queue_flowlimit_test(self, brokers):
- """Verify that the queue's flowlimit configuration and state are
- correctly replicated.
- The brokers argument allows this test to run on single broker,
- cluster of 2 pre-startd brokers or cluster where second broker
- starts after queue is in flow control.
- """
- # configure a queue with a specific flow limit on first broker
- ssn0 = brokers.first().connect().session()
- s0 = ssn0.sender("flq; {create:always, node:{type:queue, x-declare:{arguments:{'qpid.flow_stop_count':5, 'qpid.flow_resume_count':3}}}}")
- brokers.first().startQmf()
- q1 = [q for q in brokers.first().qmf_session.getObjects(_class="queue") if q.name == "flq"][0]
- oid = q1.getObjectId()
- self.assertEqual(q1.name, "flq")
- self.assertEqual(q1.arguments, {u'qpid.flow_stop_count': 5L, u'qpid.flow_resume_count': 3L})
- assert not q1.flowStopped
- self.assertEqual(q1.flowStoppedCount, 0)
-
- # fill the queue on one broker until flow control is active
- for x in range(5): s0.send(Message(str(x)))
- sender = ShortTests.BlockedSend(s0, Message(str(6)))
- sender.start() # Tests that sender does block
- # Verify the broker queue goes into a flowStopped state
- deadline = time.time() + 1
- while not q1.flowStopped and time.time() < deadline: q1.update()
- assert q1.flowStopped
- self.assertEqual(q1.flowStoppedCount, 1)
- sender.assert_blocked() # Still blocked
-
- # Now verify the both brokers in cluster have same configuration
- brokers.second().startQmf()
- qs = brokers.second().qmf_session.getObjects(_objectId=oid)
- self.assertEqual(len(qs), 1)
- q2 = qs[0]
- self.assertEqual(q2.name, "flq")
- self.assertEqual(q2.arguments, {u'qpid.flow_stop_count': 5L, u'qpid.flow_resume_count': 3L})
- assert q2.flowStopped
- self.assertEqual(q2.flowStoppedCount, 1)
-
- # now drain the queue using a session to the other broker
- ssn1 = brokers.second().connect().session()
- r1 = ssn1.receiver("flq", capacity=6)
- for x in range(4):
- r1.fetch(timeout=0)
- ssn1.acknowledge()
- sender.wait() # Verify no longer blocked.
-
- # and re-verify state of queue on both brokers
- q1.update()
- assert not q1.flowStopped
- q2.update()
- assert not q2.flowStopped
-
- ssn0.connection.close()
- ssn1.connection.close()
- cluster_test_logs.verify_logs()
-
- def test_queue_flowlimit(self):
- """Test flow limits on a standalone broker"""
- broker = self.broker()
- class Brokers:
- def first(self): return broker
- def second(self): return broker
- self.queue_flowlimit_test(Brokers())
-
- def test_queue_flowlimit_cluster(self):
- cluster = self.cluster(2)
- class Brokers:
- def first(self): return cluster[0]
- def second(self): return cluster[1]
- self.queue_flowlimit_test(Brokers())
-
- def test_queue_flowlimit_cluster_join(self):
- cluster = self.cluster(1)
- class Brokers:
- def first(self): return cluster[0]
- def second(self):
- if len(cluster) == 1: cluster.start()
- return cluster[1]
- self.queue_flowlimit_test(Brokers())
-
- def test_queue_flowlimit_replicate(self):
- """ Verify that a queue which is in flow control BUT has drained BELOW
- the flow control 'stop' threshold, is correctly replicated when a new
- broker is added to the cluster.
- """
-
- class AsyncSender(Thread):
- """Send a fixed number of msgs from a sender in a separate thread
- so it may block without blocking the test.
- """
- def __init__(self, broker, address, count=1, size=4):
- Thread.__init__(self)
- self.daemon = True
- self.broker = broker
- self.queue = address
- self.count = count
- self.size = size
- self.done = False
-
- def run(self):
- self.sender = subprocess.Popen(["qpid-send",
- "--capacity=1",
- "--content-size=%s" % self.size,
- "--messages=%s" % self.count,
- "--failover-updates",
- "--connection-options={%s}"%(Cluster.CONNECTION_OPTIONS),
- "--address=%s" % self.queue,
- "--broker=%s" % self.broker.host_port()])
- self.sender.wait()
- self.done = True
-
- cluster = self.cluster(2)
- # create a queue with rather draconian flow control settings
- ssn0 = cluster[0].connect().session()
- s0 = ssn0.sender("flq; {create:always, node:{type:queue, x-declare:{arguments:{'qpid.flow_stop_count':100, 'qpid.flow_resume_count':20}}}}")
-
- # fire off the sending thread to broker[0], and wait until the queue
- # hits flow control on broker[1]
- sender = AsyncSender(cluster[0], "flq", count=110);
- sender.start();
-
- cluster[1].startQmf()
- q_obj = [q for q in cluster[1].qmf_session.getObjects(_class="queue") if q.name == "flq"][0]
- deadline = time.time() + 10
- while not q_obj.flowStopped and time.time() < deadline:
- q_obj.update()
- assert q_obj.flowStopped
- assert not sender.done
- assert q_obj.msgDepth < 110
-
- # Now drain enough messages on broker[1] to drop below the flow stop
- # threshold, but not relieve flow control...
- receiver = subprocess.Popen(["qpid-receive",
- "--messages=15",
- "--timeout=1",
- "--print-content=no",
- "--failover-updates",
- "--connection-options={%s}"%(Cluster.CONNECTION_OPTIONS),
- "--ack-frequency=1",
- "--address=flq",
- "--broker=%s" % cluster[1].host_port()])
- receiver.wait()
- q_obj.update()
- assert q_obj.flowStopped
- assert not sender.done
- current_depth = q_obj.msgDepth
-
- # add a new broker to the cluster, and verify that the queue is in flow
- # control on that broker
- cluster.start()
- cluster[2].startQmf()
- q_obj = [q for q in cluster[2].qmf_session.getObjects(_class="queue") if q.name == "flq"][0]
- assert q_obj.flowStopped
- assert q_obj.msgDepth == current_depth
-
- # now drain the queue on broker[2], and verify that the sender becomes
- # unblocked
- receiver = subprocess.Popen(["qpid-receive",
- "--messages=95",
- "--timeout=1",
- "--print-content=no",
- "--failover-updates",
- "--connection-options={%s}"%(Cluster.CONNECTION_OPTIONS),
- "--ack-frequency=1",
- "--address=flq",
- "--broker=%s" % cluster[2].host_port()])
- receiver.wait()
- q_obj.update()
- assert not q_obj.flowStopped
- self.assertEqual(q_obj.msgDepth, 0)
-
- # verify that the sender has become unblocked
- sender.join(timeout=5)
- assert not sender.isAlive()
- assert sender.done
-
- def test_blocked_queue_delete(self):
- """Verify that producers which are blocked on a queue due to flow
- control are unblocked when that queue is deleted.
- """
-
- cluster = self.cluster(2)
- cluster[0].startQmf()
- cluster[1].startQmf()
-
- # configure a queue with a specific flow limit on first broker
- ssn0 = cluster[0].connect().session()
- s0 = ssn0.sender("flq; {create:always, node:{type:queue, x-declare:{arguments:{'qpid.flow_stop_count':5, 'qpid.flow_resume_count':3}}}}")
- q1 = [q for q in cluster[0].qmf_session.getObjects(_class="queue") if q.name == "flq"][0]
- oid = q1.getObjectId()
- self.assertEqual(q1.name, "flq")
- self.assertEqual(q1.arguments, {u'qpid.flow_stop_count': 5L, u'qpid.flow_resume_count': 3L})
- assert not q1.flowStopped
- self.assertEqual(q1.flowStoppedCount, 0)
-
- # fill the queue on one broker until flow control is active
- for x in range(5): s0.send(Message(str(x)))
- sender = ShortTests.BlockedSend(s0, Message(str(6)))
- sender.start() # Tests that sender does block
- # Verify the broker queue goes into a flowStopped state
- deadline = time.time() + 1
- while not q1.flowStopped and time.time() < deadline: q1.update()
- assert q1.flowStopped
- self.assertEqual(q1.flowStoppedCount, 1)
- sender.assert_blocked() # Still blocked
-
- # Now verify the both brokers in cluster have same configuration
- qs = cluster[1].qmf_session.getObjects(_objectId=oid)
- self.assertEqual(len(qs), 1)
- q2 = qs[0]
- self.assertEqual(q2.name, "flq")
- self.assertEqual(q2.arguments, {u'qpid.flow_stop_count': 5L, u'qpid.flow_resume_count': 3L})
- assert q2.flowStopped
- self.assertEqual(q2.flowStoppedCount, 1)
-
- # now delete the blocked queue from other broker
- ssn1 = cluster[1].connect().session()
- self.evaluate_address(ssn1, "flq;{delete:always}")
- sender.wait() # Verify no longer blocked.
-
- ssn0.connection.close()
- ssn1.connection.close()
- cluster_test_logs.verify_logs()
-
-
- def test_alternate_exchange_update(self):
- """Verify that alternate-exchange on exchanges and queues is propagated to new members of a cluster. """
- cluster = self.cluster(1)
- s0 = cluster[0].connect().session()
- # create alt queue bound to amq.fanout exchange, will be destination for alternate exchanges
- self.evaluate_address(s0, "alt;{create:always,node:{x-bindings:[{exchange:'amq.fanout',queue:alt}]}}")
- # create direct exchange ex with alternate-exchange amq.fanout and no queues bound
- self.evaluate_address(s0, "ex;{create:always,node:{type:topic, x-declare:{type:'direct', alternate-exchange:'amq.fanout'}}}")
- # create queue q with alternate-exchange amq.fanout
- self.evaluate_address(s0, "q;{create:always,node:{type:queue, x-declare:{alternate-exchange:'amq.fanout'}}}")
-
- def verify(broker):
- s = broker.connect().session()
- # Verify unmatched message goes to ex's alternate.
- s.sender("ex").send("foo")
- self.assertEqual("foo", s.receiver("alt").fetch(timeout=0).content)
- # Verify rejected message goes to q's alternate.
- s.sender("q").send("bar")
- msg = s.receiver("q").fetch(timeout=0)
- self.assertEqual("bar", msg.content)
- s.acknowledge(msg, Disposition(REJECTED)) # Reject the message
- self.assertEqual("bar", s.receiver("alt").fetch(timeout=0).content)
-
- verify(cluster[0])
- cluster.start()
- verify(cluster[1])
-
- def test_binding_order(self):
- """Regression test for binding order inconsistency in cluster"""
- cluster = self.cluster(1)
- c0 = cluster[0].connect()
- s0 = c0.session()
- # Declare multiple queues bound to same key on amq.topic
- def declare(q,max=0):
- if max: declare = 'x-declare:{arguments:{"qpid.max_count":%d, "qpid.flow_stop_count":0}}'%max
- else: declare = 'x-declare:{}'
- bind='x-bindings:[{queue:%s,key:key,exchange:"amq.topic"}]'%(q)
- s0.sender("%s;{create:always,node:{%s,%s}}" % (q,declare,bind))
- declare('d',max=4) # Only one with a limit
- for q in ['c', 'b','a']: declare(q)
- # Add a cluster member, send enough messages to exceed the max count
- cluster.start()
- try:
- s = s0.sender('amq.topic/key')
- for m in xrange(1,6): s.send(Message(str(m)))
- self.fail("Expected capacity exceeded exception")
- except messaging.exceptions.TargetCapacityExceeded: pass
- c1 = cluster[1].connect()
- s1 = c1.session()
- s0 = c0.session() # Old session s0 is broken by exception.
- # Verify queue contents are consistent.
- for q in ['a','b','c','d']:
- self.assertEqual(self.browse(s0, q), self.browse(s1, q))
- # Verify queue contents are "best effort"
- for q in ['a','b','c']: self.assert_browse(s1,q,[str(n) for n in xrange(1,6)])
- self.assert_browse(s1,'d',[str(n) for n in xrange(1,5)])
-
- def test_deleted_exchange(self):
- """QPID-3215: cached exchange reference can cause cluster inconsistencies
- if exchange is deleted/recreated
- Verify stand-alone case
- """
- cluster = self.cluster()
- # Verify we do not route message via an exchange that has been destroyed.
- cluster.start()
- s0 = cluster[0].connect().session()
- self.evaluate_address(s0, "ex;{create:always,node:{type:topic}}")
- self.evaluate_address(s0, "q;{create:always,node:{x-bindings:[{exchange:'ex',queue:q,key:foo}]}}")
- send0 = s0.sender("ex/foo")
- send0.send("foo")
- self.assert_browse(s0, "q", ["foo"])
- self.evaluate_address(s0, "ex;{delete:always}")
- try:
- send0.send("bar") # Should fail, exchange is deleted.
- self.fail("Expected not-found exception")
- except qpid.messaging.NotFound: pass
- self.assert_browse(cluster[0].connect().session(), "q", ["foo"])
-
- def test_deleted_exchange_inconsistent(self):
- """QPID-3215: cached exchange reference can cause cluster inconsistencies
- if exchange is deleted/recreated
-
- Verify cluster inconsistency.
- """
- cluster = self.cluster()
- cluster.start()
- s0 = cluster[0].connect().session()
- self.evaluate_address(s0, "ex;{create:always,node:{type:topic}}")
- self.evaluate_address(s0, "q;{create:always,node:{x-bindings:[{exchange:'ex',queue:q,key:foo}]}}")
- send0 = s0.sender("ex/foo")
- send0.send("foo")
- self.assert_browse(s0, "q", ["foo"])
-
- cluster.start()
- s1 = cluster[1].connect().session()
- self.evaluate_address(s0, "ex;{delete:always}")
- try:
- send0.send("bar")
- self.fail("Expected not-found exception")
- except qpid.messaging.NotFound: pass
-
- self.assert_browse(s1, "q", ["foo"])
-
-
- def test_ttl_consistent(self):
- """Ensure we don't get inconsistent errors with message that have TTL very close together"""
- messages = [ Message(str(i), ttl=i/1000.0) for i in xrange(0,1000)]
- messages.append(Message("x"))
- cluster = self.cluster(2)
- sender = cluster[0].connect().session().sender("q;{create:always}")
-
- def fetch(b):
- receiver = b.connect().session().receiver("q;{create:always}")
- while receiver.fetch().content != "x": pass
-
- for m in messages: sender.send(m, sync=False)
- for m in messages: sender.send(m, sync=False)
- fetch(cluster[0])
- fetch(cluster[1])
- for m in messages: sender.send(m, sync=False)
- cluster.start()
- fetch(cluster[2])
-
-
- def _verify_federation(self, src_broker, src, dst_broker, dst, timeout=30):
- """ Prove that traffic can pass between two federated brokers.
- """
- tot_time = 0
- active = False
- send_session = src_broker.connect().session()
- sender = send_session.sender(src)
- receive_session = dst_broker.connect().session()
- receiver = receive_session.receiver(dst)
- while not active and tot_time < timeout:
- sender.send(Message("Hello from Source!"))
- try:
- receiver.fetch(timeout = 1)
- receive_session.acknowledge()
- # Get this far without Empty exception, and the link is good!
- active = True
- while True:
- # Keep receiving msgs, as several may have accumulated
- receiver.fetch(timeout = 1)
- receive_session.acknowledge()
- except Empty:
- if not active:
- tot_time += 1
- receiver.close()
- receive_session.close()
- sender.close()
- send_session.close()
- return active
-
- def test_federation_failover(self):
- """
- Verify that federation operates across failures occuring in a cluster.
- Specifically:
- 1) Destination cluster learns of membership changes in the source
- cluster
- 2) Destination cluster replicates the current state of the source
- cluster to newly-added members
- """
-
- # 2 node cluster source, 2 node cluster destination
- src_cluster = self.cluster(2, expect=EXPECT_EXIT_FAIL)
- src_cluster.ready();
- dst_cluster = self.cluster(2, expect=EXPECT_EXIT_FAIL)
- dst_cluster.ready();
-
- cmd = self.popen(["qpid-config",
- "--broker", src_cluster[0].host_port(),
- "add", "queue", "srcQ"], EXPECT_EXIT_OK)
- cmd.wait()
-
- cmd = self.popen(["qpid-config",
- "--broker", dst_cluster[0].host_port(),
- "add", "exchange", "fanout", "destX"], EXPECT_EXIT_OK)
- cmd.wait()
-
- cmd = self.popen(["qpid-config",
- "--broker", dst_cluster[0].host_port(),
- "add", "queue", "destQ"], EXPECT_EXIT_OK)
- cmd.wait()
-
- cmd = self.popen(["qpid-config",
- "--broker", dst_cluster[0].host_port(),
- "bind", "destX", "destQ"], EXPECT_EXIT_OK)
- cmd.wait()
-
- # federate the srcQ to the destination exchange
- dst_cluster[0].startQmf()
- dst_broker = dst_cluster[0].qmf_session.getObjects(_class="broker")[0]
- result = dst_broker.connect(src_cluster[0].host(), src_cluster[0].port(), False, "PLAIN",
- "guest", "guest", "tcp")
- self.assertEqual(result.status, 0, result);
-
- link = dst_cluster[0].qmf_session.getObjects(_class="link")[0]
- result = link.bridge(False, "srcQ", "destX", "", "", "", True, False, False, 10)
- self.assertEqual(result.status, 0, result)
-
- # check that traffic passes
- assert self._verify_federation(src_cluster[0], "srcQ", dst_cluster[0], "destQ")
-
- # add src[2] broker to source cluster
- src_cluster.start(expect=EXPECT_EXIT_FAIL);
- src_cluster.ready();
- assert self._verify_federation(src_cluster[2], "srcQ", dst_cluster[0], "destQ")
-
- # Kill src[0]. dst[0] should fail over to src[1]
- src_cluster[0].kill()
- for b in src_cluster[1:]: b.ready()
- assert self._verify_federation(src_cluster[1], "srcQ", dst_cluster[0], "destQ")
-
- # Kill src[1], dst[0] should fail over to src[2]
- src_cluster[1].kill()
- for b in src_cluster[2:]: b.ready()
- assert self._verify_federation(src_cluster[2], "srcQ", dst_cluster[0], "destQ")
-
- # Kill dest[0], force failover to dest[1]
- dst_cluster[0].kill()
- for b in dst_cluster[1:]: b.ready()
- assert self._verify_federation(src_cluster[2], "srcQ", dst_cluster[1], "destQ")
-
- # Add dest[2]
- # dest[1] syncs dest[2] to current remote state
- dst_cluster.start(expect=EXPECT_EXIT_FAIL);
- for b in dst_cluster[1:]: b.ready()
- assert self._verify_federation(src_cluster[2], "srcQ", dst_cluster[1], "destQ")
-
- # Kill dest[1], force failover to dest[2]
- dst_cluster[1].kill()
- for b in dst_cluster[2:]: b.ready()
- assert self._verify_federation(src_cluster[2], "srcQ", dst_cluster[2], "destQ")
-
- for i in range(2, len(src_cluster)): src_cluster[i].kill()
- for i in range(2, len(dst_cluster)): dst_cluster[i].kill()
-
-
- def test_federation_multilink_failover(self):
- """
- Verify that multi-link federation operates across failures occuring in
- a cluster.
- """
-
- # 1 node cluster source, 1 node cluster destination
- src_cluster = self.cluster(1, expect=EXPECT_EXIT_FAIL)
- src_cluster.ready();
- dst_cluster = self.cluster(1, expect=EXPECT_EXIT_FAIL)
- dst_cluster.ready();
-
- # federate a direct binding across two separate links
-
- # first, create a direct exchange bound to two queues using different
- # bindings
- cmd = self.popen(["qpid-config",
- "--broker", src_cluster[0].host_port(),
- "add", "exchange", "direct", "FedX"],
- EXPECT_EXIT_OK)
- cmd.wait()
-
- cmd = self.popen(["qpid-config",
- "--broker", dst_cluster[0].host_port(),
- "add", "exchange", "direct", "FedX"],
- EXPECT_EXIT_OK)
- cmd.wait()
-
- cmd = self.popen(["qpid-config",
- "--broker", dst_cluster[0].host_port(),
- "add", "queue", "destQ1"],
- EXPECT_EXIT_OK)
- cmd.wait()
-
- cmd = self.popen(["qpid-config",
- "--broker", dst_cluster[0].host_port(),
- "bind", "FedX", "destQ1", "one"],
- EXPECT_EXIT_OK)
- cmd.wait()
-
- cmd = self.popen(["qpid-config",
- "--broker", dst_cluster[0].host_port(),
- "add", "queue", "destQ2"],
- EXPECT_EXIT_OK)
- cmd.wait()
-
- cmd = self.popen(["qpid-config",
- "--broker", dst_cluster[0].host_port(),
- "bind", "FedX", "destQ2", "two"],
- EXPECT_EXIT_OK)
- cmd.wait()
-
- # Create two separate links between the dst and source brokers, bind
- # each to different keys
- dst_cluster[0].startQmf()
- dst_broker = dst_cluster[0].qmf_session.getObjects(_class="broker")[0]
-
- for _l in [("link1", "bridge1", "one"),
- ("link2", "bridge2", "two")]:
- result = dst_broker.create("link", _l[0],
- {"host":src_cluster[0].host(),
- "port":src_cluster[0].port()},
- False)
- self.assertEqual(result.status, 0, result);
- result = dst_broker.create("bridge", _l[1],
- {"link":_l[0],
- "src":"FedX",
- "dest":"FedX",
- "key":_l[2]}, False)
- self.assertEqual(result.status, 0);
-
- # check that traffic passes
- assert self._verify_federation(src_cluster[0], "FedX/one", dst_cluster[0], "destQ1")
- assert self._verify_federation(src_cluster[0], "FedX/two", dst_cluster[0], "destQ2")
-
- # add new member, verify traffic
- src_cluster.start(expect=EXPECT_EXIT_FAIL);
- src_cluster.ready();
-
- dst_cluster.start(expect=EXPECT_EXIT_FAIL);
- dst_cluster.ready();
-
- assert self._verify_federation(src_cluster[0], "FedX/one", dst_cluster[0], "destQ1")
- assert self._verify_federation(src_cluster[0], "FedX/two", dst_cluster[0], "destQ2")
-
- src_cluster[0].kill()
- for b in src_cluster[1:]: b.ready()
-
- assert self._verify_federation(src_cluster[1], "FedX/one", dst_cluster[0], "destQ1")
- assert self._verify_federation(src_cluster[1], "FedX/two", dst_cluster[0], "destQ2")
-
- dst_cluster[0].kill()
- for b in dst_cluster[1:]: b.ready()
-
- assert self._verify_federation(src_cluster[1], "FedX/one", dst_cluster[1], "destQ1")
- assert self._verify_federation(src_cluster[1], "FedX/two", dst_cluster[1], "destQ2")
-
- for i in range(1, len(src_cluster)): src_cluster[i].kill()
- for i in range(1, len(dst_cluster)): dst_cluster[i].kill()
-
-
-
-# Some utility code for transaction tests
-XA_RBROLLBACK = 1
-XA_RBTIMEOUT = 2
-XA_OK = 0
-dtx_branch_counter = 0
-
-class DtxStatusException(Exception):
- def __init__(self, expect, actual):
- self.expect = expect
- self.actual = actual
-
- def str(self):
- return "DtxStatusException(expect=%s, actual=%s)"%(self.expect, self.actual)
-
-class DtxTestFixture:
- """Bundle together some common requirements for dtx tests."""
- def __init__(self, test, broker, name, exclusive=False):
- self.test = test
- self.broker = broker
- self.name = name
- # Use old API. DTX is not supported in messaging API.
- self.connection = broker.connect_old()
- self.session = self.connection.session(name, 1) # 1 second timeout
- self.queue = self.session.queue_declare(name, exclusive=exclusive)
- self.session.dtx_select()
- self.consumer = None
-
- def xid(self, id=None):
- if id is None: id = self.name
- return self.session.xid(format=0, global_id=id)
-
- def check_status(self, expect, actual):
- if expect != actual: raise DtxStatusException(expect, actual)
-
- def start(self, id=None, resume=False):
- self.check_status(XA_OK, self.session.dtx_start(xid=self.xid(id), resume=resume).status)
-
- def end(self, id=None, suspend=False):
- self.check_status(XA_OK, self.session.dtx_end(xid=self.xid(id), suspend=suspend).status)
-
- def prepare(self, id=None):
- self.check_status(XA_OK, self.session.dtx_prepare(xid=self.xid(id)).status)
-
- def commit(self, id=None, one_phase=True):
- self.check_status(
- XA_OK, self.session.dtx_commit(xid=self.xid(id), one_phase=one_phase).status)
-
- def rollback(self, id=None):
- self.check_status(XA_OK, self.session.dtx_rollback(xid=self.xid(id)).status)
-
- def set_timeout(self, timeout, id=None):
- self.session.dtx_set_timeout(xid=self.xid(id),timeout=timeout)
-
- def send(self, messages):
- for m in messages:
- dp=self.session.delivery_properties(routing_key=self.name)
- mp=self.session.message_properties()
- self.session.message_transfer(message=qpid.datatypes.Message(dp, mp, m))
-
- def accept(self):
- """Accept 1 message from queue"""
- consumer_tag="%s-consumer"%(self.name)
- self.session.message_subscribe(queue=self.name, destination=consumer_tag)
- self.session.message_flow(unit = self.session.credit_unit.message, value = 1, destination = consumer_tag)
- self.session.message_flow(unit = self.session.credit_unit.byte, value = 0xFFFFFFFFL, destination = consumer_tag)
- msg = self.session.incoming(consumer_tag).get(timeout=1)
- self.session.message_cancel(destination=consumer_tag)
- self.session.message_accept(qpid.datatypes.RangedSet(msg.id))
- return msg
-
-
- def verify(self, sessions, messages):
- for s in sessions:
- self.test.assert_browse(s, self.name, messages)
-
-class DtxTests(BrokerTest):
-
- def test_dtx_update(self):
- """Verify that DTX transaction state is updated to a new broker.
- Start a collection of transactions, then add a new cluster member,
- then verify they commit/rollback correctly on the new broker."""
-
- # Note: multiple test have been bundled into one to avoid the need to start/stop
- # multiple brokers per test.
-
- cluster=self.cluster(1)
- sessions = [cluster[0].connect().session()] # For verify
-
- # Transaction that will be open when new member joins, then committed.
- t1 = DtxTestFixture(self, cluster[0], "t1")
- t1.start()
- t1.send(["1", "2"])
- t1.verify(sessions, []) # Not visible outside of transaction
-
- # Transaction that will be open when new member joins, then rolled back.
- t2 = DtxTestFixture(self, cluster[0], "t2")
- t2.start()
- t2.send(["1", "2"])
-
- # Transaction that will be prepared when new member joins, then committed.
- t3 = DtxTestFixture(self, cluster[0], "t3")
- t3.start()
- t3.send(["1", "2"])
- t3.end()
- t3.prepare()
- t1.verify(sessions, []) # Not visible outside of transaction
-
- # Transaction that will be prepared when new member joins, then rolled back.
- t4 = DtxTestFixture(self, cluster[0], "t4")
- t4.start()
- t4.send(["1", "2"])
- t4.end()
- t4.prepare()
-
- # Transaction using an exclusive queue
- t5 = DtxTestFixture(self, cluster[0], "t5", exclusive=True)
- t5.start()
- t5.send(["1", "2"])
-
- # Accept messages in a transaction before/after join then commit
- # Note: Message sent outside transaction, we're testing transactional acceptance.
- t6 = DtxTestFixture(self, cluster[0], "t6")
- t6.send(["a","b","c"])
- t6.start()
- self.assertEqual(t6.accept().body, "a");
- t6.verify(sessions, ["b", "c"])
-
- # Accept messages in a transaction before/after join then roll back
- # Note: Message sent outside transaction, we're testing transactional acceptance.
- t7 = DtxTestFixture(self, cluster[0], "t7")
- t7.send(["a","b","c"])
- t7.start()
- self.assertEqual(t7.accept().body, "a");
- t7.verify(sessions, ["b", "c"])
-
- # Ended, suspended transactions across join.
- t8 = DtxTestFixture(self, cluster[0], "t8")
- t8.start(id="1")
- t8.send(["x"])
- t8.end(id="1", suspend=True)
- t8.start(id="2")
- t8.send(["y"])
- t8.end(id="2")
- t8.start()
- t8.send("z")
-
-
- # Start new cluster member
- cluster.start()
- sessions.append(cluster[1].connect().session())
-
- # Commit t1
- t1.send(["3","4"])
- t1.verify(sessions, [])
- t1.end()
- t1.commit(one_phase=True)
- t1.verify(sessions, ["1","2","3","4"])
-
- # Rollback t2
- t2.send(["3","4"])
- t2.end()
- t2.rollback()
- t2.verify(sessions, [])
-
- # Commit t3
- t3.commit(one_phase=False)
- t3.verify(sessions, ["1","2"])
-
- # Rollback t4
- t4.rollback()
- t4.verify(sessions, [])
-
- # Commit t5
- t5.send(["3","4"])
- t5.verify(sessions, [])
- t5.end()
- t5.commit(one_phase=True)
- t5.verify(sessions, ["1","2","3","4"])
-
- # Commit t6
- self.assertEqual(t6.accept().body, "b");
- t6.verify(sessions, ["c"])
- t6.end()
- t6.commit(one_phase=True)
- t6.session.close() # Make sure they're not requeued by the session.
- t6.verify(sessions, ["c"])
-
- # Rollback t7
- self.assertEqual(t7.accept().body, "b");
- t7.verify(sessions, ["c"])
- t7.end()
- t7.rollback()
- t7.verify(sessions, ["a", "b", "c"])
-
- # Resume t8
- t8.end()
- t8.commit(one_phase=True)
- t8.start("1", resume=True)
- t8.end("1")
- t8.commit("1", one_phase=True)
- t8.commit("2", one_phase=True)
- t8.verify(sessions, ["z", "x","y"])
-
-
- def test_dtx_failover_rollback(self):
- """Kill a broker during a transaction, verify we roll back correctly"""
- cluster=self.cluster(1, expect=EXPECT_EXIT_FAIL)
- cluster.start(expect=EXPECT_RUNNING)
-
- # Test unprepared at crash
- t1 = DtxTestFixture(self, cluster[0], "t1")
- t1.send(["a"]) # Not in transaction
- t1.start()
- t1.send(["b"]) # In transaction
-
- # Test prepared at crash
- t2 = DtxTestFixture(self, cluster[0], "t2")
- t2.send(["a"]) # Not in transaction
- t2.start()
- t2.send(["b"]) # In transaction
- t2.end()
- t2.prepare()
-
- # Crash the broker
- cluster[0].kill()
-
- # Transactional changes should not appear
- s = cluster[1].connect().session();
- self.assert_browse(s, "t1", ["a"])
- self.assert_browse(s, "t2", ["a"])
-
- def test_dtx_timeout(self):
- """Verify that dtx timeout works"""
- cluster = self.cluster(1)
- t1 = DtxTestFixture(self, cluster[0], "t1")
- t1.start()
- t1.set_timeout(1)
- time.sleep(1.1)
- try:
- t1.end()
- self.fail("Expected rollback timeout.")
- except DtxStatusException, e:
- self.assertEqual(e.actual, XA_RBTIMEOUT)
-
-class TxTests(BrokerTest):
-
- def test_tx_update(self):
- """Verify that transaction state is updated to a new broker"""
-
- def make_message(session, body=None, key=None, id=None):
- dp=session.delivery_properties(routing_key=key)
- mp=session.message_properties(correlation_id=id)
- return qpid.datatypes.Message(dp, mp, body)
-
- cluster=self.cluster(1)
- # Use old API. TX is not supported in messaging API.
- c = cluster[0].connect_old()
- s = c.session("tx-session", 1)
- s.queue_declare(queue="q")
- # Start transaction
- s.tx_select()
- s.message_transfer(message=make_message(s, "1", "q"))
- # Start new member mid-transaction
- cluster.start()
- # Do more work
- s.message_transfer(message=make_message(s, "2", "q"))
- # Commit the transaction and verify the results.
- s.tx_commit()
- for b in cluster: self.assert_browse(b.connect().session(), "q", ["1","2"])
-
-
-class LongTests(BrokerTest):
- """Tests that can run for a long time if -DDURATION=<minutes> is set"""
- def duration(self):
- d = self.config.defines.get("DURATION")
- if d: return float(d)*60
- else: return 3 # Default is to be quick
-
- def test_failover(self):
- """Test fail-over during continuous send-receive with errors"""
-
- # Original cluster will all be killed so expect exit with failure
- cluster = self.cluster(3, expect=EXPECT_EXIT_FAIL)
- for b in cluster: b.ready() # Wait for brokers to be ready
- for b in cluster: ErrorGenerator(b)
-
- # Start sender and receiver threads
- cluster[0].declare_queue("test-queue")
- sender = NumberedSender(cluster[0], max_depth=1000)
- receiver = NumberedReceiver(cluster[0], sender=sender)
- receiver.start()
- sender.start()
- # Wait for sender & receiver to get up and running
- retry(lambda: receiver.received > 0)
-
- # Kill original brokers, start new ones for the duration.
- endtime = time.time() + self.duration()
- i = 0
- while time.time() < endtime:
- sender.sender.assert_running()
- receiver.receiver.assert_running()
- cluster[i].kill()
- i += 1
- b = cluster.start(expect=EXPECT_EXIT_FAIL)
- for b in cluster[i:]: b.ready()
- ErrorGenerator(b)
- time.sleep(5)
- sender.stop()
- receiver.stop()
- for i in range(i, len(cluster)): cluster[i].kill()
-
- def test_management(self, args=[]):
- """
- Stress test: Run management clients and other clients concurrently
- while killing and restarting brokers.
- """
-
- class ClientLoop(StoppableThread):
- """Run a client executable in a loop."""
- def __init__(self, broker, cmd):
- StoppableThread.__init__(self)
- self.broker=broker
- self.cmd = cmd # Client command.
- self.lock = Lock()
- self.process = None # Client process.
- self.start()
-
- def run(self):
- try:
- while True:
- self.lock.acquire()
- try:
- if self.stopped: break
- self.process = self.broker.test.popen(
- self.cmd, expect=EXPECT_UNKNOWN)
- finally:
- self.lock.release()
- try:
- exit = self.process.wait()
- except OSError, e:
- # Process may already have been killed by self.stop()
- break
- except Exception, e:
- self.process.unexpected(
- "client of %s: %s"%(self.broker.name, e))
- self.lock.acquire()
- try:
- if self.stopped: break
- if exit != 0:
- self.process.unexpected(
- "client of %s exit code %s"%(self.broker.name, exit))
- finally:
- self.lock.release()
- except Exception, e:
- self.error = RethrownException("Error in ClientLoop.run")
-
- def stop(self):
- """Stop the running client and wait for it to exit"""
- self.lock.acquire()
- try:
- if self.stopped: return
- self.stopped = True
- if self.process:
- try: self.process.kill() # Kill the client.
- except OSError: pass # The client might not be running.
- finally: self.lock.release()
- StoppableThread.stop(self)
-
- # body of test_management()
-
- args += ["--mgmt-pub-interval", 1]
- args += ["--log-enable=trace+:management"]
- # Use store if present.
- if BrokerTest.store_lib: args +=["--load-module", BrokerTest.store_lib]
- cluster = self.cluster(3, args, expect=EXPECT_EXIT_FAIL) # brokers will be killed
-
- clients = [] # Per-broker list of clients that only connect to one broker.
- mclients = [] # Management clients that connect to every broker in the cluster.
-
- def start_clients(broker):
- """Start ordinary clients for a broker."""
- cmds=[
- ["qpid-tool", "localhost:%s"%(broker.port())],
- ["qpid-perftest", "--count=5000", "--durable=yes",
- "--base-name", str(qpid.datatypes.uuid4()), "--port", broker.port()],
- ["qpid-txtest", "--queue-base-name", "tx-%s"%str(qpid.datatypes.uuid4()),
- "--port", broker.port()],
- ["qpid-queue-stats", "-a", "localhost:%s" %(broker.port())]
- ]
- clients.append([ClientLoop(broker, cmd) for cmd in cmds])
-
- def start_mclients(broker):
- """Start management clients that make multiple connections."""
- cmd = ["qpid-cluster", "-C", "localhost:%s" %(broker.port())]
- mclients.append(ClientLoop(broker, cmd))
-
- endtime = time.time() + self.duration()
- # For long duration, first run is a quarter of the duration.
- runtime = min(5.0, self.duration() / 3.0)
- alive = 0 # First live cluster member
- for i in range(len(cluster)): start_clients(cluster[i])
- start_mclients(cluster[alive])
-
- while time.time() < endtime:
- time.sleep(runtime)
- runtime = 5 # Remaining runs 5 seconds, frequent broker kills
- for b in cluster[alive:]: b.ready() # Check if a broker crashed.
- # Kill the first broker, expect the clients to fail.
- b = cluster[alive]
- b.ready()
- b.kill()
- # Stop the brokers clients and all the mclients.
- for c in clients[alive] + mclients:
- try: c.stop()
- except: pass # Ignore expected errors due to broker shutdown.
- clients[alive] = []
- mclients = []
- # Start another broker and clients
- alive += 1
- cluster.start(expect=EXPECT_EXIT_FAIL)
- cluster[-1].ready() # Wait till its ready
- start_clients(cluster[-1])
- start_mclients(cluster[alive])
- for c in chain(mclients, *clients):
- c.stop()
- for b in cluster[alive:]:
- b.ready() # Verify still alive
- b.kill()
- # Verify that logs are consistent
- cluster_test_logs.verify_logs()
-
- def test_management_qmf2(self):
- self.test_management(args=["--mgmt-qmf2=yes"])
-
- def test_connect_consistent(self):
- args=["--mgmt-pub-interval=1","--log-enable=trace+:management"]
- cluster = self.cluster(2, args=args)
- end = time.time() + self.duration()
- while (time.time() < end): # Get a management interval
- for i in xrange(1000): cluster[0].connect().close()
- cluster_test_logs.verify_logs()
-
- def test_flowlimit_failover(self):
- """Test fail-over during continuous send-receive with flow control
- active.
- """
-
- # Original cluster will all be killed so expect exit with failure
- cluster = self.cluster(3, expect=EXPECT_EXIT_FAIL)
- for b in cluster: b.ready() # Wait for brokers to be ready
-
- # create a queue with rather draconian flow control settings
- ssn0 = cluster[0].connect().session()
- s0 = ssn0.sender("test-queue; {create:always, node:{type:queue, x-declare:{arguments:{'qpid.flow_stop_count':2000, 'qpid.flow_resume_count':100}}}}")
-
- receiver = NumberedReceiver(cluster[0])
- receiver.start()
- sender = NumberedSender(cluster[0])
- sender.start()
- # Wait for senders & receiver to get up and running
- retry(lambda: receiver.received > 10)
-
- # Kill original brokers, start new ones for the duration.
- endtime = time.time() + self.duration();
- i = 0
- while time.time() < endtime:
- sender.sender.assert_running()
- receiver.receiver.assert_running()
- for b in cluster[i:]: b.ready() # Check if any broker crashed.
- cluster[i].kill()
- i += 1
- b = cluster.start(expect=EXPECT_EXIT_FAIL)
- time.sleep(5)
- sender.stop()
- receiver.stop()
- for i in range(i, len(cluster)): cluster[i].kill()
-
- def test_ttl_failover(self):
- """Test that messages with TTL don't cause problems in a cluster with failover"""
-
- class Client(StoppableThread):
-
- def __init__(self, broker):
- StoppableThread.__init__(self)
- self.connection = broker.connect(reconnect=True)
- self.auto_fetch_reconnect_urls(self.connection)
- self.session = self.connection.session()
-
- def auto_fetch_reconnect_urls(self, conn):
- """Replacment for qpid.messaging.util version which is noisy"""
- ssn = conn.session("auto-fetch-reconnect-urls")
- rcv = ssn.receiver("amq.failover")
- rcv.capacity = 10
-
- def main():
- while True:
- try:
- msg = rcv.fetch()
- qpid.messaging.util.set_reconnect_urls(conn, msg)
- ssn.acknowledge(msg, sync=False)
- except messaging.exceptions.LinkClosed: return
- except messaging.exceptions.ConnectionError: return
-
- thread = Thread(name="auto-fetch-reconnect-urls", target=main)
- thread.setDaemon(True)
- thread.start()
-
- def stop(self):
- StoppableThread.stop(self)
- self.connection.detach()
-
- class Sender(Client):
- def __init__(self, broker, address):
- Client.__init__(self, broker)
- self.sent = 0 # Number of messages _reliably_ sent.
- self.sender = self.session.sender(address, capacity=1000)
-
- def send_counted(self, ttl):
- self.sender.send(Message(str(self.sent), ttl=ttl))
- self.sent += 1
-
- def run(self):
- while not self.stopped:
- choice = random.randint(0,4)
- if choice == 0: self.send_counted(None) # No ttl
- elif choice == 1: self.send_counted(100000) # Large ttl
- else: # Small ttl, might expire
- self.sender.send(Message("", ttl=random.random()/10))
- self.sender.send(Message("z"), sync=True) # Chaser.
-
- class Receiver(Client):
-
- def __init__(self, broker, address):
- Client.__init__(self, broker)
- self.received = 0 # Number of non-empty (reliable) messages received.
- self.receiver = self.session.receiver(address, capacity=1000)
- def run(self):
- try:
- while True:
- m = self.receiver.fetch(1)
- if m.content == "z": break
- if m.content: # Ignore unreliable messages
- # Ignore duplicates
- if int(m.content) == self.received: self.received += 1
- except Exception,e: self.error = e
-
- # def test_ttl_failover
-
- # Original cluster will all be killed so expect exit with failure
- # Set small purge interval.
- cluster = self.cluster(3, expect=EXPECT_EXIT_FAIL, args=["--queue-purge-interval=1"])
- for b in cluster: b.ready() # Wait for brokers to be ready
-
- # Python client failover produces noisy WARN logs, disable temporarily
- logger = logging.getLogger()
- log_level = logger.getEffectiveLevel()
- logger.setLevel(logging.ERROR)
- sender = None
- receiver = None
- try:
- # Start sender and receiver threads
- receiver = Receiver(cluster[0], "q;{create:always}")
- receiver.start()
- sender = Sender(cluster[0], "q;{create:always}")
- sender.start()
- # Wait for sender & receiver to get up and running
- retry(lambda: receiver.received > 0)
-
- # Kill brokers in a cycle.
- endtime = time.time() + self.duration()
- runtime = min(5.0, self.duration() / 4.0)
- i = 0
- while time.time() < endtime:
- for b in cluster[i:]: b.ready() # Check if any broker crashed.
- cluster[i].kill()
- i += 1
- b = cluster.start(expect=EXPECT_EXIT_FAIL)
- b.ready()
- time.sleep(runtime)
- sender.stop()
- receiver.stop()
- for b in cluster[i:]:
- b.ready() # Check it didn't crash
- b.kill()
- self.assertEqual(sender.sent, receiver.received)
- cluster_test_logs.verify_logs()
-
- finally:
- # Detach to avoid slow reconnect attempts during shut-down if test fails.
- if sender: sender.connection.detach()
- if receiver: receiver.connection.detach()
- logger.setLevel(log_level)
-
- def test_msg_group_failover(self):
- """Test fail-over during continuous send-receive of grouped messages.
- """
-
- class GroupedTrafficGenerator(Thread):
- def __init__(self, url, queue, group_key):
- Thread.__init__(self)
- self.url = url
- self.queue = queue
- self.group_key = group_key
- self.status = -1
-
- def run(self):
- # generate traffic for approx 10 seconds (2011msgs / 200 per-sec)
- cmd = ["msg_group_test",
- "--broker=%s" % self.url,
- "--address=%s" % self.queue,
- "--connection-options={%s}" % (Cluster.CONNECTION_OPTIONS),
- "--group-key=%s" % self.group_key,
- "--receivers=2",
- "--senders=3",
- "--messages=2011",
- "--send-rate=200",
- "--capacity=11",
- "--ack-frequency=23",
- "--allow-duplicates",
- "--group-size=37",
- "--randomize-group-size",
- "--interleave=13"]
- # "--trace"]
- self.generator = Popen( cmd );
- self.status = self.generator.wait()
- return self.status
-
- def results(self):
- self.join(timeout=30) # 3x assumed duration
- if self.isAlive(): return -1
- return self.status
-
- # Original cluster will all be killed so expect exit with failure
- cluster = self.cluster(3, expect=EXPECT_EXIT_FAIL, args=["-t"])
- for b in cluster: b.ready() # Wait for brokers to be ready
-
- # create a queue with rather draconian flow control settings
- ssn0 = cluster[0].connect().session()
- q_args = "{'qpid.group_header_key':'group-id', 'qpid.shared_msg_group':1}"
- s0 = ssn0.sender("test-group-q; {create:always, node:{type:queue, x-declare:{arguments:%s}}}" % q_args)
-
- # Kill original brokers, start new ones for the duration.
- endtime = time.time() + self.duration();
- i = 0
- while time.time() < endtime:
- traffic = GroupedTrafficGenerator( cluster[i].host_port(),
- "test-group-q", "group-id" )
- traffic.start()
- time.sleep(1)
-
- for x in range(2):
- for b in cluster[i:]: b.ready() # Check if any broker crashed.
- cluster[i].kill()
- i += 1
- b = cluster.start(expect=EXPECT_EXIT_FAIL)
- time.sleep(1)
-
- # wait for traffic to finish, verify success
- self.assertEqual(0, traffic.results())
-
- for i in range(i, len(cluster)): cluster[i].kill()
-
-
-class StoreTests(BrokerTest):
- """
- Cluster tests that can only be run if there is a store available.
- """
- def args(self):
- assert BrokerTest.store_lib
- return ["--load-module", BrokerTest.store_lib]
-
- def test_store_loaded(self):
- """Ensure we are indeed loading a working store"""
- broker = self.broker(self.args(), name="recoverme", expect=EXPECT_EXIT_FAIL)
- m = Message("x", durable=True)
- broker.send_message("q", m)
- broker.kill()
- broker = self.broker(self.args(), name="recoverme")
- self.assertEqual("x", broker.get_message("q").content)
-
- def test_kill_restart(self):
- """Verify we can kill/resetart a broker with store in a cluster"""
- cluster = self.cluster(1, self.args())
- cluster.start("restartme", expect=EXPECT_EXIT_FAIL).kill()
-
- # Send a message, retrieve from the restarted broker
- cluster[0].send_message("q", "x")
- m = cluster.start("restartme").get_message("q")
- self.assertEqual("x", m.content)
-
- def stop_cluster(self,broker):
- """Clean shut-down of a cluster"""
- self.assertEqual(0, qpid_cluster.main(
- ["-kf", broker.host_port()]))
-
- def test_persistent_restart(self):
- """Verify persistent cluster shutdown/restart scenarios"""
- cluster = self.cluster(0, args=self.args() + ["--cluster-size=3"])
- a = cluster.start("a", expect=EXPECT_EXIT_OK, wait=False)
- b = cluster.start("b", expect=EXPECT_EXIT_OK, wait=False)
- c = cluster.start("c", expect=EXPECT_EXIT_FAIL, wait=True)
- a.send_message("q", Message("1", durable=True))
- # Kill & restart one member.
- c.kill()
- self.assertEqual(a.get_message("q").content, "1")
- a.send_message("q", Message("2", durable=True))
- c = cluster.start("c", expect=EXPECT_EXIT_OK)
- self.assertEqual(c.get_message("q").content, "2")
- # Shut down the entire cluster cleanly and bring it back up
- a.send_message("q", Message("3", durable=True))
- self.stop_cluster(a)
- a = cluster.start("a", wait=False)
- b = cluster.start("b", wait=False)
- c = cluster.start("c", wait=True)
- self.assertEqual(a.get_message("q").content, "3")
-
- def test_persistent_partial_failure(self):
- # Kill 2 members, shut down the last cleanly then restart
- # Ensure we use the clean database
- cluster = self.cluster(0, args=self.args() + ["--cluster-size=3"])
- a = cluster.start("a", expect=EXPECT_EXIT_FAIL, wait=False)
- b = cluster.start("b", expect=EXPECT_EXIT_FAIL, wait=False)
- c = cluster.start("c", expect=EXPECT_EXIT_OK, wait=True)
- a.send_message("q", Message("4", durable=True))
- a.kill()
- b.kill()
- self.assertEqual(c.get_message("q").content, "4")
- c.send_message("q", Message("clean", durable=True))
- self.stop_cluster(c)
- a = cluster.start("a", wait=False)
- b = cluster.start("b", wait=False)
- c = cluster.start("c", wait=True)
- self.assertEqual(a.get_message("q").content, "clean")
-
- def test_wrong_cluster_id(self):
- # Start a cluster1 broker, then try to restart in cluster2
- cluster1 = self.cluster(0, args=self.args())
- a = cluster1.start("a", expect=EXPECT_EXIT_OK)
- a.terminate()
- cluster2 = self.cluster(1, args=self.args())
- try:
- a = cluster2.start("a", expect=EXPECT_EXIT_FAIL)
- a.ready()
- self.fail("Expected exception")
- except: pass
-
- def test_wrong_shutdown_id(self):
- # Start 2 members and shut down.
- cluster = self.cluster(0, args=self.args()+["--cluster-size=2"])
- a = cluster.start("a", expect=EXPECT_EXIT_OK, wait=False)
- b = cluster.start("b", expect=EXPECT_EXIT_OK, wait=False)
- self.stop_cluster(a)
- self.assertEqual(a.wait(), 0)
- self.assertEqual(b.wait(), 0)
-
- # Restart with a different member and shut down.
- a = cluster.start("a", expect=EXPECT_EXIT_OK, wait=False)
- c = cluster.start("c", expect=EXPECT_EXIT_OK, wait=False)
- self.stop_cluster(a)
- self.assertEqual(a.wait(), 0)
- self.assertEqual(c.wait(), 0)
- # Mix members from both shutdown events, they should fail
- # TODO aconway 2010-03-11: can't predict the exit status of these
- # as it depends on the order of delivery of initial-status messages.
- # See comment at top of this file.
- a = cluster.start("a", expect=EXPECT_UNKNOWN, wait=False)
- b = cluster.start("b", expect=EXPECT_UNKNOWN, wait=False)
- self.assertRaises(Exception, lambda: a.ready())
- self.assertRaises(Exception, lambda: b.ready())
-
- def test_solo_store_clean(self):
- # A single node cluster should always leave a clean store.
- cluster = self.cluster(0, self.args())
- a = cluster.start("a", expect=EXPECT_EXIT_FAIL)
- a.send_message("q", Message("x", durable=True))
- a.kill()
- a = cluster.start("a")
- self.assertEqual(a.get_message("q").content, "x")
-
- def test_last_store_clean(self):
- # Verify that only the last node in a cluster to shut down has
- # a clean store. Start with cluster of 3, reduce to 1 then
- # increase again to ensure that a node that was once alone but
- # finally did not finish as the last node does not get a clean
- # store.
- cluster = self.cluster(0, self.args())
- a = cluster.start("a", expect=EXPECT_EXIT_FAIL)
- self.assertEqual(a.store_state(), "clean")
- b = cluster.start("b", expect=EXPECT_EXIT_FAIL)
- c = cluster.start("c", expect=EXPECT_EXIT_FAIL)
- self.assertEqual(b.store_state(), "dirty")
- self.assertEqual(c.store_state(), "dirty")
- retry(lambda: a.store_state() == "dirty")
-
- a.send_message("q", Message("x", durable=True))
- a.kill()
- b.kill() # c is last man, will mark store clean
- retry(lambda: c.store_state() == "clean")
- a = cluster.start("a", expect=EXPECT_EXIT_FAIL) # c no longer last man
- retry(lambda: c.store_state() == "dirty")
- c.kill() # a is now last man
- retry(lambda: a.store_state() == "clean")
- a.kill()
- self.assertEqual(a.store_state(), "clean")
- self.assertEqual(b.store_state(), "dirty")
- self.assertEqual(c.store_state(), "dirty")
-
- def test_restart_clean(self):
- """Verify that we can re-start brokers one by one in a
- persistent cluster after a clean oshutdown"""
- cluster = self.cluster(0, self.args())
- a = cluster.start("a", expect=EXPECT_EXIT_OK)
- b = cluster.start("b", expect=EXPECT_EXIT_OK)
- c = cluster.start("c", expect=EXPECT_EXIT_OK)
- a.send_message("q", Message("x", durable=True))
- self.stop_cluster(a)
- a = cluster.start("a")
- b = cluster.start("b")
- c = cluster.start("c")
- self.assertEqual(c.get_message("q").content, "x")
-
- def test_join_sub_size(self):
- """Verify that after starting a cluster with cluster-size=N,
- we can join new members even if size < N-1"""
- cluster = self.cluster(0, self.args()+["--cluster-size=3"])
- a = cluster.start("a", wait=False, expect=EXPECT_EXIT_FAIL)
- b = cluster.start("b", wait=False, expect=EXPECT_EXIT_FAIL)
- c = cluster.start("c")
- a.send_message("q", Message("x", durable=True))
- a.send_message("q", Message("y", durable=True))
- a.kill()
- b.kill()
- a = cluster.start("a")
- self.assertEqual(c.get_message("q").content, "x")
- b = cluster.start("b")
- self.assertEqual(c.get_message("q").content, "y")
diff --git a/cpp/src/tests/clustered_replication_test b/cpp/src/tests/clustered_replication_test
deleted file mode 100755
index 5a9f143eb4..0000000000
--- a/cpp/src/tests/clustered_replication_test
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/bin/bash
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Test reliability of the replication feature in the face of link
-# failures:
-source ./test_env.sh
-
-trap stop_brokers INT EXIT
-
-fail() {
- echo $1
- exit 1
-}
-
-stop_brokers() {
- if [[ $PRIMARY1 ]] ; then
- $QPIDD_EXEC --no-module-dir -q --port $PRIMARY1
- unset PRIMARY1
- fi
- if [[ $PRIMARY2 ]] ; then
- $QPIDD_EXEC --no-module-dir -q --port $PRIMARY2
- unset PRIMARY2
- fi
- if [[ $DR1 ]] ; then
- $QPIDD_EXEC --no-module-dir -q --port $DR1
- unset DR1
- fi
- if [[ $DR2 ]] ; then
- $QPIDD_EXEC --no-module-dir -q --port $DR2
- unset DR2
- fi
-}
-
-if test -d $PYTHON_DIR; then
- . cpg_check.sh
- cpg_enabled || exit 0
-
- #todo: these cluster names need to be unique to prevent clashes
- PRIMARY_CLUSTER=PRIMARY_$(hostname)_$$
- DR_CLUSTER=DR_$(hostname)_$$
-
- GENERAL_OPTS="--auth no --no-module-dir --no-data-dir --daemon --port 0 --log-to-stderr false"
- PRIMARY_OPTS="--load-module $REPLICATING_LISTENER_LIB --create-replication-queue true --replication-queue REPLICATION_QUEUE --load-module $CLUSTER_LIB --cluster-name $PRIMARY_CLUSTER"
- DR_OPTS="--load-module $REPLICATION_EXCHANGE_LIB --load-module $CLUSTER_LIB --cluster-name $DR_CLUSTER"
-
- rm -f repl*.tmp #cleanup any files left from previous run
-
- #start first node of primary cluster and set up test queue
- echo Starting primary cluster
- PRIMARY1=$(with_ais_group $QPIDD_EXEC $GENERAL_OPTS $PRIMARY_OPTS --log-to-file repl.primary.1.tmp) || fail "Could not start PRIMARY1"
- $PYTHON_COMMANDS/qpid-config -b "localhost:$PRIMARY1" add queue test-queue --generate-queue-events 2
- $PYTHON_COMMANDS/qpid-config -b "localhost:$PRIMARY1" add queue control-queue --generate-queue-events 1
-
- #send 10 messages, consume 5 of them
- for i in `seq 1 10`; do echo Message$i; done | ./sender --port $PRIMARY1
- ./receiver --port $PRIMARY1 --messages 5 > /dev/null
-
- #add new node to primary cluster, testing correct transfer of state:
- echo Adding node to primary cluster
- PRIMARY2=$(with_ais_group $QPIDD_EXEC $GENERAL_OPTS $PRIMARY_OPTS --log-to-file repl.primary.2.tmp) || fail "Could not start PRIMARY2 "
-
- #start DR cluster, set up test queue there and establish replication bridge
- echo Starting DR cluster
- DR1=$(with_ais_group $QPIDD_EXEC $GENERAL_OPTS $DR_OPTS --log-to-file repl.dr.1.tmp) || fail "Could not start DR1"
- DR2=$(with_ais_group $QPIDD_EXEC $GENERAL_OPTS $DR_OPTS --log-to-file repl.dr.2.tmp) || fail "Could not start DR2"
-
- $PYTHON_COMMANDS/qpid-config -b "localhost:$DR1" add queue test-queue
- $PYTHON_COMMANDS/qpid-config -b "localhost:$DR1" add queue control-queue
- $PYTHON_COMMANDS/qpid-config -b "localhost:$DR1" add exchange replication REPLICATION_EXCHANGE
- $PYTHON_COMMANDS/qpid-route queue add localhost:$DR2 localhost:$PRIMARY2 REPLICATION_EXCHANGE REPLICATION_QUEUE || fail "Could not add route."
-
- #send more messages to primary
- for i in `seq 11 20`; do echo Message$i; done | ./sender --port $PRIMARY1 --send-eos 1
-
- #wait for replication events to all be processed:
- echo Waiting for replication to complete
- echo Done | ./sender --port $PRIMARY1 --routing-key control-queue --send-eos 1
- ./receiver --queue control-queue --port $DR1 > /dev/null
-
- #verify contents of test queue on dr cluster:
- echo Verifying...
- ./receiver --port $DR2 > repl.out.tmp
- for i in `seq 6 20`; do echo Message$i; done | diff repl.out.tmp - || FAIL=1
-
- if [[ $FAIL ]]; then
- echo Clustered replication test failed: expectations not met!
- exit 1
- else
- echo Clustered replication test passed
- rm -f repl*.tmp
- fi
-
-fi
diff --git a/cpp/src/tests/dynamic_log_hires_timestamp b/cpp/src/tests/dynamic_log_hires_timestamp
new file mode 100755
index 0000000000..afe79cc9b3
--- /dev/null
+++ b/cpp/src/tests/dynamic_log_hires_timestamp
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Run a simple test to verify dynamic log highres timestamp changes
+source ./test_env.sh
+test -d $PYTHON_DIR || { echo "Skipping python tests, no python dir."; exit 0; }
+
+LOG_FILE=hires_test.log
+trap cleanup EXIT
+
+cleanup() {
+ test -n "$PORT" && $QPIDD_EXEC --no-module-dir --quit --port $PORT
+}
+
+error() {
+ echo $*;
+ exit 1;
+}
+
+rm -rf $LOG_FILE
+PORT=$($QPIDD_EXEC --auth=no --no-module-dir --daemon --port=0 --log-to-file $LOG_FILE) || error "Could not start broker"
+
+echo Broker for log highres timestamp test started on $PORT, pid is $($QPIDD_EXEC --no-module-dir --check --port $PORT)
+
+$srcdir/qpid-ctrl -b localhost:$PORT setLogLevel level='debug+:Broker' > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT echo sequence=1 body=LOWRES > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT setLogHiresTimestamp logHires='true' > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT echo sequence=2 body=HI_RES > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT setLogHiresTimestamp logHires='false' > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT echo sequence=3 body=LOWRES > /dev/null
+
+# Expect 3 log entries with 'echo' in them
+if [[ $(grep echo $LOG_FILE | wc -l) -ne 3 ]]; then
+ cat $LOG_FILE
+ error "Log content error - expected 3 echo log entries"
+fi
+
+# Lines 1 and 3 are length X
+# Line 2 is length X+10 because of timestamp addition
+LEN1=$(grep echo $LOG_FILE | grep \(1 | wc -m)
+LEN2=$(grep echo $LOG_FILE | grep \(2 | wc -m)
+LEN3=$(grep echo $LOG_FILE | grep \(3 | wc -m)
+EXPECTED_LEN2=$(( $LEN1 + 10 ))
+
+if [ $LEN1 -ne $LEN3 ]; then
+ cat $LOG_FILE
+ error "Log content error - expected echo 3 to be same line length as echo 1"
+fi
+
+if [ $LEN2 -ne $EXPECTED_LEN2 ]; then
+ cat $LOG_FILE
+ error "Log content error - expected echo 2 to be 10 characters longer than echo 1"
+fi
+
+rm -rf $LOG_FILE
+echo OK
+
diff --git a/cpp/src/tests/failover_soak.cpp b/cpp/src/tests/failover_soak.cpp
deleted file mode 100644
index c2ac36a757..0000000000
--- a/cpp/src/tests/failover_soak.cpp
+++ /dev/null
@@ -1,827 +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 <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <sys/time.h>
-#include <string.h>
-#include <sys/types.h>
-#include <signal.h>
-
-#include <string>
-#include <iostream>
-#include <sstream>
-#include <vector>
-
-#include <boost/assign.hpp>
-
-#include "qpid/framing/Uuid.h"
-
-#include <ForkedBroker.h>
-#include <qpid/client/Connection.h>
-
-
-
-
-
-using namespace std;
-using boost::assign::list_of;
-using namespace qpid::framing;
-using namespace qpid::client;
-
-
-namespace qpid {
-namespace tests {
-
-vector<pid_t> pids;
-
-typedef vector<ForkedBroker *> brokerVector;
-
-typedef enum
-{
- NO_STATUS,
- RUNNING,
- COMPLETED
-}
-childStatus;
-
-
-typedef enum
-{
- NO_TYPE,
- DECLARING_CLIENT,
- SENDING_CLIENT,
- RECEIVING_CLIENT
-}
-childType;
-
-
-ostream& operator<< ( ostream& os, const childType& ct ) {
- switch ( ct ) {
- case DECLARING_CLIENT: os << "Declaring Client"; break;
- case SENDING_CLIENT: os << "Sending Client"; break;
- case RECEIVING_CLIENT: os << "Receiving Client"; break;
- default: os << "No Client"; break;
- }
-
- return os;
-}
-
-
-
-
-struct child
-{
- child ( string & name, pid_t pid, childType type )
- : name(name), pid(pid), retval(-999), status(RUNNING), type(type)
- {
- gettimeofday ( & startTime, 0 );
- }
-
-
- void
- done ( int _retval )
- {
- retval = _retval;
- status = COMPLETED;
- gettimeofday ( & stopTime, 0 );
- }
-
-
- void
- setType ( childType t )
- {
- type = t;
- }
-
-
- string name;
- pid_t pid;
- int retval;
- childStatus status;
- childType type;
- struct timeval startTime,
- stopTime;
-};
-
-
-
-
-struct children : public vector<child *>
-{
-
- void
- add ( string & name, pid_t pid, childType type )
- {
- push_back ( new child ( name, pid, type ) );
- }
-
-
- child *
- get ( pid_t pid )
- {
- vector<child *>::iterator i;
- for ( i = begin(); i != end(); ++ i )
- if ( pid == (*i)->pid )
- return *i;
-
- return 0;
- }
-
-
- void
- exited ( pid_t pid, int retval )
- {
- child * kid = get ( pid );
- if(! kid)
- {
- if ( verbosity > 1 )
- {
- cerr << "children::exited warning: Can't find child with pid "
- << pid
- << endl;
- }
- return;
- }
-
- kid->done ( retval );
- }
-
-
- int
- unfinished ( )
- {
- int count = 0;
-
- vector<child *>::iterator i;
- for ( i = begin(); i != end(); ++ i )
- if ( COMPLETED != (*i)->status )
- ++ count;
-
- return count;
- }
-
-
- int
- checkChildren ( )
- {
- for ( unsigned int i = 0; i < pids.size(); ++ i )
- {
- int pid = pids[i];
- int returned_pid;
- int status;
-
- child * kid = get ( pid );
-
- if ( kid->status != COMPLETED )
- {
- returned_pid = waitpid ( pid, &status, WNOHANG );
-
- if ( returned_pid == pid )
- {
- int exit_status = WEXITSTATUS(status);
- exited ( pid, exit_status );
- if ( exit_status ) // this is a child error.
- return exit_status;
- }
- }
- }
-
- return 0;
- }
-
-
- void
- killEverybody ( )
- {
- vector<child *>::iterator i;
- for ( i = begin(); i != end(); ++ i )
- kill ( (*i)->pid, 9 );
- }
-
-
-
- void
- print ( )
- {
- cout << "--- status of all children --------------\n";
- vector<child *>::iterator i;
- for ( i = begin(); i != end(); ++ i )
- cout << "child: " << (*i)->name
- << " status: " << (*i)->status
- << endl;
- cout << "\n\n\n\n";
- }
-
- int verbosity;
-};
-
-
-children allMyChildren;
-
-
-void
-childExit ( int )
-{
- int childReturnCode;
- pid_t pid = waitpid ( 0, & childReturnCode, WNOHANG);
-
- if ( pid > 0 )
- allMyChildren.exited ( pid, childReturnCode );
-}
-
-
-
-int
-mrand ( int maxDesiredVal ) {
- double zeroToOne = (double) rand() / (double) RAND_MAX;
- return (int) (zeroToOne * (double) maxDesiredVal);
-}
-
-
-
-int
-mrand ( int minDesiredVal, int maxDesiredVal ) {
- int interval = maxDesiredVal - minDesiredVal;
- return minDesiredVal + mrand ( interval );
-}
-
-
-
-void
-makeClusterName ( string & s ) {
- stringstream ss;
- ss << "soakTestCluster_" << Uuid(true).str();
- s = ss.str();
-}
-
-
-
-
-
-void
-printBrokers ( brokerVector & brokers )
-{
- cout << "Broker List ------------ size: " << brokers.size() << "\n";
- for ( brokerVector::iterator i = brokers.begin(); i != brokers.end(); ++ i) {
- cout << "pid: "
- << (*i)->getPID()
- << " port: "
- << (*i)->getPort()
- << endl;
- }
- cout << "end Broker List ------------\n";
-}
-
-
-
-
-ForkedBroker * newbie = 0;
-int newbie_port = 0;
-
-
-
-bool
-wait_for_newbie ( )
-{
- if ( ! newbie )
- return true;
-
- try
- {
- Connection connection;
- connection.open ( "127.0.0.1", newbie_port );
- connection.close();
- newbie = 0; // He's no newbie anymore!
- return true;
- }
- catch ( const std::exception& error )
- {
- std::cerr << "wait_for_newbie error: "
- << error.what()
- << endl;
- return false;
- }
-}
-
-bool endsWith(const char* str, const char* suffix) {
- return (strlen(suffix) < strlen(str) && 0 == strcmp(str+strlen(str)-strlen(suffix), suffix));
-}
-
-
-void
-startNewBroker ( brokerVector & brokers,
- char const * moduleOrDir,
- string const clusterName,
- int verbosity,
- int durable )
-{
- static int brokerId = 0;
- stringstream path, prefix;
- prefix << "soak-" << brokerId;
- std::vector<std::string> argv = list_of<string>
- ("qpidd")
- ("--cluster-name")(clusterName)
- ("--auth=no")
- ("--mgmt-enable=no")
- ("--log-prefix")(prefix.str())
- ("--log-to-file")(prefix.str()+".log")
- ("--log-enable=info+")
- ("--log-enable=debug+:cluster")
- ("TMP_DATA_DIR");
-
- if (endsWith(moduleOrDir, "cluster.so")) {
- // Module path specified, load only that module.
- argv.push_back(string("--load-module=")+moduleOrDir);
- argv.push_back("--no-module-dir");
- if ( durable ) {
- std::cerr << "failover_soak warning: durable arg hass no effect. Use \"dir\" option of \"moduleOrDir\".\n";
- }
- }
- else {
- // Module directory specified, load all modules in dir.
- argv.push_back(string("--module-dir=")+moduleOrDir);
- }
-
- newbie = new ForkedBroker (argv);
- newbie_port = newbie->getPort();
- ForkedBroker * broker = newbie;
-
- if ( verbosity > 0 )
- std::cerr << "new broker created: pid == "
- << broker->getPID()
- << " log-prefix == "
- << "soak-" << brokerId
- << endl;
- brokers.push_back ( broker );
-
- ++ brokerId;
-}
-
-
-
-
-
-bool
-killFrontBroker ( brokerVector & brokers, int verbosity )
-{
- cerr << "killFrontBroker: waiting for newbie sync...\n";
- if ( ! wait_for_newbie() )
- return false;
- cerr << "killFrontBroker: newbie synced.\n";
-
- if ( verbosity > 0 )
- cout << "killFrontBroker pid: " << brokers[0]->getPID() << " on port " << brokers[0]->getPort() << endl;
- try { brokers[0]->kill(9); }
- catch ( const exception& error ) {
- if ( verbosity > 0 )
- {
- cout << "error killing broker: "
- << error.what()
- << endl;
- }
-
- return false;
- }
- delete brokers[0];
- brokers.erase ( brokers.begin() );
- return true;
-}
-
-
-
-
-
-/*
- * The optional delay is to avoid killing newbie brokers that have just
- * been added and are still in the process of updating. This causes
- * spurious, test-generated errors that scare everybody.
- */
-void
-killAllBrokers ( brokerVector & brokers, int delay )
-{
- if ( delay > 0 )
- {
- std::cerr << "Killing all brokers after delay of " << delay << endl;
- sleep ( delay );
- }
-
- for ( uint i = 0; i < brokers.size(); ++ i )
- try { brokers[i]->kill(9); }
- catch ( const exception& error )
- {
- std::cerr << "killAllBrokers Warning: exception during kill on broker "
- << i
- << " "
- << error.what()
- << endl;
- }
-}
-
-
-
-
-
-pid_t
-runDeclareQueuesClient ( brokerVector brokers,
- char const * host,
- char const * path,
- int verbosity,
- int durable,
- char const * queue_prefix,
- int n_queues
- )
-{
- string name("declareQueues");
- int port = brokers[0]->getPort ( );
-
- if ( verbosity > 1 )
- cout << "startDeclareQueuesClient: host: "
- << host
- << " port: "
- << port
- << endl;
- stringstream portSs;
- portSs << port;
-
- vector<const char*> argv;
- argv.push_back ( "declareQueues" );
- argv.push_back ( host );
- string portStr = portSs.str();
- argv.push_back ( portStr.c_str() );
- if ( durable )
- argv.push_back ( "1" );
- else
- argv.push_back ( "0" );
-
- argv.push_back ( queue_prefix );
-
- char n_queues_str[20];
- sprintf ( n_queues_str, "%d", n_queues );
- argv.push_back ( n_queues_str );
-
- argv.push_back ( 0 );
- pid_t pid = fork();
-
- if ( ! pid ) {
- execv ( path, const_cast<char * const *>(&argv[0]) );
- perror ( "error executing declareQueues: " );
- return 0;
- }
-
- allMyChildren.add ( name, pid, DECLARING_CLIENT );
- return pid;
-}
-
-
-
-
-
-pid_t
-startReceivingClient ( brokerVector brokers,
- char const * host,
- char const * receiverPath,
- char const * reportFrequency,
- int verbosity,
- char const * queue_name
- )
-{
- string name("receiver");
- int port = brokers[0]->getPort ( );
-
- if ( verbosity > 1 )
- cout << "startReceivingClient: port " << port << endl;
-
- // verbosity has to be > 1 to let clients talk.
- int client_verbosity = (verbosity > 1 ) ? 1 : 0;
-
- char portStr[100];
- char verbosityStr[100];
- sprintf(portStr, "%d", port);
- sprintf(verbosityStr, "%d", client_verbosity);
-
-
- vector<const char*> argv;
- argv.push_back ( "resumingReceiver" );
- argv.push_back ( host );
- argv.push_back ( portStr );
- argv.push_back ( reportFrequency );
- argv.push_back ( verbosityStr );
- argv.push_back ( queue_name );
- argv.push_back ( 0 );
-
- pid_t pid = fork();
- pids.push_back ( pid );
-
- if ( ! pid ) {
- execv ( receiverPath, const_cast<char * const *>(&argv[0]) );
- perror ( "error executing receiver: " );
- return 0;
- }
-
- allMyChildren.add ( name, pid, RECEIVING_CLIENT );
- return pid;
-}
-
-
-
-
-
-pid_t
-startSendingClient ( brokerVector brokers,
- char const * host,
- char const * senderPath,
- char const * nMessages,
- char const * reportFrequency,
- int verbosity,
- int durability,
- char const * queue_name
- )
-{
- string name("sender");
- int port = brokers[0]->getPort ( );
-
- if ( verbosity > 1)
- cout << "startSenderClient: port " << port << endl;
- char portStr[100];
- char verbosityStr[100];
- //
- // verbosity has to be > 1 to let clients talk.
- int client_verbosity = (verbosity > 1 ) ? 1 : 0;
-
- sprintf ( portStr, "%d", port);
- sprintf ( verbosityStr, "%d", client_verbosity);
-
- vector<const char*> argv;
- argv.push_back ( "replayingSender" );
- argv.push_back ( host );
- argv.push_back ( portStr );
- argv.push_back ( nMessages );
- argv.push_back ( reportFrequency );
- argv.push_back ( verbosityStr );
- if ( durability )
- argv.push_back ( "1" );
- else
- argv.push_back ( "0" );
- argv.push_back ( queue_name );
- argv.push_back ( 0 );
-
- pid_t pid = fork();
- pids.push_back ( pid );
-
- if ( ! pid ) {
- execv ( senderPath, const_cast<char * const *>(&argv[0]) );
- perror ( "error executing sender: " );
- return 0;
- }
-
- allMyChildren.add ( name, pid, SENDING_CLIENT );
- return pid;
-}
-
-
-
-#define HUNKY_DORY 0
-#define BAD_ARGS 1
-#define CANT_FORK_DQ 2
-#define CANT_FORK_RECEIVER 3
-#define CANT_FORK_SENDER 4
-#define DQ_FAILED 5
-#define ERROR_ON_CHILD 6
-#define HANGING 7
-#define ERROR_KILLING_BROKER 8
-
-}} // namespace qpid::tests
-
-using namespace qpid::tests;
-
-// If you want durability, use the "dir" option of "moduleOrDir" .
-int
-main ( int argc, char const ** argv )
-{
- int brokerKills = 0;
- if ( argc != 11 ) {
- cerr << "Usage: "
- << argv[0]
- << "moduleOrDir declareQueuesPath senderPath receiverPath nMessages reportFrequency verbosity durable n_queues n_brokers"
- << endl;
- cerr << "\tverbosity is an integer, durable is 0 or 1\n";
- return BAD_ARGS;
- }
- signal ( SIGCHLD, childExit );
-
- int i = 1;
- char const * moduleOrDir = argv[i++];
- char const * declareQueuesPath = argv[i++];
- char const * senderPath = argv[i++];
- char const * receiverPath = argv[i++];
- char const * nMessages = argv[i++];
- char const * reportFrequency = argv[i++];
- int verbosity = atoi(argv[i++]);
- int durable = atoi(argv[i++]);
- int n_queues = atoi(argv[i++]);
- int n_brokers = atoi(argv[i++]);
-
- char const * host = "127.0.0.1";
-
- allMyChildren.verbosity = verbosity;
-
- string clusterName;
-
- srand ( getpid() );
-
- makeClusterName ( clusterName );
-
- brokerVector brokers;
-
- if ( verbosity > 1 )
- cout << "Starting initial cluster...\n";
-
- for ( int i = 0; i < n_brokers; ++ i ) {
- startNewBroker ( brokers,
- moduleOrDir,
- clusterName,
- verbosity,
- durable );
- }
-
-
- if ( verbosity > 0 )
- printBrokers ( brokers );
-
- // Get prefix for each queue name.
- stringstream queue_prefix;
- queue_prefix << "failover_soak_" << getpid();
- string queue_prefix_str(queue_prefix.str());
-
- // Run the declareQueues child.
- int childStatus;
- pid_t dqClientPid =
- runDeclareQueuesClient ( brokers,
- host,
- declareQueuesPath,
- verbosity,
- durable,
- queue_prefix_str.c_str(),
- n_queues
- );
- if ( -1 == dqClientPid ) {
- cerr << "END_OF_TEST ERROR_START_DECLARE_1\n";
- return CANT_FORK_DQ;
- }
-
- // Don't continue until declareQueues is finished.
- pid_t retval = waitpid ( dqClientPid, & childStatus, 0);
- if ( retval != dqClientPid) {
- cerr << "END_OF_TEST ERROR_START_DECLARE_2\n";
- return DQ_FAILED;
- }
- allMyChildren.exited ( dqClientPid, childStatus );
-
-
- /*
- Start one receiving and one sending client for each queue.
- */
- for ( int i = 0; i < n_queues; ++ i ) {
-
- stringstream queue_name;
- queue_name << queue_prefix.str() << '_' << i;
- string queue_name_str(queue_name.str());
-
- // Receiving client ---------------------------
- pid_t receivingClientPid =
- startReceivingClient ( brokers,
- host,
- receiverPath,
- reportFrequency,
- verbosity,
- queue_name_str.c_str() );
- if ( -1 == receivingClientPid ) {
- cerr << "END_OF_TEST ERROR_START_RECEIVER\n";
- return CANT_FORK_RECEIVER;
- }
-
-
- // Sending client ---------------------------
- pid_t sendingClientPid =
- startSendingClient ( brokers,
- host,
- senderPath,
- nMessages,
- reportFrequency,
- verbosity,
- durable,
- queue_name_str.c_str() );
- if ( -1 == sendingClientPid ) {
- cerr << "END_OF_TEST ERROR_START_SENDER\n";
- return CANT_FORK_SENDER;
- }
- }
-
-
- int minSleep = 2,
- maxSleep = 6;
-
- int totalBrokers = n_brokers;
-
- int loop = 0;
-
- while ( 1 )
- {
- ++ loop;
-
- /*
- if ( verbosity > 1 )
- std::cerr << "------- loop " << loop << " --------\n";
-
- if ( verbosity > 0 )
- cout << totalBrokers << " brokers have been added to the cluster.\n\n\n";
- */
-
- // Sleep for a while. -------------------------
- int sleepyTime = mrand ( minSleep, maxSleep );
- sleep ( sleepyTime );
-
- int bullet = mrand ( 100 );
- if ( bullet >= 95 )
- {
- fprintf ( stderr, "Killing oldest broker...\n" );
-
- // Kill the oldest broker. --------------------------
- if ( ! killFrontBroker ( brokers, verbosity ) )
- {
- allMyChildren.killEverybody();
- killAllBrokers ( brokers, 5 );
- std::cerr << "END_OF_TEST ERROR_BROKER\n";
- return ERROR_KILLING_BROKER;
- }
- ++ brokerKills;
-
- // Start a new broker. --------------------------
- if ( verbosity > 0 )
- cout << "Starting new broker.\n\n";
-
- startNewBroker ( brokers,
- moduleOrDir,
- clusterName,
- verbosity,
- durable );
- ++ totalBrokers;
- printBrokers ( brokers );
- cerr << brokerKills << " brokers have been killed.\n\n\n";
- }
-
- int retval = allMyChildren.checkChildren();
- if ( retval )
- {
- std::cerr << "END_OF_TEST ERROR_CLIENT\n";
- allMyChildren.killEverybody();
- killAllBrokers ( brokers, 5 );
- return ERROR_ON_CHILD;
- }
-
- // If all children have exited, quit.
- int unfinished = allMyChildren.unfinished();
- if ( unfinished == 0 ) {
- killAllBrokers ( brokers, 5 );
-
- if ( verbosity > 1 )
- cout << "failoverSoak: all children have exited.\n";
-
- std::cerr << "END_OF_TEST SUCCESSFUL\n";
- return HUNKY_DORY;
- }
-
- }
-
- allMyChildren.killEverybody();
- killAllBrokers ( brokers, 5 );
-
- std::cerr << "END_OF_TEST SUCCESSFUL\n";
-
- return HUNKY_DORY;
-}
-
-
-
diff --git a/cpp/src/tests/federated_cluster_test b/cpp/src/tests/federated_cluster_test
deleted file mode 100755
index f42b7501b8..0000000000
--- a/cpp/src/tests/federated_cluster_test
+++ /dev/null
@@ -1,153 +0,0 @@
-#!/bin/bash
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Test reliability of the replication feature in the face of link
-# failures:
-srcdir=`dirname $0`
-source ./test_env.sh
-
-trap stop_brokers EXIT
-
-fail() {
- echo $1
- exit 1
-}
-
-stop_brokers() {
- if [[ $BROKER_A ]] ; then
- ../qpidd --no-module-dir -q --port $BROKER_A
- unset BROKER_A
- fi
- if [[ $NODE_1 ]] ; then
- ../qpidd --no-module-dir -q --port $NODE_1
- unset NODE_1
- fi
- if [[ $NODE_2 ]] ; then
- ../qpidd --no-module-dir -q --port $NODE_2
- unset NODE_2
- fi
- if [ -f cluster.ports ]; then
- rm cluster.ports
- fi
-}
-
-start_brokers() {
- #start single node...
- BROKER_A=`../qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no --log-enable info+` || fail "BROKER_A failed to start"
-
- #...and start cluster
- $srcdir/start_cluster 2 || fail "Could not start cluster"
- NODE_1=$(head -1 cluster.ports)
- NODE_2=$(tail -1 cluster.ports)
- test -n "$NODE_1" || fail "NODE_1 failed to start"
- test -n "$NODE_2" || fail "NODE_2 failed to start"
-}
-
-setup() {
- #create exchange on both cluster and single broker
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add exchange direct test-exchange
- $PYTHON_COMMANDS/qpid-config -b "localhost:$NODE_1" add exchange direct test-exchange
-
- #create dynamic routes for test exchange
- $PYTHON_COMMANDS/qpid-route dynamic add "localhost:$NODE_2" "localhost:$BROKER_A" test-exchange
- $PYTHON_COMMANDS/qpid-route dynamic add "localhost:$BROKER_A" "localhost:$NODE_2" test-exchange
-
- #create test queue on cluster and bind it to the test exchange
- $PYTHON_COMMANDS/qpid-config -b "localhost:$NODE_1" add queue test-queue
- $PYTHON_COMMANDS/qpid-config -b "localhost:$NODE_1" bind test-exchange test-queue to-cluster
-
- #create test queue on single broker and bind it to the test exchange
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue test-queue
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" bind test-exchange test-queue from-cluster
-}
-
-run_test_pull_to_cluster_two_consumers() {
- #start consumers on each of the two nodes of the cluster
- ./receiver --port $NODE_1 --queue test-queue --credit-window 1 > fed1.out.tmp &
- ./receiver --port $NODE_2 --queue test-queue --credit-window 1 > fed2.out.tmp &
-
- #send stream of messages to test exchange on single broker
- for i in `seq 1 1000`; do echo Message $i >> fed.in.tmp; done
- ./sender --port $BROKER_A --exchange test-exchange --routing-key to-cluster --send-eos 2 < fed.in.tmp
-
- #combine output of the two consumers, sort it and compare with the expected stream
- wait
- sort -g -k 2 fed1.out.tmp fed2.out.tmp > fed.out.tmp
- diff fed.in.tmp fed.out.tmp || fail "federated link to cluster failed: expectations not met!"
-
- rm -f fed*.tmp #cleanup
-}
-
-run_test_pull_to_cluster() {
- #send stream of messages to test exchange on single broker
- for i in `seq 1 1000`; do echo Message $i >> fed.in.tmp; done
- ./sender --port $BROKER_A --exchange test-exchange --routing-key to-cluster --send-eos 1 < fed.in.tmp
-
- #consume from remaining node of the cluster
- ./receiver --port $NODE_2 --queue test-queue > fed.out.tmp
-
- #verify all messages are received
- diff fed.in.tmp fed.out.tmp || fail "federated link to cluster failed: expectations not met!"
-
- rm -f fed*.tmp #cleanup
-}
-
-run_test_pull_from_cluster() {
- #start consumer on single broker
- ./receiver --port $BROKER_A --queue test-queue --credit-window 1 > fed.out.tmp &
-
- #send stream of messages to test exchange on cluster
- for i in `seq 1 1000`; do echo Message $i >> fed.in.tmp; done
- ./sender --port $NODE_2 --exchange test-exchange --routing-key from-cluster --send-eos 1 < fed.in.tmp
-
- #verify all messages are received
- wait
- diff fed.in.tmp fed.out.tmp || fail "federated link from cluster failed: expectations not met!"
-
- rm -f fed*.tmp #cleanup
-}
-
-
-if test -d ${PYTHON_DIR}; then
- . cpg_check.sh
- cpg_enabled || exit 0
-
- rm -f fed*.tmp #cleanup any files left from previous run
- start_brokers
- echo "brokers started"
- setup
- echo "setup completed"
- run_test_pull_to_cluster_two_consumers
- echo "federated link to cluster verified"
- run_test_pull_from_cluster
- echo "federated link from cluster verified"
- if [[ $TEST_NODE_FAILURE ]] ; then
- #kill first cluster node and retest
- kill -9 $(../qpidd --check --port $NODE_1) && unset NODE_1
- echo "killed first cluster node; waiting for links to re-establish themselves..."
- sleep 5
- echo "retesting..."
- run_test_pull_to_cluster
- echo "federated link to cluster verified"
- run_test_pull_from_cluster
- echo "federated link from cluster verified"
- fi
-fi
diff --git a/cpp/src/tests/federation.py b/cpp/src/tests/federation.py
index dcd074eda9..6477c6effd 100755
--- a/cpp/src/tests/federation.py
+++ b/cpp/src/tests/federation.py
@@ -2604,3 +2604,109 @@ class FederationTests(TestBase010):
self.verify_cleanup()
+ def test_dynamic_bounce_unbinds_named_queue(self):
+ """ Verify that a propagated binding is removed when the connection is
+ bounced
+ """
+ session = self.session
+
+ # create the federation
+
+ self.startQmf()
+ qmf = self.qmf
+
+ self._setup_brokers()
+
+ # create exchange on each broker, and retrieve the corresponding
+ # management object for that exchange
+
+ exchanges=[]
+ for _b in self._brokers[0:2]:
+ _b.client_session.exchange_declare(exchange="fedX", type="direct")
+ self.assertEqual(_b.client_session.exchange_query(name="fedX").type,
+ "direct", "exchange_declare failed!")
+ # pull the exchange out of qmf...
+ retries = 0
+ my_exchange = None
+ timeout = time() + 10
+ while my_exchange is None and time() <= timeout:
+ objs = qmf.getObjects(_broker=_b.qmf_broker, _class="exchange")
+ for ooo in objs:
+ if ooo.name == "fedX":
+ my_exchange = ooo
+ break
+ if my_exchange is None:
+ self.fail("QMF failed to find new exchange!")
+ exchanges.append(my_exchange)
+
+ # on the destination broker, create a binding for propagation
+ self._brokers[0].client_session.queue_declare(queue="fedDstQ")
+ self._brokers[0].client_session.exchange_bind(queue="fedDstQ", exchange="fedX", binding_key="spud")
+
+ # on the source broker, create a bridge queue
+ self._brokers[1].client_session.queue_declare(queue="fedSrcQ")
+
+ # connect B1 --> B0
+ result = self._brokers[0].qmf_object.create( "link",
+ "Link-dynamic",
+ {"host":self._brokers[1].host,
+ "port":self._brokers[1].port}, False)
+ self.assertEqual(result.status, 0)
+
+ # bridge the "fedX" exchange:
+ result = self._brokers[0].qmf_object.create("bridge",
+ "Bridge-dynamic",
+ {"link":"Link-dynamic",
+ "src":"fedX",
+ "dest":"fedX",
+ "dynamic":True,
+ "queue":"fedSrcQ"}, False)
+ self.assertEqual(result.status, 0)
+
+ # wait for the inter-broker links to become operational
+ operational = False
+ timeout = time() + 10
+ while not operational and time() <= timeout:
+ operational = True
+ for _l in qmf.getObjects(_class="link"):
+ #print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.state)))
+ if _l.state != "Operational":
+ operational = False
+ self.failUnless(operational, "inter-broker links failed to become operational.")
+
+ # wait until the binding key has propagated to the src broker
+ exchanges[1].update()
+ timeout = time() + 10
+ while exchanges[1].bindingCount < 1 and time() <= timeout:
+ exchanges[1].update()
+ self.failUnless(exchanges[1].bindingCount == 1)
+
+ #
+ # Tear down the bridges between the two exchanges, then wait
+ # for the bindings to be cleaned up
+ #
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+ exchanges[1].update()
+ timeout = time() + 10
+ while exchanges[1].bindingCount != 0 and time() <= timeout:
+ exchanges[1].update()
+ self.failUnless(exchanges[1].bindingCount == 0)
+
+ self._brokers[1].client_session.queue_delete(queue="fedSrcQ")
+
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ for _b in self._brokers[0:2]:
+ _b.client_session.exchange_delete(exchange="fedX")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
diff --git a/cpp/src/tests/federation_sys.py b/cpp/src/tests/federation_sys.py
index 11590f684e..e2553e4cf3 100755
--- a/cpp/src/tests/federation_sys.py
+++ b/cpp/src/tests/federation_sys.py
@@ -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
@@ -37,12 +37,12 @@ class Enum(object):
class QmfTestBase010(TestBase010):
-
+
_brokers = []
_links = []
_bridges = []
_alt_exch_ops = Enum(none=0, create=1, delete=2)
-
+
class _Broker(object):
"""
This broker proxy object holds the Qmf proxy to a broker of known address as well as the QMF broker
@@ -64,7 +64,7 @@ class QmfTestBase010(TestBase010):
for session in self.sessions:
try: # Session may have been closed by broker error
session.close()
- except Exception, e: print "WARNING: %s: Unable to close session %s (%s): %s %s" % (self, session, hex(id(session)), type(e), e)
+ except Exception, e: print "WARNING: %s: Unable to close session %s (%s): %s %s" % (self, session, hex(id(session)), type(e), e)
try: # Connection may have been closed by broker error
self.connection.close()
except Exception, e: print "WARNING: %s: Unable to close connection %s (%s): %s %s" % (self, self.connection, hex(id(self.connection)), type(e), e)
@@ -72,7 +72,7 @@ class QmfTestBase010(TestBase010):
session = self.connection.session(name, transactional_flag)
self.sessions.append(session)
return session
-
+
def setUp(self):
"""
Called one before each test starts
@@ -96,34 +96,22 @@ class QmfTestBase010(TestBase010):
b.destroy(self.qmf)
TestBase010.tearDown(self)
self.qmf.close()
-
+
#--- General test utility functions
-
+
def _get_name(self):
"""
Return the name of method which called this method stripped of "test_" prefix. Used for naming
queues and exchanges on a per-test basis.
"""
return stack()[1][3][5:]
-
+
def _get_broker_port(self, key):
"""
Get the port of a broker defined in the environment using -D<key>=portno
"""
return int(self.defines[key])
-
- def _get_cluster_ports(self, key):
- """
- Get the cluster ports from the parameters of the test which place it in the environment using
- -D<key>="port0 port1 ... portN" (space-separated)
- """
- ports = []
- ports_str = self.defines[key]
- if ports_str:
- for p in ports_str.split():
- ports.append(int(p))
- return ports
-
+
def _get_send_address(self, exch_name, queue_name):
"""
Get an address to which to send messages based on the exchange name and queue name, but taking into account
@@ -132,18 +120,15 @@ class QmfTestBase010(TestBase010):
if len(exch_name) == 0: # Default exchange
return queue_name
return "%s/%s" % (exch_name, queue_name)
-
- def _get_broker(self, cluster_flag, broker_port_key, cluster_ports_key):
+
+ def _get_broker(self, broker_port_key):
"""
Read the port numbers for pre-started brokers from the environment using keys, then find or create and return
the Qmf broker proxy for the appropriate broker
"""
- if cluster_flag:
- port = self._get_cluster_ports(cluster_ports_key)[0] # Always use the first node in the cluster
- else:
- port = self._get_broker_port(broker_port_key)
+ port = self._get_broker_port(broker_port_key)
return self._find_create_broker("localhost:%s" % port)
-
+ ################
def _get_msg_subject(self, topic_key):
"""
Return an appropriate subject for sending a message to a known topic. Return None if there is no topic.
@@ -152,7 +137,7 @@ class QmfTestBase010(TestBase010):
if "*" in topic_key: return topic_key.replace("*", "test")
if "#" in topic_key: return topic_key.replace("#", "multipart.test")
return topic_key
-
+
def _send_msgs(self, session_name, broker, addr, msg_count, msg_content = "Message_%03d", topic_key = "",
msg_durable_flag = False, enq_txn_size = 0):
"""
@@ -172,7 +157,7 @@ class QmfTestBase010(TestBase010):
send_session.commit()
sender.close()
send_session.close()
-
+
def _receive_msgs(self, session_name, broker, addr, msg_count, msg_content = "Message_%03d", deq_txn_size = 0,
timeout = 0):
"""
@@ -214,9 +199,9 @@ class QmfTestBase010(TestBase010):
receive_session.commit()
receiver.close()
receive_session.close()
-
+
#--- QMF-specific utility functions
-
+
def _get_qmf_property(self, props, key):
"""
Get the value of a named property key kj from a property list [(k0, v0), (k1, v1), ... (kn, vn)].
@@ -225,13 +210,13 @@ class QmfTestBase010(TestBase010):
if k.name == key:
return v
return None
-
+
def _check_qmf_return(self, method_result):
"""
Check the result of a Qmf-defined method call
"""
self.assertTrue(method_result.status == 0, method_result.text)
-
+
def _check_optional_qmf_property(self, qmf_broker, type, qmf_object, key, expected_val, obj_ref_flag):
"""
Optional Qmf properties don't show up in the properties list when they are not specified. Checks for
@@ -253,9 +238,9 @@ class QmfTestBase010(TestBase010):
(type, qmf_object.name, key, val, expected_val))
else:
self.fail("%s %s exists, but has an unexpected %s property \"%s\" set." % (type, qmf_object.name, key, val))
-
+
#--- Find/create Qmf broker objects
-
+
def _find_qmf_broker(self, url):
"""
Find the Qmf broker object for the given broker URL. The broker must have been previously added to Qmf through
@@ -265,7 +250,7 @@ class QmfTestBase010(TestBase010):
if b.getBroker().getUrl() == url:
return b
return None
-
+
def _find_create_broker(self, url):
"""
Find a running broker through Qmf. If it does not exist, add it (assuming the broker is already running).
@@ -280,9 +265,9 @@ class QmfTestBase010(TestBase010):
else:
broker.qmf_broker = qmf_broker
return broker
-
+
#--- Find/create/delete exchanges
-
+
def _find_qmf_exchange(self, qmf_broker, name, type, alternate, durable, auto_delete):
"""
Find Qmf exchange object
@@ -302,7 +287,7 @@ class QmfTestBase010(TestBase010):
(name, e.autoDelete, auto_delete))
return e
return None
-
+
def _find_create_qmf_exchange(self, qmf_broker, name, type, alternate, durable, auto_delete, args):
"""
Find Qmf exchange object if exchange exists, create exchange and return its Qmf object if not
@@ -315,7 +300,7 @@ class QmfTestBase010(TestBase010):
e = self._find_qmf_exchange(qmf_broker, name, type, alternate, durable, auto_delete)
self.assertNotEqual(e, None, "Creation of exchange %s on broker %s failed" % (name, qmf_broker.getBroker().getUrl()))
return e
-
+
def _find_delete_qmf_exchange(self, qmf_broker, name, type, alternate, durable, auto_delete):
"""
Find and delete Qmf exchange object if it exists
@@ -323,9 +308,9 @@ class QmfTestBase010(TestBase010):
e = self._find_qmf_exchange(qmf_broker, name, type, alternate, durable, auto_delete)
if e is not None and not auto_delete:
self._check_qmf_return(qmf_broker.delete(type="exchange", name=name, options={}))
-
+
#--- Find/create/delete queues
-
+
def _find_qmf_queue(self, qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete):
"""
Find a Qmf queue object
@@ -344,7 +329,7 @@ class QmfTestBase010(TestBase010):
(name, q.autoDelete, auto_delete))
return q
return None
-
+
def _find_create_qmf_queue(self, qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete, args):
"""
Find Qmf queue object if queue exists, create queue and return its Qmf object if not
@@ -357,7 +342,7 @@ class QmfTestBase010(TestBase010):
q = self._find_qmf_queue(qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete)
self.assertNotEqual(q, None, "Creation of queue %s on broker %s failed" % (name, qmf_broker.getBroker().getUrl()))
return q
-
+
def _find_delete_qmf_queue(self, qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete, args):
"""
Find and delete Qmf queue object if it exists
@@ -365,9 +350,9 @@ class QmfTestBase010(TestBase010):
q = self._find_qmf_queue(qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete)
if q is not None and not auto_delete:
self._check_qmf_return(qmf_broker.delete(type="queue", name=name, options={}))
-
+
#--- Find/create/delete bindings (between an exchange and a queue)
-
+
def _find_qmf_binding(self, qmf_broker, qmf_exchange, qmf_queue, binding_key, binding_args):
"""
Find a Qmf binding object
@@ -383,7 +368,7 @@ class QmfTestBase010(TestBase010):
(qmf_exchange.name, qmf_queue.name, b.arguments, binding_args))
return b
return None
-
+
def _find_create_qmf_binding(self, qmf_broker, qmf_exchange, qmf_queue, binding_key, binding_args):
"""
Find Qmf binding object if it exists, create binding and return its Qmf object if not
@@ -396,7 +381,7 @@ class QmfTestBase010(TestBase010):
self.assertNotEqual(b, None, "Creation of binding between exchange %s and queue %s with key %s failed" %
(qmf_exchange.name, qmf_queue.name, binding_key))
return b
-
+
def _find_delete_qmf_binding(self, qmf_broker, qmf_exchange, qmf_queue, binding_key, binding_args):
"""
Find and delete Qmf binding object if it exists
@@ -405,7 +390,7 @@ class QmfTestBase010(TestBase010):
if b is not None:
if len(qmf_exchange.name) > 0: # not default exchange
self._check_qmf_return(qmf_broker.delete(type="binding", name="%s/%s/%s" % (qmf_exchange.name, qmf_queue.name, binding_key), options={}))
-
+
#--- Find/create a link
def _find_qmf_link(self, qmf_from_broker_proxy, host, port):
@@ -416,7 +401,7 @@ class QmfTestBase010(TestBase010):
if l.host == host and l.port == port:
return l
return None
-
+
def _find_create_qmf_link(self, qmf_from_broker, qmf_to_broker_proxy, link_durable_flag, auth_mechanism, user_id,
password, transport, pause_interval, link_ready_timeout):
"""
@@ -433,24 +418,19 @@ class QmfTestBase010(TestBase010):
(qmf_from_broker.getBroker().getUrl(), qmf_to_broker_proxy.getUrl()))
self._wait_for_link(l, pause_interval, link_ready_timeout)
return l
-
+
def _wait_for_link(self, link, pause_interval, link_ready_timeout):
"""
Wait for link to become active (state=Operational)
"""
tot_time = 0
link.update()
- if link.state == "":
- # Link mgmt updates for the c++ link object are disabled when in a cluster because of inconsistent state:
- # one is "Operational", the other "Passive". In this case, wait a bit and hope for the best...
- sleep(2*pause_interval)
- else:
- while link.state != "Operational" and tot_time < link_ready_timeout:
- sleep(pause_interval)
- tot_time += pause_interval
- link.update()
- self.assertEqual(link.state, "Operational", "Timeout: Link not operational, state=%s" % link.state)
-
+ while link.state != "Operational" and tot_time < link_ready_timeout:
+ sleep(pause_interval)
+ tot_time += pause_interval
+ link.update()
+ self.assertEqual(link.state, "Operational", "Timeout: Link not operational, state=%s" % link.state)
+
#--- Find/create a bridge
def _find_qmf_bridge(self, qmf_broker_proxy, qmf_link, source, destination, key):
@@ -461,7 +441,7 @@ class QmfTestBase010(TestBase010):
if b.linkRef == qmf_link.getObjectId() and b.src == source and b.dest == destination and b.key == key:
return b
return None
-
+
def _find_create_qmf_bridge(self, qmf_broker_proxy, qmf_link, queue_name, exch_name, topic_key,
queue_route_type_flag, bridge_durable_flag):
"""
@@ -486,7 +466,7 @@ class QmfTestBase010(TestBase010):
b = self._find_qmf_bridge(qmf_broker_proxy, qmf_link, src, dest, key)
self.assertNotEqual(b, None, "Bridge creation failed: src=%s dest=%s key=%s" % (src, dest, key))
return b
-
+
def _wait_for_bridge(self, bridge, src_broker, dest_broker, exch_name, queue_name, topic_key, pause_interval,
bridge_ready_timeout):
"""
@@ -522,9 +502,9 @@ class QmfTestBase010(TestBase010):
sender.close()
send_session.close()
self.assertTrue(active, "Bridge failed to become active after %ds: %s" % (bridge_ready_timeout, bridge))
-
+
#--- Find/create/delete utility functions
-
+
def _create_and_bind(self, qmf_broker, exchange_args, queue_args, binding_args):
"""
Create a binding between a named exchange and queue on a broker
@@ -532,7 +512,7 @@ class QmfTestBase010(TestBase010):
e = self._find_create_qmf_exchange(qmf_broker, **exchange_args)
q = self._find_create_qmf_queue(qmf_broker, **queue_args)
return self._find_create_qmf_binding(qmf_broker, e, q, **binding_args)
-
+
def _check_alt_exchange(self, qmf_broker, alt_exch_name, alt_exch_type, alt_exch_op):
"""
Check for existence of alternate exchange. Return the Qmf exchange proxy object for the alternate exchange
@@ -546,7 +526,7 @@ class QmfTestBase010(TestBase010):
alternate="", durable=False, auto_delete=False)
return self._find_qmf_exchange(qmf_broker=qmf_broker, name=alt_exchange_name, type=alt_exchange_type,
alternate="", durable=False, auto_delete=False)
-
+
def _delete_queue_binding(self, qmf_broker, exchange_args, queue_args, binding_args):
"""
Delete a queue and the binding between it and the exchange
@@ -555,7 +535,7 @@ class QmfTestBase010(TestBase010):
q = self._find_qmf_queue(qmf_broker, queue_args["name"], queue_args["alternate_exchange"], queue_args["durable"], queue_args["exclusive"], queue_args["auto_delete"])
self._find_delete_qmf_binding(qmf_broker, e, q, **binding_args)
self._find_delete_qmf_queue(qmf_broker, **queue_args)
-
+
def _create_route(self, queue_route_type_flag, src_broker, dest_broker, exch_name, queue_name, topic_key,
link_durable_flag, bridge_durable_flag, auth_mechanism, user_id, password, transport,
pause_interval = 1, link_ready_timeout = 20, bridge_ready_timeout = 20):
@@ -571,7 +551,7 @@ class QmfTestBase010(TestBase010):
self._wait_for_bridge(b, src_broker, dest_broker, exch_name, queue_name, topic_key, pause_interval, bridge_ready_timeout)
# Parameterized test - entry point for tests
-
+
def _do_test(self,
test_name, # Name of test
exch_name = "amq.direct", # Remote exchange name
@@ -597,8 +577,6 @@ class QmfTestBase010(TestBase010):
queue_route_type_flag = False, # Route type: false = bridge route, true = queue route
enq_txn_size = 0, # Enqueue transaction size, 0 = no transactions
deq_txn_size = 0, # Dequeue transaction size, 0 = no transactions
- local_cluster_flag = False, # Use a node from the local cluster, otherwise use single local broker
- remote_cluster_flag = False, # Use a node from the remote cluster, otherwise use single remote broker
alt_exch_op = _alt_exch_ops.create,# Op on alt exch [create (ensure present), delete (ensure not present), none (neither create nor delete)]
auth_mechanism = "", # Authorization mechanism for linked broker
user_id = "", # User ID for authorization on linked broker
@@ -609,32 +587,32 @@ class QmfTestBase010(TestBase010):
Parameterized federation test. Sets up a federated link between a source broker and a destination broker and
checks that messages correctly pass over the link to the destination. Where appropriate (non-queue-routes), also
checks for the presence of messages on the source broker.
-
+
In these tests, the concept is to create a LOCAL broker, then create a link to a REMOTE broker using federation.
In other words, the messages sent to the LOCAL broker will be replicated on the REMOTE broker, and tests are
performed on the REMOTE broker to check that the required messages are present. In the case of regular routes,
the LOCAL broker will also retain the messages, and a similar test is performed on this broker.
-
+
TODO: There are several items to improve here:
1. _do_test() is rather general. Rather create a version for each exchange type and test the exchange/queue
interaction in more detail based on the exchange type
2. Add a headers and an xml exchange type
- 3. Restructure the tests to start and stop brokers and clusters directly rather than relying on previously
- started brokers. Then persistence can be checked by stopping and restarting the brokers/clusters. In particular,
+ 3. Restructure the tests to start and stop brokers directly rather than relying on previously
+ started brokers. Then persistence can be checked by stopping and restarting the brokers. In particular,
test the persistence of links and bridges, both of which take a persistence flag.
4. Test the behavior of the alternate exchanges when messages are sourced through a link. Also check behavior
when the alternate exchange is not present or is deleted after the reference is made.
5. Test special queue types (eg LVQ)
"""
- local_broker = self._get_broker(local_cluster_flag, "local-port", "local-cluster-ports")
- remote_broker = self._get_broker(remote_cluster_flag, "remote-port", "remote-cluster-ports")
-
+ local_broker = self._get_broker("local-port")
+ remote_broker = self._get_broker("remote-port")
+
# Check alternate exchanges exist (and create them if not) on both local and remote brokers
self._check_alt_exchange(local_broker.qmf_broker, exch_alt_exch, exch_alt_exch_type, alt_exch_op)
self._check_alt_exchange(local_broker.qmf_broker, queue_alt_exch, queue_alt_exch_type, alt_exch_op)
self._check_alt_exchange(remote_broker.qmf_broker, exch_alt_exch, exch_alt_exch_type, alt_exch_op)
self._check_alt_exchange(remote_broker.qmf_broker, queue_alt_exch, queue_alt_exch_type, alt_exch_op)
-
+
queue_name = "queue_%s" % test_name
exchange_args = {"name": exch_name, "type": exch_type, "alternate": exch_alt_exch,
"durable": exch_durable_flag, "auto_delete": exch_auto_delete_flag, "args": exch_x_args}
@@ -658,70 +636,70 @@ class QmfTestBase010(TestBase010):
if not queue_route_type_flag:
self._receive_msgs("local_receive_session", local_broker, addr = queue_name, msg_count = msg_count, deq_txn_size = deq_txn_size)
self._receive_msgs("remote_receive_session", remote_broker, addr = queue_name, msg_count = msg_count, deq_txn_size = deq_txn_size, timeout = 5)
-
+
# Clean up
self._delete_queue_binding(qmf_broker=local_broker.qmf_broker, exchange_args=exchange_args, queue_args=queue_args, binding_args=binding_args)
self._delete_queue_binding(qmf_broker=remote_broker.qmf_broker, exchange_args=exchange_args, queue_args=queue_args, binding_args=binding_args)
class A_ShortTests(QmfTestBase010):
-
+
def test_route_defaultExch(self):
self._do_test(self._get_name())
-
+
def test_queueRoute_defaultExch(self):
self._do_test(self._get_name(), queue_route_type_flag=True)
-
-
+
+
class A_LongTests(QmfTestBase010):
-
+
def test_route_amqDirectExch(self):
self._do_test(self._get_name(), exch_name="amq.direct")
-
+
def test_queueRoute_amqDirectExch(self):
self._do_test(self._get_name(), exch_name="amq.direct", queue_route_type_flag=True)
-
-
+
+
def test_route_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange")
-
+
def test_queueRoute_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True)
-
-
+
+
def test_route_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout")
-
+
def test_queueRoute_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True)
-
-
+
+
def test_route_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#")
-
+
def test_queueRoute_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True)
-
-
+
+
class B_ShortTransactionTests(QmfTestBase010):
-
+
def test_txEnq01_route_defaultExch(self):
self._do_test(self._get_name(), enq_txn_size=1)
-
+
def test_txEnq01_queueRoute_defaultExch(self):
self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1)
-
+
def test_txEnq01_txDeq01_route_defaultExch(self):
self._do_test(self._get_name(), enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_queueRoute_defaultExch(self):
self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
-
+
class B_LongTransactionTests(QmfTestBase010):
-
+
def test_txEnq10_route_defaultExch(self):
self._do_test(self._get_name(), enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_queueRoute_defaultExch(self):
self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
@@ -730,1171 +708,270 @@ class B_LongTransactionTests(QmfTestBase010):
def test_txEnq01_route_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1)
-
+
def test_txEnq01_queueRoute_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1)
def test_txEnq10_route_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_queueRoute_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq01_txDeq01_route_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_queueRoute_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
def test_txEnq01_route_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1)
-
+
def test_txEnq01_queueRoute_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1)
def test_txEnq10_route_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_queueRoute_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq01_txDeq01_route_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_queueRoute_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
def test_txEnq01_route_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1)
-
+
def test_txEnq01_queueRoute_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1)
def test_txEnq10_route_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_queueRoute_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq01_txDeq01_route_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_queueRoute_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
-class C_ShortClusterTests(QmfTestBase010):
-
- def test_locCluster_route_defaultExch(self):
- self._do_test(self._get_name(), local_cluster_flag=True)
-
- def test_locCluster_queueRoute_defaultExch(self):
- self._do_test(self._get_name(), queue_route_type_flag=True, local_cluster_flag=True)
-
- def test_remCluster_route_defaultExch(self):
- self._do_test(self._get_name(), remote_cluster_flag=True)
-
- def test_remCluster_queueRoute_defaultExch(self):
- self._do_test(self._get_name(), queue_route_type_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_route_defaultExch(self):
- self._do_test(self._get_name(), local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_queueRoute_defaultExch(self):
- self._do_test(self._get_name(), queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
-
-class C_LongClusterTests(QmfTestBase010):
-
- def test_locCluster_route_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", local_cluster_flag=True)
-
- def test_locCluster_queueRoute_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, local_cluster_flag=True)
-
- def test_remCluster_route_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", remote_cluster_flag=True)
-
- def test_remCluster_queueRoute_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_route_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_queueRoute_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
-
- def test_locCluster_route_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", local_cluster_flag=True)
-
- def test_locCluster_queueRoute_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, local_cluster_flag=True)
-
- def test_remCluster_route_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", remote_cluster_flag=True)
-
- def test_remCluster_queueRoute_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_route_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_queueRoute_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
-
- def test_locCluster_route_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", local_cluster_flag=True)
-
- def test_locCluster_queueRoute_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, local_cluster_flag=True)
-
- def test_remCluster_route_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", remote_cluster_flag=True)
-
- def test_remCluster_queueRoute_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_route_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_queueRoute_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
-
-class D_ShortClusterTransactionTests(QmfTestBase010):
-
- def test_txEnq01_locCluster_route_defaultExch(self):
- self._do_test(self._get_name(), enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_queueRoute_defaultExch(self):
- self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_route_defaultExch(self):
- self._do_test(self._get_name(), enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_queueRoute_defaultExch(self):
- self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_remCluster_route_defaultExch(self):
- self._do_test(self._get_name(), enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_queueRoute_defaultExch(self):
- self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_route_defaultExch(self):
- self._do_test(self._get_name(), enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_queueRoute_defaultExch(self):
- self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_route_defaultExch(self):
- self._do_test(self._get_name(), enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_queueRoute_defaultExch(self):
- self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_route_defaultExch(self):
- self._do_test(self._get_name(), enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_defaultExch(self):
- self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
-
-class D_LongClusterTransactionTests(QmfTestBase010):
-
- def test_txEnq10_locCluster_route_defaultExch(self):
- self._do_test(self._get_name(), enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_queueRoute_defaultExch(self):
- self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_remCluster_route_defaultExch(self):
- self._do_test(self._get_name(), enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_queueRoute_defaultExch(self):
- self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_route_defaultExch(self):
- self._do_test(self._get_name(), enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_queueRoute_defaultExch(self):
- self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
-
- def test_txEnq01_locCluster_route_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_queueRoute_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_route_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_queueRoute_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_route_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_queueRoute_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_remCluster_route_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_queueRoute_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_route_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_queueRoute_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_route_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_queueRoute_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_route_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_queueRoute_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_route_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_queueRoute_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_route_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
-
- def test_txEnq01_locCluster_route_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_queueRoute_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_route_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_queueRoute_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_route_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_queueRoute_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_remCluster_route_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_queueRoute_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_route_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_queueRoute_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_route_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_queueRoute_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_route_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_queueRoute_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_route_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_queueRoute_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_route_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
-
- def test_txEnq01_locCluster_route_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_queueRoute_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_route_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_queueRoute_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_route_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_queueRoute_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_remCluster_route_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_queueRoute_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_route_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_queueRoute_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_route_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_queueRoute_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_route_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_queueRoute_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_route_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_queueRoute_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_route_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
-
-class E_ShortPersistenceTests(QmfTestBase010):
-
+class E_ShortPersistenceTests(QmfTestBase010):
+
def test_route_durQueue_defaultExch(self):
self._do_test(self._get_name(), queue_durable_flag=True)
-
+
def test_route_durMsg_durQueue_defaultExch(self):
self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True)
-
+
def test_queueRoute_durQueue_defaultExch(self):
self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True)
-
+
def test_queueRoute_durMsg_durQueue_defaultExch(self):
self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True)
-class E_LongPersistenceTests(QmfTestBase010):
+class E_LongPersistenceTests(QmfTestBase010):
+
-
def test_route_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True)
-
+
def test_route_durMsg_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True)
-
+
def test_queueRoute_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True)
-
+
def test_queueRoute_durMsg_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True)
-
+
def test_route_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True)
-
+
def test_route_durMsg_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True)
-
+
def test_queueRoute_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True)
-
+
def test_queueRoute_durMsg_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True)
-
+
def test_route_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True)
-
+
def test_route_durMsg_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True)
-
+
def test_queueRoute_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True)
-
+
def test_queueRoute_durMsg_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True)
-
+
class F_ShortPersistenceTransactionTests(QmfTestBase010):
-
+
def test_txEnq01_route_durQueue_defaultExch(self):
self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1)
-
+
def test_txEnq01_route_durMsg_durQueue_defaultExch(self):
self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1)
-
+
def test_txEnq01_queueRoute_durQueue_defaultExch(self):
self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
-
+
def test_txEnq01_queueRoute_durMsg_durQueue_defaultExch(self):
self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
-
+
def test_txEnq01_txDeq01_route_durQueue_defaultExch(self):
self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_route_durMsg_durQueue_defaultExch(self):
self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_queueRoute_durQueue_defaultExch(self):
self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_queueRoute_durMsg_durQueue_defaultExch(self):
self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
-
+
class F_LongPersistenceTransactionTests(QmfTestBase010):
-
+
def test_txEnq10_route_durQueue_defaultExch(self):
self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_route_durMsg_durQueue_defaultExch(self):
self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_queueRoute_durQueue_defaultExch(self):
self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_queueRoute_durMsg_durQueue_defaultExch(self):
self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
-
-
+
+
def test_txEnq01_route_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1)
-
+
def test_txEnq01_route_durMsg_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1)
-
+
def test_txEnq01_queueRoute_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
-
+
def test_txEnq01_queueRoute_durMsg_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
-
+
def test_txEnq10_route_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_route_durMsg_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_queueRoute_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_queueRoute_durMsg_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq01_txDeq01_route_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_route_durMsg_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_queueRoute_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_queueRoute_durMsg_durQueue_directExch(self):
self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_route_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1)
-
+
def test_txEnq01_route_durMsg_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1)
-
+
def test_txEnq01_queueRoute_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
-
+
def test_txEnq01_queueRoute_durMsg_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
-
+
def test_txEnq10_route_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_route_durMsg_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_queueRoute_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_queueRoute_durMsg_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq01_txDeq01_route_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_route_durMsg_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_queueRoute_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_queueRoute_durMsg_durQueue_fanoutExch(self):
self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
def test_txEnq01_route_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1)
-
+
def test_txEnq01_route_durMsg_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1)
-
+
def test_txEnq01_queueRoute_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
-
+
def test_txEnq01_queueRoute_durMsg_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
def test_txEnq10_route_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_route_durMsg_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_queueRoute_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq10_queueRoute_durMsg_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
-
+
def test_txEnq01_txDeq01_route_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_route_durMsg_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_queueRoute_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
-
+
def test_txEnq01_txDeq01_queueRoute_durMsg_durQueue_topicExch(self):
self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
-class G_ShortPersistenceClusterTests(QmfTestBase010):
-
- def test_locCluster_route_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, local_cluster_flag=True)
-
- def test_locCluster_route_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True)
-
- def test_locCluster_queueRoute_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
-
- def test_locCluster_queueRoute_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
-
- def test_remCluster_route_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, remote_cluster_flag=True)
-
- def test_remCluster_route_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, remote_cluster_flag=True)
-
- def test_remCluster_queueRoute_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
-
- def test_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_route_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_route_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_queueRoute_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
-
-class G_LongPersistenceClusterTests(QmfTestBase010):
-
-
-
- def test_locCluster_route_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, local_cluster_flag=True)
-
- def test_locCluster_route_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True)
-
- def test_locCluster_queueRoute_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
-
- def test_locCluster_queueRoute_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
-
- def test_remCluster_route_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, remote_cluster_flag=True)
-
- def test_remCluster_route_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, remote_cluster_flag=True)
-
- def test_remCluster_queueRoute_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
-
- def test_remCluster_queueRoute_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_route_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_route_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_queueRoute_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_queueRoute_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
-
- def test_locCluster_route_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, local_cluster_flag=True)
-
- def test_locCluster_route_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True)
-
- def test_locCluster_queueRoute_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
-
- def test_locCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
-
- def test_remCluster_route_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, remote_cluster_flag=True)
-
- def test_remCluster_route_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, remote_cluster_flag=True)
-
- def test_remCluster_queueRoute_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
-
- def test_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_route_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_route_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_queueRoute_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
-
- def test_locCluster_route_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, local_cluster_flag=True)
-
- def test_locCluster_route_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True)
-
- def test_locCluster_queueRoute_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
-
- def test_locCluster_queueRoute_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
-
- def test_remCluster_route_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, remote_cluster_flag=True)
-
- def test_remCluster_route_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, remote_cluster_flag=True)
-
- def test_remCluster_queueRoute_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
-
- def test_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_route_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_route_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_queueRoute_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_locCluster_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
-
-
-class H_ShortPersistenceClusterTransactionTests(QmfTestBase010):
-
- def test_txEnq01_locCluster_route_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_route_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_queueRoute_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_queueRoute_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_route_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_route_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_queueRoute_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_queueRoute_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_remCluster_route_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_route_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_queueRoute_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_route_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_route_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_queueRoute_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_route_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_route_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_queueRoute_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_route_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_route_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
-
-class H_LongPersistenceClusterTransactionTests(QmfTestBase010):
-
- def test_txEnq10_locCluster_route_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_route_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_queueRoute_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_queueRoute_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_remCluster_route_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_route_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_queueRoute_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_route_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_route_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_queueRoute_durQueue_defaultExch(self):
- self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
- self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
-
-
-
- def test_txEnq01_locCluster_route_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_route_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_queueRoute_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_queueRoute_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_route_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_route_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_queueRoute_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_queueRoute_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_route_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_route_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_queueRoute_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_queueRoute_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_remCluster_route_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_route_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_queueRoute_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_queueRoute_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_route_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_route_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_queueRoute_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_queueRoute_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_route_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_route_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_queueRoute_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_queueRoute_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_route_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_route_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_queueRoute_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_queueRoute_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_route_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_route_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_queueRoute_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_queueRoute_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_route_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_route_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durMsg_durQueue_directExch(self):
- self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
-
- def test_txEnq01_locCluster_route_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_route_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_queueRoute_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_route_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_route_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_queueRoute_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_route_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_route_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_queueRoute_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_remCluster_route_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_route_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_queueRoute_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_route_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_route_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_queueRoute_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_route_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_route_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_queueRoute_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_route_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_route_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_queueRoute_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_route_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_route_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_queueRoute_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_route_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_route_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
- self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
-
- def test_txEnq01_locCluster_route_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_route_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_queueRoute_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_locCluster_queueRoute_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_route_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_route_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_queueRoute_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq10_locCluster_queueRoute_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_route_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_route_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_queueRoute_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_queueRoute_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
-
- def test_txEnq01_remCluster_route_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_route_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_queueRoute_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_route_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_route_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_queueRoute_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq10_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_route_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_route_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_queueRoute_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_route_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_route_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_queueRoute_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_locCluster_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_route_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_route_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_queueRoute_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq10_locCluster_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_route_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_route_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
- def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
- self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
-
diff --git a/cpp/src/tests/ha_store_tests.py b/cpp/src/tests/ha_store_tests.py
new file mode 100755
index 0000000000..d1eaca1b87
--- /dev/null
+++ b/cpp/src/tests/ha_store_tests.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+"""
+This module contains tests for HA functionality that requires a store.
+It is not included as part of "make check" since it will not function
+without a store. Currently it can be run from a build of the message
+store.
+"""
+
+import os, signal, sys, time, imp, re, subprocess, glob, random, logging, shutil, math, unittest, random
+import traceback
+from qpid.messaging import Message, NotFound, ConnectionError, ReceiverError, Connection, Timeout, Disposition, REJECTED, Empty
+from qpid.datatypes import uuid4
+from brokertest import *
+from ha_test import *
+from threading import Thread, Lock, Condition
+from logging import getLogger, WARN, ERROR, DEBUG, INFO
+from qpidtoollibs import BrokerAgent
+from uuid import UUID
+
+
+class StoreTests(BrokerTest):
+ """Test for HA with persistence."""
+
+ def test_store_recovery(self):
+ """Verify basic store and recover functionality"""
+ cluster = HaCluster(self, 2)
+ sn = cluster[0].connect().session()
+ s = sn.sender("qq;{create:always,node:{durable:true}}")
+ sk = sn.sender("xx/k;{create:always,node:{type:topic, durable:true, x-declare:{type:'direct'}, x-bindings:[{exchange:xx,key:k,queue:qq}]}}")
+ s.send(Message("foo", durable=True))
+ s.send(Message("bar", durable=True))
+ sk.send(Message("baz", durable=True))
+ r = cluster[0].connect().session().receiver("qq")
+ self.assertEqual(r.fetch().content, "foo")
+ r.session.acknowledge()
+ # FIXME aconway 2012-09-21: sending this message is an ugly hack to flush
+ # the dequeue operation on qq.
+ s.send(Message("flush", durable=True))
+
+ def verify(broker, x_count):
+ sn = broker.connect().session()
+ assert_browse(sn, "qq", [ "bar", "baz", "flush" ]+ (x_count)*["x"])
+ sn.sender("xx/k").send(Message("x", durable=True))
+ assert_browse(sn, "qq", [ "bar", "baz", "flush" ]+ (x_count+1)*["x"])
+
+ verify(cluster[0], 0)
+ cluster.bounce(0, promote_next=False)
+ cluster[0].promote()
+ cluster[0].wait_status("active")
+ verify(cluster[0], 1)
+ cluster.kill(0, promote_next=False)
+ cluster[1].promote()
+ cluster[1].wait_status("active")
+ verify(cluster[1], 2)
+ cluster.bounce(1, promote_next=False)
+ cluster[1].promote()
+ cluster[1].wait_status("active")
+ verify(cluster[1], 3)
+
+ def test_catchup_store(self):
+ """Verify that a backup erases queue data from store recovery before
+ doing catch-up from the primary."""
+ cluster = HaCluster(self, 2)
+ sn = cluster[0].connect().session()
+ s1 = sn.sender("q1;{create:always,node:{durable:true}}")
+ for m in ["foo","bar"]: s1.send(Message(m, durable=True))
+ s2 = sn.sender("q2;{create:always,node:{durable:true}}")
+ sk2 = sn.sender("ex/k2;{create:always,node:{type:topic, durable:true, x-declare:{type:'direct'}, x-bindings:[{exchange:ex,key:k2,queue:q2}]}}")
+ sk2.send(Message("hello", durable=True))
+ # Wait for backup to catch up.
+ cluster[1].assert_browse_backup("q1", ["foo","bar"])
+ cluster[1].assert_browse_backup("q2", ["hello"])
+
+ # Make changes that the backup doesn't see
+ cluster.kill(1, promote_next=False)
+ time.sleep(1) # FIXME aconway 2012-09-25:
+ r1 = cluster[0].connect().session().receiver("q1")
+ for m in ["foo", "bar"]: self.assertEqual(r1.fetch().content, m)
+ r1.session.acknowledge()
+ for m in ["x","y","z"]: s1.send(Message(m, durable=True))
+ # Use old connection to unbind
+ us = cluster[0].connect_old().session(str(uuid4()))
+ us.exchange_unbind(exchange="ex", binding_key="k2", queue="q2")
+ us.exchange_bind(exchange="ex", binding_key="k1", queue="q1")
+ # Restart both brokers from store to get inconsistent sequence numbering.
+ cluster.bounce(0, promote_next=False)
+ cluster[0].promote()
+ cluster[0].wait_status("active")
+ cluster.restart(1)
+ cluster[1].wait_status("ready")
+
+ # Verify state
+ cluster[0].assert_browse("q1", ["x","y","z"])
+ cluster[1].assert_browse_backup("q1", ["x","y","z"])
+ sn = cluster[0].connect().session() # FIXME aconway 2012-09-25: should fail over!
+ sn.sender("ex/k1").send("boo")
+ cluster[0].assert_browse_backup("q1", ["x","y","z", "boo"])
+ cluster[1].assert_browse_backup("q1", ["x","y","z", "boo"])
+ sn.sender("ex/k2").send("hoo") # q2 was unbound so this should be dropped.
+ sn.sender("q2").send("end") # mark the end of the queue for assert_browse
+ cluster[0].assert_browse("q2", ["hello", "end"])
+ cluster[1].assert_browse_backup("q2", ["hello", "end"])
+
+if __name__ == "__main__":
+ shutil.rmtree("brokertest.tmp", True)
+ qpid_ha = os.getenv("QPID_HA_EXEC")
+ if qpid_ha and os.path.exists(qpid_ha):
+ os.execvp("qpid-python-test",
+ ["qpid-python-test", "-m", "ha_store_tests"] + sys.argv[1:])
+ else:
+ print "Skipping ha_store_tests, %s not available"%(qpid_ha)
diff --git a/cpp/src/tests/ha_test.py b/cpp/src/tests/ha_test.py
new file mode 100755
index 0000000000..cdf9d3dd17
--- /dev/null
+++ b/cpp/src/tests/ha_test.py
@@ -0,0 +1,304 @@
+#!/usr/bin/env python
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import os, signal, sys, time, imp, re, subprocess, glob, random, logging, shutil, math, unittest, random
+import traceback
+from qpid.messaging import Message, NotFound, ConnectionError, ReceiverError, Connection, Timeout, Disposition, REJECTED, Empty
+from qpid.datatypes import uuid4
+from brokertest import *
+from threading import Thread, Lock, Condition
+from logging import getLogger, WARN, ERROR, DEBUG, INFO
+from qpidtoollibs import BrokerAgent
+from uuid import UUID
+
+log = getLogger(__name__)
+
+class LogLevel:
+ """
+ Temporarily change the log settings on the root logger.
+ Used to suppress expected WARN messages from the python client.
+ """
+ def __init__(self, level):
+ self.save_level = getLogger().getEffectiveLevel()
+ getLogger().setLevel(level)
+
+ def restore(self):
+ getLogger().setLevel(self.save_level)
+
+class QmfAgent(object):
+ """Access to a QMF broker agent."""
+ def __init__(self, address, **kwargs):
+ self._connection = Connection.establish(
+ address, client_properties={"qpid.ha-admin":1}, **kwargs)
+ self._agent = BrokerAgent(self._connection)
+
+ def __getattr__(self, name):
+ a = getattr(self._agent, name)
+ return a
+
+class Credentials(object):
+ """SASL credentials: username, password, and mechanism"""
+ def __init__(self, username, password, mechanism):
+ (self.username, self.password, self.mechanism) = (username, password, mechanism)
+
+ def __str__(self): return "Credentials%s"%(self.tuple(),)
+
+ def tuple(self): return (self.username, self.password, self.mechanism)
+
+ def add_user(self, url): return "%s/%s@%s"%(self.username, self.password, url)
+
+class HaBroker(Broker):
+ """Start a broker with HA enabled
+ @param client_cred: (user, password, mechanism) for admin clients started by the HaBroker.
+ """
+ def __init__(self, test, args=[], brokers_url=None, ha_cluster=True, ha_replicate="all",
+ client_credentials=None, **kwargs):
+ assert BrokerTest.ha_lib, "Cannot locate HA plug-in"
+ args = copy(args)
+ args += ["--load-module", BrokerTest.ha_lib,
+ "--log-enable=debug+:ha::",
+ # FIXME aconway 2012-02-13: workaround slow link failover.
+ "--link-maintenance-interval=0.1",
+ "--ha-cluster=%s"%ha_cluster]
+ if ha_replicate is not None:
+ args += [ "--ha-replicate=%s"%ha_replicate ]
+ if brokers_url: args += [ "--ha-brokers-url", brokers_url ]
+ Broker.__init__(self, test, args, **kwargs)
+ self.qpid_ha_path=os.path.join(os.getenv("PYTHON_COMMANDS"), "qpid-ha")
+ assert os.path.exists(self.qpid_ha_path)
+ self.qpid_config_path=os.path.join(os.getenv("PYTHON_COMMANDS"), "qpid-config")
+ assert os.path.exists(self.qpid_config_path)
+ self.qpid_ha_script=import_script(self.qpid_ha_path)
+ self._agent = None
+ self.client_credentials = client_credentials
+
+ def __str__(self): return Broker.__str__(self)
+
+ def qpid_ha(self, args):
+ cred = self.client_credentials
+ url = self.host_port()
+ if cred:
+ url =cred.add_user(url)
+ args = args + ["--sasl-mechanism", cred.mechanism]
+ self.qpid_ha_script.main_except(["", "-b", url]+args)
+
+ def promote(self):
+ self.ready(); self.qpid_ha(["promote"])
+ def set_public_url(self, url): self.qpid_ha(["set", "--public-url", url])
+ def set_brokers_url(self, url): self.qpid_ha(["set", "--brokers-url", url])
+ def replicate(self, from_broker, queue): self.qpid_ha(["replicate", from_broker, queue])
+
+ def agent(self):
+ if not self._agent:
+ cred = self.client_credentials
+ if cred:
+ self._agent = QmfAgent(cred.add_user(self.host_port()), sasl_mechanisms=cred.mechanism)
+ else:
+ self._agent = QmfAgent(self.host_port())
+ return self._agent
+
+ def qmf(self):
+ hb = self.agent().getHaBroker()
+ hb.update()
+ return hb
+
+ def ha_status(self): return self.qmf().status
+
+ def wait_status(self, status):
+ def try_get_status():
+ self._status = "<unknown>"
+ # Ignore ConnectionError, the broker may not be up yet.
+ try:
+ self._status = self.ha_status()
+ return self._status == status;
+ except ConnectionError: return False
+ assert retry(try_get_status, timeout=20), "%s expected=%r, actual=%r"%(
+ self, status, self._status)
+
+ def wait_queue(self, queue, timeout=1):
+ """ Wait for queue to be visible via QMF"""
+ agent = self.agent()
+ assert retry(lambda: agent.getQueue(queue) is not None, timeout=timeout)
+
+ def wait_no_queue(self, queue, timeout=1):
+ """ Wait for queue to be invisible via QMF"""
+ agent = self.agent()
+ assert retry(lambda: agent.getQueue(queue) is None, timeout=timeout)
+
+ # FIXME aconway 2012-05-01: do direct python call to qpid-config code.
+ def qpid_config(self, args):
+ assert subprocess.call(
+ [self.qpid_config_path, "--broker", self.host_port()]+args,
+ stdout=1, stderr=subprocess.STDOUT
+ ) == 0
+
+ def config_replicate(self, from_broker, queue):
+ self.qpid_config(["add", "queue", "--start-replica", from_broker, queue])
+
+ def config_declare(self, queue, replication):
+ self.qpid_config(["add", "queue", queue, "--replicate", replication])
+
+ def connect_admin(self, **kwargs):
+ cred = self.client_credentials
+ if cred:
+ return Broker.connect(
+ self, client_properties={"qpid.ha-admin":1},
+ username=cred.username, password=cred.password, sasl_mechanisms=cred.mechanism,
+ **kwargs)
+ else:
+ return Broker.connect(self, client_properties={"qpid.ha-admin":1}, **kwargs)
+
+ def wait_address(self, address):
+ """Wait for address to become valid on the broker."""
+ bs = self.connect_admin().session()
+ try: wait_address(bs, address)
+ finally: bs.connection.close()
+
+ def wait_backup(self, address): self.wait_address(address)
+
+ def assert_browse(self, queue, expected, **kwargs):
+ """Verify queue contents by browsing."""
+ bs = self.connect().session()
+ try:
+ wait_address(bs, queue)
+ assert_browse_retry(bs, queue, expected, **kwargs)
+ finally: bs.connection.close()
+
+ def assert_browse_backup(self, queue, expected, **kwargs):
+ """Combines wait_backup and assert_browse_retry."""
+ bs = self.connect_admin().session()
+ try:
+ wait_address(bs, queue)
+ assert_browse_retry(bs, queue, expected, **kwargs)
+ finally: bs.connection.close()
+
+ def assert_connect_fail(self):
+ try:
+ self.connect()
+ self.test.fail("Expected ConnectionError")
+ except ConnectionError: pass
+
+ def try_connect(self):
+ try: return self.connect()
+ except ConnectionError: return None
+
+ def ready(self):
+ return Broker.ready(self, client_properties={"qpid.ha-admin":1})
+
+ def kill(self):
+ self._agent = None
+ return Broker.kill(self)
+
+class HaCluster(object):
+ _cluster_count = 0
+
+ def __init__(self, test, n, promote=True, wait=True, **kwargs):
+ """Start a cluster of n brokers.
+
+ @test: The test being run
+ @n: start n brokers
+ @promote: promote self[0] to primary
+ @wait: wait for primary active and backups ready. Ignored if promote=False
+ """
+ self.test = test
+ self.kwargs = kwargs
+ self._brokers = []
+ self.id = HaCluster._cluster_count
+ self.broker_id = 0
+ HaCluster._cluster_count += 1
+ for i in xrange(n): self.start(False)
+ self.update_urls()
+ if promote:
+ self[0].promote()
+ if wait:
+ self[0].wait_status("active")
+ for b in self[1:]: b.wait_status("ready")
+
+
+ def next_name(self):
+ name="cluster%s-%s"%(self.id, self.broker_id)
+ self.broker_id += 1
+ return name
+
+ def start(self, update_urls=True, args=[]):
+ """Start a new broker in the cluster"""
+ b = HaBroker(self.test, name=self.next_name(), **self.kwargs)
+ b.ready()
+ self._brokers.append(b)
+ if update_urls: self.update_urls()
+ return b
+
+ def update_urls(self):
+ self.url = ",".join([b.host_port() for b in self])
+ if len(self) > 1: # No failover addresses on a 1 cluster.
+ for b in self:
+ b.set_brokers_url(self.url)
+ b.set_public_url(self.url)
+
+ def connect(self, i):
+ """Connect with reconnect_urls"""
+ return self[i].connect(reconnect=True, reconnect_urls=self.url.split(","))
+
+ def kill(self, i, promote_next=True):
+ """Kill broker i, promote broker i+1"""
+ self[i].kill()
+ if promote_next: self[(i+1) % len(self)].promote()
+
+ def restart(self, i):
+ """Start a broker with the same port, name and data directory. It will get
+ a separate log file: foo.n.log"""
+ b = self._brokers[i]
+ self._brokers[i] = HaBroker(
+ self.test, name=b.name, port=b.port(), brokers_url=self.url,
+ **self.kwargs)
+ self._brokers[i].ready()
+
+ def bounce(self, i, promote_next=True):
+ """Stop and restart a broker in a cluster."""
+ if (len(self) == 1):
+ self.kill(i, promote_next=False)
+ self.restart(i)
+ self[i].ready()
+ if promote_next: self[i].promote()
+ else:
+ self.kill(i, promote_next)
+ self.restart(i)
+
+ # Behave like a list of brokers.
+ def __len__(self): return len(self._brokers)
+ def __getitem__(self,index): return self._brokers[index]
+ def __iter__(self): return self._brokers.__iter__()
+
+
+def wait_address(session, address):
+ """Wait for an address to become valid."""
+ def check():
+ try: session.sender(address); return True
+ except NotFound: return False
+ assert retry(check), "Timed out waiting for address %s"%(address)
+
+def valid_address(session, address):
+ """Test if an address is valid"""
+ try:
+ session.receiver(address)
+ return True
+ except NotFound: return False
+
+
diff --git a/cpp/src/tests/ha_tests.py b/cpp/src/tests/ha_tests.py
index 310ef844bd..77399bf2e2 100755
--- a/cpp/src/tests/ha_tests.py
+++ b/cpp/src/tests/ha_tests.py
@@ -18,235 +18,32 @@
# under the License.
#
-import os, signal, sys, time, imp, re, subprocess, glob, random, logging, shutil, math, unittest
+import os, signal, sys, time, imp, re, subprocess, glob, random, logging, shutil, math, unittest, random
import traceback
-from qpid.messaging import Message, NotFound, ConnectionError, ReceiverError, Connection, Timeout, Disposition, REJECTED
+from qpid.messaging import Message, SessionError, NotFound, ConnectionError, ReceiverError, Connection, Timeout, Disposition, REJECTED, Empty
from qpid.datatypes import uuid4
from brokertest import *
+from ha_test import *
from threading import Thread, Lock, Condition
from logging import getLogger, WARN, ERROR, DEBUG, INFO
-from qpidtoollibs import BrokerAgent
+from qpidtoollibs import BrokerAgent, EventHelper
from uuid import UUID
log = getLogger(__name__)
-class QmfAgent(object):
- """Access to a QMF broker agent."""
- def __init__(self, address, **kwargs):
- self._connection = Connection.establish(
- address, client_properties={"qpid.ha-admin":1}, **kwargs)
- self._agent = BrokerAgent(self._connection)
- assert self._agent.getHaBroker(), "HA module not loaded in broker at: %s"%(address)
+def grep(filename, regexp):
+ for line in open(filename).readlines():
+ if (regexp.search(line)): return True
+ return False
- def __getattr__(self, name):
- a = getattr(self._agent, name)
- return a
+class HaBrokerTest(BrokerTest):
+ """Base class for HA broker tests"""
+ def assert_log_no_errors(self, broker):
+ log = broker.get_log()
+ if grep(log, re.compile("] error|] critical")):
+ self.fail("Errors in log file %s"%(log))
-class Credentials(object):
- """SASL credentials: username, password, and mechanism"""
- def __init__(self, username, password, mechanism):
- (self.username, self.password, self.mechanism) = (username, password, mechanism)
-
- def __str__(self): return "Credentials%s"%(self.tuple(),)
-
- def tuple(self): return (self.username, self.password, self.mechanism)
-
- def add_user(self, url): return "%s/%s@%s"%(self.username, self.password, url)
-
-class HaBroker(Broker):
- """Start a broker with HA enabled
- @param client_cred: (user, password, mechanism) for admin clients started by the HaBroker.
- """
- def __init__(self, test, args=[], brokers_url=None, ha_cluster=True, ha_replicate="all",
- client_credentials=None, **kwargs):
- assert BrokerTest.ha_lib, "Cannot locate HA plug-in"
- args = copy(args)
- args += ["--load-module", BrokerTest.ha_lib,
- "--log-enable=debug+:ha::",
- # FIXME aconway 2012-02-13: workaround slow link failover.
- "--link-maintenace-interval=0.1",
- "--ha-cluster=%s"%ha_cluster]
- if ha_replicate is not None:
- args += [ "--ha-replicate=%s"%ha_replicate ]
- if brokers_url: args += [ "--ha-brokers-url", brokers_url ]
- Broker.__init__(self, test, args, **kwargs)
- self.qpid_ha_path=os.path.join(os.getenv("PYTHON_COMMANDS"), "qpid-ha")
- assert os.path.exists(self.qpid_ha_path)
- self.qpid_config_path=os.path.join(os.getenv("PYTHON_COMMANDS"), "qpid-config")
- assert os.path.exists(self.qpid_config_path)
- getLogger().setLevel(ERROR) # Hide expected WARNING log messages from failover.
- self.qpid_ha_script=import_script(self.qpid_ha_path)
- self._agent = None
- self.client_credentials = client_credentials
-
- def __str__(self): return Broker.__str__(self)
-
- def qpid_ha(self, args):
- cred = self.client_credentials
- url = self.host_port()
- if cred:
- url =cred.add_user(url)
- args = args + ["--sasl-mechanism", cred.mechanism]
- self.qpid_ha_script.main_except(["", "-b", url]+args)
-
- def promote(self): self.qpid_ha(["promote"])
- def set_client_url(self, url): self.qpid_ha(["set", "--public-url", url])
- def set_brokers_url(self, url): self.qpid_ha(["set", "--brokers-url", url])
- def replicate(self, from_broker, queue): self.qpid_ha(["replicate", from_broker, queue])
-
- def agent(self):
- if not self._agent:
- cred = self.client_credentials
- if cred:
- self._agent = QmfAgent(cred.add_user(self.host_port()), sasl_mechanisms=cred.mechanism)
- else:
- self._agent = QmfAgent(self.host_port())
- return self._agent
-
- def ha_status(self):
- hb = self.agent().getHaBroker()
- hb.update()
- return hb.status
-
- def wait_status(self, status):
- def try_get_status():
- # Ignore ConnectionError, the broker may not be up yet.
- try:
- self._status = self.ha_status()
- return self._status == status;
- except ConnectionError: return False
- assert retry(try_get_status, timeout=20), "%s %r != %r"%(self, self._status, status)
-
- # FIXME aconway 2012-05-01: do direct python call to qpid-config code.
- def qpid_config(self, args):
- assert subprocess.call(
- [self.qpid_config_path, "--broker", self.host_port()]+args) == 0
-
- def config_replicate(self, from_broker, queue):
- self.qpid_config(["add", "queue", "--start-replica", from_broker, queue])
-
- def config_declare(self, queue, replication):
- self.qpid_config(["add", "queue", queue, "--replicate", replication])
-
- def connect_admin(self, **kwargs):
- cred = self.client_credentials
- if cred:
- return Broker.connect(
- self, client_properties={"qpid.ha-admin":1},
- username=cred.username, password=cred.password, sasl_mechanisms=cred.mechanism,
- **kwargs)
- else:
- return Broker.connect(self, client_properties={"qpid.ha-admin":1}, **kwargs)
-
- def wait_backup(self, address):
- """Wait for address to become valid on a backup broker."""
- bs = self.connect_admin().session()
- try: wait_address(bs, address)
- finally: bs.connection.close()
-
- def assert_browse(self, queue, expected, **kwargs):
- """Verify queue contents by browsing."""
- bs = self.connect().session()
- try:
- wait_address(bs, queue)
- assert_browse_retry(bs, queue, expected, **kwargs)
- finally: bs.connection.close()
-
- def assert_browse_backup(self, queue, expected, **kwargs):
- """Combines wait_backup and assert_browse_retry."""
- bs = self.connect_admin().session()
- try:
- wait_address(bs, queue)
- assert_browse_retry(bs, queue, expected, **kwargs)
- finally: bs.connection.close()
-
- def assert_connect_fail(self):
- try:
- self.connect()
- self.test.fail("Expected ConnectionError")
- except ConnectionError: pass
-
- def try_connect(self):
- try: return self.connect()
- except ConnectionError: return None
-
-class HaCluster(object):
- _cluster_count = 0
-
- def __init__(self, test, n, promote=True, **kwargs):
- """Start a cluster of n brokers"""
- self.test = test
- self.kwargs = kwargs
- self._brokers = []
- self.id = HaCluster._cluster_count
- self.broker_id = 0
- HaCluster._cluster_count += 1
- for i in xrange(n): self.start(False)
- self.update_urls()
- self[0].promote()
-
- def next_name(self):
- name="cluster%s-%s"%(self.id, self.broker_id)
- self.broker_id += 1
- return name
-
- def start(self, update_urls=True, args=[]):
- """Start a new broker in the cluster"""
- b = HaBroker(self.test, name=self.next_name(), **self.kwargs)
- self._brokers.append(b)
- if update_urls: self.update_urls()
- return b
-
- def update_urls(self):
- self.url = ",".join([b.host_port() for b in self])
- if len(self) > 1: # No failover addresses on a 1 cluster.
- for b in self: b.set_brokers_url(self.url)
-
- def connect(self, i):
- """Connect with reconnect_urls"""
- return self[i].connect(reconnect=True, reconnect_urls=self.url.split(","))
-
- def kill(self, i, promote_next=True):
- """Kill broker i, promote broker i+1"""
- self[i].expect = EXPECT_EXIT_FAIL
- self[i].kill()
- if promote_next: self[(i+1) % len(self)].promote()
-
- def restart(self, i):
- """Start a broker with the same port, name and data directory. It will get
- a separate log file: foo.n.log"""
- b = self._brokers[i]
- self._brokers[i] = HaBroker(
- self.test, name=b.name, port=b.port(), brokers_url=self.url,
- **self.kwargs)
-
- def bounce(self, i, promote_next=True):
- """Stop and restart a broker in a cluster."""
- self.kill(i, promote_next)
- self.restart(i)
-
- # Behave like a list of brokers.
- def __len__(self): return len(self._brokers)
- def __getitem__(self,index): return self._brokers[index]
- def __iter__(self): return self._brokers.__iter__()
-
-def wait_address(session, address):
- """Wait for an address to become valid."""
- def check():
- try:
- session.sender(address)
- return True
- except NotFound: return False
- assert retry(check), "Timed out waiting for address %s"%(address)
-
-def valid_address(session, address):
- """Test if an address is valid"""
- try:
- session.receiver(address)
- return True
- except NotFound: return False
-
-class ReplicationTests(BrokerTest):
+class ReplicationTests(HaBrokerTest):
"""Correctness tests for HA replication."""
def test_replication(self):
@@ -256,8 +53,9 @@ class ReplicationTests(BrokerTest):
def queue(name, replicate):
return "%s;{create:always,node:{x-declare:{arguments:{'qpid.replicate':%s}}}}"%(name, replicate)
- def exchange(name, replicate, bindq):
- return"%s;{create:always,node:{type:topic,x-declare:{arguments:{'qpid.replicate':%s}, type:'fanout'},x-bindings:[{exchange:'%s',queue:'%s'}]}}"%(name, replicate, name, bindq)
+ def exchange(name, replicate, bindq, key):
+ return "%s/%s;{create:always,node:{type:topic,x-declare:{arguments:{'qpid.replicate':%s}, type:'topic'},x-bindings:[{exchange:'%s',queue:'%s',key:'%s'}]}}"%(name, key, replicate, name, bindq, key)
+
def setup(p, prefix, primary):
"""Create config, send messages on the primary p"""
s = p.sender(queue(prefix+"q1", "all"))
@@ -267,16 +65,21 @@ class ReplicationTests(BrokerTest):
p.acknowledge()
p.sender(queue(prefix+"q2", "configuration")).send(Message("2"))
p.sender(queue(prefix+"q3", "none")).send(Message("3"))
- p.sender(exchange(prefix+"e1", "all", prefix+"q1")).send(Message("4"))
- p.sender(exchange(prefix+"e2", "all", prefix+"q2")).send(Message("5"))
+ p.sender(exchange(prefix+"e1", "all", prefix+"q1", "key1")).send(Message("4"))
+ p.sender(exchange(prefix+"e2", "configuration", prefix+"q2", "key2")).send(Message("5"))
# Test unbind
p.sender(queue(prefix+"q4", "all")).send(Message("6"))
- s3 = p.sender(exchange(prefix+"e4", "all", prefix+"q4"))
+ s3 = p.sender(exchange(prefix+"e4", "all", prefix+"q4", "key4"))
s3.send(Message("7"))
# Use old connection to unbind
us = primary.connect_old().session(str(uuid4()))
- us.exchange_unbind(exchange=prefix+"e4", binding_key="", queue=prefix+"q4")
+ us.exchange_unbind(exchange=prefix+"e4", binding_key="key4", queue=prefix+"q4")
p.sender(prefix+"e4").send(Message("drop1")) # Should be dropped
+ # Test replication of deletes
+ p.sender(queue(prefix+"dq", "all"))
+ p.sender(exchange(prefix+"de", "all", prefix+"dq", ""))
+ p.sender(prefix+"dq;{delete:always}").close()
+ p.sender(prefix+"de;{delete:always}").close()
# Need a marker so we can wait till sync is done.
p.sender(queue(prefix+"x", "configuration"))
@@ -292,50 +95,61 @@ class ReplicationTests(BrokerTest):
self.assert_browse_retry(b, prefix+"q2", []) # configuration only
assert not valid_address(b, prefix+"q3")
- b.sender(prefix+"e1").send(Message(prefix+"e1")) # Verify binds with replicate=all
+
+ # Verify exchange with replicate=all
+ b.sender(prefix+"e1/key1").send(Message(prefix+"e1"))
self.assert_browse_retry(b, prefix+"q1", ["1", "4", prefix+"e1"])
- b.sender(prefix+"e2").send(Message(prefix+"e2")) # Verify binds with replicate=configuration
+
+ # Verify exchange with replicate=configuration
+ b.sender(prefix+"e2/key2").send(Message(prefix+"e2"))
self.assert_browse_retry(b, prefix+"q2", [prefix+"e2"])
- b.sender(prefix+"e4").send(Message("drop2")) # Verify unbind.
+ b.sender(prefix+"e4/key4").send(Message("drop2")) # Verify unbind.
self.assert_browse_retry(b, prefix+"q4", ["6","7"])
- primary = HaBroker(self, name="primary")
- primary.promote()
- p = primary.connect().session()
+ # Verify deletes
+ assert not valid_address(b, prefix+"dq")
+ assert not valid_address(b, prefix+"de")
- # Create config, send messages before starting the backup, to test catch-up replication.
- setup(p, "1", primary)
- backup = HaBroker(self, name="backup", brokers_url=primary.host_port())
- # Create config, send messages after starting the backup, to test steady-state replication.
- setup(p, "2", primary)
-
- # Verify the data on the backup
- b = backup.connect_admin().session()
- verify(b, "1", p)
- verify(b, "2", p)
- # Test a series of messages, enqueue all then dequeue all.
- s = p.sender(queue("foo","all"))
- wait_address(b, "foo")
- msgs = [str(i) for i in range(10)]
- for m in msgs: s.send(Message(m))
- self.assert_browse_retry(p, "foo", msgs)
- self.assert_browse_retry(b, "foo", msgs)
- r = p.receiver("foo")
- for m in msgs: self.assertEqual(m, r.fetch(timeout=0).content)
- p.acknowledge()
- self.assert_browse_retry(p, "foo", [])
- self.assert_browse_retry(b, "foo", [])
-
- # Another series, this time verify each dequeue individually.
- for m in msgs: s.send(Message(m))
- self.assert_browse_retry(p, "foo", msgs)
- self.assert_browse_retry(b, "foo", msgs)
- for i in range(len(msgs)):
- self.assertEqual(msgs[i], r.fetch(timeout=0).content)
+ l = LogLevel(ERROR) # Hide expected WARNING log messages from failover.
+ try:
+ primary = HaBroker(self, name="primary")
+ primary.promote()
+ p = primary.connect().session()
+
+ # Create config, send messages before starting the backup, to test catch-up replication.
+ setup(p, "1", primary)
+ backup = HaBroker(self, name="backup", brokers_url=primary.host_port())
+ # Create config, send messages after starting the backup, to test steady-state replication.
+ setup(p, "2", primary)
+
+ # Verify the data on the backup
+ b = backup.connect_admin().session()
+ verify(b, "1", p)
+ verify(b, "2", p)
+ # Test a series of messages, enqueue all then dequeue all.
+ s = p.sender(queue("foo","all"))
+ wait_address(b, "foo")
+ msgs = [str(i) for i in range(10)]
+ for m in msgs: s.send(Message(m))
+ self.assert_browse_retry(p, "foo", msgs)
+ self.assert_browse_retry(b, "foo", msgs)
+ r = p.receiver("foo")
+ for m in msgs: self.assertEqual(m, r.fetch(timeout=0).content)
p.acknowledge()
- self.assert_browse_retry(p, "foo", msgs[i+1:])
- self.assert_browse_retry(b, "foo", msgs[i+1:])
+ self.assert_browse_retry(p, "foo", [])
+ self.assert_browse_retry(b, "foo", [])
+
+ # Another series, this time verify each dequeue individually.
+ for m in msgs: s.send(Message(m))
+ self.assert_browse_retry(p, "foo", msgs)
+ self.assert_browse_retry(b, "foo", msgs)
+ for i in range(len(msgs)):
+ self.assertEqual(msgs[i], r.fetch(timeout=0).content)
+ p.acknowledge()
+ self.assert_browse_retry(p, "foo", msgs[i+1:])
+ self.assert_browse_retry(b, "foo", msgs[i+1:])
+ finally: l.restore()
def test_sync(self):
primary = HaBroker(self, name="primary")
@@ -361,53 +175,59 @@ class ReplicationTests(BrokerTest):
def test_send_receive(self):
"""Verify sequence numbers of messages sent by qpid-send"""
- brokers = HaCluster(self, 3)
- sender = self.popen(
- ["qpid-send",
- "--broker", brokers[0].host_port(),
- "--address", "q;{create:always}",
- "--messages=1000",
- "--content-string=x"
- ])
- receiver = self.popen(
- ["qpid-receive",
- "--broker", brokers[0].host_port(),
- "--address", "q;{create:always}",
- "--messages=990",
- "--timeout=10"
- ])
- self.assertEqual(sender.wait(), 0)
- self.assertEqual(receiver.wait(), 0)
- expect = [long(i) for i in range(991, 1001)]
- sn = lambda m: m.properties["sn"]
- brokers[1].assert_browse_backup("q", expect, transform=sn)
- brokers[2].assert_browse_backup("q", expect, transform=sn)
+ l = LogLevel(ERROR) # Hide expected WARNING log messages from failover.
+ try:
+ brokers = HaCluster(self, 3)
+ sender = self.popen(
+ ["qpid-send",
+ "--broker", brokers[0].host_port(),
+ "--address", "q;{create:always}",
+ "--messages=1000",
+ "--content-string=x"
+ ])
+ receiver = self.popen(
+ ["qpid-receive",
+ "--broker", brokers[0].host_port(),
+ "--address", "q;{create:always}",
+ "--messages=990",
+ "--timeout=10"
+ ])
+ self.assertEqual(sender.wait(), 0)
+ self.assertEqual(receiver.wait(), 0)
+ expect = [long(i) for i in range(991, 1001)]
+ sn = lambda m: m.properties["sn"]
+ brokers[1].assert_browse_backup("q", expect, transform=sn)
+ brokers[2].assert_browse_backup("q", expect, transform=sn)
+ finally: l.restore()
def test_failover_python(self):
"""Verify that backups rejects connections and that fail-over works in python client"""
- primary = HaBroker(self, name="primary", expect=EXPECT_EXIT_FAIL)
- primary.promote()
- backup = HaBroker(self, name="backup", brokers_url=primary.host_port())
- # Check that backup rejects normal connections
+ l = LogLevel(ERROR) # Hide expected WARNING log messages from failover.
try:
- backup.connect().session()
- self.fail("Expected connection to backup to fail")
- except ConnectionError: pass
- # Check that admin connections are allowed to backup.
- backup.connect_admin().close()
-
- # Test discovery: should connect to primary after reject by backup
- c = backup.connect(reconnect_urls=[primary.host_port(), backup.host_port()], reconnect=True)
- s = c.session()
- sender = s.sender("q;{create:always}")
- backup.wait_backup("q")
- sender.send("foo")
- primary.kill()
- assert retry(lambda: not is_running(primary.pid))
- backup.promote()
- sender.send("bar")
- self.assert_browse_retry(s, "q", ["foo", "bar"])
- c.close()
+ primary = HaBroker(self, name="primary", expect=EXPECT_EXIT_FAIL)
+ primary.promote()
+ backup = HaBroker(self, name="backup", brokers_url=primary.host_port())
+ # Check that backup rejects normal connections
+ try:
+ backup.connect().session()
+ self.fail("Expected connection to backup to fail")
+ except ConnectionError: pass
+ # Check that admin connections are allowed to backup.
+ backup.connect_admin().close()
+
+ # Test discovery: should connect to primary after reject by backup
+ c = backup.connect(reconnect_urls=[primary.host_port(), backup.host_port()], reconnect=True)
+ s = c.session()
+ sender = s.sender("q;{create:always}")
+ backup.wait_backup("q")
+ sender.send("foo")
+ primary.kill()
+ assert retry(lambda: not is_running(primary.pid))
+ backup.promote()
+ sender.send("bar")
+ self.assert_browse_retry(s, "q", ["foo", "bar"])
+ c.close()
+ finally: l.restore()
def test_failover_cpp(self):
"""Verify that failover works in the C++ client."""
@@ -456,51 +276,61 @@ class ReplicationTests(BrokerTest):
def test_standalone_queue_replica(self):
"""Test replication of individual queues outside of cluster mode"""
- getLogger().setLevel(ERROR) # Hide expected WARNING log messages from failover.
- primary = HaBroker(self, name="primary", ha_cluster=False)
- pc = primary.connect()
- ps = pc.session().sender("q;{create:always}")
- pr = pc.session().receiver("q;{create:always}")
- backup = HaBroker(self, name="backup", ha_cluster=False)
- br = backup.connect().session().receiver("q;{create:always}")
-
- # Set up replication with qpid-ha
- backup.replicate(primary.host_port(), "q")
- ps.send("a")
- backup.assert_browse_backup("q", ["a"])
- ps.send("b")
- backup.assert_browse_backup("q", ["a", "b"])
- self.assertEqual("a", pr.fetch().content)
- pr.session.acknowledge()
- backup.assert_browse_backup("q", ["b"])
-
- # Set up replication with qpid-config
- ps2 = pc.session().sender("q2;{create:always}")
- backup.config_replicate(primary.host_port(), "q2");
- ps2.send("x")
- backup.assert_browse_backup("q2", ["x"])
-
+ l = LogLevel(ERROR) # Hide expected WARNING log messages from failover.
+ try:
+ primary = HaBroker(self, name="primary", ha_cluster=False,
+ args=["--ha-queue-replication=yes"]);
+ pc = primary.connect()
+ ps = pc.session().sender("q;{create:always}")
+ pr = pc.session().receiver("q;{create:always}")
+ backup = HaBroker(self, name="backup", ha_cluster=False,
+ args=["--ha-queue-replication=yes"])
+ br = backup.connect().session().receiver("q;{create:always}")
+
+ # Set up replication with qpid-ha
+ backup.replicate(primary.host_port(), "q")
+ ps.send("a")
+ backup.assert_browse_backup("q", ["a"])
+ ps.send("b")
+ backup.assert_browse_backup("q", ["a", "b"])
+ self.assertEqual("a", pr.fetch().content)
+ pr.session.acknowledge()
+ backup.assert_browse_backup("q", ["b"])
+
+ # Set up replication with qpid-config
+ ps2 = pc.session().sender("q2;{create:always}")
+ backup.config_replicate(primary.host_port(), "q2");
+ ps2.send("x")
+ backup.assert_browse_backup("q2", ["x"])
+ finally: l.restore()
def test_queue_replica_failover(self):
- """Test individual queue replication from a cluster to a standalone backup broker, verify it fails over."""
- cluster = HaCluster(self, 2)
- primary = cluster[0]
- pc = cluster.connect(0)
- ps = pc.session().sender("q;{create:always}")
- pr = pc.session().receiver("q;{create:always}")
- backup = HaBroker(self, name="backup", ha_cluster=False)
- br = backup.connect().session().receiver("q;{create:always}")
- backup.replicate(cluster.url, "q")
- ps.send("a")
- backup.assert_browse_backup("q", ["a"])
- cluster.bounce(0)
- backup.assert_browse_backup("q", ["a"])
- ps.send("b")
- backup.assert_browse_backup("q", ["a", "b"])
- cluster.bounce(1)
- self.assertEqual("a", pr.fetch().content)
- pr.session.acknowledge()
- backup.assert_browse_backup("q", ["b"])
+ """Test individual queue replication from a cluster to a standalone
+ backup broker, verify it fails over."""
+ l = LogLevel(ERROR) # Hide expected WARNING log messages from failover.
+ try:
+ cluster = HaCluster(self, 2)
+ primary = cluster[0]
+ pc = cluster.connect(0)
+ ps = pc.session().sender("q;{create:always}")
+ pr = pc.session().receiver("q;{create:always}")
+ backup = HaBroker(self, name="backup", ha_cluster=False,
+ args=["--ha-queue-replication=yes"])
+ br = backup.connect().session().receiver("q;{create:always}")
+ backup.replicate(cluster.url, "q")
+ ps.send("a")
+ backup.assert_browse_backup("q", ["a"])
+ cluster.bounce(0)
+ backup.assert_browse_backup("q", ["a"])
+ ps.send("b")
+ backup.assert_browse_backup("q", ["a", "b"])
+ cluster.bounce(1)
+ self.assertEqual("a", pr.fetch().content)
+ pr.session.acknowledge()
+ backup.assert_browse_backup("q", ["b"])
+ pc.close()
+ br.close()
+ finally: l.restore()
def test_lvq(self):
"""Verify that we replicate to an LVQ correctly"""
@@ -634,8 +464,10 @@ class ReplicationTests(BrokerTest):
def test_replicate_default(self):
"""Make sure we don't replicate if ha-replicate is unspecified or none"""
cluster1 = HaCluster(self, 2, ha_replicate=None)
+ cluster1[1].wait_status("ready")
c1 = cluster1[0].connect().session().sender("q;{create:always}")
cluster2 = HaCluster(self, 2, ha_replicate="none")
+ cluster2[1].wait_status("ready")
cluster2[0].connect().session().sender("q;{create:always}")
time.sleep(.1) # Give replication a chance.
try:
@@ -647,6 +479,23 @@ class ReplicationTests(BrokerTest):
self.fail("Excpected no-such-queue exception")
except NotFound: pass
+ def test_replicate_binding(self):
+ """Verify that binding replication can be disabled"""
+ primary = HaBroker(self, name="primary", expect=EXPECT_EXIT_FAIL)
+ primary.promote()
+ backup = HaBroker(self, name="backup", brokers_url=primary.host_port())
+ ps = primary.connect().session()
+ ps.sender("ex;{create:always,node:{type:topic,x-declare:{arguments:{'qpid.replicate':all}, type:'fanout'}}}")
+ ps.sender("q;{create:always,node:{type:queue,x-declare:{arguments:{'qpid.replicate':all}},x-bindings:[{exchange:'ex',queue:'q',key:'',arguments:{'qpid.replicate':none}}]}}")
+ backup.wait_backup("q")
+
+ primary.kill()
+ assert retry(lambda: not is_running(primary.pid)) # Wait for primary to die
+ backup.promote()
+ bs = backup.connect_admin().session()
+ bs.sender("ex").send(Message("msg"))
+ self.assert_browse_retry(bs, "q", [])
+
def test_invalid_replication(self):
"""Verify that we reject an attempt to declare a queue with invalid replication value."""
cluster = HaCluster(self, 1, ha_replicate="all")
@@ -672,20 +521,26 @@ class ReplicationTests(BrokerTest):
def test_auto_delete_exclusive(self):
"""Verify that we ignore auto-delete, exclusive, non-auto-delete-timeout queues"""
- cluster = HaCluster(self,2)
- s = cluster[0].connect().session()
- s.receiver("exad;{create:always,node:{x-declare:{exclusive:True,auto-delete:True}}}")
- s.receiver("ex;{create:always,node:{x-declare:{exclusive:True}}}")
- s.receiver("ad;{create:always,node:{x-declare:{auto-delete:True}}}")
- s.receiver("time;{create:always,node:{x-declare:{exclusive:True,auto-delete:True,arguments:{'qpid.auto_delete_timeout':1}}}}")
- s.receiver("q;{create:always}")
+ cluster = HaCluster(self, 2)
+ s0 = cluster[0].connect().session()
+ s0.receiver("exad;{create:always,node:{x-declare:{exclusive:True,auto-delete:True}}}")
+ s0.receiver("ex;{create:always,node:{x-declare:{exclusive:True}}}")
+ ad = s0.receiver("ad;{create:always,node:{x-declare:{auto-delete:True}}}")
+ s0.receiver("time;{create:always,node:{x-declare:{exclusive:True,auto-delete:True,arguments:{'qpid.auto_delete_timeout':1}}}}")
+ s0.receiver("q;{create:always}")
- s = cluster[1].connect_admin().session()
+ s1 = cluster[1].connect_admin().session()
cluster[1].wait_backup("q")
- assert not valid_address(s, "exad")
- assert valid_address(s, "ex")
- assert valid_address(s, "ad")
- assert valid_address(s, "time")
+ assert not valid_address(s1, "exad")
+ assert valid_address(s1, "ex")
+ assert valid_address(s1, "ad")
+ assert valid_address(s1, "time")
+
+ # Verify that auto-delete queues are not kept alive by
+ # replicating subscriptions
+ ad.close()
+ s0.sync()
+ assert not valid_address(s0, "ad")
def test_broker_info(self):
"""Check that broker information is correctly published via management"""
@@ -763,18 +618,18 @@ acl deny all all
s.sender("altex;{create:always,node:{type:topic,x-declare:{type:'fanout'}}}")
# altq queue bound to altex, collect re-routed messages.
s.sender("altq;{create:always,node:{x-bindings:[{exchange:'altex',queue:altq}]}}")
- # 0ex exchange with alternate-exchange altex and no queues bound
- s.sender("0ex;{create:always,node:{type:topic, x-declare:{type:'direct', alternate-exchange:'altex'}}}")
+ # ex exchange with alternate-exchange altex and no queues bound
+ s.sender("ex;{create:always,node:{type:topic, x-declare:{type:'direct', alternate-exchange:'altex'}}}")
# create queue q with alternate-exchange altex
s.sender("q;{create:always,node:{type:queue, x-declare:{alternate-exchange:'altex'}}}")
# create a bunch of exchanges to ensure we don't clean up prematurely if the
# response comes in multiple fragments.
- for i in xrange(200): s.sender("00ex%s;{create:always,node:{type:topic}}"%i)
+ for i in xrange(200): s.sender("ex.%s;{create:always,node:{type:topic}}"%i)
def verify(broker):
s = broker.connect().session()
# Verify unmatched message goes to ex's alternate.
- s.sender("0ex").send("foo")
+ s.sender("ex").send("foo")
altq = s.receiver("altq")
self.assertEqual("foo", altq.fetch(timeout=0).content)
s.acknowledge()
@@ -786,20 +641,265 @@ acl deny all all
self.assertEqual("bar", altq.fetch(timeout=0).content)
s.acknowledge()
+ def ss(n): return cluster[n].connect().session()
+
# Sanity check: alternate exchanges on original broker
verify(cluster[0])
+ # Altex is in use as an alternate exchange.
+ self.assertRaises(SessionError,
+ lambda:ss(0).sender("altex;{delete:always}").close())
# Check backup that was connected during setup.
- cluster[1].wait_backup("0ex")
+ cluster[1].wait_status("ready")
+ cluster[1].wait_backup("ex")
cluster[1].wait_backup("q")
cluster.bounce(0)
verify(cluster[1])
+
# Check a newly started backup.
cluster.start()
- cluster[2].wait_backup("0ex")
+ cluster[2].wait_status("ready")
+ cluster[2].wait_backup("ex")
cluster[2].wait_backup("q")
cluster.bounce(1)
verify(cluster[2])
+ # Check that alt-exchange in-use count is replicated
+ s = cluster[2].connect().session();
+
+ self.assertRaises(SessionError,
+ lambda:ss(2).sender("altex;{delete:always}").close())
+ s.sender("q;{delete:always}").close()
+ self.assertRaises(SessionError,
+ lambda:ss(2).sender("altex;{delete:always}").close())
+ s.sender("ex;{delete:always}").close()
+ s.sender("altex;{delete:always}").close()
+
+ def test_priority_reroute(self):
+ """Regression test for QPID-4262, rerouting messages from a priority queue
+ to itself causes a crash"""
+ cluster = HaCluster(self, 2)
+ primary = cluster[0]
+ session = primary.connect().session()
+ s = session.sender("pq; {create:always, node:{x-declare:{arguments:{'qpid.priorities':10}},x-bindings:[{exchange:'amq.fanout',queue:pq}]}}")
+ for m in xrange(100): s.send(Message(str(m), priority=m%10))
+ pq = QmfAgent(primary.host_port()).getQueue("pq")
+ pq.reroute(request=0, useAltExchange=False, exchange="amq.fanout")
+ # Verify that consuming is in priority order
+ expect = [str(10*i+p) for p in xrange(9,-1,-1) for i in xrange(0,10) ]
+ actual = [m.content for m in primary.get_messages("pq", 100)]
+ self.assertEqual(expect, actual)
+
+ def test_delete_missing_response(self):
+ """Check that a backup correctly deletes leftover queues and exchanges that are
+ missing from the initial reponse set."""
+ # This test is a bit contrived, we set up the situation on backup brokers
+ # and then promote one.
+ cluster = HaCluster(self, 2, promote=False)
+
+ # cluster[0] Will be the primary
+ s = cluster[0].connect_admin().session()
+ s.sender("q1;{create:always}")
+ s.sender("e1;{create:always, node:{type:topic}}")
+
+ # cluster[1] will be the backup, has extra queues/exchanges
+ xdecl = "x-declare:{arguments:{'qpid.replicate':'all'}}"
+ node = "node:{%s}"%(xdecl)
+ s = cluster[1].connect_admin().session()
+ s.sender("q1;{create:always, %s}"%(node))
+ s.sender("q2;{create:always, %s}"%(node))
+ s.sender("e1;{create:always, node:{type:topic, %s}}"%(xdecl))
+ s.sender("e2;{create:always, node:{type:topic, %s}}"%(xdecl))
+ for a in ["q1", "q2", "e1", "e2"]: cluster[1].wait_backup(a)
+
+ cluster[0].promote()
+ # Verify the backup deletes the surplus queue and exchange
+ cluster[1].wait_status("ready")
+ s = cluster[1].connect_admin().session()
+ self.assertRaises(NotFound, s.receiver, ("q2"));
+ self.assertRaises(NotFound, s.receiver, ("e2"));
+
+
+ def test_delete_qpid_4285(self):
+ """Regression test for QPID-4285: on deleting a queue it gets stuck in a
+ partially deleted state and causes replication errors."""
+ cluster = HaCluster(self,2)
+ s = cluster[0].connect().session()
+ s.receiver("q;{create:always}")
+ cluster[1].wait_backup("q")
+ cluster.kill(0) # Make the backup take over.
+ s = cluster[1].connect().session()
+ s.receiver("q;{delete:always}").close() # Delete q on new primary
+ try:
+ s.receiver("q")
+ self.fail("Expected NotFound exception") # Should not be avaliable
+ except NotFound: pass
+ assert not cluster[1].agent().getQueue("q") # Should not be in QMF
+
+ def alt_setup(self, session, suffix):
+ # Create exchange to use as alternate and a queue bound to it.
+ # altex exchange: acts as alternate exchange
+ session.sender("altex%s;{create:always,node:{type:topic,x-declare:{type:'fanout'}}}"%(suffix))
+ # altq queue bound to altex, collect re-routed messages.
+ session.sender("altq%s;{create:always,node:{x-bindings:[{exchange:'altex%s',queue:altq%s}]}}"%(suffix,suffix,suffix))
+
+ def test_auto_delete_close(self):
+ """Verify auto-delete queues are deleted on backup if auto-deleted
+ on primary"""
+ cluster=HaCluster(self, 2)
+ p = cluster[0].connect().session()
+ self.alt_setup(p, "1")
+ r = p.receiver("adq1;{create:always,node:{x-declare:{auto-delete:True,alternate-exchange:'altex1'}}}", capacity=1)
+ s = p.sender("adq1")
+ for m in ["aa","bb","cc"]: s.send(m)
+ p.sender("adq2;{create:always,node:{x-declare:{auto-delete:True}}}")
+ cluster[1].wait_queue("adq1")
+ cluster[1].wait_queue("adq2")
+ r.close() # trigger auto-delete of adq1
+ cluster[1].wait_no_queue("adq1")
+ cluster[1].assert_browse_backup("altq1", ["aa","bb","cc"])
+ cluster[1].wait_queue("adq2")
+
+ def test_auto_delete_crash(self):
+ """Verify auto-delete queues are deleted on backup if the primary crashes"""
+ cluster=HaCluster(self, 2)
+ p = cluster[0].connect().session()
+ self.alt_setup(p,"1")
+
+ # adq1 is subscribed so will be auto-deleted.
+ r = p.receiver("adq1;{create:always,node:{x-declare:{auto-delete:True,alternate-exchange:'altex1'}}}", capacity=1)
+ s = p.sender("adq1")
+ for m in ["aa","bb","cc"]: s.send(m)
+ # adq2 is subscribed after cluster[2] starts.
+ p.sender("adq2;{create:always,node:{x-declare:{auto-delete:True}}}")
+ # adq3 is never subscribed.
+ p.sender("adq3;{create:always,node:{x-declare:{auto-delete:True}}}")
+
+ cluster.start()
+ cluster[2].wait_status("ready")
+
+ p.receiver("adq2") # Subscribed after cluster[2] joined
+
+ for q in ["adq1","adq2","adq3","altq1"]: cluster[1].wait_queue(q)
+ for q in ["adq1","adq2","adq3","altq1"]: cluster[2].wait_queue(q)
+ cluster[0].kill()
+
+ cluster[1].wait_no_queue("adq1")
+ cluster[1].wait_no_queue("adq2")
+ cluster[1].wait_queue("adq3")
+
+ cluster[2].wait_no_queue("adq1")
+ cluster[2].wait_no_queue("adq2")
+ cluster[2].wait_queue("adq3")
+
+ cluster[1].assert_browse_backup("altq1", ["aa","bb","cc"])
+ cluster[2].assert_browse_backup("altq1", ["aa","bb","cc"])
+
+ def test_auto_delete_timeout(self):
+ cluster = HaCluster(self, 2)
+ # Test timeout
+ r1 = cluster[0].connect().session().receiver("q1;{create:always,node:{x-declare:{auto-delete:True,arguments:{'qpid.auto_delete_timeout':1}}}}")
+ # Test special case of timeout = 0
+ r0 = cluster[0].connect().session().receiver("q0;{create:always,node:{x-declare:{auto-delete:True,arguments:{'qpid.auto_delete_timeout':0}}}}")
+ cluster[1].wait_queue("q0")
+ cluster[1].wait_queue("q1")
+ cluster[0].kill()
+ cluster[1].wait_queue("q1") # Not timed out yet
+ cluster[1].wait_no_queue("q1", timeout=5) # Wait for timeout
+ cluster[1].wait_no_queue("q0", timeout=5) # Wait for timeout
+
+ def test_alt_exchange_dup(self):
+ """QPID-4349: if a queue has an alterante exchange and is deleted the
+ messages appear twice on the alternate, they are rerouted once by the
+ primary and again by the backup."""
+ cluster = HaCluster(self,2)
+
+ # Set up q with alternate exchange altex bound to altq.
+ s = cluster[0].connect().session()
+ s.sender("altex;{create:always,node:{type:topic,x-declare:{type:'fanout'}}}")
+ s.sender("altq;{create:always,node:{x-bindings:[{exchange:'altex',queue:altq}]}}")
+ snd = s.sender("q;{create:always,node:{x-declare:{alternate-exchange:'altex'}}}")
+ messages = [ str(n) for n in xrange(10) ]
+ for m in messages: snd.send(m)
+ cluster[1].assert_browse_backup("q", messages)
+ s.sender("q;{delete:always}").close()
+ cluster[1].assert_browse_backup("altq", messages)
+
+ def test_expired(self):
+ """Regression test for QPID-4379: HA does not properly handle expired messages"""
+ # Race between messages expiring and HA replicating consumer.
+ cluster = HaCluster(self, 2)
+ s = cluster[0].connect().session().sender("q;{create:always}", capacity=2)
+ def send_ttl_messages():
+ for i in xrange(100): s.send(Message(str(i), ttl=0.001), timeout=1)
+ send_ttl_messages()
+ cluster.start()
+ send_ttl_messages()
+
+ def test_stale_response(self):
+ """Check for race condition where a stale response is processed after an
+ event for the same queue/exchange """
+ cluster = HaCluster(self, 2)
+ s = cluster[0].connect().session()
+ s.sender("keep;{create:always}") # Leave this queue in place.
+ for i in xrange(1000):
+ s.sender("deleteme%s;{create:always,delete:always}"%(i)).close()
+ # It is possible for the backup to attempt to subscribe after the queue
+ # is deleted. This is not an error, but is logged as an error on the primary.
+ # The backup does not log this as an error so we only check the backup log for errors.
+ self.assert_log_no_errors(cluster[1])
+
+ def test_missed_recreate(self):
+ """If a queue or exchange is destroyed and one with the same name re-created
+ while a backup is disconnected, the backup should also delete/recreate
+ the object when it re-connects"""
+ cluster = HaCluster(self, 3)
+ sn = cluster[0].connect().session()
+ # Create a queue with messages
+ s = sn.sender("qq;{create:always}")
+ msgs = [str(i) for i in xrange(3)]
+ for m in msgs: s.send(m)
+ cluster[1].assert_browse_backup("qq", msgs)
+ cluster[2].assert_browse_backup("qq", msgs)
+ # Set up an exchange with a binding.
+ sn.sender("xx;{create:always,node:{type:topic}}")
+ sn.sender("xxq;{create:always,node:{x-bindings:[{exchange:'xx',queue:'xxq',key:xxq}]}}")
+ cluster[1].wait_address("xx")
+ self.assertEqual(cluster[1].agent().getExchange("xx").values["bindingCount"], 1)
+ cluster[2].wait_address("xx")
+ self.assertEqual(cluster[2].agent().getExchange("xx").values["bindingCount"], 1)
+
+ # Simulate the race by re-creating the objects before promoting the new primary
+ cluster.kill(0, False)
+ xdecl = "x-declare:{arguments:{'qpid.replicate':'all'}}"
+ node = "node:{%s}"%(xdecl)
+ sn = cluster[1].connect_admin().session()
+ sn.sender("qq;{delete:always}").close()
+ s = sn.sender("qq;{create:always, %s}"%(node))
+ s.send("foo")
+ sn.sender("xx;{delete:always}").close()
+ sn.sender("xx;{create:always,node:{type:topic,%s}}"%(xdecl))
+ cluster[1].promote()
+ cluster[1].wait_status("active")
+ # Verify we are not still using the old objects on cluster[2]
+ cluster[2].assert_browse_backup("qq", ["foo"])
+ cluster[2].wait_address("xx")
+ self.assertEqual(cluster[2].agent().getExchange("xx").values["bindingCount"], 0)
+
+ def test_redeclare_exchange(self):
+ """Ensure that re-declaring an exchange is an HA no-op"""
+ cluster = HaCluster(self, 2)
+ ps = cluster[0].connect().session()
+ ps.sender("ex1;{create:always,node:{type:topic,x-declare:{arguments:{'qpid.replicate':all}, type:'fanout'}}}")
+ ps.sender("ex2;{create:always,node:{type:topic,x-declare:{arguments:{'qpid.replicate':all}, type:'fanout', alternate-exchange:'ex1'}}}")
+ cluster[1].wait_backup("ex1")
+ cluster[1].wait_backup("ex2")
+
+ # Use old API to re-declare the exchange
+ old_conn = cluster[0].connect_old()
+ old_sess = old_conn.session(str(qpid.datatypes.uuid4()))
+ old_sess.exchange_declare(exchange='ex1', type='fanout')
+ cluster[1].wait_backup("ex1")
+
def fairshare(msgs, limit, levels):
"""
Generator to return prioritised messages in expected order for a given fairshare limit
@@ -812,7 +912,7 @@ def fairshare(msgs, limit, levels):
msgs = postponed
count = 0
last_priority = None
- postponed = []
+ postponed = [ ]
msg = msgs.pop(0)
if last_priority and priority_level(msg.priority, levels) == last_priority:
count += 1
@@ -834,7 +934,7 @@ def priority_level(value, levels):
offset = 5-math.ceil(levels/2.0)
return min(max(value - offset, 0), levels-1)
-class LongTests(BrokerTest):
+class LongTests(HaBrokerTest):
"""Tests that can run for a long time if -DDURATION=<minutes> is set"""
def duration(self):
@@ -860,7 +960,7 @@ class LongTests(BrokerTest):
"""Wait for receiver r to pass n"""
def check():
r.check() # Verify no exceptions
- return r.received > n
+ return r.received > n + 100
assert retry(check), "Stalled %s at %s"%(r.queue, n)
for r in receivers: wait_passed(r, 0)
@@ -868,87 +968,176 @@ class LongTests(BrokerTest):
# Kill and restart brokers in a cycle:
endtime = time.time() + self.duration()
i = 0
+ primary = 0
try:
while time.time() < endtime or i < 3: # At least 3 iterations
+ # Precondition: All 3 brokers running,
+ # primary = index of promoted primary
+ # one or two backups are running,
for s in senders: s.sender.assert_running()
for r in receivers: r.receiver.assert_running()
- checkpoint = [ r.received for r in receivers ]
- # Don't kill primary till it is active and the next
- # backup is ready, otherwise we can lose messages.
- brokers[i%3].wait_status("active")
- brokers[(i+1)%3].wait_status("ready")
- brokers.bounce(i%3)
+ checkpoint = [ r.received+100 for r in receivers ]
+ dead = None
+ victim = random.randint(0,2)
+ if victim == primary:
+ # Don't kill primary till it is active and the next
+ # backup is ready, otherwise we can lose messages.
+ brokers[victim].wait_status("active")
+ next = (victim+1)%3
+ brokers[next].wait_status("ready")
+ brokers.bounce(victim) # Next one is promoted
+ primary = next
+ else:
+ brokers.kill(victim, False)
+ dead = victim
+
+ # At this point the primary is running with 1 or 2 backups
+ # Make sure we are not stalled
+ map(wait_passed, receivers, checkpoint)
+ # Run another checkpoint to ensure things work in this configuration
+ checkpoint = [ r.received+100 for r in receivers ]
+ map(wait_passed, receivers, checkpoint)
+
+ if dead is not None:
+ brokers.restart(dead) # Restart backup
+ brokers[dead].ready()
+ dead = None
i += 1
- map(wait_passed, receivers, checkpoint) # Wait for all receivers
except:
traceback.print_exc()
raise
finally:
for s in senders: s.stop()
for r in receivers: r.stop()
- dead = []
+ unexpected_dead = []
for i in xrange(3):
- if not brokers[i].is_running(): dead.append(i)
- brokers.kill(i, False)
- if dead: raise Exception("Brokers not running: %s"%dead)
+ if not brokers[i].is_running() and i != dead:
+ unexpected_dead.append(i)
+ if brokers[i].is_running(): brokers.kill(i, False)
+ if unexpected_dead:
+ raise Exception("Brokers not running: %s"%unexpected_dead)
+
+ def test_qmf_order(self):
+ """QPID 4402: HA QMF events can be out of order.
+ This test mimics the test described in the JIRA. Two threads repeatedly
+ declare the same auto-delete queue and close their connection.
+ """
+ broker = Broker(self)
+ class Receiver(Thread):
+ def __init__(self, qname):
+ Thread.__init__(self)
+ self.qname = qname
+ self.stopped = False
+
+ def run(self):
+ while not self.stopped:
+ self.connection = broker.connect()
+ try:
+ self.connection.session().receiver(
+ self.qname+";{create:always,node:{x-declare:{auto-delete:True}}}")
+ except NotFound: pass # Can occur occasionally, not an error.
+ try: self.connection.close()
+ except: pass
+
+ class QmfObject(object):
+ """Track existance of an object and validate QMF events"""
+ def __init__(self, type_name, name_field, name):
+ self.type_name, self.name_field, self.name = type_name, name_field, name
+ self.exists = False
+
+ def qmf_event(self, event):
+ content = event.content[0]
+ event_type = content['_schema_id']['_class_name']
+ values = content['_values']
+ if event_type == self.type_name+"Declare" and values[self.name_field] == self.name:
+ disp = values['disp']
+ log.debug("Event %s: disp=%s exists=%s"%(
+ event_type, values['disp'], self.exists))
+ if self.exists: assert values['disp'] == 'existing'
+ else: assert values['disp'] == 'created'
+ self.exists = True
+ elif event_type == self.type_name+"Delete" and values[self.name_field] == self.name:
+ log.debug("Event %s: exists=%s"%(event_type, self.exists))
+ assert self.exists
+ self.exists = False
+
+ # Verify order of QMF events.
+ helper = EventHelper()
+ r = broker.connect().session().receiver(helper.eventAddress())
+ threads = [Receiver("qq"), Receiver("qq")]
+ for t in threads: t.start()
+ queue = QmfObject("queue", "qName", "qq")
+ finish = time.time() + self.duration()
+ try:
+ while time.time() < finish:
+ queue.qmf_event(r.fetch())
+ finally:
+ for t in threads: t.stopped = True; t.join()
-class RecoveryTests(BrokerTest):
+class RecoveryTests(HaBrokerTest):
"""Tests for recovery after a failure."""
def test_queue_hold(self):
"""Verify that the broker holds queues without sufficient backup,
i.e. does not complete messages sent to those queues."""
- # We don't want backups to time out for this test, set long timeout.
- cluster = HaCluster(self, 4, args=["--ha-backup-timeout=100000"]);
- # Wait for the primary to be ready
- cluster[0].wait_status("active")
- # Create a queue before the failure.
- s1 = cluster.connect(0).session().sender("q1;{create:always}")
- for b in cluster: b.wait_backup("q1")
- for i in xrange(100): s1.send(str(i))
- # Kill primary and 2 backups
- for i in [0,1,2]: cluster.kill(i, False)
- cluster[3].promote() # New primary, backups will be 1 and 2
- cluster[3].wait_status("recovering")
-
- def assertSyncTimeout(s):
- try:
- s.sync(timeout=.01)
- self.fail("Expected Timeout exception")
- except Timeout: pass
-
- # Create a queue after the failure
- s2 = cluster.connect(3).session().sender("q2;{create:always}")
-
- # Verify that messages sent are not completed
- for i in xrange(100,200): s1.send(str(i), sync=False); s2.send(str(i), sync=False)
- assertSyncTimeout(s1)
- self.assertEqual(s1.unsettled(), 100)
- assertSyncTimeout(s2)
- self.assertEqual(s2.unsettled(), 100)
-
- # Verify we can receive even if sending is on hold:
- cluster[3].assert_browse("q1", [str(i) for i in range(100)+range(100,200)])
-
- # Restart backups, verify queues are released only when both backups are up
- cluster.restart(1)
- assertSyncTimeout(s1)
- self.assertEqual(s1.unsettled(), 100)
- assertSyncTimeout(s2)
- self.assertEqual(s2.unsettled(), 100)
- self.assertEqual(cluster[3].ha_status(), "recovering")
- cluster.restart(2)
-
- # Verify everything is up to date and active
- def settled(sender): sender.sync(); return sender.unsettled() == 0;
- assert retry(lambda: settled(s1)), "Unsetttled=%s"%(s1.unsettled())
- assert retry(lambda: settled(s2)), "Unsetttled=%s"%(s2.unsettled())
- cluster[1].assert_browse_backup("q1", [str(i) for i in range(100)+range(100,200)])
- cluster[1].assert_browse_backup("q2", [str(i) for i in range(100,200)])
- cluster[3].wait_status("active"),
- s1.session.connection.close()
- s2.session.connection.close()
+ l = LogLevel(ERROR) # Hide expected WARNING log messages from failover.
+ try:
+ # We don't want backups to time out for this test, set long timeout.
+ cluster = HaCluster(self, 4, args=["--ha-backup-timeout=120"]);
+ # Wait for the primary to be ready
+ cluster[0].wait_status("active")
+ for b in cluster[1:4]: b.wait_status("ready")
+ # Create a queue before the failure.
+ s1 = cluster.connect(0).session().sender("q1;{create:always}")
+ for b in cluster: b.wait_backup("q1")
+ for i in xrange(100): s1.send(str(i))
+
+ # Kill primary and 2 backups
+ cluster[3].wait_status("ready")
+ for i in [0,1,2]: cluster.kill(i, False)
+ cluster[3].promote() # New primary, backups will be 1 and 2
+ cluster[3].wait_status("recovering")
+
+ def assertSyncTimeout(s):
+ try:
+ s.sync(timeout=.01)
+ self.fail("Expected Timeout exception")
+ except Timeout: pass
+
+ # Create a queue after the failure
+ s2 = cluster.connect(3).session().sender("q2;{create:always}")
+
+ # Verify that messages sent are not completed
+ for i in xrange(100,200):
+ s1.send(str(i), sync=False);
+ s2.send(str(i), sync=False)
+ assertSyncTimeout(s1)
+ self.assertEqual(s1.unsettled(), 100)
+ assertSyncTimeout(s2)
+ self.assertEqual(s2.unsettled(), 100)
+
+ # Verify we can receive even if sending is on hold:
+ cluster[3].assert_browse("q1", [str(i) for i in range(200)])
+
+ # Restart backups, verify queues are released only when both backups are up
+ cluster.restart(1)
+ assertSyncTimeout(s1)
+ self.assertEqual(s1.unsettled(), 100)
+ assertSyncTimeout(s2)
+ self.assertEqual(s2.unsettled(), 100)
+ cluster.restart(2)
+
+ # Verify everything is up to date and active
+ def settled(sender): sender.sync(timeout=1); return sender.unsettled() == 0;
+ assert retry(lambda: settled(s1)), "Unsetttled=%s"%(s1.unsettled())
+ assert retry(lambda: settled(s2)), "Unsetttled=%s"%(s2.unsettled())
+ cluster[1].assert_browse_backup("q1", [str(i) for i in range(100)+range(100,200)])
+ cluster[1].assert_browse_backup("q2", [str(i) for i in range(100,200)])
+ cluster[3].wait_status("active"),
+ s1.session.connection.close()
+ s2.session.connection.close()
+ finally: l.restore()
def test_expected_backup_timeout(self):
"""Verify that we time-out expected backups and release held queues
@@ -972,6 +1161,52 @@ class RecoveryTests(BrokerTest):
s.sync(timeout=1) # And released after the timeout.
self.assertEqual(cluster[2].ha_status(), "active")
+ def test_join_ready_cluster(self):
+ """If we join a cluster where the primary is dead, the new primary is
+ not yet promoted and there are ready backups then we should refuse
+ promotion so that one of the ready backups can be chosen."""
+ # FIXME aconway 2012-10-05: smaller timeout
+ cluster = HaCluster(self, 2, args=["--link-heartbeat-interval", 1])
+ cluster[0].wait_status("active")
+ cluster[1].wait_status("ready")
+ cluster.bounce(0, promote_next=False)
+ self.assertRaises(Exception, cluster[0].promote)
+ os.kill(cluster[1].pid, signal.SIGSTOP) # Test for timeout if unresponsive.
+ cluster.bounce(0, promote_next=False)
+ cluster[0].promote()
+
+
+class ConfigurationTests(HaBrokerTest):
+ """Tests for configuration settings."""
+
+ def test_client_broker_url(self):
+ """Check that setting of broker and public URLs obeys correct defaulting
+ and precedence"""
+
+ def check(broker, brokers, public):
+ qmf = broker.qmf()
+ self.assertEqual(brokers, qmf.brokersUrl)
+ self.assertEqual(public, qmf.publicUrl)
+
+ def start(brokers, public, known=None):
+ args=[]
+ if brokers: args.append("--ha-brokers-url="+brokers)
+ if public: args.append("--ha-public-url="+public)
+ if known: args.append("--known-hosts-url="+known)
+ return HaBroker(self, args=args)
+
+ # Both set explictily, no defaulting
+ b = start("foo:123", "bar:456")
+ check(b, "amqp:tcp:foo:123", "amqp:tcp:bar:456")
+ b.set_brokers_url("foo:999")
+ check(b, "amqp:tcp:foo:999", "amqp:tcp:bar:456")
+ b.set_public_url("bar:999")
+ check(b, "amqp:tcp:foo:999", "amqp:tcp:bar:999")
+
+ # Allow "none" to mean "not set"
+ b = start("none", "none")
+ check(b, "", "")
+
if __name__ == "__main__":
shutil.rmtree("brokertest.tmp", True)
qpid_ha = os.getenv("QPID_HA_EXEC")
@@ -979,5 +1214,5 @@ if __name__ == "__main__":
os.execvp("qpid-python-test",
["qpid-python-test", "-m", "ha_tests"] + sys.argv[1:])
else:
- print "Skipping ha_tests, qpid_ha not available"
+ print "Skipping ha_tests, %s not available"%(qpid_ha)
diff --git a/cpp/src/tests/ipv6_test b/cpp/src/tests/ipv6_test
index 9d1cb2acdd..f47e721513 100755
--- a/cpp/src/tests/ipv6_test
+++ b/cpp/src/tests/ipv6_test
@@ -122,43 +122,3 @@ else
rm rdata-in rdata-out
fi
-# Cluster smoke test follows
-test -z $CLUSTER_LIB && exit 0 # Exit if cluster not supported.
-
-## Test failover in a cluster using IPv6 only
-. cpg_check.sh
-cpg_enabled || exit 0
-
-pick_port() {
- # We need a fixed port to set --cluster-url. Use qpidd to pick a free port.
- # Note this method is racy
- PICK=$($QPIDD_EXEC -dp0)
- $QPIDD_EXEC -qp $PICK
- echo $PICK
-}
-
-ssl_cluster_broker() { # $1 = port
- $QPIDD_EXEC $COMMON_OPTS --load-module $CLUSTER_LIB --cluster-name ipv6_test.$HOSTNAME.$$ --cluster-url amqp:[$TEST_HOSTNAME]:$1 --port $1
- # Wait for broker to be ready
- ./qpid-ping -b $TEST_HOSTNAME -qp $1 || { echo "Cannot connect to broker on $1"; exit 1; }
- echo "Running IPv6 cluster broker on port $1"
-}
-
-PORT1=`pick_port`; ssl_cluster_broker $PORT1
-PORT2=`pick_port`; ssl_cluster_broker $PORT2
-
-# Pipe receive output to uniq to remove duplicates
-./qpid-receive --connection-options "{reconnect:true, reconnect-timeout:5}" --failover-updates -b amqp:[$TEST_HOSTNAME]:$PORT1 -a "foo;{create:always}" -f | uniq > ssl_test_receive.tmp &
-
-./qpid-send -b amqp:[$TEST_HOSTNAME]:$PORT2 --content-string=one -a "foo;{create:always}"
-
-$QPIDD_EXEC -qp $PORT1 # Kill broker 1 receiver should fail-over.
-./qpid-send -b amqp:[$TEST_HOSTNAME]:$PORT2 --content-string=two -a "foo;{create:always}" --send-eos 1
-wait # Wait for qpid-receive
-{ echo one; echo two; } > ssl_test_receive.cmp
-diff ssl_test_receive.tmp ssl_test_receive.cmp || { echo "Failover failed"; exit 1; }
-
-$QPIDD_EXEC -qp $PORT2
-
-rm -f ssl_test_receive.*
-
diff --git a/cpp/src/tests/legacystore/.valgrind.supp b/cpp/src/tests/legacystore/.valgrind.supp
new file mode 100644
index 0000000000..5c1c5377bf
--- /dev/null
+++ b/cpp/src/tests/legacystore/.valgrind.supp
@@ -0,0 +1,35 @@
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:_Znwm
+ fun:_ZNSs4_Rep9_S_createEmmRKSaIcE
+ fun:_ZNSs12_S_constructIPKcEEPcT_S3_RKSaIcESt20forward_iterator_tag
+ fun:_ZNSsC1EPKcRKSaIcE
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:_Znwm
+ fun:_ZNSs4_Rep9_S_createEmmRKSaIcE
+ fun:_ZNSs4_Rep8_M_cloneERKSaIcEm
+ fun:_ZNSs7reserveEm
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:_Znwm
+ fun:_ZNSs4_Rep9_S_createEmmRKSaIcE
+ fun:_ZNSs9_M_mutateEmmm
+ fun:_ZNSs15_M_replace_safeEmmPKcm
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:_Znwm
+ fun:_ZNSs4_Rep9_S_createEmmRKSaIcE
+ fun:_ZNSsC1IPcEET_S1_RKSaIcE
+}
+
diff --git a/cpp/src/tests/legacystore/.valgrindrc b/cpp/src/tests/legacystore/.valgrindrc
new file mode 100644
index 0000000000..4aba7661de
--- /dev/null
+++ b/cpp/src/tests/legacystore/.valgrindrc
@@ -0,0 +1,7 @@
+--gen-suppressions=all
+--leak-check=full
+--demangle=yes
+--suppressions=.valgrind.supp
+--num-callers=25
+--trace-children=yes
+
diff --git a/cpp/src/tests/legacystore/CMakeLists.txt b/cpp/src/tests/legacystore/CMakeLists.txt
new file mode 100644
index 0000000000..6cfaa7ec17
--- /dev/null
+++ b/cpp/src/tests/legacystore/CMakeLists.txt
@@ -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.
+#
+
+if(BUILD_LEGACYSTORE)
+
+message(STATUS "Building legacystore tests")
+
+# Enable dashboard reporting.
+include (CTest)
+
+# Make sure that everything get built before the tests
+# Need to create a var with all the necessary top level targets
+
+# If we're linking Boost for DLLs, turn that on for the unit test too.
+if (QPID_LINK_BOOST_DYNAMIC)
+ add_definitions(-DBOOST_TEST_DYN_LINK)
+endif (QPID_LINK_BOOST_DYNAMIC)
+
+include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
+
+include (FindPythonInterp)
+
+# # Inherit environment from parent script
+# set (abs_srcdir ${CMAKE_CURRENT_SOURCE_DIR})
+# set (abs_builddir ${CMAKE_CURRENT_BINARY_DIR})
+# set (abs_top_srcdir ${CMAKE_SOURCE_DIR})
+# set (abs_top_builddir ${CMAKE_BINARY_DIR})
+# set (builddir_lib_suffix "")
+
+# If valgrind is selected in the configuration step, set up the path to it
+# for CTest.
+if (ENABLE_VALGRIND)
+ set (MEMORYCHECK_COMMAND ${VALGRIND})
+ set (MEMORYCHECK_COMMAND_OPTIONS "--gen-suppressions=all
+--leak-check=full
+--demangle=yes
+--suppressions=${CMAKE_CURRENT_SOURCE_DIR}/.valgrind.supp
+--num-callers=25
+--log-file=ctest_valgrind.vglog")
+endif (ENABLE_VALGRIND)
+
+# Like this to work with cmake 2.4 on Unix
+set (qpid_test_boost_libs
+ ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${Boost_SYSTEM_LIBRARY})
+
+#
+# Unit test program
+#
+# Unit tests are built as a single program to reduce valgrind overhead
+# when running the tests. If you want to build a subset of the tests run
+# ccmake and set unit_tests_to_build to the set you want to build.
+# HACK ALERT - Unit tests are built individually to resolve a conflict
+# with running multiple brokers that connect to 0.0.0.0:5672 and that
+# womp on each other's store directory.
+
+#
+# define_selftest
+# macro to accept the name of a single source file and to create a
+# unit test executable that runs the source.
+#
+MACRO (define_selftest theSourceFile)
+add_executable (legacystore_${theSourceFile}
+ unit_test
+ ${theSourceFile}
+ ${platform_test_additions})
+target_link_libraries (legacystore_${theSourceFile}
+ ${qpid_test_boost_libs}
+ qpidmessaging qpidbroker qmfconsole legacystore)
+get_property(ls_include TARGET legacystore_${theSourceFile} PROPERTY INCLUDE_DIRECTORIES)
+list(APPEND ls_include ${abs_top_srcdir}/src/qpid/legacystore)
+list(APPEND ls_include ${abs_top_srcdir}/src/tests)
+set_target_properties (legacystore_${theSourceFile} PROPERTIES
+ INCLUDE_DIRECTORIES "${ls_include}"
+ COMPILE_DEFINITIONS _IN_QPID_BROKER)
+remember_location(legacystore_${theSourceFile})
+set(test_wrap ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_test${test_script_suffix})
+
+add_test (legacystore_${theSourceFile} ${test_wrap} ${legacystore_${theSourceFile}_LOCATION})
+ENDMACRO (define_selftest)
+
+# add_definitions(-H)
+
+define_selftest (SimpleTest)
+define_selftest (OrderingTest)
+define_selftest (TransactionalTest)
+define_selftest (TwoPhaseCommitTest)
+
+#
+# Other test programs
+#
+
+# This should ideally be done as part of the test run, but I don't know a way
+# to get these arguments and the working directory set like Makefile.am does,
+# and have that run during the test pass.
+if (PYTHON_EXECUTABLE)
+ set (python_bld ${CMAKE_CURRENT_BINARY_DIR}/python)
+ execute_process(COMMAND ${PYTHON_EXECUTABLE} setup.py install --prefix=${pythoon_bld} --install-lib=${python_bld} --install-scripts=${python_bld}/commands
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/../python)
+endif (PYTHON_EXECUTABLE)
+
+endif (BUILD_LEGACYSTORE)
diff --git a/cpp/src/tests/legacystore/MessageUtils.h b/cpp/src/tests/legacystore/MessageUtils.h
new file mode 100644
index 0000000000..6552357c72
--- /dev/null
+++ b/cpp/src/tests/legacystore/MessageUtils.h
@@ -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/broker/Message.h>
+#include <qpid/broker/Queue.h>
+#include <qpid/broker/amqp_0_10/MessageTransfer.h>
+#include <qpid/framing/AMQFrame.h>
+#include <qpid/framing/all_method_bodies.h>
+#include <qpid/framing/Uuid.h>
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+struct MessageUtils
+{
+ static Message createMessage(const std::string& exchange, const std::string& routingKey,
+ const Uuid& messageId=Uuid(), const bool durable = false,
+ const uint64_t contentSize = 0, const std::string& correlationId = std::string())
+ {
+ boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> msg(new qpid::broker::amqp_0_10::MessageTransfer());
+
+ AMQFrame method(( MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+
+ msg->getFrames().append(method);
+ msg->getFrames().append(header);
+ MessageProperties* props = msg->getFrames().getHeaders()->get<MessageProperties>(true);
+ props->setContentLength(contentSize);
+ props->setMessageId(messageId);
+ props->setCorrelationId(correlationId);
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey);
+ if (durable)
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setDeliveryMode(PERSISTENT);
+ return Message(msg, msg);
+ }
+
+ static void addContent(Message msg, const std::string& data)
+ {
+ AMQFrame content((AMQContentBody(data)));
+ qpid::broker::amqp_0_10::MessageTransfer::get(msg).getFrames().append(content);
+ }
+
+ struct MessageRetriever : public Consumer
+ {
+ MessageRetriever(Queue& q) : Consumer("test", CONSUMER), queue(q) {};
+
+ bool deliver(const QueueCursor& c, const Message& m)
+ {
+ message = m;
+ cursor = c;
+ return true;
+ };
+ void notify() {}
+ void cancel() {}
+ void acknowledged(const DeliveryRecord&) {}
+ OwnershipToken* getSession() { return 0; }
+
+ const Queue& queue;
+ Message message;
+ QueueCursor cursor;
+ };
+
+ static Message get(Queue& queue, QueueCursor* cursor = 0)
+ {
+ boost::shared_ptr<MessageRetriever> consumer(new MessageRetriever(queue));
+ if (!queue.dispatch(consumer))throw qpid::Exception("No message found!");
+ if (cursor) *cursor = consumer->cursor;
+ return consumer->message;
+ }
+
+ static Uuid getMessageId(const Message& message)
+ {
+ return qpid::broker::amqp_0_10::MessageTransfer::get(message).getProperties<MessageProperties>()->getMessageId();
+ }
+
+ static std::string getCorrelationId(const Message& message)
+ {
+ return qpid::broker::amqp_0_10::MessageTransfer::get(message).getProperties<MessageProperties>()->getCorrelationId();
+ }
+
+ static void deliver(Message& msg, FrameHandler& h, uint16_t framesize)
+ {
+ qpid::broker::amqp_0_10::MessageTransfer::get(msg).sendHeader(h, framesize, false, 0, 0, qpid::types::Variant::Map());
+ qpid::broker::amqp_0_10::MessageTransfer::get(msg).sendContent(h, framesize);
+ }
+
+};
diff --git a/cpp/src/tests/legacystore/OrderingTest.cpp b/cpp/src/tests/legacystore/OrderingTest.cpp
new file mode 100644
index 0000000000..92a09f0c60
--- /dev/null
+++ b/cpp/src/tests/legacystore/OrderingTest.cpp
@@ -0,0 +1,168 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "unit_test.h"
+
+#include "qpid/legacystore/MessageStoreImpl.h"
+#include <iostream>
+#include "MessageUtils.h"
+#include <qpid/broker/Queue.h>
+#include <qpid/broker/RecoveryManagerImpl.h>
+#include <qpid/framing/AMQHeaderBody.h>
+#include "qpid/log/Logger.h"
+#include "qpid/sys/Timer.h"
+
+using namespace qpid;
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace mrg::msgstore;
+
+qpid::broker::Broker::Options opts;
+qpid::broker::Broker br(opts);
+
+QPID_AUTO_TEST_SUITE(OrderingTest)
+
+#define SET_LOG_LEVEL(level) \
+ qpid::log::Options opts(""); \
+ opts.selectors.clear(); \
+ opts.selectors.push_back(level); \
+ qpid::log::Logger::instance().configure(opts);
+
+const std::string test_filename("OrderingTest");
+const char* tdp = getenv("TMP_DATA_DIR");
+const std::string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/tmp/OrderingTest");
+
+// === Helper fns ===
+
+const std::string name("OrderingQueue");
+std::auto_ptr<MessageStoreImpl> store;
+QueueRegistry queues;
+Queue::shared_ptr queue;
+std::queue<Uuid> ids;
+
+class TestConsumer : public Consumer
+{
+ public:
+
+ TestConsumer(Queue::shared_ptr q, std::queue<Uuid>& i) : Consumer("test", CONSUMER), queue(q), ids(i) {};
+
+ bool deliver(const QueueCursor& cursor, const Message& message)
+ {
+ queue->dequeue(0, cursor);
+ BOOST_CHECK_EQUAL(ids.front(), MessageUtils::getMessageId(message));
+ ids.pop();
+ return true;
+ };
+ void notify() {}
+ void cancel() {}
+ void acknowledged(const DeliveryRecord&) {}
+ OwnershipToken* getSession() { return 0; }
+ private:
+ Queue::shared_ptr queue;
+ std::queue<Uuid>& ids;
+};
+boost::shared_ptr<TestConsumer> consumer;
+
+void setup()
+{
+ store = std::auto_ptr<MessageStoreImpl>(new MessageStoreImpl(&br));
+ store->init(test_dir, 4, 1, true); // truncate store
+
+ queue = Queue::shared_ptr(new Queue(name, 0, store.get(), 0));
+ queue->create();
+ consumer = boost::shared_ptr<TestConsumer>(new TestConsumer(queue, ids));
+}
+
+void push()
+{
+ Uuid messageId(true);
+ ids.push(messageId);
+
+ Message msg = MessageUtils::createMessage("exchange", "routing_key", messageId, true, 0);
+
+ queue->deliver(msg);
+}
+
+bool pop()
+{
+ return queue->dispatch(consumer);
+}
+
+void restart()
+{
+ queue.reset();
+ store.reset();
+
+ store = std::auto_ptr<MessageStoreImpl>(new MessageStoreImpl(&br));
+ store->init(test_dir, 4, 1);
+ ExchangeRegistry exchanges;
+ LinkRegistry links;
+ sys::Timer t;
+ DtxManager mgr(t);
+ mgr.setStore (store.get());
+ RecoveryManagerImpl recoveryMgr(queues, exchanges, links, mgr, br.getProtocolRegistry());
+ store->recover(recoveryMgr);
+
+ queue = queues.find(name);
+ consumer = boost::shared_ptr<TestConsumer>(new TestConsumer(queue, ids));
+}
+
+void check()
+{
+ BOOST_REQUIRE(queue);
+ BOOST_CHECK_EQUAL((u_int32_t) ids.size(), queue->getMessageCount());
+ while (pop()) ;//keeping popping 'till all messages are dequeued
+ BOOST_CHECK_EQUAL((u_int32_t) 0, queue->getMessageCount());
+ BOOST_CHECK_EQUAL((size_t) 0, ids.size());
+}
+
+
+// === Test suite ===
+
+QPID_AUTO_TEST_CASE(Basic)
+{
+ SET_LOG_LEVEL("error+"); // This only needs to be set once.
+
+ std::cout << test_filename << ".Basic: " << std::flush;
+ setup();
+ //push on 10 messages
+ for (int i = 0; i < 10; i++) push();
+ restart();
+ check();
+ std::cout << "ok" << std::endl;
+}
+
+QPID_AUTO_TEST_CASE(Cycle)
+{
+ std::cout << test_filename << ".Cycle: " << std::flush;
+ setup();
+ //push on 10 messages:
+ for (int i = 0; i < 10; i++) push();
+ //pop 5:
+ for (int i = 0; i < 5; i++) pop();
+ //push on another 5:
+ for (int i = 0; i < 5; i++) push();
+ restart();
+ check();
+ std::cout << "ok" << std::endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/cpp/src/tests/legacystore/SimpleTest.cpp b/cpp/src/tests/legacystore/SimpleTest.cpp
new file mode 100644
index 0000000000..a49333d876
--- /dev/null
+++ b/cpp/src/tests/legacystore/SimpleTest.cpp
@@ -0,0 +1,497 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "unit_test.h"
+
+#include "qpid/legacystore/MessageStoreImpl.h"
+#include <iostream>
+#include "tests/legacystore/MessageUtils.h"
+#include "qpid/legacystore/StoreException.h"
+#include "qpid/broker/DirectExchange.h"
+#include <qpid/broker/Queue.h>
+#include <qpid/broker/QueueSettings.h>
+#include <qpid/broker/RecoveryManagerImpl.h>
+#include <qpid/framing/AMQHeaderBody.h>
+#include <qpid/framing/FieldTable.h>
+#include <qpid/framing/FieldValue.h>
+#include "qpid/log/Logger.h"
+#include "qpid/sys/Timer.h"
+
+qpid::broker::Broker::Options opts;
+qpid::broker::Broker br(opts);
+
+#define SET_LOG_LEVEL(level) \
+ qpid::log::Options opts(""); \
+ opts.selectors.clear(); \
+ opts.selectors.push_back(level); \
+ qpid::log::Logger::instance().configure(opts);
+
+
+using boost::intrusive_ptr;
+using boost::static_pointer_cast;
+using namespace qpid;
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace mrg::msgstore;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(SimpleTest)
+
+const string test_filename("SimpleTest");
+const char* tdp = getenv("TMP_DATA_DIR");
+const string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/tmp/SimpleTest");
+
+// === Helper fns ===
+
+struct DummyHandler : OutputHandler
+{
+ std::vector<AMQFrame> frames;
+
+ virtual void send(AMQFrame& frame){
+ frames.push_back(frame);
+ }
+};
+
+void recover(MessageStoreImpl& store, QueueRegistry& queues, ExchangeRegistry& exchanges, LinkRegistry& links)
+{
+ sys::Timer t;
+ DtxManager mgr(t);
+ mgr.setStore (&store);
+ RecoveryManagerImpl recovery(queues, exchanges, links, mgr, br.getProtocolRegistry());
+ store.recover(recovery);
+}
+
+void recover(MessageStoreImpl& store, ExchangeRegistry& exchanges)
+{
+ QueueRegistry queues;
+ LinkRegistry links;
+ recover(store, queues, exchanges, links);
+}
+
+void recover(MessageStoreImpl& store, QueueRegistry& queues)
+{
+ ExchangeRegistry exchanges;
+ LinkRegistry links;
+ recover(store, queues, exchanges, links);
+}
+
+void bindAndUnbind(const string& exchangeName, const string& queueName,
+ const string& key, const FieldTable& args)
+{
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1, true); // truncate store
+ Exchange::shared_ptr exchange(new DirectExchange(exchangeName, true, args));
+ Queue::shared_ptr queue(new Queue(queueName, 0, &store, 0));
+ store.create(*exchange, qpid::framing::FieldTable());
+ store.create(*queue, qpid::framing::FieldTable());
+ BOOST_REQUIRE(exchange->bind(queue, key, &args));
+ store.bind(*exchange, *queue, key, args);
+ }//db will be closed
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1);
+ ExchangeRegistry exchanges;
+ QueueRegistry queues;
+ LinkRegistry links;
+
+ recover(store, queues, exchanges, links);
+
+ Exchange::shared_ptr exchange = exchanges.get(exchangeName);
+ Queue::shared_ptr queue = queues.find(queueName);
+ // check exchange args are still set
+ for (FieldTable::ValueMap::const_iterator i = args.begin(); i!=args.end(); i++) {
+ BOOST_CHECK(exchange->getArgs().get((*i).first)->getData() == (*i).second->getData());
+ }
+ //check it is bound by unbinding
+ BOOST_REQUIRE(exchange->unbind(queue, key, &args));
+ store.unbind(*exchange, *queue, key, args);
+ }
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1);
+ ExchangeRegistry exchanges;
+ QueueRegistry queues;
+ LinkRegistry links;
+
+ recover(store, queues, exchanges, links);
+
+ Exchange::shared_ptr exchange = exchanges.get(exchangeName);
+ Queue::shared_ptr queue = queues.find(queueName);
+ // check exchange args are still set
+ for (FieldTable::ValueMap::const_iterator i = args.begin(); i!=args.end(); i++) {
+ BOOST_CHECK(exchange->getArgs().get((*i).first)->getData() == (*i).second->getData());
+ }
+ //make sure it is no longer bound
+ BOOST_REQUIRE(!exchange->unbind(queue, key, &args));
+ }
+}
+
+
+// === Test suite ===
+
+QPID_AUTO_TEST_CASE(CreateDelete)
+{
+ SET_LOG_LEVEL("error+"); // This only needs to be set once.
+
+ cout << test_filename << ".CreateDelete: " << flush;
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1, true); // truncate store
+ string name("CreateDeleteQueue");
+ Queue queue(name, 0, &store, 0);
+ store.create(queue, qpid::framing::FieldTable());
+// TODO - check dir exists
+ BOOST_REQUIRE(queue.getPersistenceId());
+ store.destroy(queue);
+// TODO - check dir is deleted
+
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(EmptyRecover)
+{
+ cout << test_filename << ".EmptyRecover: " << flush;
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1, true); // truncate store
+ QueueRegistry registry;
+ registry.setStore (&store);
+ recover(store, registry);
+ //nothing to assert, just testing it doesn't blow up
+
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(QueueCreate)
+{
+ cout << test_filename << ".QueueCreate: " << flush;
+
+ uint64_t id(0);
+ string name("MyDurableQueue");
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1, true); // truncate store
+ Queue queue(name, 0, &store, 0);
+ store.create(queue, qpid::framing::FieldTable());
+ BOOST_REQUIRE(queue.getPersistenceId());
+ id = queue.getPersistenceId();
+ }//db will be closed
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1);
+ QueueRegistry registry;
+ registry.setStore (&store);
+ recover(store, registry);
+ Queue::shared_ptr queue = registry.find(name);
+ BOOST_REQUIRE(queue.get());
+ BOOST_CHECK_EQUAL(id, queue->getPersistenceId());
+ }
+
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(QueueCreateWithSettings)
+{
+ cout << test_filename << ".QueueCreateWithSettings: " << flush;
+
+ FieldTable arguments;
+ arguments.setInt("qpid.max_count", 202);
+ arguments.setInt("qpid.max_size", 1003);
+ QueueSettings settings;
+ settings.populate(arguments, settings.storeSettings);
+ string name("MyDurableQueue");
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1, true); // truncate store
+ Queue queue(name, settings, &store, 0);
+ queue.create();
+ BOOST_REQUIRE(queue.getPersistenceId());
+ }//db will be closed
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1);
+ QueueRegistry registry;
+ registry.setStore (&store);
+ recover(store, registry);
+ Queue::shared_ptr queue = registry.find(name);
+ BOOST_REQUIRE(queue);
+ BOOST_CHECK_EQUAL(settings.maxDepth.getCount(), 202);
+ BOOST_CHECK_EQUAL(settings.maxDepth.getSize(), 1003);
+ BOOST_CHECK_EQUAL(settings.maxDepth.getCount(), queue->getSettings().maxDepth.getCount());
+ BOOST_CHECK_EQUAL(settings.maxDepth.getSize(), queue->getSettings().maxDepth.getSize());
+ }
+
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(QueueDestroy)
+{
+ cout << test_filename << ".QueueDestroy: " << flush;
+
+ string name("MyDurableQueue");
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1, true); // truncate store
+ Queue queue(name, 0, &store, 0);
+ store.create(queue, qpid::framing::FieldTable());
+ store.destroy(queue);
+ }//db will be closed
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1);
+ QueueRegistry registry;
+ registry.setStore (&store);
+ recover(store, registry);
+ BOOST_REQUIRE(!registry.find(name));
+ }
+
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(Enqueue)
+{
+ cout << test_filename << ".Enqueue: " << flush;
+
+ //TODO: this is largely copy & paste'd from MessageTest in
+ //qpid tree. ideally need some helper routines for reducing
+ //this to a simpler less duplicated form
+
+ string name("MyDurableQueue");
+ string exchange("MyExchange");
+ string routingKey("MyRoutingKey");
+ Uuid messageId(true);
+ string data1("abcdefg");
+ string data2("hijklmn");
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1, true); // truncate store
+ Queue::shared_ptr queue(new Queue(name, 0, &store, 0));
+ queue->create();
+
+ Message msg = MessageUtils::createMessage(exchange, routingKey, messageId, true, 14);
+ MessageUtils::addContent(msg, data1);
+ MessageUtils::addContent(msg, data2);
+
+ msg.addAnnotation("abc", "xyz");
+
+ queue->deliver(msg);
+ }//db will be closed
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1);
+ QueueRegistry registry;
+ registry.setStore (&store);
+ recover(store, registry);
+ Queue::shared_ptr queue = registry.find(name);
+ BOOST_REQUIRE(queue);
+ BOOST_CHECK_EQUAL((u_int32_t) 1, queue->getMessageCount());
+ Message msg = MessageUtils::get(*queue);
+
+ BOOST_CHECK_EQUAL(routingKey, msg.getRoutingKey());
+ BOOST_CHECK_EQUAL(messageId, MessageUtils::getMessageId(msg));
+ BOOST_CHECK_EQUAL(std::string("xyz"), msg.getAnnotation("abc"));
+ BOOST_CHECK_EQUAL((u_int64_t) 14, msg.getContentSize());
+
+ DummyHandler handler;
+ MessageUtils::deliver(msg, handler, 100);
+ BOOST_CHECK_EQUAL((size_t) 2, handler.frames.size());
+ AMQContentBody* contentBody(dynamic_cast<AMQContentBody*>(handler.frames[1].getBody()));
+ BOOST_REQUIRE(contentBody);
+ BOOST_CHECK_EQUAL(data1.size() + data2.size(), contentBody->getData().size());
+ BOOST_CHECK_EQUAL(data1 + data2, contentBody->getData());
+ }
+
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(Dequeue)
+{
+ cout << test_filename << ".Dequeue: " << flush;
+
+ //TODO: reduce the duplication in these tests
+ string name("MyDurableQueue");
+ {
+ string exchange("MyExchange");
+ string routingKey("MyRoutingKey");
+ Uuid messageId(true);
+ string data("abcdefg");
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1, true); // truncate store
+ Queue::shared_ptr queue(new Queue(name, 0, &store, 0));
+ queue->create();
+
+ Message msg = MessageUtils::createMessage(exchange, routingKey, messageId, true, 7);
+ MessageUtils::addContent(msg, data);
+
+ queue->deliver(msg);
+
+ QueueCursor cursor;
+ MessageUtils::get(*queue, &cursor);
+ queue->dequeue(0, cursor);
+ }//db will be closed
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1);
+ QueueRegistry registry;
+ registry.setStore (&store);
+ recover(store, registry);
+ Queue::shared_ptr queue = registry.find(name);
+ BOOST_REQUIRE(queue);
+ BOOST_CHECK_EQUAL((u_int32_t) 0, queue->getMessageCount());
+ }
+
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(ExchangeCreateAndDestroy)
+{
+ cout << test_filename << ".ExchangeCreateAndDestroy: " << flush;
+
+ uint64_t id(0);
+ string name("MyDurableExchange");
+ string type("direct");
+ FieldTable args;
+ args.setString("a", "A");
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1, true); // truncate store
+ ExchangeRegistry registry;
+ Exchange::shared_ptr exchange = registry.declare(name, type, true, args).first;
+ store.create(*exchange, qpid::framing::FieldTable());
+ id = exchange->getPersistenceId();
+ BOOST_REQUIRE(id);
+ }//db will be closed
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1);
+ ExchangeRegistry registry;
+
+ recover(store, registry);
+
+ Exchange::shared_ptr exchange = registry.get(name);
+ BOOST_CHECK_EQUAL(id, exchange->getPersistenceId());
+ BOOST_CHECK_EQUAL(type, exchange->getType());
+ BOOST_REQUIRE(exchange->isDurable());
+ BOOST_CHECK_EQUAL(*args.get("a"), *exchange->getArgs().get("a"));
+ store.destroy(*exchange);
+ }
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1);
+ ExchangeRegistry registry;
+
+ recover(store, registry);
+
+ try {
+ Exchange::shared_ptr exchange = registry.get(name);
+ BOOST_FAIL("Expected exchange not to be found");
+ } catch (const SessionException& e) {
+ BOOST_CHECK_EQUAL((framing::ReplyCode) 404, e.code);
+ }
+ }
+
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(ExchangeBindAndUnbind)
+{
+ cout << test_filename << ".ExchangeBindAndUnbind: " << flush;
+
+ bindAndUnbind("MyDurableExchange", "MyDurableQueue", "my-routing-key", FieldTable());
+
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(ExchangeBindAndUnbindWithArgs)
+{
+ cout << test_filename << ".ExchangeBindAndUnbindWithArgs: " << flush;
+
+ FieldTable args;
+ args.setString("a", "A");
+ args.setString("b", "B");
+ bindAndUnbind("MyDurableExchange", "MyDurableQueue", "my-routing-key", args);
+
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(ExchangeImplicitUnbind)
+{
+ cout << test_filename << ".ExchangeImplicitUnbind: " << flush;
+
+ string exchangeName("MyDurableExchange");
+ string queueName1("MyDurableQueue1");
+ string queueName2("MyDurableQueue2");
+ string key("my-routing-key");
+ FieldTable args;
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1, true); // truncate store
+ Exchange::shared_ptr exchange(new DirectExchange(exchangeName, true, args));
+ Queue::shared_ptr queue1(new Queue(queueName1, 0, &store, 0));
+ Queue::shared_ptr queue2(new Queue(queueName2, 0, &store, 0));
+ store.create(*exchange, qpid::framing::FieldTable());
+ store.create(*queue1, qpid::framing::FieldTable());
+ store.create(*queue2, qpid::framing::FieldTable());
+ store.bind(*exchange, *queue1, key, args);
+ store.bind(*exchange, *queue2, key, args);
+ //delete queue1:
+ store.destroy(*queue1);
+ }//db will be closed
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1);
+ ExchangeRegistry exchanges;
+ QueueRegistry queues;
+ LinkRegistry links;
+
+ //ensure recovery works ok:
+ recover(store, queues, exchanges, links);
+
+ Exchange::shared_ptr exchange = exchanges.get(exchangeName);
+ BOOST_REQUIRE(!queues.find(queueName1).get());
+ BOOST_REQUIRE(queues.find(queueName2).get());
+
+ //delete exchange:
+ store.destroy(*exchange);
+ }
+ {
+ MessageStoreImpl store(&br);
+ store.init(test_dir, 4, 1);
+ ExchangeRegistry exchanges;
+ QueueRegistry queues;
+ LinkRegistry links;
+
+ //ensure recovery works ok:
+ recover(store, queues, exchanges, links);
+
+ try {
+ Exchange::shared_ptr exchange = exchanges.get(exchangeName);
+ BOOST_FAIL("Expected exchange not to be found");
+ } catch (const SessionException& e) {
+ BOOST_CHECK_EQUAL((framing::ReplyCode) 404, e.code);
+ }
+ Queue::shared_ptr queue = queues.find(queueName2);
+ store.destroy(*queue);
+ }
+
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/cpp/src/tests/legacystore/TestFramework.cpp b/cpp/src/tests/legacystore/TestFramework.cpp
new file mode 100644
index 0000000000..2f7faf7682
--- /dev/null
+++ b/cpp/src/tests/legacystore/TestFramework.cpp
@@ -0,0 +1,30 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// Defines broker to be used by tests
+
+#include "unit_test.h"
+#include "TestFramework.h"
+#include "qpid/broker/Broker.h"
+
+#include <iostream>
+
+//BOOST_GLOBAL_FIXTURE( testBroker )
diff --git a/cpp/src/tests/legacystore/TestFramework.h b/cpp/src/tests/legacystore/TestFramework.h
new file mode 100644
index 0000000000..f3066db602
--- /dev/null
+++ b/cpp/src/tests/legacystore/TestFramework.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// Defines broker to be used by tests
+
+#include "unit_test.h"
+
+#include <qpid/broker/Broker.h>
+
+namespace {
+ // test broker
+ qpid::broker::Broker::Options opts;
+ qpid::broker::Broker br(opts);
+/*
+ struct testBroker {
+ testBroker() {}
+ ~testBroker() {}
+ };*/
+}
diff --git a/cpp/src/tests/legacystore/TransactionalTest.cpp b/cpp/src/tests/legacystore/TransactionalTest.cpp
new file mode 100644
index 0000000000..2d3f6f922c
--- /dev/null
+++ b/cpp/src/tests/legacystore/TransactionalTest.cpp
@@ -0,0 +1,351 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "unit_test.h"
+
+#include "qpid/legacystore/MessageStoreImpl.h"
+#include <iostream>
+#include "MessageUtils.h"
+#include "qpid/legacystore/StoreException.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/RecoveryManagerImpl.h"
+#include "qpid/framing/AMQHeaderBody.h"
+#include "qpid/log/Statement.h"
+#include "qpid/log/Logger.h"
+#include "qpid/sys/Timer.h"
+
+using namespace mrg::msgstore;
+using namespace qpid;
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace std;
+
+namespace {
+qpid::broker::Broker::Options opts;
+qpid::broker::Broker br(opts);
+}
+
+QPID_AUTO_TEST_SUITE(TransactionalTest)
+
+#define SET_LOG_LEVEL(level) \
+ qpid::log::Options opts(""); \
+ opts.selectors.clear(); \
+ opts.selectors.push_back(level); \
+ qpid::log::Logger::instance().configure(opts);
+
+const string test_filename("TransactionalTest");
+const char* tdp = getenv("TMP_DATA_DIR");
+const string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/tmp/TransactionalTest");
+
+// Test txn context which has special setCompleteFailure() method which prevents entire "txn complete" process from hapenning
+class TestTxnCtxt : public TxnCtxt
+{
+ public:
+ TestTxnCtxt(IdSequence* _loggedtx) : TxnCtxt(_loggedtx) {}
+ void setCompleteFailure(const unsigned num_queues_rem) {
+ // Remove queue members from back of impactedQueues until queues_rem reamin.
+ // to end to simulate multi-queue txn complete failure.
+ while (impactedQueues.size() > num_queues_rem) impactedQueues.erase(impactedQueues.begin());
+ }
+ void resetPreparedXidStorePtr() { preparedXidStorePtr = 0; }
+};
+
+// Test store which has special begin() which returns a TestTPCTxnCtxt, and a method to check for
+// remaining open transactions.
+// begin(), commit(), and abort() all hide functions in MessageStoreImpl. To avoid the compiler
+// warnings/errors these are renamed with a 'TMS' prefix.
+class TestMessageStore: public MessageStoreImpl
+{
+ public:
+ TestMessageStore(qpid::broker::Broker* br, const char* envpath = 0) : MessageStoreImpl(br, envpath) {}
+ std::auto_ptr<qpid::broker::TransactionContext> TMSbegin() {
+ checkInit();
+ // pass sequence number for c/a
+ return auto_ptr<TransactionContext>(new TestTxnCtxt(&messageIdSequence));
+ }
+ void TMScommit(TransactionContext& ctxt, const bool complete_prepared_list) {
+ checkInit();
+ TxnCtxt* txn(check(&ctxt));
+ if (!txn->isTPC()) {
+ localPrepare(dynamic_cast<TxnCtxt*>(txn));
+ if (!complete_prepared_list) dynamic_cast<TestTxnCtxt*>(txn)->resetPreparedXidStorePtr();
+ }
+ completed(*dynamic_cast<TxnCtxt*>(txn), true);
+ }
+ void TMSabort(TransactionContext& ctxt, const bool complete_prepared_list)
+ {
+ checkInit();
+ TxnCtxt* txn(check(&ctxt));
+ if (!txn->isTPC()) {
+ localPrepare(dynamic_cast<TxnCtxt*>(txn));
+ if (!complete_prepared_list) dynamic_cast<TestTxnCtxt*>(txn)->resetPreparedXidStorePtr();
+ }
+ completed(*dynamic_cast<TxnCtxt*>(txn), false);
+ }
+};
+
+// === Helper fns ===
+
+const string nameA("queueA");
+const string nameB("queueB");
+//const Uuid messageId(true);
+std::auto_ptr<MessageStoreImpl> store;
+std::auto_ptr<QueueRegistry> queues;
+Queue::shared_ptr queueA;
+Queue::shared_ptr queueB;
+
+template <class T>
+void setup()
+{
+ store = std::auto_ptr<T>(new T(&br));
+ store->init(test_dir, 4, 1, true); // truncate store
+
+ //create two queues:
+ queueA = Queue::shared_ptr(new Queue(nameA, 0, store.get(), 0));
+ queueA->create();
+ queueB = Queue::shared_ptr(new Queue(nameB, 0, store.get(), 0));
+ queueB->create();
+}
+
+template <class T>
+void restart()
+{
+ queueA.reset();
+ queueB.reset();
+ queues.reset();
+ store.reset();
+
+ store = std::auto_ptr<T>(new T(&br));
+ store->init(test_dir, 4, 1);
+ queues = std::auto_ptr<QueueRegistry>(new QueueRegistry);
+ ExchangeRegistry exchanges;
+ LinkRegistry links;
+ sys::Timer t;
+ DtxManager mgr(t);
+ mgr.setStore (store.get());
+ RecoveryManagerImpl recovery(*queues, exchanges, links, mgr, br.getProtocolRegistry());
+ store->recover(recovery);
+
+ queueA = queues->find(nameA);
+ queueB = queues->find(nameB);
+}
+
+Message createMessage(const string& id, const string& exchange="exchange", const string& key="routing_key")
+{
+ return MessageUtils::createMessage(exchange, key, Uuid(), true, 0, id);
+}
+
+void checkMsg(Queue::shared_ptr& queue, u_int32_t size, const string& msgid = "<none>")
+{
+ BOOST_REQUIRE(queue);
+ BOOST_CHECK_EQUAL(size, queue->getMessageCount());
+ if (size > 0) {
+ Message msg = MessageUtils::get(*queue);
+ BOOST_REQUIRE(msg);
+ BOOST_CHECK_EQUAL(msgid, MessageUtils::getCorrelationId(msg));
+ }
+}
+
+void swap(bool commit)
+{
+ setup<MessageStoreImpl>();
+
+ //create message and enqueue it onto first queue:
+ Message msgA = createMessage("Message", "exchange", "routing_key");
+ queueA->deliver(msgA);
+
+ QueueCursor cursorB;
+ Message msgB = MessageUtils::get(*queueA, &cursorB);
+ BOOST_REQUIRE(msgB);
+ //move the message from one queue to the other as a transaction
+ std::auto_ptr<TransactionContext> txn = store->begin();
+ TxBuffer tx;
+ queueB->deliver(msgB, &tx);//note: need to enqueue it first to avoid message being deleted
+
+ queueA->dequeue(txn.get(), cursorB);
+ tx.prepare(txn.get());
+ if (commit) {
+ store->commit(*txn);
+ } else {
+ store->abort(*txn);
+ }
+
+ restart<MessageStoreImpl>();
+
+ // Check outcome
+ BOOST_REQUIRE(queueA);
+ BOOST_REQUIRE(queueB);
+
+ Queue::shared_ptr x;//the queue from which the message was swapped
+ Queue::shared_ptr y;//the queue on which the message is expected to be
+
+ if (commit) {
+ x = queueA;
+ y = queueB;
+ } else {
+ x = queueB;
+ y = queueA;
+ }
+
+ checkMsg(x, 0);
+ checkMsg(y, 1, "Message");
+ checkMsg(y, 0);
+}
+
+void testMultiQueueTxn(const unsigned num_queues_rem, const bool complete_prepared_list, const bool commit)
+{
+ setup<TestMessageStore>();
+ TestMessageStore* tmsp = static_cast<TestMessageStore*>(store.get());
+ std::auto_ptr<TransactionContext> txn(tmsp->TMSbegin());
+ TxBuffer tx;
+
+ //create two messages and enqueue them onto both queues:
+ Message msgA = createMessage("MessageA", "exchange", "routing_key");
+ queueA->deliver(msgA, &tx);
+ queueB->deliver(msgA, &tx);
+ Message msgB = createMessage("MessageB", "exchange", "routing_key");
+ queueA->deliver(msgB, &tx);
+ queueB->deliver(msgB, &tx);
+
+ tx.prepare(txn.get());
+ static_cast<TestTxnCtxt*>(txn.get())->setCompleteFailure(num_queues_rem);
+ if (commit)
+ tmsp->TMScommit(*txn, complete_prepared_list);
+ else
+ tmsp->TMSabort(*txn, complete_prepared_list);
+ restart<TestMessageStore>();
+
+ // Check outcome
+ if (commit)
+ {
+ checkMsg(queueA, 2, "MessageA");
+ checkMsg(queueB, 2, "MessageA");
+ checkMsg(queueA, 1, "MessageB");
+ checkMsg(queueB, 1, "MessageB");
+ }
+ checkMsg(queueA, 0);
+ checkMsg(queueB, 0);
+}
+
+// === Test suite ===
+
+QPID_AUTO_TEST_CASE(Commit)
+{
+ SET_LOG_LEVEL("error+"); // This only needs to be set once.
+
+ cout << test_filename << ".Commit: " << flush;
+ swap(true);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(Abort)
+{
+ cout << test_filename << ".Abort: " << flush;
+ swap(false);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueCommit)
+{
+ cout << test_filename << ".MultiQueueCommit: " << flush;
+ testMultiQueueTxn(2, true, true);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueAbort)
+{
+ cout << test_filename << ".MultiQueueAbort: " << flush;
+ testMultiQueueTxn(2, true, false);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueNoQueueCommitRecover)
+{
+ cout << test_filename << ".MultiQueueNoQueueCommitRecover: " << flush;
+ testMultiQueueTxn(0, false, true);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueNoQueueAbortRecover)
+{
+ cout << test_filename << ".MultiQueueNoQueueAbortRecover: " << flush;
+ testMultiQueueTxn(0, false, false);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueSomeQueueCommitRecover)
+{
+ cout << test_filename << ".MultiQueueSomeQueueCommitRecover: " << flush;
+ testMultiQueueTxn(1, false, true);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueSomeQueueAbortRecover)
+{
+ cout << test_filename << ".MultiQueueSomeQueueAbortRecover: " << flush;
+ testMultiQueueTxn(1, false, false);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueAllQueueCommitRecover)
+{
+ cout << test_filename << ".MultiQueueAllQueueCommitRecover: " << flush;
+ testMultiQueueTxn(2, false, true);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueAllQueueAbortRecover)
+{
+ cout << test_filename << ".MultiQueueAllQueueAbortRecover: " << flush;
+ testMultiQueueTxn(2, false, false);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(LockedRecordTest)
+{
+ cout << test_filename << ".LockedRecordTest: " << flush;
+
+ setup<MessageStoreImpl>();
+ queueA->deliver(createMessage("Message", "exchange", "routingKey"));
+ std::auto_ptr<TransactionContext> txn = store->begin();
+
+ QueueCursor cursor;
+ Message msg = MessageUtils::get(*queueA, &cursor);
+ queueA->dequeue(txn.get(), cursor);
+
+ try {
+ store->dequeue(0, msg.getPersistentContext(), *queueA);
+ BOOST_ERROR("Did not throw JERR_MAP_LOCKED exception as expected.");
+ }
+ catch (const mrg::msgstore::StoreException& e) {
+ if (std::strstr(e.what(), "JERR_MAP_LOCKED") == 0)
+ BOOST_ERROR("Unexpected StoreException: " << e.what());
+ }
+ catch (const std::exception& e) {
+ BOOST_ERROR("Unexpected exception: " << e.what());
+ }
+ store->commit(*txn);
+ checkMsg(queueA, 0);
+
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/cpp/src/tests/legacystore/TwoPhaseCommitTest.cpp b/cpp/src/tests/legacystore/TwoPhaseCommitTest.cpp
new file mode 100644
index 0000000000..92e49df9e3
--- /dev/null
+++ b/cpp/src/tests/legacystore/TwoPhaseCommitTest.cpp
@@ -0,0 +1,675 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "unit_test.h"
+
+#include "qpid/legacystore/MessageStoreImpl.h"
+#include <iostream>
+#include "MessageUtils.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/RecoveryManagerImpl.h"
+#include "qpid/framing/AMQHeaderBody.h"
+#include "qpid/log/Statement.h"
+#include "qpid/legacystore/TxnCtxt.h"
+#include "qpid/log/Logger.h"
+#include "qpid/sys/Timer.h"
+
+using namespace mrg::msgstore;
+using namespace qpid;
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace std;
+
+
+qpid::broker::Broker::Options opts;
+qpid::broker::Broker br(opts);
+
+
+QPID_AUTO_TEST_SUITE(TwoPhaseCommitTest)
+
+#define SET_LOG_LEVEL(level) \
+ qpid::log::Options opts(""); \
+ opts.selectors.clear(); \
+ opts.selectors.push_back(level); \
+ qpid::log::Logger::instance().configure(opts);
+
+
+const string test_filename("TwoPhaseCommitTest");
+const char* tdp = getenv("TMP_DATA_DIR");
+string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/tmp/TwoPhaseCommitTest");
+
+// === Helper fns ===
+
+class TwoPhaseCommitTest
+{
+
+ class Strategy
+ {
+ public:
+ virtual void init() = 0;
+ virtual void run(TPCTransactionContext* txn) = 0;
+ virtual void check(bool committed) = 0;
+ virtual ~Strategy(){}
+ };
+
+ class Swap : public Strategy
+ {
+ TwoPhaseCommitTest* const test;
+ const string messageId;
+ Message msg;
+ public:
+ Swap(TwoPhaseCommitTest* const test_, const string& messageId_): test(test_), messageId(messageId_) {}
+ void init(){ msg = test->deliver(messageId, test->queueA); }
+ void run(TPCTransactionContext* txn) { test->swap(txn, test->queueA, test->queueB); }
+ void check(bool committed) { test->swapCheck(committed, messageId, test->queueA, test->queueB); }
+ };
+
+ class Enqueue : public Strategy
+ {
+ TwoPhaseCommitTest* const test;
+ Message msg1;
+ Message msg2;
+ Message msg3;
+ public:
+ Enqueue(TwoPhaseCommitTest* const test_): test(test_) {}
+ void init() {}
+ void run(TPCTransactionContext* txn) {
+ msg1 = test->enqueue(txn, "Enqueue1", test->queueA);
+ msg2 = test->enqueue(txn, "Enqueue2", test->queueA);
+ msg3 = test->enqueue(txn, "Enqueue3", test->queueA);
+ }
+ void check(bool committed) {
+ if (committed) {
+ test->checkMsg(test->queueA, 3, "Enqueue1");
+ test->checkMsg(test->queueA, 2, "Enqueue2");
+ test->checkMsg(test->queueA, 1, "Enqueue3");
+ }
+ test->checkMsg(test->queueA, 0);
+ }
+ };
+
+ class Dequeue : public Strategy
+ {
+ TwoPhaseCommitTest* const test;
+ Message msg1;
+ Message msg2;
+ Message msg3;
+ public:
+ Dequeue(TwoPhaseCommitTest* const test_): test(test_) {}
+ void init() {
+ msg1 = test->deliver("Dequeue1", test->queueA);
+ msg2 = test->deliver("Dequeue2", test->queueA);
+ msg3 = test->deliver("Dequeue3", test->queueA);
+ }
+ void run(TPCTransactionContext* txn) {
+ test->dequeue(txn, test->queueA);
+ test->dequeue(txn, test->queueA);
+ test->dequeue(txn, test->queueA);
+ }
+ void check(bool committed) {
+ if (!committed) {
+ test->checkMsg(test->queueA, 3, "Dequeue1");
+ test->checkMsg(test->queueA, 2, "Dequeue2");
+ test->checkMsg(test->queueA, 1, "Dequeue3");
+ }
+ test->checkMsg(test->queueA, 0);
+ }
+ };
+
+ class MultiQueueTxn : public Strategy
+ {
+ TwoPhaseCommitTest* const test;
+ Message msg1;
+ Message msg2;
+ std::set<Queue::shared_ptr> queueset;
+ public:
+ MultiQueueTxn(TwoPhaseCommitTest* const test_): test(test_) {}
+ virtual void init() {}
+ virtual void run(TPCTransactionContext* txn) {
+ queueset.insert(test->queueA);
+ queueset.insert(test->queueB);
+ msg1 = test->enqueue(txn, "Message1", queueset);
+ msg2 = test->enqueue(txn, "Message2", queueset);
+ queueset.clear();
+ }
+ virtual void check(bool committed) {
+ TestMessageStore* sptr = static_cast<TestMessageStore*>(test->store.get());
+ if (committed)
+ {
+ test->checkMsg(test->queueA, 2, "Message1");
+ test->checkMsg(test->queueB, 2, "Message1");
+ test->checkMsg(test->queueA, 1, "Message2");
+ test->checkMsg(test->queueB, 1, "Message2");
+ }
+ test->checkMsg(test->queueA, 0);
+ test->checkMsg(test->queueB, 0);
+ // Check there are no remaining open txns in store
+ BOOST_CHECK_EQUAL(u_int32_t(0), sptr->getRemainingTxns(*(test->queueA)));
+ BOOST_CHECK_EQUAL(u_int32_t(0), sptr->getRemainingTxns(*(test->queueB)));
+ BOOST_CHECK_EQUAL(u_int32_t(0), sptr->getRemainingPreparedListTxns());
+ }
+ };
+
+ // Test txn context which has special setCompleteFailure() method which prevents entire "txn complete" process from hapenning
+ class TestTPCTxnCtxt : public TPCTxnCtxt
+ {
+ public:
+ TestTPCTxnCtxt(const std::string& _xid, IdSequence* _loggedtx) : TPCTxnCtxt(_xid, _loggedtx) {}
+ void setCompleteFailure(const unsigned num_queues_rem, const bool complete_prepared_list) {
+ // Remove queue members from back of impactedQueues until queues_rem reamin.
+ // to end to simulate multi-queue txn complete failure.
+ while (impactedQueues.size() > num_queues_rem) impactedQueues.erase(impactedQueues.begin());
+ // If prepared list is not to be committed, set pointer to 0
+ if (!complete_prepared_list) preparedXidStorePtr = 0;
+ }
+ };
+
+ // Test store which has sepcial begin() which returns a TestTPCTxnCtxt, and a method to check for
+ // reamining open transactions
+ class TestMessageStore: public MessageStoreImpl
+ {
+ public:
+ TestMessageStore(qpid::broker::Broker* br, const char* envpath = 0) : MessageStoreImpl(br, envpath) {}
+ std::auto_ptr<qpid::broker::TPCTransactionContext> TMSbegin(const std::string& xid) {
+ checkInit();
+ IdSequence* jtx = &messageIdSequence;
+ // pass sequence number for c/a
+ return auto_ptr<TPCTransactionContext>(new TestTPCTxnCtxt(xid, jtx));
+ }
+ u_int32_t getRemainingTxns(const PersistableQueue& queue) {
+ return static_cast<JournalImpl*>(queue.getExternalQueueStore())->get_open_txn_cnt();
+ }
+ u_int32_t getRemainingPreparedListTxns() {
+ return tplStorePtr->get_open_txn_cnt();
+ }
+ };
+
+ const string nameA;
+ const string nameB;
+ std::auto_ptr<MessageStoreImpl> store;
+ std::auto_ptr<DtxManager> dtxmgr;
+ std::auto_ptr<QueueRegistry> queues;
+ std::auto_ptr<LinkRegistry> links;
+ Queue::shared_ptr queueA;
+ Queue::shared_ptr queueB;
+ Message msg1;
+ Message msg2;
+ Message msg4;
+ std::auto_ptr<TxBuffer> tx;
+
+ void recoverPrepared(bool commit)
+ {
+ setup<MessageStoreImpl>();
+
+ Swap swap(this, "RecoverPrepared");
+ swap.init();
+ std::auto_ptr<TPCTransactionContext> txn(store->begin("my-xid"));
+ swap.run(txn.get());
+ if (tx.get()) {
+ tx->prepare(txn.get());
+ tx.reset();
+ }
+
+ store->prepare(*txn);
+ restart<MessageStoreImpl>();
+
+ //check that the message is not available from either queue
+ BOOST_CHECK_EQUAL((u_int32_t) 0, queueA->getMessageCount());
+ BOOST_CHECK_EQUAL((u_int32_t) 0, queueB->getMessageCount());
+
+ //commit/abort the txn - through the dtx manager, not directly on the store
+ if (commit) {
+ dtxmgr->commit("my-xid", false);
+ } else {
+ dtxmgr->rollback("my-xid");
+ }
+
+ swap.check(commit);
+ restart<MessageStoreImpl>();
+ swap.check(commit);
+ }
+
+ void testMultiQueueTxn(const unsigned num_queues_rem, const bool complete_prepared_list, const bool commit)
+ {
+ setup<TestMessageStore>();
+ MultiQueueTxn mqtTest(this);
+ mqtTest.init();
+ std::auto_ptr<TPCTransactionContext> txn(static_cast<TestMessageStore*>(store.get())->begin("my-xid"));
+ mqtTest.run(txn.get());
+ if (tx.get()) {
+ tx->prepare(txn.get());
+ tx.reset();
+ }
+ store->prepare(*txn);
+
+ // As the commits and aborts should happen through DtxManager, and it is too complex to
+ // pass all these test params through, we bypass DtxManager and use the store directly.
+ // This will prevent the queues from seeing committed txns, however. To test the success
+ // or failure of
+ static_cast<TestTPCTxnCtxt*>(txn.get())->setCompleteFailure(num_queues_rem, complete_prepared_list);
+ if (commit)
+ store->commit(*txn);
+ else
+ store->abort(*txn);
+ restart<TestMessageStore>();
+ mqtTest.check(commit);
+ }
+
+ void commit(Strategy& strategy)
+ {
+ setup<MessageStoreImpl>();
+ strategy.init();
+
+ std::auto_ptr<TPCTransactionContext> txn(store->begin("my-xid"));
+ strategy.run(txn.get());
+ if (tx.get()) {
+ tx->prepare(txn.get());
+ tx.reset();
+ }
+ store->prepare(*txn);
+ store->commit(*txn);
+ restart<MessageStoreImpl>();
+ strategy.check(true);
+ }
+
+ void abort(Strategy& strategy, bool prepare)
+ {
+ setup<MessageStoreImpl>();
+ strategy.init();
+
+ std::auto_ptr<TPCTransactionContext> txn(store->begin("my-xid"));
+ strategy.run(txn.get());
+ if (tx.get()) {
+ tx->prepare(txn.get());
+ tx.reset();
+ }
+ if (prepare) store->prepare(*txn);
+ store->abort(*txn);
+ restart<MessageStoreImpl>();
+ strategy.check(false);
+ }
+
+ void swap(TPCTransactionContext* txn, Queue::shared_ptr& from, Queue::shared_ptr& to)
+ {
+ QueueCursor c;
+ Message msg1 = MessageUtils::get(*from, &c);//just dequeues in memory
+ //move the message from one queue to the other as part of a
+ //distributed transaction
+ if (!tx.get()) tx = std::auto_ptr<TxBuffer>(new TxBuffer);
+ to->deliver(msg1, tx.get());//note: need to enqueue it first to avoid message being deleted
+ from->dequeue(txn, c);
+ }
+
+ void dequeue(TPCTransactionContext* txn, Queue::shared_ptr& queue)
+ {
+ QueueCursor c;
+ Message msg2 = MessageUtils::get(*queue, &c);//just dequeues in memory
+ queue->dequeue(txn, c);
+ }
+
+ Message enqueue(TPCTransactionContext* /*txn*/, const string& msgid, Queue::shared_ptr& queue)
+ {
+ Message msg = createMessage(msgid);
+ if (!tx.get()) tx = std::auto_ptr<TxBuffer>(new TxBuffer);
+ queue->deliver(msg, tx.get());
+ return msg;
+ }
+
+ Message enqueue(TPCTransactionContext* /*txn*/, const string& msgid, std::set<Queue::shared_ptr>& queueset)
+ {
+ if (!tx.get()) tx = std::auto_ptr<TxBuffer>(new TxBuffer);
+ Message msg = createMessage(msgid);
+ for (std::set<Queue::shared_ptr>::iterator i = queueset.begin(); i != queueset.end(); i++) {
+ (*i)->deliver(msg, tx.get());
+ }
+ return msg;
+ }
+
+ Message deliver(const string& msgid, Queue::shared_ptr& queue)
+ {
+ Message m = createMessage(msgid);
+ queue->deliver(m);
+ return m;
+ }
+
+ template <class T>
+ void setup()
+ {
+ store = std::auto_ptr<T>(new T(&br));
+ store->init(test_dir, 4, 1, true); // truncate store
+
+ //create two queues:
+ queueA = Queue::shared_ptr(new Queue(nameA, 0, store.get(), 0));
+ queueA->create();
+ queueB = Queue::shared_ptr(new Queue(nameB, 0, store.get(), 0));
+ queueB->create();
+ }
+
+ Message createMessage(const string& id, const string& exchange="exchange", const string& key="routing_key")
+ {
+ Message msg = MessageUtils::createMessage(exchange, key, Uuid(), true, 0, id);
+ return msg;
+ }
+
+ template <class T>
+ void restart()
+ {
+ queueA.reset();
+ queueB.reset();
+ store.reset();
+ queues.reset();
+ links.reset();
+
+ store = std::auto_ptr<T>(new T(&br));
+ store->init(test_dir, 4, 1);
+ sys::Timer t;
+ ExchangeRegistry exchanges;
+ queues = std::auto_ptr<QueueRegistry>(new QueueRegistry);
+ links = std::auto_ptr<LinkRegistry>(new LinkRegistry);
+ dtxmgr = std::auto_ptr<DtxManager>(new DtxManager(t));
+ dtxmgr->setStore (store.get());
+ RecoveryManagerImpl recovery(*queues, exchanges, *links, *dtxmgr, br.getProtocolRegistry());
+ store->recover(recovery);
+
+ queueA = queues->find(nameA);
+ queueB = queues->find(nameB);
+ }
+
+ void checkMsg(Queue::shared_ptr& queue, u_int32_t size, const string& msgid = "<none>")
+ {
+ BOOST_REQUIRE(queue);
+ BOOST_CHECK_EQUAL(size, queue->getMessageCount());
+ if (size > 0) {
+ Message msg = MessageUtils::get(*queue);
+ BOOST_REQUIRE(msg);
+ BOOST_CHECK_EQUAL(msgid, MessageUtils::getCorrelationId(msg));
+ }
+ }
+
+ void swapCheck(bool swapped, const string& msgid, Queue::shared_ptr& from, Queue::shared_ptr& to)
+ {
+ BOOST_REQUIRE(from);
+ BOOST_REQUIRE(to);
+
+ Queue::shared_ptr x; //the queue from which the message was swapped
+ Queue::shared_ptr y; //the queue on which the message is expected to be
+
+ if (swapped) {
+ x = from;
+ y = to;
+ } else {
+ x = to;
+ y = from;
+ }
+
+ checkMsg(x, 0);
+ checkMsg(y, 1, msgid);
+ checkMsg(y, 0);
+ }
+
+public:
+ TwoPhaseCommitTest() : nameA("queueA"), nameB("queueB") {}
+
+ void testCommitEnqueue()
+ {
+ Enqueue enqueue(this);
+ commit(enqueue);
+ }
+
+ void testCommitDequeue()
+ {
+ Dequeue dequeue(this);
+ commit(dequeue);
+ }
+
+ void testCommitSwap()
+ {
+ Swap swap(this, "SwapMessageId");
+ commit(swap);
+ }
+
+ void testPrepareAndAbortEnqueue()
+ {
+ Enqueue enqueue(this);
+ abort(enqueue, true);
+ }
+
+ void testPrepareAndAbortDequeue()
+ {
+ Dequeue dequeue(this);
+ abort(dequeue, true);
+ }
+
+ void testPrepareAndAbortSwap()
+ {
+ Swap swap(this, "SwapMessageId");
+ abort(swap, true);
+ }
+
+ void testAbortNoPrepareEnqueue()
+ {
+ Enqueue enqueue(this);
+ abort(enqueue, false);
+ }
+
+ void testAbortNoPrepareDequeue()
+ {
+ Dequeue dequeue(this);
+ abort(dequeue, false);
+ }
+
+ void testAbortNoPrepareSwap()
+ {
+ Swap swap(this, "SwapMessageId");
+ abort(swap, false);
+ }
+
+ void testRecoverPreparedThenCommitted()
+ {
+ recoverPrepared(true);
+ }
+
+ void testRecoverPreparedThenAborted()
+ {
+ recoverPrepared(false);
+ }
+
+ void testMultiQueueCommit()
+ {
+ testMultiQueueTxn(2, true, true);
+ }
+
+ void testMultiQueueAbort()
+ {
+ testMultiQueueTxn(2, true, false);
+ }
+
+ void testMultiQueueNoQueueCommitRecover()
+ {
+ testMultiQueueTxn(0, false, true);
+ }
+
+ void testMultiQueueNoQueueAbortRecover()
+ {
+ testMultiQueueTxn(0, false, false);
+ }
+
+ void testMultiQueueSomeQueueCommitRecover()
+ {
+ testMultiQueueTxn(1, false, true);
+ }
+
+ void testMultiQueueSomeQueueAbortRecover()
+ {
+ testMultiQueueTxn(1, false, false);
+ }
+
+ void testMultiQueueAllQueueCommitRecover()
+ {
+ testMultiQueueTxn(2, false, true);
+ }
+
+ void testMultiQueueAllQueueAbortRecover()
+ {
+ testMultiQueueTxn(2, false, false);
+ }
+};
+
+TwoPhaseCommitTest tpct;
+
+// === Test suite ===
+
+QPID_AUTO_TEST_CASE(CommitEnqueue)
+{
+ SET_LOG_LEVEL("error+"); // This only needs to be set once.
+
+ cout << test_filename << ".CommitEnqueue: " << flush;
+ tpct.testCommitEnqueue();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(CommitDequeue)
+{
+ cout << test_filename << ".CommitDequeue: " << flush;
+ tpct.testCommitDequeue();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(CommitSwap)
+{
+ cout << test_filename << ".CommitSwap: " << flush;
+ tpct.testCommitSwap();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(PrepareAndAbortEnqueue)
+{
+ cout << test_filename << ".PrepareAndAbortEnqueue: " << flush;
+ tpct.testPrepareAndAbortEnqueue();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(PrepareAndAbortDequeue)
+{
+ cout << test_filename << ".PrepareAndAbortDequeue: " << flush;
+ tpct.testPrepareAndAbortDequeue();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(PrepareAndAbortSwap)
+{
+ cout << test_filename << ".PrepareAndAbortSwap: " << flush;
+ tpct.testPrepareAndAbortSwap();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(AbortNoPrepareEnqueue)
+{
+ cout << test_filename << ".AbortNoPrepareEnqueue: " << flush;
+ tpct.testAbortNoPrepareEnqueue();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(AbortNoPrepareDequeue)
+{
+ cout << test_filename << ".AbortNoPrepareDequeue: " << flush;
+ tpct.testAbortNoPrepareDequeue();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(AbortNoPrepareSwap)
+{
+ cout << test_filename << ".AbortNoPrepareSwap: " << flush;
+ tpct.testAbortNoPrepareSwap();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(RecoverPreparedThenCommitted)
+{
+ cout << test_filename << ".RecoverPreparedThenCommitted: " << flush;
+ tpct.testRecoverPreparedThenCommitted();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(RecoverPreparedThenAborted)
+{
+ cout << test_filename << ".RecoverPreparedThenAborted: " << flush;
+ tpct.testRecoverPreparedThenAborted();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueCommit)
+{
+ cout << test_filename << ".MultiQueueCommit: " << flush;
+ tpct.testMultiQueueCommit();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueAbort)
+{
+ cout << test_filename << ".MultiQueueAbort: " << flush;
+ tpct.testMultiQueueAbort();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueNoQueueCommitRecover)
+{
+ cout << test_filename << ".MultiQueueNoQueueCommitRecover: " << flush;
+ tpct.testMultiQueueNoQueueCommitRecover();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueNoQueueAbortRecover)
+{
+ cout << test_filename << ".MultiQueueNoQueueAbortRecover: " << flush;
+ tpct.testMultiQueueNoQueueAbortRecover();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueSomeQueueCommitRecover)
+{
+ cout << test_filename << ".MultiQueueSomeQueueCommitRecover: " << flush;
+ tpct.testMultiQueueSomeQueueCommitRecover();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueSomeQueueAbortRecover)
+{
+ cout << test_filename << ".MultiQueueSomeQueueAbortRecover: " << flush;
+ tpct.testMultiQueueSomeQueueAbortRecover();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueAllQueueCommitRecover)
+{
+ cout << test_filename << ".MultiQueueAllQueueCommitRecover: " << flush;
+ tpct.testMultiQueueAllQueueCommitRecover();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(MultiQueueAllQueueAbortRecover)
+{
+ cout << test_filename << ".MultiQueueAllQueueAbortRecover: " << flush;
+ tpct.testMultiQueueAllQueueAbortRecover();
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/cpp/src/tests/stop_cluster b/cpp/src/tests/legacystore/clean.sh
index 02436c60b7..efb19586fa 100755..100644
--- a/cpp/src/tests/stop_cluster
+++ b/cpp/src/tests/legacystore/clean.sh
@@ -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
@@ -19,15 +19,14 @@
# under the License.
#
-# Stop brokers on ports listed in cluster.ports
+# This script cleans up any previous database and journal files, and should
+# be run prior to the store system tests, as these are prone to crashing or
+# hanging under some circumstances if the database is old or inconsistent.
-PORTS=`cat cluster.ports`
-for PORT in $PORTS ; do
- $QPIDD_EXEC --no-module-dir -qp $PORT || ERROR="$ERROR $PORT"
-done
-rm -f cluster.ports qpidd.port
-
-if [ -n "$ERROR" ]; then
- echo "Errors stopping brokers on ports: $ERROR"
- exit 1
+if [ -d ${TMP_DATA_DIR} ]; then
+ rm -rf ${TMP_DATA_DIR}
+fi
+if [ -d ${TMP_PYTHON_TEST_DIR} ]; then
+ rm -rf ${TMP_PYTHON_TEST_DIR}
fi
+rm -f ${abs_srcdir}/*.vglog*
diff --git a/cpp/src/tests/legacystore/persistence.py b/cpp/src/tests/legacystore/persistence.py
new file mode 100644
index 0000000000..c4ab712f14
--- /dev/null
+++ b/cpp/src/tests/legacystore/persistence.py
@@ -0,0 +1,574 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import sys, re, traceback, socket
+from getopt import getopt, GetoptError
+
+from qpid.connection import Connection
+from qpid.util import connect
+from qpid.datatypes import Message, RangedSet
+from qpid.queue import Empty
+from qpid.session import SessionException
+from qpid.testlib import TestBase010
+from time import sleep
+
+class PersistenceTest(TestBase010):
+
+ XA_RBROLLBACK = 1
+ XA_RBTIMEOUT = 2
+ XA_OK = 0
+
+ def createMessage(self, **kwargs):
+ session = self.session
+ dp = {}
+ dp['delivery_mode'] = 2
+ mp = {}
+ for k, v in kwargs.iteritems():
+ if k in ['routing_key', 'delivery_mode']: dp[k] = v
+ if k in ['message_id', 'correlation_id', 'application_headers']: mp[k] = v
+ args = []
+ args.append(session.delivery_properties(**dp))
+ if len(mp):
+ args.append(session.message_properties(**mp))
+ if kwargs.has_key('body'): args.append(kwargs['body'])
+ return Message(*args)
+
+ def phase1(self):
+ session = self.session
+
+ session.queue_declare(queue="queue-a", durable=True)
+ session.queue_declare(queue="queue-b", durable=True)
+ session.exchange_bind(queue="queue-a", exchange="amq.direct", binding_key="a")
+ session.exchange_bind(queue="queue-b", exchange="amq.direct", binding_key="b")
+
+ session.message_transfer(destination="amq.direct",
+ message=self.createMessage(routing_key="a", correlation_id="Msg0001", body="A_Message1"))
+ session.message_transfer(destination="amq.direct",
+ message=self.createMessage(routing_key="b", correlation_id="Msg0002", body="B_Message1"))
+
+# session.queue_declare(queue="lvq-test", durable=True, arguments={"qpid.last_value_queue":True})
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"B"}, body="B1"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"A"}, body="A1"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"A"}, body="A2"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"B"}, body="B2"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"B"}, body="B3"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"C"}, body="C1"))
+
+
+
+ def phase2(self):
+ session = self.session
+
+ #check queues exists
+ session.queue_declare(queue="queue-a", durable=True, passive=True)
+ session.queue_declare(queue="queue-b", durable=True, passive=True)
+
+ #check they are still bound to amq.direct correctly
+ responses = []
+ responses.append(session.exchange_bound(queue="queue-a", exchange="amq.direct", binding_key="a"))
+ responses.append(session.exchange_bound(queue="queue-b", exchange="amq.direct", binding_key="b"))
+ for r in responses:
+ self.assert_(not r.exchange_not_found)
+ self.assert_(not r.queue_not_found)
+ self.assert_(not r.key_not_matched)
+
+
+ #check expected messages are there
+ self.assertMessageOnQueue("queue-a", "Msg0001", "A_Message1")
+ self.assertMessageOnQueue("queue-b", "Msg0002", "B_Message1")
+
+ self.assertEmptyQueue("queue-a")
+ self.assertEmptyQueue("queue-b")
+
+ session.queue_declare(queue="queue-c", durable=True)
+
+ #send a message to a topic such that it reaches all queues
+ session.exchange_bind(queue="queue-a", exchange="amq.topic", binding_key="abc")
+ session.exchange_bind(queue="queue-b", exchange="amq.topic", binding_key="abc")
+ session.exchange_bind(queue="queue-c", exchange="amq.topic", binding_key="abc")
+
+ session.message_transfer(destination="amq.topic",
+ message=self.createMessage(routing_key="abc", correlation_id="Msg0003", body="AB_Message2"))
+
+# #check LVQ exists and has exepected messages:
+# session.queue_declare(queue="lvq-test", durable=True, passive=True)
+# session.message_subscribe(destination="lvq", queue="lvq-test")
+# lvq = session.incoming("lvq")
+# lvq.start()
+# accepted = RangedSet()
+# for m in ["A2", "B3", "C1"]:
+# msg = lvq.get(timeout=1)
+# self.assertEquals(m, msg.body)
+# accepted.add(msg.id)
+# try:
+# extra = lvq.get(timeout=1)
+# self.fail("lvq-test not empty, contains: " + extra.body)
+# except Empty: None
+# #publish some more messages while subscriber is active (no replacement):
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"C"}, body="C2"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"C"}, body="C3"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"A"}, body="A3"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"A"}, body="A4"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"C"}, body="C4"))
+# #check that accepting replaced messages is safe
+# session.message_accept(accepted)
+
+
+ def phase3(self):
+ session = self.session
+
+# #lvq recovery validation
+# session.queue_declare(queue="lvq-test", durable=True, passive=True)
+# session.message_subscribe(destination="lvq", queue="lvq-test")
+# lvq = session.incoming("lvq")
+# lvq.start()
+# accepted = RangedSet()
+# lvq.start()
+# for m in ["C4", "A4"]:
+# msg = lvq.get(timeout=1)
+# self.assertEquals(m, msg.body)
+# accepted.add(msg.id)
+# session.message_accept(accepted)
+# try:
+# extra = lvq.get(timeout=1)
+# self.fail("lvq-test not empty, contains: " + extra.body)
+# except Empty: None
+# session.message_cancel(destination="lvq")
+# session.queue_delete(queue="lvq-test")
+
+
+ #check queues exists
+ session.queue_declare(queue="queue-a", durable=True, passive=True)
+ session.queue_declare(queue="queue-b", durable=True, passive=True)
+ session.queue_declare(queue="queue-c", durable=True, passive=True)
+
+ session.tx_select()
+ #check expected messages are there
+ self.assertMessageOnQueue("queue-a", "Msg0003", "AB_Message2")
+ self.assertMessageOnQueue("queue-b", "Msg0003", "AB_Message2")
+ self.assertMessageOnQueue("queue-c", "Msg0003", "AB_Message2")
+
+ self.assertEmptyQueue("queue-a")
+ self.assertEmptyQueue("queue-b")
+ self.assertEmptyQueue("queue-c")
+
+ #note: default bindings must be restored for this to work
+ session.message_transfer(message=self.createMessage(
+ routing_key="queue-a", correlation_id="Msg0004", body="A_Message3"))
+ session.message_transfer(message=self.createMessage(
+ routing_key="queue-a", correlation_id="Msg0005", body="A_Message4"))
+ session.message_transfer(message=self.createMessage(
+ routing_key="queue-a", correlation_id="Msg0006", body="A_Message5"))
+
+ session.tx_commit()
+
+
+ #delete a queue
+ session.queue_delete(queue="queue-c")
+
+ session.message_subscribe(destination="ctag", queue="queue-a", accept_mode=0)
+ session.message_flow(destination="ctag", unit=0, value=0xFFFFFFFF)
+ session.message_flow(destination="ctag", unit=1, value=0xFFFFFFFF)
+ included = session.incoming("ctag")
+ msg1 = included.get(timeout=1)
+ self.assertExpectedContent(msg1, "Msg0004", "A_Message3")
+ msg2 = included.get(timeout=1)
+ self.assertExpectedContent(msg2, "Msg0005", "A_Message4")
+ msg3 = included.get(timeout=1)
+ self.assertExpectedContent(msg3, "Msg0006", "A_Message5")
+ self.ack(msg1, msg2, msg3)
+
+ session.message_transfer(destination="amq.direct", message=self.createMessage(
+ routing_key="queue-b", correlation_id="Msg0007", body="B_Message3"))
+
+ session.tx_rollback()
+
+
+ def phase4(self):
+ session = self.session
+
+ #check queues exists
+ session.queue_declare(queue="queue-a", durable=True, passive=True)
+ session.queue_declare(queue="queue-b", durable=True, passive=True)
+
+ self.assertMessageOnQueue("queue-a", "Msg0004", "A_Message3")
+ self.assertMessageOnQueue("queue-a", "Msg0005", "A_Message4")
+ self.assertMessageOnQueue("queue-a", "Msg0006", "A_Message5")
+
+ self.assertEmptyQueue("queue-a")
+ self.assertEmptyQueue("queue-b")
+
+ #check this queue doesn't exist
+ try:
+ session.queue_declare(queue="queue-c", durable=True, passive=True)
+ raise Exception("Expected queue-c to have been deleted")
+ except SessionException, e:
+ self.assertEquals(404, e.args[0].error_code)
+
+ def phase5(self):
+
+ session = self.session
+ queues = ["queue-a1", "queue-a2", "queue-b1", "queue-b2", "queue-c1", "queue-c2", "queue-d1", "queue-d2"]
+
+ for q in queues:
+ session.queue_declare(queue=q, durable=True)
+ session.queue_purge(queue=q)
+
+ session.message_transfer(message=self.createMessage(
+ routing_key="queue-a1", correlation_id="MsgA", body="MessageA"))
+ session.message_transfer(message=self.createMessage(
+ routing_key="queue-b1", correlation_id="MsgB", body="MessageB"))
+ session.message_transfer(message=self.createMessage(
+ routing_key="queue-c1", correlation_id="MsgC", body="MessageC"))
+ session.message_transfer(message=self.createMessage(
+ routing_key="queue-d1", correlation_id="MsgD", body="MessageD"))
+
+ session.dtx_select()
+ txa = self.xid('a')
+ txb = self.xid('b')
+ txc = self.xid('c')
+ txd = self.xid('d')
+
+ self.txswap("queue-a1", "queue-a2", txa)
+ self.txswap("queue-b1", "queue-b2", txb)
+ self.txswap("queue-c1", "queue-c2", txc)
+ self.txswap("queue-d1", "queue-d2", txd)
+
+ #no queue should have any messages accessible
+ for q in queues:
+ self.assertEqual(0, session.queue_query(queue=q).message_count, "Bad count for %s" % (q))
+
+ self.assertEqual(self.XA_OK, session.dtx_commit(xid=txa, one_phase=True).status)
+ self.assertEqual(self.XA_OK, session.dtx_rollback(xid=txb).status)
+ self.assertEqual(self.XA_OK, session.dtx_prepare(xid=txc).status)
+ self.assertEqual(self.XA_OK, session.dtx_prepare(xid=txd).status)
+
+ #further checks
+ not_empty = ["queue-a2", "queue-b1"]
+ for q in queues:
+ if q in not_empty:
+ self.assertEqual(1, session.queue_query(queue=q).message_count, "Bad count for %s" % (q))
+ else:
+ self.assertEqual(0, session.queue_query(queue=q).message_count, "Bad count for %s" % (q))
+
+
+ def phase6(self):
+ session = self.session
+
+ #check prepared transaction are reported correctly by recover
+ txc = self.xid('c')
+ txd = self.xid('d')
+
+ xids = session.dtx_recover().in_doubt
+ ids = [x.global_id for x in xids] #TODO: come up with nicer way to test these
+
+ if txc.global_id not in ids:
+ self.fail("Recovered xids not as expected. missing: %s" % (txc))
+ if txd.global_id not in ids:
+ self.fail("Recovered xids not as expected. missing: %s" % (txd))
+ self.assertEqual(2, len(xids))
+
+
+ queues = ["queue-a1", "queue-a2", "queue-b1", "queue-b2", "queue-c1", "queue-c2", "queue-d1", "queue-d2"]
+ not_empty = ["queue-a2", "queue-b1"]
+
+ #re-check
+ not_empty = ["queue-a2", "queue-b1"]
+ for q in queues:
+ if q in not_empty:
+ self.assertEqual(1, session.queue_query(queue=q).message_count, "Bad count for %s" % (q))
+ else:
+ self.assertEqual(0, session.queue_query(queue=q).message_count, "Bad count for %s" % (q))
+
+ #complete the prepared transactions
+ self.assertEqual(self.XA_OK, session.dtx_commit(xid=txc).status)
+ self.assertEqual(self.XA_OK, session.dtx_rollback(xid=txd).status)
+ not_empty.append("queue-c2")
+ not_empty.append("queue-d1")
+
+ for q in queues:
+ if q in not_empty:
+ self.assertEqual(1, session.queue_query(queue=q).message_count)
+ else:
+ self.assertEqual(0, session.queue_query(queue=q).message_count)
+
+ def phase7(self):
+ session = self.session
+ session.synchronous = False
+
+ # check xids from phase 6 are gone
+ txc = self.xid('c')
+ txd = self.xid('d')
+
+ xids = session.dtx_recover().in_doubt
+ ids = [x.global_id for x in xids] #TODO: come up with nicer way to test these
+
+ if txc.global_id in ids:
+ self.fail("Xid still present : %s" % (txc))
+ if txd.global_id in ids:
+ self.fail("Xid still present : %s" % (txc))
+ self.assertEqual(0, len(xids))
+
+ #test deletion of queue after publish
+ #create queue
+ session.queue_declare(queue = "q", auto_delete=True, durable=True)
+
+ #send message
+ for i in range(1, 10):
+ session.message_transfer(message=self.createMessage(routing_key = "q", body = "my-message"))
+
+ session.synchronous = True
+ #explicitly delete queue
+ session.queue_delete(queue = "q")
+
+ #test acking of message from auto-deleted queue
+ #create queue
+ session.queue_declare(queue = "q", auto_delete=True, durable=True)
+
+ #send message
+ session.message_transfer(message=self.createMessage(routing_key = "q", body = "my-message"))
+
+ #create consumer
+ session.message_subscribe(queue = "q", destination = "a", accept_mode=0, acquire_mode=0)
+ session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a")
+ session.message_flow(unit = 0, value = 10, destination = "a")
+ queue = session.incoming("a")
+
+ #consume the message, cancel subscription (triggering auto-delete), then ack it
+ msg = queue.get(timeout = 5)
+ session.message_cancel(destination = "a")
+ self.ack(msg)
+
+ #test implicit deletion of bindings when queue is deleted
+ session.queue_declare(queue = "durable-subscriber-queue", exclusive=True, durable=True)
+ session.exchange_bind(exchange="amq.topic", queue="durable-subscriber-queue", binding_key="xyz")
+ session.message_transfer(destination= "amq.topic", message=self.createMessage(routing_key = "xyz", body = "my-message"))
+ session.queue_delete(queue = "durable-subscriber-queue")
+
+ #test unbind:
+ #create a series of bindings to a queue
+ session.queue_declare(queue = "binding-test-queue", durable=True)
+ session.exchange_bind(exchange="amq.direct", queue="binding-test-queue", binding_key="abc")
+ session.exchange_bind(exchange="amq.direct", queue="binding-test-queue", binding_key="pqr")
+ session.exchange_bind(exchange="amq.direct", queue="binding-test-queue", binding_key="xyz")
+ session.exchange_bind(exchange="amq.match", queue="binding-test-queue", binding_key="a", arguments={"x-match":"all", "p":"a"})
+ session.exchange_bind(exchange="amq.match", queue="binding-test-queue", binding_key="b", arguments={"x-match":"all", "p":"b"})
+ session.exchange_bind(exchange="amq.match", queue="binding-test-queue", binding_key="c", arguments={"x-match":"all", "p":"c"})
+ #then restart broker...
+
+
+ def phase8(self):
+ session = self.session
+
+ #continue testing unbind:
+ #send messages to the queue via each of the bindings
+ for k in ["abc", "pqr", "xyz"]:
+ data = "first %s" % (k)
+ session.message_transfer(destination= "amq.direct", message=self.createMessage(routing_key=k, body=data))
+ for a in [{"p":"a"}, {"p":"b"}, {"p":"c"}]:
+ data = "first %s" % (a["p"])
+ session.message_transfer(destination="amq.match", message=self.createMessage(application_headers=a, body=data))
+ #unbind some bindings (using final 0-10 semantics)
+ session.exchange_unbind(exchange="amq.direct", queue="binding-test-queue", binding_key="pqr")
+ session.exchange_unbind(exchange="amq.match", queue="binding-test-queue", binding_key="b")
+ #send messages again
+ for k in ["abc", "pqr", "xyz"]:
+ data = "second %s" % (k)
+ session.message_transfer(destination= "amq.direct", message=self.createMessage(routing_key=k, body=data))
+ for a in [{"p":"a"}, {"p":"b"}, {"p":"c"}]:
+ data = "second %s" % (a["p"])
+ session.message_transfer(destination="amq.match", message=self.createMessage(application_headers=a, body=data))
+
+ #check that only the correct messages are received
+ expected = []
+ for k in ["abc", "pqr", "xyz"]:
+ expected.append("first %s" % (k))
+ for a in [{"p":"a"}, {"p":"b"}, {"p":"c"}]:
+ expected.append("first %s" % (a["p"]))
+ for k in ["abc", "xyz"]:
+ expected.append("second %s" % (k))
+ for a in [{"p":"a"}, {"p":"c"}]:
+ expected.append("second %s" % (a["p"]))
+
+ session.message_subscribe(queue = "binding-test-queue", destination = "binding-test")
+ session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "binding-test")
+ session.message_flow(unit = 0, value = 10, destination = "binding-test")
+ queue = session.incoming("binding-test")
+
+ while len(expected):
+ msg = queue.get(timeout=1)
+ if msg.body not in expected:
+ self.fail("Missing message: %s" % msg.body)
+ expected.remove(msg.body)
+ try:
+ msg = queue.get(timeout=1)
+ self.fail("Got extra message: %s" % msg.body)
+ except Empty: pass
+
+
+
+ session.queue_declare(queue = "durable-subscriber-queue", exclusive=True, durable=True)
+ session.exchange_bind(exchange="amq.topic", queue="durable-subscriber-queue", binding_key="xyz")
+ session.message_transfer(destination= "amq.topic", message=self.createMessage(routing_key = "xyz", body = "my-message"))
+ session.queue_delete(queue = "durable-subscriber-queue")
+
+
+ def xid(self, txid, branchqual = ''):
+ return self.session.xid(format=0, global_id=txid, branch_id=branchqual)
+
+ def txswap(self, src, dest, tx):
+ self.assertEqual(self.XA_OK, self.session.dtx_start(xid=tx).status)
+ self.session.message_subscribe(destination="temp-swap", queue=src, accept_mode=0)
+ self.session.message_flow(destination="temp-swap", unit=0, value=1)
+ self.session.message_flow(destination="temp-swap", unit=1, value=0xFFFFFFFF)
+ msg = self.session.incoming("temp-swap").get(timeout=1)
+ self.session.message_cancel(destination="temp-swap")
+ self.session.message_transfer(message=self.createMessage(routing_key=dest, correlation_id=self.getProperty(msg, 'correlation_id'),
+ body=msg.body))
+ self.ack(msg)
+ self.assertEqual(self.XA_OK, self.session.dtx_end(xid=tx).status)
+
+ def assertEmptyQueue(self, name):
+ self.assertEqual(0, self.session.queue_query(queue=name).message_count)
+
+ def assertConnectionException(self, expectedCode, message):
+ self.assertEqual("connection", message.method.klass.name)
+ self.assertEqual("close", message.method.name)
+ self.assertEqual(expectedCode, message.reply_code)
+
+ def assertExpectedMethod(self, reply, klass, method):
+ self.assertEqual(klass, reply.method.klass.name)
+ self.assertEqual(method, reply.method.name)
+
+ def assertExpectedContent(self, msg, id, body):
+ self.assertEqual(id, self.getProperty(msg, 'correlation_id'))
+ self.assertEqual(body, msg.body)
+ return msg
+
+ def getProperty(self, msg, name):
+ for h in msg.headers:
+ if hasattr(h, name): return getattr(h, name)
+ return None
+
+ def ack(self, *msgs):
+ session = self.session
+ set = RangedSet()
+ for m in msgs:
+ set.add(m.id)
+ #TODO: tidy up completion
+ session.receiver._completed.add(m.id)
+ session.message_accept(set)
+ session.channel.session_completed(session.receiver._completed)
+
+ def assertExpectedGetResult(self, id, body):
+ return self.assertExpectedContent(session.incoming("incoming-gets").get(timeout=1), id, body)
+
+ def assertEqual(self, expected, actual, msg=''):
+ if expected != actual: raise Exception("%s expected: %s actual: %s" % (msg, expected, actual))
+
+ def assertMessageOnQueue(self, queue, id, body):
+ self.session.message_subscribe(destination="incoming-gets", queue=queue, accept_mode=0)
+ self.session.message_flow(destination="incoming-gets", unit=0, value=1)
+ self.session.message_flow(destination="incoming-gets", unit=1, value=0xFFFFFFFF)
+ msg = self.session.incoming("incoming-gets").get(timeout=1)
+ self.assertExpectedContent(msg, id, body)
+ self.ack(msg)
+ self.session.message_cancel(destination="incoming-gets")
+
+
+ def __init__(self):
+ TestBase010.__init__(self, "run")
+ self.setBroker("localhost")
+ self.errata = []
+
+ def connect(self):
+ """ Connects to the broker """
+ self.conn = Connection(connect(self.host, self.port))
+ self.conn.start(timeout=10)
+ self.session = self.conn.session("test-session", timeout=10)
+
+ def run(self, args=sys.argv[1:]):
+ try:
+ opts, extra = getopt(args, "r:s:e:b:p:h", ["retry=", "spec=", "errata=", "broker=", "phase=", "help"])
+ except GetoptError, e:
+ self._die(str(e))
+ phase = 0
+ retry = 0;
+ for opt, value in opts:
+ if opt in ("-h", "--help"): self._die()
+ if opt in ("-s", "--spec"): self.spec = value
+ if opt in ("-e", "--errata"): self.errata.append(value)
+ if opt in ("-b", "--broker"): self.setBroker(value)
+ if opt in ("-p", "--phase"): phase = int(value)
+ if opt in ("-r", "--retry"): retry = int(value)
+
+ if not phase: self._die("please specify the phase to run")
+ phase = "phase%d" % phase
+ self.connect()
+
+ try:
+ getattr(self, phase)()
+ print phase, "succeeded"
+ res = True;
+ except Exception, e:
+ print phase, "failed: ", e
+ traceback.print_exc()
+ res = False
+
+
+ if not self.session.error(): self.session.close(timeout=10)
+ self.conn.close(timeout=10)
+
+ # Crude fix to wait for thread in client to exit after return from session_close()
+ # Reduces occurrences of "Unhandled exception in thread" messages after each test
+ import time
+ time.sleep(1)
+
+ return res
+
+
+ def setBroker(self, broker):
+ rex = re.compile(r"""
+ # [ <user> [ / <password> ] @] <host> [ :<port> ]
+ ^ (?: ([^/]*) (?: / ([^@]*) )? @)? ([^:]+) (?: :([0-9]+))?$""", re.X)
+ match = rex.match(broker)
+ if not match: self._die("'%s' is not a valid broker" % (broker))
+ self.user, self.password, self.host, self.port = match.groups()
+ self.port = int(default(self.port, 5672))
+ self.user = default(self.user, "guest")
+ self.password = default(self.password, "guest")
+
+ def _die(self, message = None):
+ if message: print message
+ print """
+Options:
+ -h/--help : this message
+ -s/--spec <spec.xml> : file containing amqp XML spec
+ -p/--phase : test phase to run
+ -b/--broker [<user>[/<password>]@]<host>[:<port>] : broker to connect to
+ """
+ sys.exit(1)
+
+def default(value, default):
+ if (value == None): return default
+ else: return value
+
+if __name__ == "__main__":
+ test = PersistenceTest()
+ if not test.run(): sys.exit(1)
diff --git a/cpp/src/tests/federated_cluster_test_with_node_failure b/cpp/src/tests/legacystore/run_long_python_tests
index e9ae4b5914..e43b2236ec 100755..100644
--- a/cpp/src/tests/federated_cluster_test_with_node_failure
+++ b/cpp/src/tests/legacystore/run_long_python_tests
@@ -1,5 +1,4 @@
#!/bin/bash
-
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
@@ -8,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,5 +18,4 @@
# under the License.
#
-srcdir=`dirname $0`
-TEST_NODE_FAILURE=1 $srcdir/federated_cluster_test
+./run_python_tests LONG_TEST
diff --git a/cpp/src/tests/legacystore/run_python_tests b/cpp/src/tests/legacystore/run_python_tests
new file mode 100644
index 0000000000..d9dec16963
--- /dev/null
+++ b/cpp/src/tests/legacystore/run_python_tests
@@ -0,0 +1,64 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+if test -z ${QPID_DIR} ; then
+ cat <<EOF
+
+ =========== WARNING: PYTHON TESTS DISABLED ==============
+
+ QPID_DIR not set.
+
+ ===========================================================
+
+EOF
+ exit
+fi
+
+. `dirname $0`/tests_env.sh
+
+MODULENAME=python_tests
+
+echo "Running Python tests in module ${MODULENAME}..."
+
+case x$1 in
+ xSHORT_TEST)
+ DEFAULT_PYTHON_TESTS="*.client_persistence.ExchangeQueueTests.* *.flow_to_disk.SimpleMaxSizeCountTest.test_browse_recover *.flow_to_disk.SimpleMaxSizeCountTest.test_durable_browse_recover *.flow_to_disk.MultiDurableQueueDurableMsgBrowseRecoverTxPTxCTest.test_mixed_limit_2" ;;
+ xLONG_TEST)
+ DEFAULT_PYTHON_TESTS= ;;
+ x)
+ DEFAULT_PYTHON_TESTS="*.client_persistence.* *.flow_to_disk.SimpleMaxSizeCountTest.* *.flow_to_disk.MultiDurableQueue*.test_mixed_limit_1 *.flow_to_disk.MultiQueue*.test_mixed_limit_1 *.resize.SimpleTest.* *.federation.*" ;;
+ *)
+ DEFAULT_PYTHON_TESTS=$1
+esac
+
+PYTHON_TESTS=${PYTHON_TESTS:-${DEFAULT_PYTHON_TESTS}}
+
+OUTDIR=${MODULENAME}.tmp
+rm -rf $OUTDIR
+
+# To debug a test, add the following options to the end of the following line:
+# -v DEBUG -c qpid.messaging.io.ops [*.testName]
+${PYTHON_DIR}/qpid-python-test -m ${MODULENAME} -I ${FAILING_PYTHON_TESTS} ${PYTHON_TESTS} -DOUTDIR=$OUTDIR #-v DEBUG
+RETCODE=$?
+
+if test x${RETCODE} != x0; then
+ exit 1;
+fi
+exit 0
diff --git a/cpp/src/tests/run_long_cluster_tests b/cpp/src/tests/legacystore/run_short_python_tests
index 5dce0be585..523924fdba 100755..100644
--- a/cpp/src/tests/run_long_cluster_tests
+++ b/cpp/src/tests/legacystore/run_short_python_tests
@@ -1,5 +1,4 @@
#!/bin/bash
-
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
@@ -8,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,6 +18,4 @@
# under the License.
#
-srcdir=`dirname $0`
-$srcdir/run_cluster_tests 'cluster_tests.LongTests.*' -DDURATION=4
-
+./run_python_tests SHORT_TEST
diff --git a/cpp/src/tests/legacystore/run_test b/cpp/src/tests/legacystore/run_test
new file mode 100644
index 0000000000..1d5c2ae407
--- /dev/null
+++ b/cpp/src/tests/legacystore/run_test
@@ -0,0 +1,69 @@
+#!/bin/bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Set up environment and run a test executable or script.
+#
+# Output nothing if test passes, show the output if it fails and
+# leave output in <test>.log for examination.
+#
+# If qpidd.port exists run test with QPID_PORT=`cat qpidd.port`
+#
+# If $VALGRIND if is set run under valgrind. If there are valgrind
+# erros show valgrind output, also leave it in <test>.valgrind for
+# examination.
+#
+
+source `dirname $0`/vg_check
+
+# Export variables from makefile.
+export VALGRIND srcdir
+
+# Export QPID_PORT if qpidd.port exists.
+test -f qpidd.port && export QPID_PORT=`cat qpidd.port`
+
+# Avoid silly libtool error messages if these are not defined
+test -z "$LC_ALL" && export LC_ALL=
+test -z "$LC_CTYPE" && export LC_CTYPE=
+test -z "$LC_COLLATE" && export LC_COLLATE=
+test -z "$LC_MESSAGES" && export LC_MESSAGES=
+
+VG_LOG="$1.vglog"
+rm -f $VG_LOG*
+
+if grep -l "^# Generated by .*libtool" "$1" >/dev/null 2>&1; then
+ # This is a libtool "executable". Valgrind it if VALGRIND specified.
+ test -n "$VALGRIND" && VALGRIND="$VALGRIND --log-file=$VG_LOG --"
+ # Hide output unless there's an error.
+ libtool --mode=execute $VALGRIND "$@" 2>&1 || ERROR=$?
+ test -n "$VALGRIND" && vg_check $VG_LOG*
+else
+ # This is a non-libtool shell script, just execute it.
+ export VALGRIND srcdir
+ exec "$@"
+fi
+
+if test -z "$ERROR"; then
+ # Clean up logs if there was no error.
+ rm -f $VG_LOG*
+ exit 0
+else
+ exit $ERROR
+fi
diff --git a/cpp/src/tests/run_cluster_test b/cpp/src/tests/legacystore/start_broker
index 11df3d63a3..30e4659030 100755..100644
--- a/cpp/src/tests/run_cluster_test
+++ b/cpp/src/tests/legacystore/start_broker
@@ -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
@@ -19,9 +19,7 @@
# under the License.
#
-
-# Run the tests
-srcdir=`dirname $0`
-source cpg_check.sh
-cpg_enabled || exit 0
-with_ais_group $srcdir/run_test ./cluster_test
+QPIDD=$QPID_BLD/src/qpidd
+rm -f qpidd.vglog* qpidd.log
+test -n "$VALGRIND" && VALGRIND="$VALGRIND --log-file=qpidd.vglog --suppressions=$QPID_DIR/cpp/src/tests/.valgrind.supp --"
+exec libtool --mode=execute $VALGRIND $QPIDD --daemon --port=0 --log-enable error+ --log-to-file qpidd.log "$@" > qpidd.port
diff --git a/cpp/src/tests/cpg_check.sh.in b/cpp/src/tests/legacystore/stop_broker
index ed97776218..dcefff376f 100755..100644
--- a/cpp/src/tests/cpg_check.sh.in
+++ b/cpp/src/tests/legacystore/stop_broker
@@ -1,4 +1,5 @@
#!/bin/bash
+
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
@@ -18,21 +19,28 @@
# under the License.
#
-QPID_USE_CPG=${QPID_USE_CPG:-@USE_CPG@}
+# Stop the broker, check for errors.
+#
+if test -f qpidd.port; then
+ export QPID_PORT=`cat qpidd.port`
+ QPIDD=$QPID_BLD/src/qpidd
+ rm -f qpidd.port
+
+ $QPIDD --quit || ERROR=$?
-# Check if CPG is enabled
-cpg_enabled() {
- test x$QPID_USE_CPG = xyes || return 1 # disabled
- ps -u root | grep 'aisexec\|corosync' >/dev/null || {
- echo WARNING: Skip cluster tests, aisexec or corosync daemon is not running.
- return 1; # A warning, not a failure.
+ # Check qpidd.log.
+ grep -a 'warning\|error\|critical' qpidd.log && {
+ echo "WARNING: Suspicious broker log entries in qpidd.log, above."
}
- return 0
-}
-# Execute command with the ais group set if user is a member.
-with_ais_group() {
- if id -nG | grep '\<ais\>' >/dev/null; then sg ais -c "$*"
- else "$@"
+ # Check valgrind log.
+ if test -n "$VALGRIND"; then
+ source `dirname $0`/vg_check $VG_LOG*
+ vg_check qpidd.vglog*
fi
-}
+
+ exit $ERROR
+else
+ echo "No qpidd.port file found - cannot stop broker."
+ exit 1;
+fi
diff --git a/cpp/src/tests/legacystore/system_test.sh b/cpp/src/tests/legacystore/system_test.sh
new file mode 100644
index 0000000000..4cccc5ac8d
--- /dev/null
+++ b/cpp/src/tests/legacystore/system_test.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+error() { echo $*; exit 1; }
+
+# Make sure $QPID_DIR contains what we need.
+if ! test -d "$QPID_DIR" ; then
+ echo "WARNING: QPID_DIR is not set skipping system tests."
+ exit
+fi
+STORE_LIB=../lib/.libs/msgstore.so
+
+xml_spec=$QPID_DIR/specs/amqp.0-10-qpid-errata.xml
+test -f $xml_spec || error "$xml_spec not found: invalid \$QPID_DIR ?"
+export PYTHONPATH=$QPID_DIR/python:$QPID_DIR/extras/qmf/src/py:$QPID_DIR/tools/src/py
+
+echo "Using directory $TMP_DATA_DIR"
+
+fail=0
+
+# Run the tests with a given set of flags
+BROKER_OPTS="--no-module-dir --load-module=$STORE_LIB --data-dir=$TMP_DATA_DIR --auth=no --wcache-page-size 16"
+run_tests() {
+ for p in `seq 1 8`; do
+ $abs_srcdir/start_broker "$@" ${BROKER_OPTS} || { echo "FAIL broker start"; return 1; }
+ python "$abs_srcdir/persistence.py" -s "$xml_spec" -b localhost:`cat qpidd.port` -p $p -r 3 || fail=1;
+ $abs_srcdir/stop_broker
+ done
+}
+
+run_tests || fail=1
+
+exit $fail
diff --git a/cpp/src/tests/legacystore/tests_env.sh b/cpp/src/tests/legacystore/tests_env.sh
new file mode 100644
index 0000000000..30d255b87c
--- /dev/null
+++ b/cpp/src/tests/legacystore/tests_env.sh
@@ -0,0 +1,260 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# --- Function definitions ---
+
+
+func_check_required_env ()
+#-------------------------
+# Check that EITHER:
+# QPID_DIR is set (for running against svn QPID)
+# OR
+# QPID_PREFIX is set (for running against installed QPID
+# Will exit with error code 1 if neither of these is defined.
+# Params: None
+# Returns: 0 if env vars ok, 1 otherwise
+{
+ if test -z "${QPID_DIR}" -a -z "${QPID_PREFIX}"; then
+ # Try to find qpidd in the normal installed location
+ if test -x /usr/sbin/qpidd; then
+ QPID_PREFIX=/usr
+ else
+ echo "ERROR: Could not find installed Qpid"
+ echo "Either of the following must be set in the environment for this script to run:"
+ echo " QPID_DIR for running against a Qpid svn build"
+ echo " QPID_PREFIX for running against an installed Qpid"
+ return 1
+ fi
+ fi
+ return 0
+}
+
+
+func_check_qpid_python ()
+#------------------------
+# Check that Qpid python environment is ok
+# Params: None
+# Returns: 0 if Python environment is ok; 1 otherwise
+{
+ if ! python -c "import qpid" ; then
+ cat <<EOF
+
+ =========== WARNING: PYTHON TESTS DISABLED ==============
+
+ Unable to load python qpid module - skipping python tests.
+
+ PYTHONPATH=${PYTHONPATH}
+
+ ===========================================================
+
+EOF
+ return 1
+ fi
+ return 0
+}
+
+
+func_set_env ()
+#--------------
+# Set up the environment based on value of ${QPID_DIR}: if ${QPID_DIR} exists, assume a svn checkout,
+# otherwise set up for an installed or prefix test.
+# Params: None
+# Returns: Nothing
+{
+ if test "${QPID_DIR}" -a -d "${QPID_DIR}" ; then
+ # QPID_DIR is defined for source tree builds by the --with-qpid-checkout configure option.
+ # QPID_BLD is defined as the build directory, either $QPID_DIR/cpp or separately specified with
+ # the --with-qpid-build option for VPATH builds.
+
+ # Check QPID_BLD is also set
+ if test -z ${QPID_BLD}; then
+ QPID_BLD="${QPID_DIR}/cpp"
+ fi
+
+ # Paths and dirs
+ #if test -z ${abs_srcdir}; then
+ # abs_srcdir=`pwd`
+ #fi
+ source $QPID_BLD/src/tests/test_env.sh
+ # Override these two settings from test_env.sh:
+ export RECEIVER_EXEC=$QPID_TEST_EXEC_DIR/qpid-receive
+ export SENDER_EXEC=$QPID_TEST_EXEC_DIR/qpid-send
+
+ echo "abs_srcdir=$abs_srcdir"
+ export STORE_LIB="`pwd`/../lib/.libs/msgstore.so"
+ export STORE_ENABLE=1
+ export CLUSTER_LIB="${QPID_BLD}/src/.libs/cluster.so"
+
+ PYTHON_DIR="${QPID_DIR}/python"
+ export PYTHONPATH="${PYTHONPATH}":"${PYTHON_DIR}":"${QPID_DIR}/extras/qmf/src/py":"${QPID_DIR}/tools/src/py":"${QPID_DIR}/cpp/src/tests":"${abs_srcdir}"
+
+ # Libraries
+
+ # Executables
+ export QPIDD_EXEC="${QPID_BLD}/src/qpidd"
+
+ # Test data
+
+ else
+ # Set up the environment based on value of ${QPID_PREFIX} for testing against an installed qpid
+ # Alternatively, make sure ${QPID_BIN_DIR}, ${QPID_SBIN_DIR}, ${QPID_LIB_DIR} and ${QPID_LIBEXEC_DIR} are set for
+ # the installed location.
+ if test "${QPID_PREFIX}" -a -d "${QPID_PREFIX}" ; then
+ QPID_BIN_DIR=${QPID_PREFIX}/bin
+ QPID_SBIN_DIR=${QPID_PREFIX}/sbin
+ QPID_LIB_DIR=${QPID_PREFIX}/lib
+ QPID_LIBEXEC_DIR=${QPID_PREFIX}/libexec
+ fi
+
+ # These four env vars must be set prior to calling this script
+ func_checkpaths QPID_BIN_DIR QPID_SBIN_DIR QPID_LIB_DIR QPID_LIBEXEC_DIR
+
+ # Paths and dirs
+ export PYTHON_DIR="${QPID_BIN_DIR}"
+ export PYTHONPATH="${PYTHONPATH}":"${QPID_LIB_DIR}/python":"${QPID_LIBEXEC_DIR}/qpid/tests":"${QPID_LIB_DIR}/python2.4"
+
+
+ # Libraries
+
+ # Executables
+ export QPIDD_EXEC="${QPID_SBIN_DIR}/qpidd"
+
+ # Test Data
+
+ fi
+}
+
+
+func_mk_data_dir ()
+#------------------
+# Create a data dir at ${TMP_DATA_DIR} if not present, clear it otherwise.
+# Set TMP_DATA_DIR if it is not set.
+# Params: None
+# Returns: Nothing
+{
+ if test -z "${TMP_DATA_DIR}"; then
+ TMP_DATA_DIR=/tmp/python_tests
+ echo "TMP_DATA_DIR not set; using ${TMP_DATA_DIR}"
+ fi
+
+ # Delete old test dirs if they exist
+ if test -d "${TMP_DATA_DIR}" ; then
+ rm -rf "${TMP_DATA_DIR}/*"
+ fi
+ mkdir -p "${TMP_DATA_DIR}"
+ export TMP_DATA_DIR
+}
+
+
+func_checkvar ()
+#---------------
+# Check that an environment var is set (ie non-zero length)
+# Params: $1 - env var to be checked
+# Returns: 0 = env var is set (ie non-zero length)
+# 1 = env var is not set
+{
+ local loc_VAR=$1
+ if test -z ${!loc_VAR}; then
+ echo "WARNING: environment variable ${loc_VAR} not set."
+ return 1
+ fi
+ return 0
+}
+
+
+func_checkpaths ()
+#-----------------
+# Check a list of paths (each can contain ':'-separated sub-list) is set and valid (ie each path exists as a dir)
+# Params: $@ - List of path env vars to be checked
+# Returns: Nothing
+{
+ local loc_PATHS=$@
+ for path in ${loc_PATHS}; do
+ func_checkvar ${path}
+ if test $? == 0; then
+ local temp_IFS=${IFS}
+ IFS=":"
+ local pl=${!path}
+ for p in ${pl[@]}; do
+ if test ! -d ${p}; then
+ echo "WARNING: Directory ${p} in var ${path} not found."
+ fi
+ done
+ IFS=${temp_IFS}
+ fi
+ done
+}
+
+
+func_checklibs ()
+#----------------
+# Check that a list of libs is set and valid (ie each lib exists as an executable file)
+# Params: $@ - List of lib values to be checked
+# Returns: Nothing
+{
+ local loc_LIBS=$@
+ for lib in ${loc_LIBS[@]}; do
+ func_checkvar ${lib}
+ if test $? == 0; then
+ if test ! -x ${!lib}; then
+ echo "WARNING: Library ${lib}=${!lib} not found."
+ fi
+ fi
+ done
+}
+
+
+func_checkexecs ()
+#-----------------
+# Check that a list of executable is set and valid (ie each exec exists as an executable file)
+# Params: $@ - List of exec values to be checked
+# Returns: Nothing
+{
+ local loc_EXECS=$@
+ for exec in ${loc_EXECS[@]}; do
+ func_checkvar ${exec}
+ if test $? == 0; then
+ if test ! -x ${!exec}; then
+ echo "WARNING: Executable ${exec}=${!exec} not found or is not executable."
+ fi
+ fi
+ done
+}
+
+
+#--- Start of script ---
+
+func_check_required_env || exit 1 # Cannot run, exit with error
+
+srcdir=`dirname $0`
+if test -z ${abs_srcdir}; then
+ abs_srcdir=${srcdir}
+fi
+
+func_set_env
+func_check_qpid_python || exit 0 # A warning, not a failure.
+func_mk_data_dir
+
+# Check expected environment vars are set
+func_checkpaths PYTHON_DIR PYTHONPATH TMP_DATA_DIR
+func_checklibs STORE_LIB CLUSTER_LIB
+func_checkexecs QPIDD_EXEC QPID_CONFIG_EXEC QPID_ROUTE_EXEC SENDER_EXEC RECEIVER_EXEC
+
+FAILING_PYTHON_TESTS="${abs_srcdir}/failing_python_tests.txt"
+
diff --git a/cpp/src/tests/legacystore/unit_test.cpp b/cpp/src/tests/legacystore/unit_test.cpp
new file mode 100644
index 0000000000..add80a6f91
--- /dev/null
+++ b/cpp/src/tests/legacystore/unit_test.cpp
@@ -0,0 +1,28 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// Defines test_main function to link with actual unit test code.
+#define BOOST_AUTO_TEST_MAIN // Boost 1.33
+#define BOOST_TEST_MAIN
+
+#include "unit_test.h"
+
diff --git a/cpp/src/tests/legacystore/unit_test.h b/cpp/src/tests/legacystore/unit_test.h
new file mode 100644
index 0000000000..16b6ae2ffb
--- /dev/null
+++ b/cpp/src/tests/legacystore/unit_test.h
@@ -0,0 +1,69 @@
+#ifndef QPIPD_TEST_UNIT_TEST_H_
+#define QPIPD_TEST_UNIT_TEST_H_
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// Workaround so we can build against boost 1.32, 1.33 and boost 1.34.
+// Remove when we no longer need to support 1.32 or 1.33.
+
+#include <boost/version.hpp>
+
+#if (BOOST_VERSION < 103400) // v.1.33 and earlier
+# include <boost/test/auto_unit_test.hpp>
+#else // v.1.34 and later
+# include <boost/test/unit_test.hpp>
+#endif
+
+// Keep the test function for compilation but do not not register it.
+// TODO aconway 2008-04-23: better workaround for expected failures.
+// The following causes the test testUpdateTxState not to run at all.
+# define QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(test_name,n) \
+ namespace { struct test_name { void test_method(); }; } \
+ void test_name::test_method()
+// The following runs the test testUpdateTxState, but it fails.
+/*#define QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(test_name,n) \
+ namespace { struct test_name { void test_method(); }; } \
+ BOOST_AUTO_TEST_CASE(name)*/
+
+#if (BOOST_VERSION < 103300) // v.1.32 and earlier
+
+# define QPID_AUTO_TEST_SUITE(name)
+# define QPID_AUTO_TEST_CASE(name) BOOST_AUTO_UNIT_TEST(name)
+# define QPID_AUTO_TEST_SUITE_END()
+
+#elif (BOOST_VERSION < 103400) // v.1.33
+
+// Note the trailing ';'
+# define QPID_AUTO_TEST_SUITE(name) BOOST_AUTO_TEST_SUITE(name);
+# define QPID_AUTO_TEST_CASE(name) BOOST_AUTO_TEST_CASE(name)
+# define QPID_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END();
+
+#else // v.1.34 and later
+
+# define QPID_AUTO_TEST_SUITE(name) BOOST_AUTO_TEST_SUITE(name)
+# define QPID_AUTO_TEST_CASE(name) BOOST_AUTO_TEST_CASE(name)
+# define QPID_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
+
+#endif
+
+#endif /*!QPIPD_TEST_UNIT_TEST_H_*/
diff --git a/cpp/src/tests/long_cluster_tests.py b/cpp/src/tests/long_cluster_tests.py
deleted file mode 100755
index f77837f0c4..0000000000
--- a/cpp/src/tests/long_cluster_tests.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env python
-
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-import os, signal, sys, unittest
-from testlib import TestBaseCluster
-
-class LongClusterTests(TestBaseCluster):
- """Long/Soak cluster tests with async store ability"""
-
-
- def test_LongCluster_01_DummyTest(self):
- """Dummy test - a placeholder for the first of the long/soak python cluster tests"""
- pass
-
-# Start the test here
-
-if __name__ == '__main__':
- if os.getenv("STORE_LIB") != None:
- print "NOTE: Store enabled for the following tests:"
- if not unittest.main(): sys.exit(1)
-
diff --git a/cpp/src/tests/qpid-cluster-benchmark b/cpp/src/tests/qpid-cluster-benchmark
index 610beacebd..3e6b805692 100755
--- a/cpp/src/tests/qpid-cluster-benchmark
+++ b/cpp/src/tests/qpid-cluster-benchmark
@@ -55,12 +55,10 @@ done
shift $(($OPTIND-1))
CONNECTION_OPTIONS="--connection-options {tcp-nodelay:$TCP_NODELAY,reconnect:$RECONNECT,heartbeat:$HEARTBEAT}"
-CREATE_OPTIONS="node:{x-declare:{arguments:{'qpid.replicate':all}}}"
BROKER=$(echo $BROKERS | sed s/,.*//)
run_test() { echo $*; shift; "$@"; echo; echo; echo; }
OPTS="$OPTS $REPEAT $BROKERS --summarize $QUEUES $SENDERS $RECEIVERS $MESSAGES $CLIENT_HOSTS $SAVE_RECEIVED $CONNECTION_OPTIONS $NO_DELETE"
-OPTS="$OPTS --create-option $CREATE_OPTIONS"
run_test "Benchmark:" qpid-cpp-benchmark $OPTS "$@"
diff --git a/cpp/src/tests/qpid-cpp-benchmark b/cpp/src/tests/qpid-cpp-benchmark
index d5ad5191ca..9ac6a10883 100755
--- a/cpp/src/tests/qpid-cpp-benchmark
+++ b/cpp/src/tests/qpid-cpp-benchmark
@@ -18,7 +18,7 @@
# under the License.
#
-import optparse, time, qpid.messaging, re
+import optparse, time, qpid.messaging, re, os
from threading import Thread
from subprocess import Popen, PIPE, STDOUT
@@ -77,6 +77,10 @@ op.add_option("--no-delete", default=False, action="store_true",
help="Don't delete the test queues.")
op.add_option("--fill-drain", default=False, action="store_true",
help="First fill the queues, then drain them")
+op.add_option("--qpid-send-path", default="", type="str", metavar="PATH",
+ help="path to qpid-send binary")
+op.add_option("--qpid-receive-path", default="", type="str", metavar="PATH",
+ help="path to qpid-receive binary")
single_quote_re = re.compile("'")
def posix_quote(string):
@@ -115,7 +119,7 @@ def start_receive(queue, index, opts, ready_queue, broker, host):
messages = msg_total/opts.receivers;
if (index < msg_total%opts.receivers): messages += 1
if (messages == 0): return None
- command = ["qpid-receive",
+ command = [os.path.join(opts.qpid_receive_path, "qpid-receive"),
"-b", broker,
"-a", address,
"-m", str(messages),
@@ -138,7 +142,7 @@ def start_receive(queue, index, opts, ready_queue, broker, host):
def start_send(queue, opts, broker, host):
address="%s;{%s}"%(queue,",".join(opts.send_option + ["create:always"]))
- command = ["qpid-send",
+ command = [os.path.join(opts.qpid_send_path, "qpid-send"),
"-b", broker,
"-a", address,
"--messages", str(opts.messages),
diff --git a/cpp/src/tests/qpid-receive.cpp b/cpp/src/tests/qpid-receive.cpp
index 7a02b871db..e623d052e8 100644
--- a/cpp/src/tests/qpid-receive.cpp
+++ b/cpp/src/tests/qpid-receive.cpp
@@ -222,7 +222,7 @@ int main(int argc, char ** argv)
if (msg.getCorrelationId().size()) std::cout << "CorrelationId: " << msg.getCorrelationId() << std::endl;
if (msg.getUserId().size()) std::cout << "UserId: " << msg.getUserId() << std::endl;
if (msg.getTtl().getMilliseconds()) std::cout << "TTL: " << msg.getTtl().getMilliseconds() << std::endl;
- if (msg.getPriority()) std::cout << "Priority: " << msg.getPriority() << std::endl;
+ if (msg.getPriority()) std::cout << "Priority: " << ((uint) msg.getPriority()) << std::endl;
if (msg.getDurable()) std::cout << "Durable: true" << std::endl;
if (msg.getRedelivered()) std::cout << "Redelivered: true" << std::endl;
std::cout << "Properties: " << msg.getProperties() << std::endl;
diff --git a/cpp/src/tests/qpid-send.cpp b/cpp/src/tests/qpid-send.cpp
index b1c4f2be38..c3bba31e3b 100644
--- a/cpp/src/tests/qpid-send.cpp
+++ b/cpp/src/tests/qpid-send.cpp
@@ -200,7 +200,7 @@ struct Options : public qpid::Options
std::string name;
std::string value;
if (nameval(property, name, value)) {
- message.getProperties()[name] = value;
+ message.getProperties()[name].parse(value);
} else {
message.getProperties()[name] = Variant();
}
diff --git a/cpp/src/tests/qpid-test-cluster b/cpp/src/tests/qpid-test-cluster
deleted file mode 100755
index 40ad452a0d..0000000000
--- a/cpp/src/tests/qpid-test-cluster
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/bin/bash
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-usage() {
- echo "Usage: `basename $0` [options] start|stop|restart|check [hosts]
-Start/stop/restart a cluster on specified hosts or on \$HOSTS via ssh.
-
-Options:
- -l USER Run qpidd and copy files as USER.
- -e SCRIPT Source SCRIPT for environment settings. Copies SCRIPT to each host.
- Default is $DEFAULT_ENV.
- -c CONFIG Use CONFIG as qpidd config file. Copies CONFIG to each host.
- Default is $DEFAULT_CONF
- -d Delete data-dir and log file before starting broker.
-"
- exit 1
-}
-
-DEFAULT_CONF=~/qpid-test-qpidd.conf
-DEFAULT_ENV=~/qpid-test-env.sh
-
-test -f $DEFAULT_CONF && CONF_FILE=$DEFAULT_CONF
-test -f $DEFAULT_ENV && ENV_FILE=$DEFAULT_ENV
-
-while getopts "l:e:c:d" opt; do
- case $opt in
- l) SSHOPTS="-l$OPTARG $SSHOPTS" ; RSYNC_USER="$OPTARG@" ;;
- e) ENV_FILE=$OPTARG ;;
- c) CONF_FILE=$OPTARG ;;
- d) DO_DELETE=1 ;;
- *) usage;;
- esac
-done
-shift `expr $OPTIND - 1`
-test "$*" || usage
-CMD=$1; shift
-HOSTS=${*:-$HOSTS}
-
-conf_value() { test -f "$CONF_FILE" && awk -F= "/^$1=/ {print \$2}" $CONF_FILE; }
-
-if test -n "$CONF_FILE"; then
- test -f "$CONF_FILE" || { echo Config file not found: $CONF_FILE; exit 1; }
- RSYNCFILES="$RSYNCFILES $CONF_FILE"
- QPIDD_ARGS="$QPIDD_ARGS --config $CONF_FILE"
- CONF_PORT=`conf_value port`
- CONF_DATA_DIR=`conf_value data-dir`
- CONF_LOG_FILE=`conf_value log-to-file`
-fi
-
-if test -n "$ENV_FILE"; then
- test -f "$ENV_FILE" || { echo Environment file not found: $ENV_FILE; exit 1; }
- RSYNCFILES="$RSYNCFILES $ENV_FILE"
- SOURCE_ENV="source $ENV_FILE ; "
-fi
-
-test -n "$RSYNCFILES" && rsynchosts $RSYNCFILES # Copy conf/env files to all hosts
-
-do_start() {
- for h in $HOSTS; do
- COMMAND="qpidd -d $QPIDD_ARGS"
- id -nG | grep '\<ais\>' >/dev/null && COMMAND="sg ais -c '$COMMAND'"
- if test "$DO_DELETE"; then COMMAND="rm -rf $CONF_DATA_DIR $CONF_LOG_FILE; $COMMAND"; fi
- ssh $h "$SOURCE_ENV $COMMAND" || { echo "Failed to start on $h"; exit 1; }
- done
-}
-
-do_stop() {
- for h in $HOSTS; do
- ssh $h "$SOURCE_ENV qpidd -q --no-module-dir --no-data-dir $QPIDD_ARGS"
- done
-}
-
-do_status() {
- for h in $HOSTS; do
- if ssh $h "$SOURCE_ENV qpidd -c --no-module-dir --no-data-dir $QPIDD_ARGS > /dev/null"; then
- echo "$h ok"
- else
- echo "$h not running"
- STATUS=1
- fi
- done
-}
-
-case $CMD in
- start) do_start ;;
- stop) do_stop ;;
- restart) do_stop ; do_start ;;
- status) do_status ;;
- *) usage;;
-esac
-
-exit $STATUS
diff --git a/cpp/src/tests/reliable_replication_test b/cpp/src/tests/reliable_replication_test
deleted file mode 100755
index c660f751e5..0000000000
--- a/cpp/src/tests/reliable_replication_test
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/bin/bash
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Test reliability of the replication feature in the face of link
-# failures:
-
-source ./test_env.sh
-
-trap stop_brokers EXIT
-
-stop_brokers() {
- if [[ $BROKER_A ]] ; then
- $QPIDD_EXEC --no-module-dir -q --port $BROKER_A
- unset BROKER_A
- fi
- if [[ $BROKER_B ]] ; then
- $QPIDD_EXEC --no-module-dir -q --port $BROKER_B
- unset BROKER_B
- fi
-}
-
-setup() {
- rm -f replication-source.log replication-dest.log
- $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATING_LISTENER_LIB --replication-queue replication --create-replication-queue true --log-enable trace+ --log-to-file replication-source.log --log-to-stderr 0 > qpidd-repl.port
- BROKER_A=`cat qpidd-repl.port`
-
- $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATION_EXCHANGE_LIB --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd-repl.port
- BROKER_B=`cat qpidd-repl.port`
-
- echo "Testing replication from port $BROKER_A to port $BROKER_B"
-
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add exchange replication replication
- $PYTHON_COMMANDS/qpid-route --ack 500 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication
-
- #create test queue (only replicate enqueues for this test):
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-a --generate-queue-events 1
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-a
-}
-
-send() {
- ./sender --port $BROKER_A --routing-key queue-a --send-eos 1 < replicated.expected
-}
-
-receive() {
- rm -f replicated.actual
- ./receiver --port $BROKER_B --queue queue-a > replicated.actual
-}
-
-bounce_link() {
- $PYTHON_COMMANDS/qpid-route link del "localhost:$BROKER_B" "localhost:$BROKER_A"
- $PYTHON_COMMANDS/qpid-route --ack 500 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication
-}
-
-if test -d ${PYTHON_DIR} && test -e $REPLICATING_LISTENER_LIB && test -e $REPLICATION_EXCHANGE_LIB ; then
- setup
- for i in `seq 1 100000`; do echo Message $i; done > replicated.expected
- send &
- receive &
- for i in `seq 1 3`; do sleep 1; bounce_link; done;
- wait
- #check that received list is identical to sent list
- diff replicated.actual replicated.expected || exit 1
- rm -f replication.actual replication.expected replication-source.log replication-dest.log qpidd-repl.port
- true
-fi
-
diff --git a/cpp/src/tests/replication_test b/cpp/src/tests/replication_test
deleted file mode 100755
index f8b2136396..0000000000
--- a/cpp/src/tests/replication_test
+++ /dev/null
@@ -1,182 +0,0 @@
-#!/bin/bash
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Run a test of the replication feature
-
-source ./test_env.sh
-
-trap stop_brokers INT TERM QUIT
-
-stop_brokers() {
- if [ x$BROKER_A != x ]; then
- $QPIDD_EXEC --no-module-dir -q --port $BROKER_A
- unset BROKER_A
- fi
- if [ x$BROKER_B != x ]; then
- $QPIDD_EXEC --no-module-dir -q --port $BROKER_B
- unset BROKER_B
- fi
-}
-
-if test -d ${PYTHON_DIR} && test -f "$REPLICATING_LISTENER_LIB" && test -f "$REPLICATION_EXCHANGE_LIB"; then
- rm -f queue-*.repl replication-*.log #cleanup from any earlier runs
-
- $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATING_LISTENER_LIB --replication-queue replication --create-replication-queue true --log-enable info+ --log-to-file replication-source.log --log-to-stderr 0 > qpidd.port
- BROKER_A=`cat qpidd.port`
-
- $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATION_EXCHANGE_LIB --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd.port
- BROKER_B=`cat qpidd.port`
- echo "Running replication test between localhost:$BROKER_A and localhost:$BROKER_B"
-
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add exchange replication replication
- $PYTHON_COMMANDS/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication
-
- #create test queues
-
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-a --generate-queue-events 2
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-b --generate-queue-events 2
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-c --generate-queue-events 1
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-d --generate-queue-events 2
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-e --generate-queue-events 1
-
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-a
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-b
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-c
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-e
- #queue-d deliberately not declared on DR; this error case should be handled
-
- #publish and consume from test queues on broker A:
- i=1
- while [ $i -le 10 ]; do
- echo Message $i for A >> queue-a-input.repl
- i=`expr $i + 1`
- done
- i=1
- while [ $i -le 20 ]; do
- echo Message $i for B >> queue-b-input.repl
- i=`expr $i + 1`
- done
- i=1
- while [ $i -le 15 ]; do
- echo Message $i for C >> queue-c-input.repl
- i=`expr $i + 1`
- done
-
- ./sender --port $BROKER_A --routing-key queue-a --send-eos 1 < queue-a-input.repl
- ./sender --port $BROKER_A --routing-key queue-b --send-eos 1 < queue-b-input.repl
- ./sender --port $BROKER_A --routing-key queue-c --send-eos 1 < queue-c-input.repl
- echo dummy | ./sender --port $BROKER_A --routing-key queue-d --send-eos 1
-
- ./receiver --port $BROKER_A --queue queue-a --messages 5 > /dev/null
- ./receiver --port $BROKER_A --queue queue-b --messages 10 > /dev/null
- ./receiver --port $BROKER_A --queue queue-c --messages 10 > /dev/null
- ./receiver --port $BROKER_A --queue queue-d > /dev/null
-
-
- # What we are doing is putting a message on the end of repliaction queue & waiting for it on remote side
- # making sure all the messages have been flushed from the replication queue.
- echo dummy | ./sender --port $BROKER_A --routing-key queue-e --send-eos 1
- ./receiver --port $BROKER_B --queue queue-e --messages 1 > /dev/null
-
- #shutdown broker A then check that broker Bs versions of the queues are as expected
- $QPIDD_EXEC --no-module-dir -q --port $BROKER_A
- unset BROKER_A
-
- #validate replicated queues:
- ./receiver --port $BROKER_B --queue queue-a > queue-a-backup.repl
- ./receiver --port $BROKER_B --queue queue-b > queue-b-backup.repl
- ./receiver --port $BROKER_B --queue queue-c > queue-c-backup.repl
-
-
- tail -5 queue-a-input.repl > queue-a-expected.repl
- tail -10 queue-b-input.repl > queue-b-expected.repl
- diff queue-a-backup.repl queue-a-expected.repl || FAIL=1
- diff queue-b-backup.repl queue-b-expected.repl || FAIL=1
- diff queue-c-backup.repl queue-c-input.repl || FAIL=1
-
- grep 'queue-d does not exist' replication-dest.log > /dev/null || echo "WARNING: Expected error to be logged!"
-
- stop_brokers
-
- # now check offsets working (enqueue based on position being set, not queue abs position)
-
- $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATING_LISTENER_LIB --replication-queue replication --create-replication-queue true --log-enable info+ --log-to-file replication-source.log --log-to-stderr 0 > qpidd.port
- BROKER_A=`cat qpidd.port`
-
- $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATION_EXCHANGE_LIB --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd.port
- BROKER_B=`cat qpidd.port`
-
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add exchange replication replication
- $PYTHON_COMMANDS/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication
-
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-e --generate-queue-events 2
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-e
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-d --generate-queue-events 1
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-d
-
- i=1
- while [ $i -le 10 ]; do
- echo Message $i for A >> queue-e-input.repl
- i=`expr $i + 1`
- done
-
- ./sender --port $BROKER_A --routing-key queue-e --send-eos 1 < queue-e-input.repl
- ./receiver --port $BROKER_A --queue queue-e --messages 10 > /dev/null
-
- # What we are doing is putting a message on the end of repliaction queue & waiting for it on remote side
- # making sure all the messages have been flushed from the replication queue.
- echo dummy | ./sender --port $BROKER_A --routing-key queue-d --send-eos 1
- ./receiver --port $BROKER_B --queue queue-d --messages 1 > /dev/null
-
- # now check offsets working
- $QPIDD_EXEC --no-module-dir -q --port $BROKER_B
- unset BROKER_B
- $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATION_EXCHANGE_LIB --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd.port
- BROKER_B=`cat qpidd.port`
-
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-e
- $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add exchange replication replication
- $PYTHON_COMMANDS/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication
- # now send another 15
- i=11
- while [ $i -le 15 ]; do
- echo Message $i for A >> queue-e1-input.repl
- i=`expr $i + 1`
- done
- ./sender --port $BROKER_A --routing-key queue-e --send-eos 1 < queue-e1-input.repl
-
- ./receiver --port $BROKER_B --queue queue-e > queue-e-backup.repl
- diff queue-e-backup.repl queue-e1-input.repl || FAIL=1
-
- stop_brokers
-
- if [ x$FAIL != x ]; then
- echo replication test failed: expectations not met!
- exit 1
- else
- echo queue state replicated as expected
- rm -f queue-*.repl replication-*.log
- fi
-
-else
- echo "Skipping replication test, plugins not built or python utils not located"
-fi
-
diff --git a/cpp/src/tests/restart_cluster b/cpp/src/tests/restart_cluster
deleted file mode 100755
index 5b48e619f6..0000000000
--- a/cpp/src/tests/restart_cluster
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/bash
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Re-start a cluster on the local host.
-
-srcdir=`dirname $0`
-$srcdir/stop_cluster
-exec $srcdir/start_cluster "$@"
-#!/bin/bash
-# Re-start a cluster on the local host.
-
-srcdir=`dirname $0`
-$srcdir/stop_cluster
-exec $srcdir/start_cluster "$@"
-#!/bin/bash
-# Re-start a cluster on the local host.
-
-srcdir=`dirname $0`
-$srcdir/stop_cluster
-exec $srcdir/start_cluster "$@"
diff --git a/cpp/src/tests/run_acl_tests b/cpp/src/tests/run_acl_tests
index 25241ad75e..ebe4cf8bdb 100755
--- a/cpp/src/tests/run_acl_tests
+++ b/cpp/src/tests/run_acl_tests
@@ -24,22 +24,26 @@ source ./test_env.sh
DATA_DIR=`pwd`/data_dir
DATA_DIRI=`pwd`/data_diri
DATA_DIRU=`pwd`/data_diru
+DATA_DIRQ=`pwd`/data_dirq
trap stop_brokers INT TERM QUIT
start_brokers() {
- ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIR --load-module $ACL_LIB --acl-file policy.acl --auth no --log-to-file local.log > qpidd.port
+ ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIR --load-module $ACL_LIB --acl-file policy.acl --auth no --log-to-file local.log > qpidd.port
LOCAL_PORT=`cat qpidd.port`
- ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIRI --load-module $ACL_LIB --acl-file policy.acl --auth no --max-connections-per-ip 2 --log-to-file locali.log > qpiddi.port
+ ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIRI --load-module $ACL_LIB --acl-file policy.acl --auth no --connection-limit-per-ip 2 --log-to-file locali.log > qpiddi.port
LOCAL_PORTI=`cat qpiddi.port`
- ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIRU --load-module $ACL_LIB --acl-file policy.acl --auth no --max-connections-per-user 2 --log-to-file localu.log > qpiddu.port
+ ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIRU --load-module $ACL_LIB --acl-file policy.acl --auth no --connection-limit-per-user 2 --log-to-file localu.log > qpiddu.port
LOCAL_PORTU=`cat qpiddu.port`
+ ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIRQ --load-module $ACL_LIB --acl-file policy.acl --auth no --max-queues-per-user 2 --log-to-file localq.log > qpiddq.port
+ LOCAL_PORTQ=`cat qpiddq.port`
}
stop_brokers() {
$QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORT
$QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORTI
$QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORTU
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORTQ
}
test_loading_acl_from_absolute_path(){
@@ -59,20 +63,24 @@ if test -d ${PYTHON_DIR} ; then
rm -rf $DATA_DIR
rm -rf $DATA_DIRI
rm -rf $DATA_DIRU
+ rm -rf $DATA_DIRQ
mkdir -p $DATA_DIR
mkdir -p $DATA_DIRI
mkdir -p $DATA_DIRU
+ mkdir -p $DATA_DIRQ
cp $srcdir/policy.acl $DATA_DIR
cp $srcdir/policy.acl $DATA_DIRI
cp $srcdir/policy.acl $DATA_DIRU
+ cp $srcdir/policy.acl $DATA_DIRQ
start_brokers
- echo "Running acl tests using brokers on ports $LOCAL_PORT, $LOCAL_PORTI, and $LOCAL_PORTU"
- $QPID_PYTHON_TEST -b localhost:$LOCAL_PORT -m acl -Dport-i=$LOCAL_PORTI -Dport-u=$LOCAL_PORTU || EXITCODE=1
+ echo "Running acl tests using brokers on ports $LOCAL_PORT, $LOCAL_PORTI, $LOCAL_PORTU, and $LOCAL_PORTQ"
+ $QPID_PYTHON_TEST -b localhost:$LOCAL_PORT -m acl -Dport-i=$LOCAL_PORTI -Dport-u=$LOCAL_PORTU -Dport-q=$LOCAL_PORTQ || EXITCODE=1
stop_brokers || EXITCODE=1
test_loading_acl_from_absolute_path || EXITCODE=1
rm -rf $DATA_DIR
rm -rf $DATA_DIRI
rm -rf $DATA_DIRU
+ rm -rf $DATA_DIRQ
exit $EXITCODE
fi
diff --git a/cpp/src/tests/run_cluster_authentication_soak b/cpp/src/tests/run_cluster_authentication_soak
deleted file mode 100755
index 24befa28ba..0000000000
--- a/cpp/src/tests/run_cluster_authentication_soak
+++ /dev/null
@@ -1,27 +0,0 @@
-#! /bin/bash
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-
-source ./test_env.sh
-source sasl_test_setup.sh
-source cpg_check.sh
-cpg_enabled || exit 0
-
-with_ais_group ./cluster_authentication_soak 500
-
diff --git a/cpp/src/tests/run_cluster_authentication_test b/cpp/src/tests/run_cluster_authentication_test
deleted file mode 100755
index 844807a857..0000000000
--- a/cpp/src/tests/run_cluster_authentication_test
+++ /dev/null
@@ -1,27 +0,0 @@
-#! /bin/bash
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-
-source ./test_env.sh
-source sasl_test_setup.sh
-source cpg_check.sh
-cpg_enabled || exit 0
-
-with_ais_group ./cluster_authentication_soak
-
diff --git a/cpp/src/tests/run_cluster_tests b/cpp/src/tests/run_cluster_tests
deleted file mode 100755
index a5cea5ff6e..0000000000
--- a/cpp/src/tests/run_cluster_tests
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/bash
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-source ./test_env.sh
-source cpg_check.sh
-cpg_enabled || exit 0
-
-
-test -x $QPID_PYTHON_TEST || { echo Skipping test, $QPID_PYTHON_TEST not found; exit 0; }
-
-# Delete old cluster test data
-OUTDIR=${OUTDIR:-brokertest.tmp}
-rm -rf $OUTDIR
-mkdir -p $OUTDIR
-
-# Ignore tests requiring a store by default.
-CLUSTER_TESTS_IGNORE=${CLUSTER_TESTS_IGNORE:--i cluster_tests.StoreTests.* -I $srcdir/cluster_tests.fail}
-CLUSTER_TESTS=${CLUSTER_TESTS:-$*}
-
-with_ais_group $QPID_PYTHON_TEST -DOUTDIR=$OUTDIR -m cluster_tests $CLUSTER_TESTS_IGNORE $CLUSTER_TESTS || exit 1
-rm -rf $OUTDIR
diff --git a/cpp/src/tests/run_failover_soak b/cpp/src/tests/run_failover_soak
deleted file mode 100755
index 2c56bf7d6b..0000000000
--- a/cpp/src/tests/run_failover_soak
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/bash
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-source ./test_env.sh
-source cpg_check.sh
-cpg_enabled || exit 0
-
-host=127.0.0.1
-
-unset QPID_NO_MODULE_DIR # failover_soak uses --module-dir, dont want clash
-MODULES=${MODULES:-$moduledir}
-MESSAGES=${MESSAGES:-500000}
-REPORT_FREQUENCY=${REPORT_FREQUENCY:-20000}
-VERBOSITY=${VERBOSITY:-0}
-DURABILITY=${DURABILITY:-0}
-N_QUEUES=${N_QUEUES:-1}
-N_BROKERS=${N_BROKERS:-4}
-
-rm -f soak-*.log
-exec ./failover_soak $MODULES ./declare_queues ./replaying_sender ./resuming_receiver $MESSAGES $REPORT_FREQUENCY $VERBOSITY $DURABILITY $N_QUEUES $N_BROKERS
-
diff --git a/cpp/src/tests/run_federation_sys_tests b/cpp/src/tests/run_federation_sys_tests
index 76da176914..d9a9649c37 100755
--- a/cpp/src/tests/run_federation_sys_tests
+++ b/cpp/src/tests/run_federation_sys_tests
@@ -25,10 +25,6 @@ source ./test_env.sh
MODULENAME=federation_sys
-# Test for clustering
-source cpg_check.sh
-if cpg_enabled; then CLUSTERING_ENABLED=1; fi
-
# Test for long test
if [[ "$1" == "LONG_TEST" ]]; then
USE_LONG_TEST=1
@@ -42,11 +38,7 @@ if [ -z ${USE_LONG_TEST} ]; then
SKIPTESTS="-i federation_sys.A_Long* -i federation_sys.B_Long* ${SKIPTESTS}"
fi
echo "WARNING: Tests using persistence will be ignored."
-if [ -z ${CLUSTERING_ENABLED} ]; then
- SKIPTESTS="${SKIPTESTS} -i federation_sys.C_* -i federation_sys.D_*"
-elif [ -z ${USE_LONG_TEST} ]; then
- SKIPTESTS="${SKIPTESTS} -i federation_sys.C_Long* -i federation_sys.D_Long*"
-fi
+SKIPTESTS="${SKIPTESTS} -i federation_sys.C_* -i federation_sys.D_*"
start_brokers() {
start_broker() {
@@ -56,35 +48,21 @@ start_brokers() {
}
start_broker "" LOCAL_PORT
start_broker "" REMOTE_PORT
- if [ -n "${CLUSTERING_ENABLED}" ]; then
- start_broker "--load-module ${CLUSTER_LIB} --cluster-name test-cluster-1" CLUSTER_C1_1
- start_broker "--load-module ${CLUSTER_LIB} --cluster-name test-cluster-1" CLUSTER_C1_2
- start_broker "--load-module ${CLUSTER_LIB} --cluster-name test-cluster-2" CLUSTER_C2_1
- start_broker "--load-module ${CLUSTER_LIB} --cluster-name test-cluster-2" CLUSTER_C2_2
- fi
rm qpidd.port
}
stop_brokers() {
${QPIDD_EXEC} -q --port ${LOCAL_PORT}
${QPIDD_EXEC} -q --port ${REMOTE_PORT}
- if [ -n "${CLUSTERING_ENABLED}" ]; then
- ${QPID_CLUSTER_EXEC} --all-stop --force localhost:${CLUSTER_C1_1}
- ${QPID_CLUSTER_EXEC} --all-stop --force localhost:${CLUSTER_C2_1}
- fi
}
if test -d ${PYTHON_DIR} ; then
start_brokers
- if [ -z ${CLUSTERING_ENABLED} ]; then
- echo "Running federation tests using brokers on local port ${LOCAL_PORT}, remote port ${REMOTE_PORT} (NOTE: clustering is DISABLED)"
- else
- echo "Running federation tests using brokers on local port ${LOCAL_PORT}, remote port ${REMOTE_PORT}, local cluster nodes ${CLUSTER_C1_1} ${CLUSTER_C1_2}, remote cluster nodes ${CLUSTER_C2_1} ${CLUSTER_C2_2}"
- fi
+ echo "Running federation tests using brokers on local port ${LOCAL_PORT}, remote port ${REMOTE_PORT} (NOTE: clustering is DISABLED)"
if [ -z ${USE_LONG_TEST} ]; then
echo "NOTE: To run a full set of federation system tests, use \"make check-long\". To test with persistence, run the store version of this script."
fi
- ${QPID_PYTHON_TEST} -m ${MODULENAME} ${SKIPTESTS} -b localhost:${REMOTE_PORT} -Dlocal-port=${LOCAL_PORT} -Dremote-port=${REMOTE_PORT} -Dlocal-cluster-ports="${CLUSTER_C1_1} ${CLUSTER_C1_2}" -Dremote-cluster-ports="${CLUSTER_C2_1} ${CLUSTER_C2_2}" $@
+ ${QPID_PYTHON_TEST} -m ${MODULENAME} ${SKIPTESTS} -b localhost:${REMOTE_PORT} -Dlocal-port=${LOCAL_PORT} -Dremote-port=${REMOTE_PORT} $@
RETCODE=$?
stop_brokers
if test x${RETCODE} != x0; then
diff --git a/cpp/src/tests/sasl_fed_ex b/cpp/src/tests/sasl_fed_ex
index cc5b310067..4ea61c5a2a 100755
--- a/cpp/src/tests/sasl_fed_ex
+++ b/cpp/src/tests/sasl_fed_ex
@@ -34,17 +34,11 @@ then
echo
# These are the four different ways of creating links ( or routes+links )
# that the qpid-route command provides.
- echo "Usage: ${script_name} dynamic|link|queue|route [cluster]"
+ echo "Usage: ${script_name} dynamic|link|queue|route"
echo
exit 1
fi
-# Has the user told us to do clustering ? -----------
-clustering_flag=
-if [ $# -eq "2" ] && [ "$2" == "cluster" ]; then
- clustering_flag=true
-fi
-
qpid_route_method=$1
# Debugging print. --------------------------
@@ -128,15 +122,7 @@ DST_TCP_PORT=5807
SRC_TCP_PORT_2=5802
DST_TCP_PORT_2=5803
-CLUSTER_NAME_SUFFIX=`hostname | tr '.' ' ' | awk '{print $1}'`
-CLUSTER_1_NAME=sasl_fed_ex_cluster_1_${CLUSTER_NAME_SUFFIX}
-CLUSTER_2_NAME=sasl_fed_ex_cluster_2_${CLUSTER_NAME_SUFFIX}
-
-print "CLUSTER_1_NAME == ${CLUSTER_1_NAME}"
-print "CLUSTER_2_NAME == ${CLUSTER_2_NAME}"
-
SSL_LIB=${moduledir}/ssl.so
-CLUSTER_LIB=${moduledir}/cluster.so
export QPID_SSL_CERT_NAME=${TEST_HOSTNAME}
@@ -183,80 +169,26 @@ COMMON_BROKER_OPTIONS=" \
function start_brokers {
- if [ $1 ]; then
- # clustered ----------------------------------------
- print "Starting SRC cluster"
-
- print " src broker 1"
- $QPIDD_EXEC \
- --port=${SRC_TCP_PORT} \
- --ssl-port ${SRC_SSL_PORT} \
- ${COMMON_BROKER_OPTIONS} \
- --load-module ${CLUSTER_LIB} \
- --cluster-name ${CLUSTER_1_NAME} \
- --log-to-file $tmp_root/qpidd_src.log 2> /dev/null
-
- broker_ports[0]=${SRC_TCP_PORT}
-
- print " src broker 2"
- $QPIDD_EXEC \
- --port=${SRC_TCP_PORT_2} \
- --ssl-port ${SRC_SSL_PORT_2} \
- ${COMMON_BROKER_OPTIONS} \
- --load-module ${CLUSTER_LIB} \
- --cluster-name ${CLUSTER_1_NAME} \
- --log-to-file $tmp_root/qpidd_src_2.log 2> /dev/null
-
- broker_ports[1]=${SRC_TCP_PORT_2}
-
-
- print "Starting DST cluster"
-
- print " dst broker 1"
- $QPIDD_EXEC \
- --port=${DST_TCP_PORT} \
- --ssl-port ${DST_SSL_PORT} \
- ${COMMON_BROKER_OPTIONS} \
- --load-module ${CLUSTER_LIB} \
- --cluster-name ${CLUSTER_2_NAME} \
- --log-to-file $tmp_root/qpidd_dst.log 2> /dev/null
-
- broker_ports[2]=${DST_TCP_PORT}
-
- print " dst broker 2"
- $QPIDD_EXEC \
- --port=${DST_TCP_PORT_2} \
- --ssl-port ${DST_SSL_PORT_2} \
- ${COMMON_BROKER_OPTIONS} \
- --load-module ${CLUSTER_LIB} \
- --cluster-name ${CLUSTER_2_NAME} \
- --log-to-file $tmp_root/qpidd_dst_2.log 2> /dev/null
-
- broker_ports[3]=${DST_TCP_PORT_2}
-
- else
# vanilla brokers --------------------------------
print "Starting SRC broker"
$QPIDD_EXEC \
- --port=${SRC_TCP_PORT} \
- --ssl-port ${SRC_SSL_PORT} \
- ${COMMON_BROKER_OPTIONS} \
- --log-to-file $tmp_root/qpidd_src.log 2> /dev/null
+ --port=${SRC_TCP_PORT} \
+ --ssl-port ${SRC_SSL_PORT} \
+ ${COMMON_BROKER_OPTIONS} \
+ --log-to-file $tmp_root/qpidd_src.log 2> /dev/null
broker_ports[0]=${SRC_TCP_PORT}
print "Starting DST broker"
$QPIDD_EXEC \
- --port=${DST_TCP_PORT} \
- --ssl-port ${DST_SSL_PORT} \
- ${COMMON_BROKER_OPTIONS} \
- --log-to-file $tmp_root/qpidd_dst.log 2> /dev/null
+ --port=${DST_TCP_PORT} \
+ --ssl-port ${DST_SSL_PORT} \
+ ${COMMON_BROKER_OPTIONS} \
+ --log-to-file $tmp_root/qpidd_dst.log 2> /dev/null
broker_ports[1]=${DST_TCP_PORT}
- fi
}
-
function halt_brokers {
n_brokers=${#broker_ports[@]}
print "Halting ${n_brokers} brokers."
@@ -270,7 +202,7 @@ function halt_brokers {
}
-start_brokers $clustering_flag
+start_brokers
# I am not randomizing these names, because this test creates its own brokers.
@@ -329,9 +261,7 @@ fi
# to avoid false negatives.
sleep 5
-# This should work the same whether or not we are running a clustered test.
-# In the case of clustered tests, the status is not printed by qpid_route.
-# So in either case, I will look only at the transport field, which should be "ssl".
+# Look only at the transport field, which should be "ssl".
print "check the link"
link_status=$($QPID_ROUTE_EXEC link list localhost:${DST_TCP_PORT} | tail -1 | awk '{print $3}')
diff --git a/cpp/src/tests/sasl_fed_ex_dynamic_cluster b/cpp/src/tests/sasl_fed_ex_dynamic_cluster
deleted file mode 100755
index fd6b72a4f2..0000000000
--- a/cpp/src/tests/sasl_fed_ex_dynamic_cluster
+++ /dev/null
@@ -1,30 +0,0 @@
-#! /bin/bash
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-
-source ./test_env.sh
-source cpg_check.sh
-cpg_enabled || exit 0
-
-
-with_ais_group ${srcdir}/sasl_fed_ex dynamic cluster
-
-
diff --git a/cpp/src/tests/sasl_fed_ex_link_cluster b/cpp/src/tests/sasl_fed_ex_link_cluster
deleted file mode 100755
index 34b2aa4a5f..0000000000
--- a/cpp/src/tests/sasl_fed_ex_link_cluster
+++ /dev/null
@@ -1,29 +0,0 @@
-#! /bin/bash
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-
-source ./test_env.sh
-source cpg_check.sh
-cpg_enabled || exit 0
-
-with_ais_group ${srcdir}/sasl_fed_ex link cluster
-
-
diff --git a/cpp/src/tests/sasl_fed_ex_queue_cluster b/cpp/src/tests/sasl_fed_ex_queue_cluster
deleted file mode 100755
index 14f36f6fc4..0000000000
--- a/cpp/src/tests/sasl_fed_ex_queue_cluster
+++ /dev/null
@@ -1,29 +0,0 @@
-#! /bin/bash
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-
-source ./test_env.sh
-source cpg_check.sh
-cpg_enabled || exit 0
-
-with_ais_group ${srcdir}/sasl_fed_ex queue cluster
-
-
diff --git a/cpp/src/tests/sasl_fed_ex_route_cluster b/cpp/src/tests/sasl_fed_ex_route_cluster
deleted file mode 100755
index 756476056e..0000000000
--- a/cpp/src/tests/sasl_fed_ex_route_cluster
+++ /dev/null
@@ -1,29 +0,0 @@
-#! /bin/bash
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-
-source ./test_env.sh
-source cpg_check.sh
-cpg_enabled || exit 0
-
-with_ais_group ${srcdir}/sasl_fed_ex route cluster
-
-
diff --git a/cpp/src/tests/ssl_test b/cpp/src/tests/ssl_test
index 19a316a483..89aaf44af0 100755
--- a/cpp/src/tests/ssl_test
+++ b/cpp/src/tests/ssl_test
@@ -193,29 +193,3 @@ echo "Running SSL/TCP mux test on random port $PORT"
./qpid-perftest --count ${COUNT} --port ${PORT} -P tcp -b $TEST_HOSTNAME --summary || error "TCP connection failed!"
stop_brokers
-
-test -z $CLUSTER_LIB && exit 0 # Exit if cluster not supported.
-
-## Test failover in a cluster using SSL only
-source cpg_check.sh
-cpg_enabled || exit 0
-
-PORT1=`pick_port`; ssl_cluster_broker $PORT1
-echo "Running SSL cluster broker on port $PORT1"
-
-PORT2=`pick_port`; ssl_cluster_broker $PORT2
-echo "Running SSL cluster broker on port $PORT2"
-
-# Pipe receive output to uniq to remove duplicates
-./qpid-receive --connection-options "{reconnect:true, reconnect-timeout:5}" --failover-updates -b amqp:ssl:$TEST_HOSTNAME:$PORT1 -a "foo;{create:always}" -f | uniq > ssl_test_receive.tmp &
-./qpid-send -b amqp:ssl:$TEST_HOSTNAME:$PORT2 --content-string=one -a "foo;{create:always}"
-
-stop_broker 0 # Kill broker 1 - receiver should fail-over.
-echo "Killed SSL cluster broker on port $PORT1"
-
-./qpid-send -b amqp:ssl:$TEST_HOSTNAME:$PORT2 --content-string=two -a "foo;{create:always}" --send-eos 1
-wait # Wait for qpid-receive
-{ echo one; echo two; } > ssl_test_receive.cmp
-diff ssl_test_receive.tmp ssl_test_receive.cmp || { echo "Failover failed"; exit 1; }
-rm -f ssl_test_receive.*
-
diff --git a/cpp/src/tests/start_cluster b/cpp/src/tests/start_cluster
deleted file mode 100755
index 78fd104d9c..0000000000
--- a/cpp/src/tests/start_cluster
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/bash
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Start a cluster of brokers on local host, put the list of ports for cluster members in cluster.ports
-#
-
-# Execute command with the ais group set.
-source ./test_env.sh
-source cpg_check.sh
-cpg_enabled || exit 0
-
-rm -f cluster*.log cluster.ports qpidd.port
-
-SIZE=${1:-3}; shift
-CLUSTER=$HOSTNAME.$$
-OPTS="-d --no-module-dir --load-module $CLUSTER_LIB --cluster-name=$CLUSTER --auth=no --log-enable notice+ --log-enable debug+:cluster $@"
-
-for (( i=0; i<SIZE; ++i )); do
- DDIR=`mktemp -d /tmp/start_cluster.XXXXXXXXXX`
- PORT=`with_ais_group ../qpidd -p0 --log-to-file=cluster$i.log $OPTS --data-dir=$DDIR` || exit 1
- echo $PORT >> cluster.ports
-done
-
-head -n 1 cluster.ports > qpidd.port # First member's port for tests.
-
diff --git a/cpp/src/tests/start_cluster_hosts b/cpp/src/tests/start_cluster_hosts
deleted file mode 100755
index 778b4248da..0000000000
--- a/cpp/src/tests/start_cluster_hosts
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/bin/sh
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-#
-# Start a cluster of brokers on local host, put the list of host port addresses
-# in cluster.ports
-#
-# Arguments: [-k] [-p port] HOST [HOST...]
-# -p port to start broker on, can be 0. Actual ports recorded in cluster.addr.
-# -k kill any qpidd processes owned by this user before starting.
-#
-# Start a broker on each named host. Name a host twice to start multiple brokers.
-#
-# You must be able to ssh to each host and be in group ais.
-# $QPIDD must be executable on each host.
-# Logs go to syslog on each host, with a unique prefix per broker.
-#
-
-QPIDD=${QPIDD:-$PWD/../qpidd}
-LIBQPIDCLUSTER=${LIBQPIDCLUSTER:-$PWD/../.libs/cluster.so}
-NAME=$USER # User name is default cluster name.
-RESTART=NO
-
-while getopts "kp:n:q:r" ARG ; do
- case $ARG in
- k) KILL=yes ;;
- p) PORT="$OPTARG" ;;
- n) NAME=$OPTARG ;;
- q) QPIDD=$OPTARG ;;
- l) LIBQPIDCLUSTER=$OPTARG ;;
- r) RESTART=yes ;;
- *) echo "Error parsing options: $ARG"; exit 1 ;;
- esac
-done
-shift `expr $OPTIND - 1`
-test -n "$PORT" && PORTOPT="-p $PORT"
-test "$KILL" = yes && KILL="$QPIDD --no-module-dir -q $PORTOPT ;"
-CLUSTER=${*:-$CLUSTER} # Use args or env
-test -z "$CLUSTER" && { echo Must specify at least one host; exit 1; }
-
-
-OPTS="-d $PORTOPT --load-module $LIBQPIDCLUSTER --cluster-name=$NAME --no-data-dir --auth=no --log-to-syslog --log-enable=info+"
-
-num=0
-for h in $CLUSTER; do
- num=`expr $num + 1` # Give a unique log prefix to each node.
- cmd="$KILL $QPIDD $OPTS --log-prefix $num.$h"
- out=`echo "$cmd" | ssh $h newgrp ais` || { echo == $h error: $out ; exit 1; }
- if [ "$PORT" = 0 ] ; then p=$out; else p=$PORT; fi
- echo "$h $p"
-done
-
diff --git a/cpp/src/tests/storePerftools/asyncPerf/PerfTest.cpp b/cpp/src/tests/storePerftools/asyncPerf/PerfTest.cpp
index 85dd7fa715..7d97ded8b9 100644
--- a/cpp/src/tests/storePerftools/asyncPerf/PerfTest.cpp
+++ b/cpp/src/tests/storePerftools/asyncPerf/PerfTest.cpp
@@ -157,7 +157,7 @@ PerfTest::destroyQueues() {
int
runPerfTest(int argc, char** argv) {
// Load async store module
- qpid::tryShlib ("asyncStore.so", false);
+ qpid::tryShlib ("asyncStore.so");
qpid::CommonOptions co;
qpid::asyncStore::AsyncStoreOptions aso;
diff --git a/cpp/src/tests/test_env.ps1.in b/cpp/src/tests/test_env.ps1.in
index 60ba4305a5..5fa3a0ac31 100644
--- a/cpp/src/tests/test_env.ps1.in
+++ b/cpp/src/tests/test_env.ps1.in
@@ -62,8 +62,6 @@ $env:TEST_STORE_LIB="$testmoduledir\test_store.so"
#exportmodule() { test -f $moduledir/$2 && eval "export $1=$moduledir/$2"; }
#exportmodule ACL_LIB acl.so
#exportmodule CLUSTER_LIB cluster.so
-#exportmodule REPLICATING_LISTENER_LIB replicating_listener.so
-#exportmodule REPLICATION_EXCHANGE_LIB replication_exchange.so
#exportmodule SSLCONNECTOR_LIB sslconnector.so
#exportmodule SSL_LIB ssl.so
#exportmodule WATCHDOG_LIB watchdog.so
diff --git a/cpp/src/tests/test_env.sh.in b/cpp/src/tests/test_env.sh.in
index cee36843ae..76e88283ed 100644
--- a/cpp/src/tests/test_env.sh.in
+++ b/cpp/src/tests/test_env.sh.in
@@ -43,7 +43,6 @@ export PYTHON_COMMANDS=$QPID_TOOLS/src/py
export PYTHONPATH=$srcdir:$PYTHON_DIR:$PYTHON_COMMANDS:$QPID_TESTS_PY:$QMF_LIB:$PYTHONPATH
export QPID_CONFIG_EXEC=$PYTHON_COMMANDS/qpid-config
export QPID_ROUTE_EXEC=$PYTHON_COMMANDS/qpid-route
-export QPID_CLUSTER_EXEC=$PYTHON_COMMANDS/qpid-cluster
export QPID_HA_EXEC=$PYTHON_COMMANDS/qpid-ha
# Executables
@@ -63,10 +62,7 @@ export TEST_STORE_LIB=$testmoduledir/test_store.so
exportmodule() { test -f $moduledir/$2 && eval "export $1=$moduledir/$2"; }
exportmodule ACL_LIB acl.so
-exportmodule CLUSTER_LIB cluster.so
exportmodule HA_LIB ha.so
-exportmodule REPLICATING_LISTENER_LIB replicating_listener.so
-exportmodule REPLICATION_EXCHANGE_LIB replication_exchange.so
exportmodule SSLCONNECTOR_LIB sslconnector.so
exportmodule SSL_LIB ssl.so
exportmodule WATCHDOG_LIB watchdog.so
diff --git a/cpp/src/tests/test_store.cpp b/cpp/src/tests/test_store.cpp
index e6bc55c033..46b7ea1196 100644
--- a/cpp/src/tests/test_store.cpp
+++ b/cpp/src/tests/test_store.cpp
@@ -37,17 +37,13 @@
//#include "qpid/broker/amqp_0_10/MessageTransfer.h"
//#include "qpid/framing/AMQFrame.h"
//#include "qpid/log/Statement.h"
+//#include "qpid/sys/Thread.h"
//#include "qpid/Plugin.h"
//#include "qpid/Options.h"
//#include <boost/cast.hpp>
//#include <boost/lexical_cast.hpp>
//#include <memory>
//#include <fstream>
-//
-//using namespace qpid;
-//using namespace broker;
-//using namespace std;
-//using namespace qpid::sys;
namespace qpid {
namespace tests {
diff --git a/cpp/src/tests/testagent.mk b/cpp/src/tests/testagent.mk
index 25cf43d71e..0492f3e3bb 100644
--- a/cpp/src/tests/testagent.mk
+++ b/cpp/src/tests/testagent.mk
@@ -46,6 +46,6 @@ testagent-testagent.$(OBJEXT): $(TESTAGENT_GEN_SRC)
qpidexectest_PROGRAMS+=testagent
testagent_CXXFLAGS=$(CXXFLAGS) -Itestagent_gen
testagent_SOURCES=testagent.cpp $(TESTAGENT_GEN_SRC)
-testagent_LDADD=$(top_builddir)/src/libqmf.la
+testagent_LDADD=$(top_builddir)/src/libqmf.la $(top_builddir)/src/libqpidcommon.la $(top_builddir)/src/libqpidtypes.la $(top_builddir)/src/libqpidclient.la
EXTRA_DIST+=testagent.xml
diff --git a/cpp/src/tests/testlib.py b/cpp/src/tests/testlib.py
deleted file mode 100644
index 71ad59e5c1..0000000000
--- a/cpp/src/tests/testlib.py
+++ /dev/null
@@ -1,766 +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.
-#
-
-#
-# Support library for qpid python tests.
-#
-
-import os, re, signal, subprocess, time, unittest
-
-class TestBase(unittest.TestCase):
- """
- Base class for qpid tests. Provides broker start/stop/kill methods
- """
-
- """
- The following environment vars control if and how the test is run, and determine where many of the helper
- executables/libs are to be found.
- """
- _storeLib = os.getenv("STORE_LIB")
- _storeEnable = _storeLib != None # Must be True for durability to be enabled during the test
- _qpiddExec = os.getenv("QPIDD_EXEC", "/usr/sbin/qpidd")
- _tempStoreDir = os.path.abspath(os.getenv("TMP_DATA_DIR", "/tmp/qpid"))
-
- """Global message counter ensures unique messages"""
- _msgCnt = 0
-
- # --- Helper functions for parameter handling ---
-
- def _paramBool(self, key, val, keyOnly = False):
- if val == None:
- return ""
- if keyOnly:
- if val:
- return " --%s" % key
- else:
- return ""
- else:
- if val:
- return " --%s yes" % key
- else:
- return " --%s no" % key
-
- # --- Helper functions for message creation ---
-
- def _makeMessage(self, msgSize):
- msg = "Message-%04d" % self._msgCnt
- self._msgCnt = self._msgCnt + 1
- msgLen = len(msg)
- if msgSize > msgLen:
- for i in range(msgLen, msgSize):
- if i == msgLen:
- msg += "-"
- else:
- msg += chr(ord('a') + (i % 26))
- return msg
-
- def _makeMessageList(self, numMsgs, msgSize):
- if msgSize == None:
- msgSize = 12
- msgs = ""
- for m in range(0, numMsgs):
- msgs += "%s\n" % self._makeMessage(msgSize)
- return msgs
-
- # --- Starting and stopping a broker ---
-
- def startBroker(self, qpiddArgs, logFile = None):
- """Start a single broker daemon, returns tuple (pid, port)"""
- if self._qpiddExec == None:
- raise Exception("Environment variable QPIDD is not set")
- cmd = "%s --daemon --port=0 %s" % (self._qpiddExec, qpiddArgs)
- portStr = os.popen(cmd).read()
- if len(portStr) == 0:
- err = "Broker daemon startup failed."
- if logFile != None:
- err += " See log file %s" % logFile
- raise Exception(err)
- port = int(portStr)
- pidStr = os.popen("%s -p %d -c" % (self._qpiddExec, port)).read()
- try:
- pid = int(pidStr)
- except:
- raise Exception("Unable to get pid: \"%s -p %d -c\" returned %s" % (self._qpiddExec, port, pidStr))
- #print "started broker: pid=%d, port=%d args: %s" % (pid, port, qpiddArgs)
- return (pid, port)
-
- def killBroker(self, nodeTuple, ignoreFailures = False):
- """Kill a broker using kill -9"""
- try:
- os.kill(nodeTuple[self.PID], signal.SIGKILL)
- try:
- os.waitpid(nodeTuple[self.PID], 0)
- except:
- pass
- #print "killed broker: port=%d pid=%d" % (nodeTuple[self.PORT], nodeTuple[self.PID])
- except:
- if ignoreFailures:
- print "WARNING: killBroker (port=%d pid=%d) failed - ignoring." % (nodeTuple[self.PORT], nodeTuple[self.PID])
- else:
- raise
-
- def stopBroker(self, nodeTuple, ignoreFailures = False):
- """Stop a broker using qpidd -q"""
- try:
- ret = os.spawnl(os.P_WAIT, self._qpiddExec, self._qpiddExec, "--port=%d" % nodeTuple[self.PORT], "--quit", "--no-module-dir")
- if ret != 0:
- raise Exception("stopBroker(): port=%d: qpidd -q returned %d" % (nodeTuple[self.PORT], ret))
- try:
- os.waitpid(nodeTuple[self.PID], 0)
- except:
- pass
- #print "stopped broker: port=%d pid=%d" % (nodeTuple[self.PORT], nodeTuple[self.PID])
- except:
- if ignoreFailures:
- print "WARNING: stopBroker (port=%d pid=%d) failed - ignoring." % (nodeTuple[self.PORT], nodeTuple[self.PID])
- else:
- raise
-
-
-
-class TestBaseCluster(TestBase):
- """
- Base class for cluster tests. Provides methods for starting and stopping clusters and cluster nodes.
- """
-
- """
- The following environment vars control if and how the test is run, and determine where many of the helper
- executables/libs are to be found.
- """
- _clusterLib = os.getenv("CLUSTER_LIB")
- _clusterTestEnable = _clusterLib != None # Must be True for these cluster tests to run
- _xmlLib = os.getenv("XML_LIB")
- _xmlEnable = _xmlLib != None
- _qpidConfigExec = os.getenv("QPID_CONFIG_EXEC", "/usr/bin/qpid-config")
- _qpidRouteExec = os.getenv("QPID_ROUTE_EXEC", "/usr/bin/qpid-route")
- _receiverExec = os.getenv("RECEIVER_EXEC", "/usr/libexec/qpid/test/receiver")
- _senderExec = os.getenv("SENDER_EXEC", "/usr/libexec/qpid/test/sender")
-
-
- """
- _clusterDict is a dictionary of clusters:
- key = cluster name (string)
- val = dictionary of node numbers:
- key = integer node number
- val = tuple containing (pid, port)
- For example, two clusters "TestCluster0" and "TestCluster1" containing several nodes would look as follows:
- {"TestCluster0": {0: (pid0-0, port0-0), 1: (pid0-1, port0-1), ...}, "TestCluster1": {0: (pid1-0, port1-0), 1: (pid1-1, port1-1), ...}}
- where pidm-n and portm-n are the int pid and port for TestCluster m node n respectively.
- """
- _clusterDict = {}
-
- """Index for (pid, port) tuple"""
- PID = 0
- PORT = 1
-
- def run(self, res):
- """ Skip cluster testing if env var RUN_CLUSTER_TESTS is not defined."""
- if not self._clusterTestEnable:
- return
- unittest.TestCase.run(self, res)
-
- # --- Private helper / convenience functions ---
-
- def _checkPids(self, clusterName = None):
- for pid, port in self.getTupleList():
- try:
- os.kill(pid, 0)
- except:
- raise Exception("_checkPids(): Broker with pid %d expected but does not exist! (crashed?)" % pid)
-
-
- # --- Starting cluster node(s) ---
-
- def createClusterNode(self, nodeNumber, clusterName):
- """Create a node and add it to the named cluster"""
- if self._tempStoreDir == None:
- raise Exception("Environment variable TMP_DATA_DIR is not set")
- if self._clusterLib == None:
- raise Exception("Environment variable LIBCLUSTER is not set")
- name = "%s-%d" % (clusterName, nodeNumber)
- dataDir = os.path.join(self._tempStoreDir, "cluster", name)
- logFile = "%s.log" % dataDir
- args = "--no-module-dir --load-module=%s --data-dir=%s --cluster-name=%s --auth=no --log-enable=notice+ --log-to-file=%s" % \
- (self._clusterLib, dataDir, clusterName, logFile)
- if self._storeEnable:
- if self._storeLib == None:
- raise Exception("Environment variable LIBSTORE is not set")
- args += " --load-module %s" % self._storeLib
- self._clusterDict[clusterName][nodeNumber] = self.startBroker(args, logFile)
-
- def createCluster(self, clusterName, numberNodes = 0):
- """Create a cluster containing an initial number of nodes"""
- self._clusterDict[clusterName] = {}
- for n in range(0, numberNodes):
- self.createClusterNode(n, clusterName)
-
- def waitForNodes(self, clusterName):
- """Wait for all nodes to become active (ie finish cluster sync)"""
- # TODO - connect to each known node in cluster
- # Until this is done, wait a bit (hack)
- time.sleep(1)
-
- # --- Cluster and node status ---
-
- def getTupleList(self, clusterName = None):
- """Get list of (pid, port) tuples of all known cluster brokers"""
- tList = []
- for c, l in self._clusterDict.iteritems():
- if clusterName == None or c == clusterName:
- for t in l.itervalues():
- tList.append(t)
- return tList
-
- def getNumBrokers(self):
- """Get total number of brokers in all known clusters"""
- return len(self.getTupleList())
-
- def checkNumBrokers(self, expected = None, checkPids = True):
- """Check that the total number of brokers in all known clusters is the expected value"""
- if expected != None and self.getNumBrokers() != expected:
- raise Exception("Unexpected number of brokers: expected %d, found %d" % (expected, self.getNumBrokers()))
- if checkPids:
- self._checkPids()
-
- def getClusterTupleList(self, clusterName):
- """Get list of (pid, port) tuples of all nodes in named cluster"""
- if clusterName in self._clusterDict:
- return self._clusterDict[clusterName].values()
- return []
-
- def getNumClusterBrokers(self, clusterName):
- """Get total number of brokers in named cluster"""
- return len(self.getClusterTupleList(clusterName))
-
- def getNodeTuple(self, nodeNumber, clusterName):
- """Get the (pid, port) tuple for the given cluster node"""
- return self._clusterDict[clusterName][nodeNumber]
-
- def checkNumClusterBrokers(self, clusterName, expected = None, checkPids = True, waitForNodes = True):
- """Check that the total number of brokers in the named cluster is the expected value"""
- if expected != None and self.getNumClusterBrokers(clusterName) != expected:
- raise Exception("Unexpected number of brokers in cluster %s: expected %d, found %d" % \
- (clusterName, expected, self.getNumClusterBrokers(clusterName)))
- if checkPids:
- self._checkPids(clusterName)
- if waitForNodes:
- self.waitForNodes(clusterName)
-
- def clusterExists(self, clusterName):
- """ Return True if clusterName exists, False otherwise"""
- return clusterName in self._clusterDict.keys()
-
- def clusterNodeExists(self, clusterName, nodeNumber):
- """ Return True if nodeNumber in clusterName exists, False otherwise"""
- if clusterName in self._clusterDict.keys():
- return nodeNumber in self._clusterDict[nodeName]
- return False
-
- def createCheckCluster(self, clusterName, size):
- """Create a cluster using the given name and size, then check the number of brokers"""
- self.createCluster(clusterName, size)
- self.checkNumClusterBrokers(clusterName, size)
-
- # --- Kill cluster nodes using signal 9 ---
-
- def killNode(self, nodeNumber, clusterName, updateDict = True, ignoreFailures = False):
- """Kill the given node in the named cluster using kill -9"""
- self.killBroker(self.getNodeTuple(nodeNumber, clusterName), ignoreFailures)
- if updateDict:
- del(self._clusterDict[clusterName][nodeNumber])
-
- def killCluster(self, clusterName, updateDict = True, ignoreFailures = False):
- """Kill all nodes in the named cluster"""
- for n in self._clusterDict[clusterName].iterkeys():
- self.killNode(n, clusterName, False, ignoreFailures)
- if updateDict:
- del(self._clusterDict[clusterName])
-
- def killClusterCheck(self, clusterName):
- """Kill the named cluster and check that the name is removed from the cluster dictionary"""
- self.killCluster(clusterName)
- if self.clusterExists(clusterName):
- raise Exception("Unable to kill cluster %s; %d nodes still exist" % \
- (clusterName, self.getNumClusterBrokers(clusterName)))
-
- def killAllClusters(self, ignoreFailures = False):
- """Kill all known clusters"""
- for n in self._clusterDict.iterkeys():
- self.killCluster(n, False, ignoreFailures)
- self._clusterDict.clear()
-
- def killAllClustersCheck(self, ignoreFailures = False):
- """Kill all known clusters and check that the cluster dictionary is empty"""
- self.killAllClusters(ignoreFailures)
- self.checkNumBrokers(0)
-
- # --- Stop cluster nodes using qpidd -q ---
-
- def stopNode(self, nodeNumber, clusterName, updateDict = True, ignoreFailures = False):
- """Stop the given node in the named cluster using qpidd -q"""
- self.stopBroker(self.getNodeTuple(nodeNumber, clusterName), ignoreFailures)
- if updateDict:
- del(self._clusterDict[clusterName][nodeNumber])
-
- def stopAllClusters(self, ignoreFailures = False):
- """Stop all known clusters"""
- for n in self._clusterDict.iterkeys():
- self.stopCluster(n, False, ignoreFailures)
- self._clusterDict.clear()
-
-
- def stopCluster(self, clusterName, updateDict = True, ignoreFailures = False):
- """Stop all nodes in the named cluster"""
- for n in self._clusterDict[clusterName].iterkeys():
- self.stopNode(n, clusterName, False, ignoreFailures)
- if updateDict:
- del(self._clusterDict[clusterName])
-
- def stopCheckCluster(self, clusterName, ignoreFailures = False):
- """Stop the named cluster and check that the name is removed from the cluster dictionary"""
- self.stopCluster(clusterName, True, ignoreFailures)
- if self.clusterExists(clusterName):
- raise Exception("Unable to kill cluster %s; %d nodes still exist" % (clusterName, self.getNumClusterBrokers(clusterName)))
-
- def stopAllCheck(self, ignoreFailures = False):
- """Kill all known clusters and check that the cluster dictionary is empty"""
- self.stopAllClusters()
- self.checkNumBrokers(0)
-
- # --- qpid-config functions ---
-
- def _qpidConfig(self, nodeNumber, clusterName, action):
- """Configure some aspect of a qpid broker using the qpid_config executable"""
- port = self.getNodeTuple(nodeNumber, clusterName)[self.PORT]
- #print "%s -b localhost:%d %s" % (self._qpidConfigExec, port, action)
- ret = os.spawnl(os.P_WAIT, self._qpidConfigExec, self._qpidConfigExec, "-b", "localhost:%d" % port, *action.split())
- if ret != 0:
- raise Exception("_qpidConfig(): cluster=\"%s\" nodeNumber=%d port=%d action=\"%s\" returned %d" % \
- (clusterName, nodeNumber, port, action, ret))
-
- def addExchange(self, nodeNumber, clusterName, exchangeType, exchangeName, durable = False, sequence = False, \
- ive = False):
- """Add a named exchange."""
- action = "add exchange %s %s" % (exchangeType, exchangeName)
- action += self._paramBool("durable", durable, True)
- action += self._paramBool("sequence", sequence, True)
- action += self._paramBool("ive", ive, True)
- self._qpidConfig(nodeNumber, clusterName, action)
-
- def deleteExchange(self, nodeNumber, clusterName, exchangeName):
- """Delete a named exchange"""
- self._qpidConfig(nodeNumber, clusterName, "del exchange %s" % exchangeName)
-
- def addQueue(self, nodeNumber, clusterName, queueName, configArgs = None):
- """Add a queue using qpid-config."""
- action = "add queue %s" % queueName
- if self._storeEnable:
- action += " --durable"
- if configArgs != None:
- action += " %s" % configArgs
- self._qpidConfig(nodeNumber, clusterName, action)
-
- def delQueue(self, nodeNumber, clusterName, queueName):
- """Delete a named queue using qpid-config."""
- self._qpidConfig(nodeNumber, clusterName, "del queue %s" % queueName)
-
- def bind(self, nodeNumber, clusterName, exchangeName, queueName, key):
- """Create an exchange-queue binding using qpid-config."""
- self._qpidConfig(nodeNumber, clusterName, "bind %s %s %s" % (exchangeName, queueName, key))
-
- def unbind(self, nodeNumber, clusterName, exchangeName, queueName, key):
- """Remove an exchange-queue binding using qpid-config."""
- self._qpidConfig(nodeNumber, clusterName, "unbind %s %s %s" % (exchangeName, queueName, key))
-
- # --- qpid-route functions (federation) ---
-
- def brokerDict(self, nodeNumber, clusterName, host = "localhost", user = None, password = None):
- """Returns a dictionary containing the broker info to be passed to route functions"""
- port = self.getNodeTuple(nodeNumber, clusterName)[self.PORT]
- return {"cluster": clusterName, "node":nodeNumber, "port":port, "host":host, "user":user, "password":password}
-
- def _brokerStr(self, brokerDict):
- """Set up a broker string in the format [user/password@]host:port"""
- str = ""
- if brokerDict["user"] !=None and brokerDict["password"] != None:
- str = "%s@%s" % (brokerDict["user"], brokerDict["password"])
- str += "%s:%d" % (brokerDict["host"], brokerDict["port"])
- return str
-
- def _qpidRoute(self, action):
- """Set up a route using qpid-route"""
- #print "%s %s" % (self._qpidRouteExec, action)
- ret = os.spawnl(os.P_WAIT, self._qpidRouteExec, self._qpidRouteExec, *action.split())
- if ret != 0:
- raise Exception("_qpidRoute(): action=\"%s\" returned %d" % (action, ret))
-
- def routeDynamicAdd(self, destBrokerDict, srcBrokerDict, exchangeName):
- self._qpidRoute("dynamic add %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName))
-
- def routeDynamicDelete(self, destBrokerDict, srcBrokerDict, exchangeName):
- self._qpidRoute("dynamic del %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName))
-
- def routeAdd(self, destBrokerDict, srcBrokerDict, exchangeName, routingKey):
- self._qpidRoute("route add %s %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName, routingKey))
-
- def routeDelete(self, destBrokerDict, srcBrokerDict, exchangeName, routingKey):
- self._qpidRoute("route del %s %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName, routingKey))
-
- def routeQueueAdd(self, destBrokerDict, srcBrokerDict, exchangeName, queueName):
- self._qpidRoute("queue add %s %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName, queueName))
-
- def routeQueueDelete(self, destBrokerDict, srcBrokerDict, exchangeName, queueName):
- self._qpidRoute("queue del %s %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName, queueName))
-
- def routeLinkAdd(self, destBrokerDict, srcBrokerDict):
- self._qpidRoute("link add %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict)))
-
- def routeLinkDelete(self, destBrokerDict, srcBrokerDict):
- self._qpidRoute("link del %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict)))
-
- # --- Message send and receive functions ---
-
- def _receiver(self, action):
- if self._receiverExec == None:
- raise Exception("Environment variable RECEIVER is not set")
- cmd = "%s %s" % (self._receiverExec, action)
- #print cmd
- return subprocess.Popen(cmd.split(), stdout = subprocess.PIPE)
-
- def _sender(self, action):
- if self._senderExec == None:
- raise Exception("Environment variable SENDER is not set")
- cmd = "%s %s" % (self._senderExec, action)
- #print cmd
- return subprocess.Popen(cmd.split(), stdin = subprocess.PIPE)
-
- def createReciever(self, nodeNumber, clusterName, queueName, numMsgs = None, receiverArgs = None):
- port = self.getNodeTuple(nodeNumber, clusterName)[self.PORT]
- action = "--port %d --queue %s" % (port, queueName)
- if numMsgs != None:
- action += " --messages %d" % numMsgs
- if receiverArgs != None:
- action += " %s" % receiverArgs
- return self._receiver(action)
-
- def createSender(self, nodeNumber, clusterName, exchangeName, routingKey, senderArgs = None):
- port = self.getNodeTuple(nodeNumber, clusterName)[self.PORT]
- action = "--port %d --exchange %s" % (port, exchangeName)
- if routingKey != None and len(routingKey) > 0:
- action += " --routing-key %s" % routingKey
- if self._storeEnable:
- action += " --durable yes"
- if senderArgs != None:
- action += " %s" % senderArgs
- return self._sender(action)
-
- def createBindDirectExchangeQueue(self, nodeNumber, clusterName, exchangeName, queueName):
- self.addExchange(nodeNumber, clusterName, "direct", exchangeName)
- self.addQueue(nodeNumber, clusterName, queueName)
- self.bind(nodeNumber, clusterName, exchangeName, queueName, queueName)
-
- def createBindTopicExchangeQueues(self, nodeNumber, clusterName, exchangeName, queueNameKeyList):
- self.addExchange(nodeNumber, clusterName, "topic", exchangeName)
- for queueName, key in queueNameKeyList.iteritems():
- self.addQueue(nodeNumber, clusterName, queueName)
- self.bind(nodeNumber, clusterName, exchangeName, queueName, key)
-
- def createBindFanoutExchangeQueues(self, nodeNumber, clusterName, exchangeName, queueNameList):
- self.addExchange(nodeNumber, clusterName, "fanout", exchangeName)
- for queueName in queueNameList:
- self.addQueue(nodeNumber, clusterName, queueName)
- self.bind(nodeNumber, clusterName, exchangeName, queueName, "")
-
- def sendMsgs(self, nodeNumber, clusterName, exchangeName, routingKey, numMsgs, msgSize = None, wait = True):
- msgs = self._makeMessageList(numMsgs, msgSize)
- sender = self.createSender(nodeNumber, clusterName, exchangeName, routingKey)
- sender.stdin.write(msgs)
- sender.stdin.close()
- if wait:
- sender.wait()
- return msgs
-
- def receiveMsgs(self, nodeNumber, clusterName, queueName, numMsgs, wait = True):
- receiver = self.createReciever(nodeNumber, clusterName, queueName, numMsgs)
- cnt = 0
- msgs = ""
- while cnt < numMsgs:
- rx = receiver.stdout.readline()
- if rx == "" and receiver.poll() != None: break
- msgs += rx
- cnt = cnt + 1
- if wait:
- receiver.wait()
- return msgs
-
-
- # --- Exchange-specific helper inner classes ---
-
- class TestHelper:
- """
- This is a "virtual" superclass for test helpers, and is not useful on its own, but the
- per-exchange subclasses are designed to keep track of the messages sent to and received
- from queues which have bindings to that exchange type.
- """
-
- def __init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameList):
-
- """Dictionary of queues and lists of messages sent to them."""
- self._txMsgs = {}
- """Dictionary of queues and lists of messages received from them."""
- self._rxMsgs = {}
- """List of node numbers currently in the cluster"""
- self._nodes = []
- """List of node numbers which have been killed and can therefore be recovered"""
- self._deadNodes = []
- """Last node to be used"""
- self._lastNode = None
-
- self._testBaseCluster = testBaseCluster
- self._clusterName = clusterName
- self._exchangeName = exchangeName
- self._queueNameList = queueNameList
- self._addQueues(queueNameList)
- self._testBaseCluster.createCheckCluster(clusterName, numNodes)
- self._nodes.extend(range(0, numNodes))
-
- def _addQueues(self, queueNameList):
- for qn in queueNameList:
- if not qn in self._txMsgs:
- self._txMsgs[qn] = []
- if not qn in self._rxMsgs:
- self._rxMsgs[qn] = []
-
- def _bindQueue(self, queueName, bindingKey, nodeNumber = None):
- """Bind a queue to an exchange using a binding key."""
- if nodeNumber == None:
- nodeNumber = self._nodes[0] # first available node
- self._testBaseCluster.addQueue(nodeNumber, self._clusterName, queueName)
- self._testBaseCluster.bind(nodeNumber, self._clusterName, self._exchangeName, queueName, bindingKey)
-
- def _highestNodeNumber(self):
- """Find the highest node number used so far between the current nodes and those stopped/killed."""
- highestNode = self._nodes[-1]
- if len(self._deadNodes) == 0:
- return highestNode
- highestDeadNode = self._deadNodes[-1]
- if highestNode > highestDeadNode:
- return highestNode
- return highestDeadNode
-
- def killCluster(self):
- """Kill all nodes in the cluster"""
- self._testBaseCluster.killCluster(self._clusterName)
- self._testBaseCluster.checkNumClusterBrokers(self._clusterName, 0)
- self._deadNodes.extend(self._nodes)
- self._deadNodes.sort()
- del self._nodes[:]
-
- def restoreCluster(self, lastNode = None, restoreNodes = True):
- """Restore a previously killed cluster"""
- self._testBaseCluster.createCluster(self._clusterName)
- if restoreNodes:
- numNodes = len(self._deadNodes)
- self.restoreNodes(lastNode)
- self._testBaseCluster.checkNumClusterBrokers(self._clusterName, numNodes)
-
- def addNodes(self, numberOfNodes = 1):
- """Add a fixed number of nodes to the cluster."""
- nodeStart = self._highestNodeNumber() + 1
- for i in range(0, numberOfNodes):
- nodeNumber = nodeStart + i
- self._testBaseCluster.createClusterNode(nodeNumber, self._clusterName)
- self._nodes.append(nodeNumber)
- self._testBaseCluster.checkNumClusterBrokers(self._clusterName, len(self._nodes))
- self._testBaseCluster.waitForNodes(self._clusterName)
-
- def restoreNode(self, nodeNumber):
- """Restore a cluster node that has been previously killed"""
- if nodeNumber not in self._deadNodes:
- raise Exception("restoreNode(): Node number %d not in dead node list %s" % (nodeNumber, self._deadNodes))
- self._testBaseCluster.createClusterNode(nodeNumber, self._clusterName)
- self._deadNodes.remove(nodeNumber)
- self._nodes.append(nodeNumber)
- self._nodes.sort()
-
- def restoreNodes(self, lastNode = None):
- """Restore all known cluster nodes that have been previously killed starting with a known last-used node"""
- if len(self._nodes) == 0: # restore last-used node first
- if lastNode == None:
- lastNode = self._lastNode
- self.restoreNode(lastNode)
- while len(self._deadNodes) > 0:
- self.restoreNode(self._deadNodes[0])
- self._testBaseCluster.waitForNodes(self._clusterName)
-
- def killNode(self, nodeNumber):
- """Kill a cluster node (if it is in the _nodes list)."""
- if nodeNumber not in self._nodes:
- raise Exception("killNode(): Node number %d not in node list %s" % (nodeNumber, self._nodes))
- self._testBaseCluster.killNode(nodeNumber, self._clusterName)
- self._nodes.remove(nodeNumber)
- self._deadNodes.append(nodeNumber)
- self._deadNodes.sort()
-
- def sendMsgs(self, routingKey, numMsgs, nodeNumber = None, msgSize = None, wait = True):
- """Send a fixed number of messages using the given routing key."""
- if nodeNumber == None:
- nodeNumber = self._nodes[0] # Use first available node
- msgs = self._testBaseCluster._makeMessageList(numMsgs, msgSize)
- sender = self._testBaseCluster.createSender(nodeNumber, self._clusterName, self._exchangeName, routingKey)
- sender.stdin.write(msgs)
- sender.stdin.close()
- if wait:
- sender.wait()
- self._lastNode = nodeNumber
- return msgs.split()
-
- # TODO - this i/f is messy: one mumMsgs can be given, but a list of queues
- # so assuming numMsgs for each queue
- # A mechanism is needed to specify a different numMsgs per queue
- def receiveMsgs(self, numMsgs, nodeNumber = None, queueNameList = None, wait = True):
- """Receive a fixed number of messages from a named queue. If numMsgs == None, get all remaining messages."""
- if nodeNumber == None:
- nodeNumber = self._nodes[0] # Use first available node
- if queueNameList == None:
- queueNameList = self._txMsgs.iterkeys()
- for qn in queueNameList:
- nm = numMsgs
- if nm == None:
- nm = len(self._txMsgs[qn]) - len(self._rxMsgs[qn]) # get all remaining messages
- if nm > 0:
- while nm > 0:
- receiver = self._testBaseCluster.createReciever(nodeNumber, self._clusterName, qn, nm)
- cnt = 0
- while cnt < nm:
- rx = receiver.stdout.readline().strip()
- if rx == "":
- if receiver.poll() != None: break
- elif rx not in self._rxMsgs[qn]:
- self._rxMsgs[qn].append(rx)
- cnt = cnt + 1
- nm = nm - cnt
- if wait:
- receiver.wait()
- self._rxMsgs[qn].sort()
- self._lastNode = nodeNumber
-
- def receiveRemainingMsgs(self, nodeNumber = None, queueNameList = None, wait = True):
- """Receive all remaining messages on named queue."""
- self.receiveMsgs(None, nodeNumber, queueNameList, wait)
-
- def checkMsgs(self):
- """Return True if all expected messages have been received (ie the transmit and receive list are identical)."""
- txMsgTot = 0
- rxMsgTot = 0
- for qn, txMsgList in self._txMsgs.iteritems():
- rxMsgList = self._rxMsgs[qn]
- txMsgTot = txMsgTot + len(txMsgList)
- rxMsgTot = rxMsgTot + len(rxMsgList)
- if len(txMsgList) != len(rxMsgList):
- return False
- for i, m in enumerate(txMsgList):
- if m != rxMsgList[i]:
- return False
- if txMsgTot == 0 and rxMsgTot == 0:
- print "WARNING: No messages were either sent or received"
- return True
-
- def finalizeTest(self):
- """Recover all the remaining messages on all queues, then check that all expected messages were received."""
- self.receiveRemainingMsgs()
- self._testBaseCluster.stopAllCheck()
- if not self.checkMsgs():
- self.printMsgs()
- self._testBaseCluster.fail("Send - receive message mismatch")
-
- def printMsgs(self, txMsgs = True, rxMsgs = True):
- """Print all messages transmitted and received."""
- for qn, txMsgList in self._txMsgs.iteritems():
- print "Queue: %s" % qn
- if txMsgs:
- print " txMsgList = %s" % txMsgList
- if rxMsgs:
- rxMsgList = self._rxMsgs[qn]
- print " rxMsgList = %s" % rxMsgList
-
-
- class DirectExchangeTestHelper(TestHelper):
-
- def __init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameList):
- TestBaseCluster.TestHelper.__init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameList)
- self._testBaseCluster.addExchange(0, clusterName, "direct", exchangeName)
- for qn in queueNameList:
- self._bindQueue(qn, qn)
-
- def addQueues(self, queueNameList):
- self._addQueues(queueNameList)
- for qn in queueNameList:
- self._bindQueue(qn, qn)
-
- def sendMsgs(self, numMsgs, nodeNumber = None, queueNameList = None, msgSize = None, wait = True):
- if queueNameList == None:
- queueNameList = self._txMsgs.iterkeys()
- for qn in queueNameList:
- self._txMsgs[qn].extend(TestBaseCluster.TestHelper.sendMsgs(self, qn, numMsgs, nodeNumber, msgSize, wait))
-
-
- class TopicExchangeTestHelper(TestHelper):
-
- def __init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameKeyList):
- self._queueNameKeyList = queueNameKeyList
- TestBaseCluster.TestHelper.__init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameKeyList.iterkeys())
- self._testBaseCluster.addExchange(0, clusterName, "topic", exchangeName)
- for qn, bk in queueNameKeyList.iteritems():
- self._bindQueue(qn, bk)
-
- def addQueues(self, queueNameKeyList):
- self._addQueues(queueNameKeyList.iterkeys())
- for qn, bk in queueNameKeyList.iteritems():
- self._bindQueue(qn, bk)
-
- def _prepareRegex(self, bk):
- # This regex conversion is not very complete - there are other chars that should be escaped too
- return "^%s$" % bk.replace(".", r"\.").replace("*", r"[^.]*").replace("#", ".*")
-
- def sendMsgs(self, routingKey, numMsgs, nodeNumber = None, msgSize = None, wait = True):
- msgList = TestBaseCluster.TestHelper.sendMsgs(self, routingKey, numMsgs, nodeNumber, msgSize, wait)
- for qn, bk in self._queueNameKeyList.iteritems():
- if re.match(self._prepareRegex(bk), routingKey):
- self._txMsgs[qn].extend(msgList)
-
-
- class FanoutExchangeTestHelper(TestHelper):
-
- def __init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameList):
- TestBaseCluster.TestHelper.__init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameList)
- self._testBaseCluster.addExchange(0, clusterName, "fanout", exchangeName)
- for qn in queueNameList:
- self._bindQueue(qn, "")
-
- def addQueues(self, queueNameList):
- self._addQueues(queueNameList)
- for qn in queueNameList:
- self._bindQueue(qn, "")
-
- def sendMsgs(self, numMsgs, nodeNumber = None, msgSize = None, wait = True):
- msgList = TestBaseCluster.TestHelper.sendMsgs(self, "", numMsgs, nodeNumber, msgSize, wait)
- for ml in self._txMsgs.itervalues():
- ml.extend(msgList)
-
diff --git a/cpp/src/tests/verify_cluster_objects b/cpp/src/tests/verify_cluster_objects
deleted file mode 100755
index 94661cf6b9..0000000000
--- a/cpp/src/tests/verify_cluster_objects
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/usr/bin/env python
-
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Verify managment objects are consistent in a cluster.
-# Arguments: url of one broker in the cluster.
-
-import qmf.console, sys, re
-
-class Session(qmf.console.Session):
- """A qmf.console.Session that caches useful values"""
-
- def __init__(self):
- qmf.console.Session.__init__(self)
- self.classes = None
-
- def all_classes(self):
- if self.classes is None:
- self.classes = [c for p in self.getPackages() for c in self.getClasses(p)]
- return self.classes
-
-class Broker:
- def __init__(self, url, qmf):
- self.url = url
- self.qmf = qmf
- self.broker = self.qmf.addBroker(url)
- self.broker._waitForStable()
- self.objects = None
- self.ignore_list = [ re.compile("org.apache.qpid.broker:system:") ]
-
- def get_objects(self):
- def ignore(name):
- for m in self.ignore_list:
- if m.match(name): return True
- if self.objects is None:
- obj_list = []
- ignored=0
- for c in self.qmf.all_classes():
- for o in self.qmf.getObjects(_key=c, _broker=self.broker):
- name=o.getObjectId().getObject()
- if not ignore(name): obj_list.append(name)
- else: ignored += 1
- self.objects = set(obj_list)
- if (len(obj_list) != len(self.objects)):
- raise Exception("Duplicates in object list for %s"%(self.url))
- print "%d objects on %s, ignored %d."%(len(self.objects), self.url, ignored)
- return self.objects
-
- def compare(self,other):
- def compare1(x,y):
- diff = x.get_objects() - y.get_objects()
- if diff:
- print "ERROR: found on %s but not %s"%(x, y)
- for o in diff: print " %s"%(o)
- return False
- return True
-
- so = compare1(self, other)
- os = compare1(other, self)
- return so and os
-
- def __str__(self): return self.url
-
- def get_cluster(self):
- """Given one Broker, return list of all brokers in its cluster"""
- clusters = self.qmf.getObjects(_class="cluster")
- if not clusters: raise ("%s is not a cluster member"%(self.url))
- def first_address(url):
- """Python doesn't understand the brokers URL syntax. Extract a simple addres"""
- return re.compile("amqp:tcp:([^,]*)").match(url).group(1)
- return [Broker(first_address(url), self.qmf)
- for url in clusters[0].members.split(";")]
-
- def __del__(self): self.qmf.delBroker(self.broker)
-
-def main(argv=None):
- if argv is None: argv = sys.argv
- qmf = Session()
- brokers = Broker(argv[1], qmf).get_cluster()
- print "%d members in cluster."%(len(brokers))
- base = brokers.pop(0)
- try:
- for b in brokers:
- if not base.compare(b): return 1
- print "No differences."
- return 0
- finally:
- del base
- del brokers
-
-if __name__ == "__main__": sys.exit(main())
diff --git a/cpp/src/versions.cmake b/cpp/src/versions.cmake
index acc075e899..fda9500cc6 100644
--- a/cpp/src/versions.cmake
+++ b/cpp/src/versions.cmake
@@ -36,4 +36,5 @@ set (qpidtypes_version 1.0.0)
set (rdmawrap_version 2.0.0)
set (sslcommon_version 2.0.0)
set (asyncStore_version 1.0.0)
+#set (legacystore_version 1.0.0)
diff --git a/cpp/src/windows/QpiddBroker.cpp b/cpp/src/windows/QpiddBroker.cpp
index 89a8945d00..b383b7d6c7 100644
--- a/cpp/src/windows/QpiddBroker.cpp
+++ b/cpp/src/windows/QpiddBroker.cpp
@@ -27,11 +27,19 @@
#include "qpid/Plugin.h"
#include "qpid/sys/IntegerTypes.h"
#include "qpid/sys/windows/check.h"
+#include "qpid/sys/Thread.h"
#include "qpid/broker/Broker.h"
#include <iostream>
+#include <string>
+#include <vector>
#include <windows.h>
+namespace {
+ // This will accept args from the command line; augmented with service args.
+ std::vector<std::string> cmdline_args;
+}
+
namespace qpid {
namespace broker {
@@ -46,6 +54,10 @@ BootstrapOptions::BootstrapOptions(const char* argv0)
add(log);
}
+void BootstrapOptions::usage() const {
+ std::cout << "Usage: qpidd [OPTIONS]" << std::endl << std::endl << *this << std::endl;
+}
+
// Local functions to set and get the pid via a LockFile.
namespace {
@@ -218,10 +230,10 @@ VOID WINAPI SvcCtrlHandler(DWORD control)
::SetServiceStatus(svcStatusHandle, &svcStatus);
CtrlHandler(CTRL_C_EVENT);
break;
-
+
case SERVICE_CONTROL_INTERROGATE:
break;
-
+
default:
break;
}
@@ -229,6 +241,25 @@ VOID WINAPI SvcCtrlHandler(DWORD control)
VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
+ // The arguments can come from 2 places. Args set with the executable
+ // name when the service is installed come through main() and are now
+ // in cmdline_args. Arguments set in StartService come into argc/argv
+ // above; if they are set, argv[0] is the service name. Make command
+ // line args first; StartService args come later and can override
+ // command line args.
+ int all_argc = argc + cmdline_args.size();
+ if (argc == 0 && !cmdline_args.empty())
+ ++all_argc; // No StartService args, so need to add prog name argv[0]
+ const char **all_argv = new const char *[all_argc];
+ if (all_argc > 0) {
+ int i = 0;
+ all_argv[i++] = argc > 0 ? argv[0] : svcName.c_str();
+ for (size_t j = 0; j < cmdline_args.size(); ++j)
+ all_argv[i++] = cmdline_args[j].c_str();
+ for (DWORD k = 1; k < argc; ++k)
+ all_argv[i++] = argv[k];
+ }
+
::memset(&svcStatus, 0, sizeof(svcStatus));
svcStatusHandle = ::RegisterServiceCtrlHandler(svcName.c_str(),
SvcCtrlHandler);
@@ -238,7 +269,9 @@ VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
svcStatus.dwCurrentState = SERVICE_START_PENDING;
::SetServiceStatus(svcStatusHandle, &svcStatus);
// QpiddBroker class resets state to running.
- svcStatus.dwWin32ExitCode = run_broker(argc, argv, true);
+ svcStatus.dwWin32ExitCode = run_broker(all_argc,
+ const_cast<char**>(all_argv),
+ true);
svcStatus.dwCurrentState = SERVICE_STOPPED;
svcStatus.dwCheckPoint = 0;
svcStatus.dwWaitHint = 0;
@@ -278,7 +311,7 @@ struct ServiceOptions : public qpid::Options {
std::string password;
std::string depends;
- ServiceOptions()
+ ServiceOptions()
: qpid::Options("Service options"),
install(false),
start(false),
@@ -395,7 +428,7 @@ int QpiddBroker::execute (QpiddOptions *options) {
// Relies on port number being set via --port or QPID_PORT env variable.
NamedSharedMemory<BrokerInfo> info(brokerInfoName(options->broker.port));
int pid = info.get().pid;
- if (pid < 0)
+ if (pid < 0)
return 1;
if (myOptions->control.check)
std::cout << pid << std::endl;
@@ -464,6 +497,11 @@ int main(int argc, char* argv[])
{ "", (LPSERVICE_MAIN_FUNCTION)qpid::broker::ServiceMain },
{ NULL, NULL }
};
+ // Copy any command line args to be available in case we're started
+ // as a service. Pick these back up in ServiceMain.
+ for (int i = 1; i < argc; ++i)
+ cmdline_args.push_back(argv[i]);
+
if (!StartServiceCtrlDispatcher(dispatchTable)) {
DWORD err = ::GetLastError();
if (err == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) // Run as console
diff --git a/cpp/src/xml.mk b/cpp/src/xml.mk
index baf3803647..9376cfd54a 100644
--- a/cpp/src/xml.mk
+++ b/cpp/src/xml.mk
@@ -24,6 +24,6 @@ xml_la_SOURCES = \
qpid/xml/XmlExchangePlugin.cpp
xml_la_LIBADD = -lxerces-c -lxqilla libqpidbroker.la
-
+xml_la_CXXFLAGS = $(AM_CXXFLAGS) -D_IN_QPID_BROKER
xml_la_LDFLAGS = $(PLUGINLDFLAGS)