diff options
Diffstat (limited to 'RC9/qpid/cpp/src')
727 files changed, 90284 insertions, 0 deletions
diff --git a/RC9/qpid/cpp/src/Makefile.am b/RC9/qpid/cpp/src/Makefile.am new file mode 100644 index 0000000000..422d456fa5 --- /dev/null +++ b/RC9/qpid/cpp/src/Makefile.am @@ -0,0 +1,715 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +SUBDIRS = . tests + +# The Windows-only sources are not compiled using this Makefile, but +# are listed here to ensure they're included in releases. They are built +# using Visual Studio solutions/projects. +windows_dist = \ + qpid.sln \ + broker.vcproj \ + common.vcproj \ + client.vcproj \ + MaxMethodBodySize.vcproj \ + qmfconsole.vcproj \ + protocol_gen.mak \ + qpid/log/windows/SinkOptions.cpp \ + qpid/log/windows/SinkOptions.h \ + qpid/sys/windows/check.h \ + qpid/sys/windows/AsynchIO.cpp \ + qpid/sys/windows/AsynchIoResult.h \ + qpid/sys/windows/Condition.h \ + qpid/sys/windows/FileSysDir.cpp \ + qpid/sys/windows/IntegerTypes.h \ + qpid/sys/windows/IocpDispatcher.cpp \ + qpid/sys/windows/IocpPoller.cpp \ + qpid/sys/windows/IOHandle.cpp \ + qpid/sys/windows/IoHandlePrivate.h \ + qpid/sys/windows/LockFile.cpp \ + qpid/sys/windows/Mutex.h \ + qpid/sys/windows/Shlib.cpp \ + qpid/sys/windows/Socket.cpp \ + qpid/sys/windows/StrError.cpp \ + qpid/sys/windows/SystemInfo.cpp \ + qpid/sys/windows/Thread.cpp \ + qpid/sys/windows/Time.cpp \ + qpid/sys/windows/Time.h \ + qpid/sys/windows/uuid.cpp \ + qpid/sys/windows/uuid.h \ + windows/QpiddBroker.cpp \ + qpid/broker/windows/BrokerDefaults.cpp \ + qpid/broker/windows/SaslAuthenticator.cpp + +EXTRA_DIST= $(platform_dist) $(rgen_srcs) $(windows_dist) + +## Generated code + +# Note: generated soure and makefiles included in distribution so a +# distribution can be built without code generation tools and XML +# sources. + +# This phony target is needed by generated makefile fragments: +force: + +if GENERATE + +# AMQP_FINAL_XML is defined in ../configure.ac +amqp_0_10_xml=@AMQP_FINAL_XML@ +specs=$(amqp_0_10_xml) $(top_srcdir)/xml/cluster.xml + +# Ruby generator. +rgen_dir=$(top_srcdir)/rubygen +rgen_cmd=ruby -I $(rgen_dir) $(rgen_dir)/generate gen $(specs) all $(srcdir)/rubygen.mk + +$(rgen_srcs) $(srcdir)/rubygen.mk: rgen.timestamp +rgen.timestamp: $(rgen_generator) $(specs) + $(rgen_cmd); touch $@ +$(rgen_generator): + +# Management generator. +mgen_dir=$(top_srcdir)/managementgen +mgen_cmd=$(mgen_dir)/qmf-gen -m $(srcdir)/managementgen.mk -q -o gen/qmf \ + $(top_srcdir)/../specs/management-schema.xml \ + $(srcdir)/qpid/acl/management-schema.xml \ + $(srcdir)/qpid/cluster/management-schema.xml + +$(srcdir)/managementgen.mk $(mgen_broker_cpp) $(dist_qpid_management_HEADERS): mgen.timestamp +mgen.timestamp: $(mgen_generator) + $(mgen_cmd); touch $@ +$(mgen_generator): + +endif # GENERATE + +include $(srcdir)/rubygen.mk +include $(srcdir)/managementgen.mk + +# Code generated by C++ +noinst_PROGRAMS=generate_MaxMethodBodySize_h +generate_MaxMethodBodySize_h_SOURCES=gen/generate_MaxMethodBodySize_h.cpp +qpid/framing/MaxMethodBodySize.h: generate_MaxMethodBodySize_h + ./generate_MaxMethodBodySize_h +BUILT_SOURCES=qpid/framing/MaxMethodBodySize.h +DISTCLEANFILES=qpid/framing/MaxMethodBodySize.h + +## Compiler flags +AM_CXXFLAGS = $(WARNING_CFLAGS) +AM_LDFLAGS = -version-info $(LIBTOOL_VERSION_INFO_ARG) +INCLUDES = -Igen -I$(srcdir)/gen + +## Automake macros to build libraries and executables. +qpidd_CXXFLAGS = $(AM_CXXFLAGS) -DMODULE_DIR=\"$(dmoduledir)\" -DCONF_FILE=\"$(sysconfdir)/qpidd.conf\" +libqpidclient_la_CXXFLAGS = $(AM_CXXFLAGS) -DMODULE_DIR=\"$(cmoduledir)\" -DCONF_FILE=\"$(confdir)/qpidc.conf\" + +qpidd_LDADD = \ + libqpidbroker.la \ + libqpidcommon.la + +posix_qpidd_src = posix/QpiddBroker.cpp + +sbin_PROGRAMS = qpidd +qpidd_SOURCES = qpidd.cpp qpidd.h $(posix_qpidd_src) + +posix_plat_src = \ + qpid/log/posix/SinkOptions.cpp \ + qpid/sys/posix/IOHandle.cpp \ + qpid/sys/posix/Socket.cpp \ + qpid/sys/posix/AsynchIO.cpp \ + qpid/sys/posix/FileSysDir.cpp \ + qpid/sys/posix/LockFile.cpp \ + qpid/sys/posix/Time.cpp \ + qpid/sys/posix/Thread.cpp \ + qpid/sys/posix/Shlib.cpp \ + qpid/sys/posix/SystemInfo.cpp \ + qpid/sys/posix/Mutex.cpp \ + qpid/sys/posix/Fork.cpp \ + qpid/sys/posix/StrError.cpp \ + qpid/sys/posix/PollableCondition.cpp + +posix_plat_hdr = \ + qpid/log/posix/SinkOptions.h \ + qpid/sys/posix/check.h \ + qpid/sys/posix/Condition.h \ + qpid/sys/posix/PrivatePosix.h \ + qpid/sys/posix/Mutex.h \ + qpid/sys/posix/Fork.h \ + qpid/sys/posix/PollableCondition.h \ + qpid/sys/posix/IntegerTypes.h \ + qpid/sys/posix/Time.h + +if HAVE_EPOLL + poller = qpid/sys/epoll/EpollPoller.cpp +endif + +if HAVE_ECF + poller = qpid/sys/solaris/ECFPoller.cpp +endif + +platform_src = $(posix_plat_src) $(poller) +platform_hdr = $(posix_plat_hdr) + +posix_broker_src = \ + qpid/broker/posix/BrokerDefaults.cpp + +lib_LTLIBRARIES = libqpidcommon.la libqpidbroker.la libqpidclient.la + +# Definitions for client and daemon plugins +PLUGINLDFLAGS=-no-undefined -module -avoid-version +confdir=$(sysconfdir)/qpid +dmoduledir=$(libdir)/qpid/daemon +cmoduledir=$(libdir)/qpid/client +dmodule_LTLIBRARIES = +cmodule_LTLIBRARIES = +module_hdr = + +include cluster.mk +include acl.mk +include qmf.mk +include qmfc.mk +if HAVE_XML +include xml.mk +endif + +if RDMA + +# RDMA (Infiniband) protocol code +librdmawrap_la_SOURCES = \ + qpid/sys/rdma/rdma_exception.h \ + qpid/sys/rdma/rdma_factories.cpp \ + qpid/sys/rdma/rdma_factories.h \ + qpid/sys/rdma/RdmaIO.cpp \ + qpid/sys/rdma/RdmaIO.h \ + qpid/sys/rdma/rdma_wrap.cpp \ + qpid/sys/rdma/rdma_wrap.h +librdmawrap_la_LIBADD = \ + libqpidcommon.la \ + -lrdmacm \ + -libverbs +librdmawrap_la_CXXFLAGS = \ + $(AM_CXXFLAGS) -Wno-missing-field-initializers +lib_LTLIBRARIES += \ + librdmawrap.la +librdmawrap_la_LDFLAGS = \ + -no-undefined + +rdma_la_SOURCES = \ + qpid/sys/RdmaIOPlugin.cpp +rdma_la_LIBADD = \ + libqpidbroker.la \ + librdmawrap.la +rdma_la_LDFLAGS = $(PLUGINLDFLAGS) +rdma_la_CXXFLAGS = \ + $(AM_CXXFLAGS) -Wno-missing-field-initializers +dmodule_LTLIBRARIES += \ + rdma.la + +rdmaconnector_la_SOURCES = \ + qpid/client/RdmaConnector.cpp +rdmaconnector_la_LIBADD = \ + libqpidclient.la \ + librdmawrap.la +rdmaconnector_la_LDFLAGS = $(PLUGINLDFLAGS) +rdmaconnector_la_CXXFLAGS = \ + $(AM_CXXFLAGS) -Wno-missing-field-initializers +cmodule_LTLIBRARIES += \ + rdmaconnector.la + +# RDMA test/sample programs +noinst_PROGRAMS += RdmaServer RdmaClient +RdmaServer_SOURCES = qpid/sys/rdma/RdmaServer.cpp +RdmaServer_LDADD = \ + librdmawrap.la libqpidcommon.la +RdmaClient_SOURCES = qpid/sys/rdma/RdmaClient.cpp +RdmaClient_CXXFLAGS = \ + $(AM_CXXFLAGS) -Wno-missing-field-initializers +RdmaClient_LDADD = \ + librdmawrap.la libqpidcommon.la + +endif + +if SSL +include ssl.mk +endif + +# New 0-10 codec, to be integrated in future. +# libqpidamqp_0_10_la_SOURCES= +EXTRA_DIST +=\ + $(rgen_amqp_0_10_srcs) \ + qpid/amqp_0_10/apply.h \ + qpid/amqp_0_10/built_in_types.h \ + qpid/amqp_0_10/complex_types.cpp \ + qpid/amqp_0_10/Array.h \ + qpid/amqp_0_10/Array.cpp \ + qpid/amqp_0_10/Body.h \ + qpid/amqp_0_10/Command.h \ + qpid/amqp_0_10/CommmandPacker.h \ + qpid/amqp_0_10/Control.h \ + qpid/amqp_0_10/Header.h \ + qpid/amqp_0_10/Header.cpp \ + qpid/amqp_0_10/FrameHeader.h \ + qpid/amqp_0_10/FrameHeader.cpp \ + qpid/amqp_0_10/Holder.h \ + qpid/amqp_0_10/Codec.h \ + qpid/amqp_0_10/Packer.h \ + qpid/amqp_0_10/Decimal.h \ + qpid/amqp_0_10/SerializableString.h \ + qpid/amqp_0_10/Map.h \ + qpid/amqp_0_10/Map.cpp \ + qpid/amqp_0_10/Struct.h \ + qpid/amqp_0_10/Struct32.h \ + qpid/amqp_0_10/Struct32.cpp \ + qpid/amqp_0_10/Unit.h \ + qpid/amqp_0_10/Unit.cpp \ + qpid/amqp_0_10/UnitHandler.h \ + qpid/amqp_0_10/UnknownType.h \ + qpid/amqp_0_10/UnknownType.cpp \ + qpid/amqp_0_10/UnknownStruct.h \ + qpid/amqp_0_10/UnknownStruct.cpp + +libqpidcommon_la_LIBADD = \ + -lboost_program_options \ + -lboost_filesystem \ + -luuid \ + $(LIB_DLOPEN) \ + $(LIB_CLOCK_GETTIME) + +libqpidcommon_la_SOURCES = \ + $(rgen_framing_srcs) \ + $(platform_src) \ + qpid/assert.cpp qpid/assert.h \ + qpid/pointer_to_other.h \ + qpid/Address.cpp \ + qpid/DataDir.cpp \ + qpid/Exception.cpp \ + qpid/Modules.cpp \ + qpid/Options.cpp \ + qpid/Plugin.cpp \ + qpid/RefCountedBuffer.h \ + qpid/RefCountedBuffer.cpp \ + qpid/Serializer.h \ + qpid/SessionState.h \ + qpid/SessionState.cpp \ + qpid/SessionId.cpp \ + qpid/StringUtils.cpp \ + qpid/Url.cpp \ + qpid/amqp_0_10/SessionHandler.h \ + qpid/amqp_0_10/SessionHandler.cpp \ + qpid/framing/AccumulatedAck.cpp \ + qpid/framing/AMQBody.cpp \ + qpid/framing/AMQMethodBody.cpp \ + qpid/framing/AMQContentBody.cpp \ + qpid/framing/AMQFrame.cpp \ + qpid/framing/AMQHeaderBody.cpp \ + qpid/framing/AMQHeartbeatBody.cpp \ + qpid/framing/Array.cpp \ + qpid/framing/BodyHolder.cpp \ + qpid/framing/BodyHandler.cpp \ + qpid/framing/Buffer.cpp \ + qpid/framing/Endian.cpp \ + qpid/framing/FieldTable.cpp \ + qpid/framing/FieldValue.cpp \ + qpid/framing/FrameSet.cpp \ + qpid/framing/FrameDecoder.cpp \ + qpid/framing/ProtocolInitiation.cpp \ + qpid/framing/ProtocolVersion.cpp \ + qpid/framing/SendContent.cpp \ + qpid/framing/SequenceNumber.cpp \ + qpid/framing/SequenceNumberSet.cpp \ + qpid/framing/SequenceSet.cpp \ + qpid/framing/Proxy.cpp \ + qpid/framing/Uuid.cpp \ + qpid/framing/AMQP_HighestVersion.h \ + qpid/framing/Blob.cpp \ + qpid/framing/MaxMethodBodySize.h \ + qpid/framing/TransferContent.cpp \ + qpid/log/Logger.cpp \ + qpid/log/Options.cpp \ + qpid/log/OstreamOutput.cpp \ + qpid/log/Selector.cpp \ + qpid/log/Statement.cpp \ + qpid/management/Manageable.cpp \ + qpid/management/ManagementObject.cpp \ + qpid/sys/AggregateOutput.cpp \ + qpid/sys/AsynchIOHandler.cpp \ + qpid/sys/Dispatcher.cpp \ + qpid/sys/DispatchHandle.cpp \ + qpid/sys/PollableCondition.h \ + qpid/sys/PollableQueue.h \ + qpid/sys/Runnable.cpp \ + qpid/sys/Shlib.cpp + +libqpidbroker_la_LIBADD = libqpidcommon.la -luuid +if HAVE_SASL +libqpidbroker_la_LIBADD += -lsasl2 +endif + +libqpidbroker_la_SOURCES = \ + $(mgen_broker_cpp) \ + $(posix_broker_src) \ + qpid/amqp_0_10/Connection.h \ + qpid/amqp_0_10/Connection.cpp \ + qpid/broker/Broker.cpp \ + qpid/broker/BrokerSingleton.cpp \ + qpid/broker/Exchange.cpp \ + qpid/broker/Queue.cpp \ + qpid/broker/QueueCleaner.cpp \ + qpid/broker/QueueListeners.cpp \ + qpid/broker/PersistableMessage.cpp \ + qpid/broker/Bridge.cpp \ + qpid/broker/Connection.cpp \ + qpid/broker/ConnectionHandler.cpp \ + qpid/broker/ConnectionFactory.cpp \ + qpid/broker/Daemon.cpp \ + qpid/broker/DeliverableMessage.cpp \ + qpid/broker/DeliveryRecord.cpp \ + qpid/broker/DirectExchange.cpp \ + qpid/broker/DtxAck.cpp \ + qpid/broker/DtxBuffer.cpp \ + qpid/broker/DtxManager.cpp \ + qpid/broker/DtxTimeout.cpp \ + qpid/broker/DtxWorkRecord.cpp \ + qpid/broker/ExchangeRegistry.cpp \ + qpid/broker/FanOutExchange.cpp \ + qpid/broker/HeadersExchange.cpp \ + qpid/broker/IncompleteMessageList.cpp \ + qpid/broker/Link.cpp \ + qpid/broker/LinkRegistry.cpp \ + qpid/broker/Message.cpp \ + qpid/broker/MessageAdapter.cpp \ + qpid/broker/MessageBuilder.cpp \ + qpid/broker/MessageStoreModule.cpp \ + qpid/broker/NameGenerator.cpp \ + qpid/broker/NullMessageStore.cpp \ + qpid/broker/QueueBindings.cpp \ + qpid/broker/QueuePolicy.cpp \ + qpid/broker/QueueRegistry.cpp \ + qpid/broker/RateTracker.cpp \ + qpid/broker/RecoveryManagerImpl.cpp \ + qpid/broker/RecoveredEnqueue.cpp \ + qpid/broker/RecoveredDequeue.cpp \ + qpid/broker/SaslAuthenticator.cpp \ + qpid/broker/SemanticState.h \ + qpid/broker/SemanticState.cpp \ + qpid/broker/SessionAdapter.cpp \ + qpid/broker/SessionState.h \ + qpid/broker/SessionState.cpp \ + qpid/broker/SessionManager.h \ + qpid/broker/SessionManager.cpp \ + qpid/broker/SessionContext.h \ + qpid/broker/SessionHandler.h \ + qpid/broker/SessionHandler.cpp \ + qpid/broker/SignalHandler.h \ + qpid/broker/SignalHandler.cpp \ + qpid/broker/System.cpp \ + qpid/broker/Timer.cpp \ + qpid/broker/TopicExchange.cpp \ + qpid/broker/TxAccept.cpp \ + qpid/broker/TxBuffer.cpp \ + qpid/broker/TxPublish.cpp \ + qpid/broker/Vhost.cpp \ + qpid/management/ManagementBroker.cpp \ + qpid/management/ManagementExchange.cpp \ + qpid/sys/TCPIOPlugin.cpp + +libqpidclient_la_LIBADD = libqpidcommon.la -luuid + +libqpidclient_la_SOURCES = \ + $(rgen_client_srcs) \ + qpid/client/Bounds.cpp \ + qpid/client/Connection.cpp \ + qpid/client/ConnectionHandler.cpp \ + qpid/client/ConnectionImpl.cpp \ + qpid/client/ConnectionSettings.cpp \ + qpid/client/Connector.cpp \ + qpid/client/Demux.cpp \ + qpid/client/Dispatcher.cpp \ + qpid/client/FailoverManager.cpp \ + qpid/client/FailoverListener.h \ + qpid/client/FailoverListener.cpp \ + qpid/client/Future.cpp \ + qpid/client/FutureCompletion.cpp \ + qpid/client/FutureResult.cpp \ + qpid/client/HandlePrivate.h \ + qpid/client/LoadPlugins.cpp \ + qpid/client/LocalQueue.cpp \ + qpid/client/Message.cpp \ + qpid/client/MessageListener.cpp \ + qpid/client/MessageReplayTracker.cpp \ + qpid/client/QueueOptions.cpp \ + qpid/client/Results.cpp \ + qpid/client/SessionBase_0_10.cpp \ + qpid/client/SessionBase_0_10.h \ + qpid/client/SessionBase_0_10Access.h \ + qpid/client/ConnectionAccess.h \ + qpid/client/SessionImpl.cpp \ + qpid/client/StateManager.cpp \ + qpid/client/Subscription.cpp \ + qpid/client/SubscriptionImpl.cpp \ + qpid/client/SubscriptionManager.cpp + +nobase_include_HEADERS = \ + $(platform_hdr) \ + $(module_hdr) \ + qpid/amqp_0_10/apply.h \ + qpid/assert.h \ + qpid/Address.h \ + qpid/DataDir.h \ + qpid/Exception.h \ + qpid/sys/ExceptionHolder.h \ + qpid/amqp_0_10/Exception.h \ + qpid/Modules.h \ + qpid/Msg.h \ + qpid/Options.h \ + qpid/Plugin.h \ + qpid/ptr_map.h \ + qpid/RangeSet.h \ + qpid/RefCounted.h \ + qpid/SessionId.h \ + qpid/SessionState.h \ + qpid/SharedObject.h \ + qpid/StringUtils.h \ + qpid/Url.h \ + qpid/InlineVector.h \ + qpid/InlineAllocator.h \ + qpid/memory.h \ + qpid/shared_ptr.h \ + qpid/Version.h \ + qpid/broker/Broker.h \ + qpid/broker/AclModule.h \ + qpid/broker/SessionAdapter.h \ + qpid/broker/Exchange.h \ + qpid/broker/Queue.h \ + qpid/broker/QueueListeners.h \ + qpid/broker/QueueCleaner.h \ + qpid/broker/BrokerSingleton.h \ + qpid/broker/Bridge.h \ + qpid/broker/Connection.h \ + qpid/broker/ConnectionState.h \ + qpid/broker/ConnectionFactory.h \ + qpid/broker/ConnectionHandler.h \ + qpid/broker/ConnectionToken.h \ + qpid/broker/OwnershipToken.h \ + qpid/broker/Consumer.h \ + qpid/broker/Daemon.h \ + qpid/broker/Deliverable.h \ + qpid/broker/DeliverableMessage.h \ + qpid/broker/DeliveryAdapter.h \ + qpid/broker/DeliveryId.h \ + qpid/broker/DeliveryRecord.h \ + qpid/broker/DirectExchange.h \ + qpid/broker/DtxAck.h \ + qpid/broker/DtxBuffer.h \ + qpid/broker/DtxManager.h \ + qpid/broker/DtxTimeout.h \ + qpid/broker/DtxWorkRecord.h \ + qpid/broker/ExchangeRegistry.h \ + qpid/broker/FanOutExchange.h \ + qpid/broker/HandlerImpl.h \ + qpid/broker/HeadersExchange.h \ + qpid/broker/IncompleteMessageList.h \ + qpid/broker/Link.h \ + qpid/broker/LinkRegistry.h \ + qpid/broker/Message.h \ + qpid/broker/MessageAdapter.h \ + qpid/broker/MessageBuilder.h \ + qpid/broker/MessageStore.h \ + qpid/broker/MessageStoreModule.h \ + qpid/broker/NameGenerator.h \ + qpid/broker/NullMessageStore.h \ + qpid/broker/Persistable.h \ + qpid/broker/PersistableConfig.h \ + qpid/broker/PersistableExchange.h \ + qpid/broker/PersistableMessage.h \ + qpid/broker/PersistableQueue.h \ + qpid/broker/QueueBindings.h \ + qpid/broker/QueuedMessage.h \ + qpid/broker/QueuePolicy.h \ + qpid/broker/QueueRegistry.h \ + qpid/broker/RateTracker.h \ + qpid/broker/RecoverableConfig.h \ + qpid/broker/RecoverableExchange.h \ + qpid/broker/RecoverableMessage.h \ + qpid/broker/RecoverableQueue.h \ + qpid/broker/RecoverableTransaction.h \ + qpid/broker/RecoveredDequeue.h \ + qpid/broker/RecoveredEnqueue.h \ + qpid/broker/RecoveryManager.h \ + qpid/broker/RecoveryManagerImpl.h \ + qpid/broker/SaslAuthenticator.h \ + qpid/broker/SessionAdapter.h \ + qpid/broker/SessionManager.h \ + qpid/broker/System.h \ + qpid/broker/Timer.h \ + qpid/broker/TopicExchange.h \ + qpid/broker/TransactionalStore.h \ + qpid/broker/TxAccept.h \ + qpid/broker/TxBuffer.h \ + qpid/broker/TxOp.h \ + qpid/broker/TxOpVisitor.h \ + qpid/broker/TxPublish.h \ + qpid/broker/Vhost.h \ + qpid/client/AckMode.h \ + qpid/client/Bounds.h \ + qpid/client/ChainableFrameHandler.h \ + qpid/client/Completion.h \ + qpid/client/Connection.h \ + qpid/client/ConnectionHandler.h \ + qpid/client/ConnectionImpl.h \ + qpid/client/ConnectionSettings.h \ + qpid/client/Connector.h \ + qpid/client/Demux.h \ + qpid/client/Dispatcher.h \ + qpid/client/Execution.h \ + qpid/client/FailoverManager.h \ + qpid/client/Subscription.h \ + qpid/client/SubscriptionImpl.h \ + qpid/client/SubscriptionSettings.h \ + qpid/client/FlowControl.h \ + qpid/client/Future.h \ + qpid/client/FutureCompletion.h \ + qpid/client/FutureResult.h \ + qpid/client/Handle.h \ + qpid/client/LocalQueue.h \ + qpid/client/QueueOptions.h \ + qpid/client/Message.h \ + qpid/client/MessageListener.h \ + qpid/client/MessageReplayTracker.h \ + qpid/client/Results.h \ + qpid/client/SessionBase_0_10.h \ + qpid/client/Session.h \ + qpid/client/SessionImpl.h \ + qpid/client/AsyncSession.h \ + qpid/client/StateManager.h \ + qpid/client/SubscriptionManager.h \ + qpid/client/TypedResult.h \ + qpid/framing/AMQBody.h \ + qpid/framing/AMQCommandControlBody.h \ + qpid/framing/AMQContentBody.h \ + qpid/framing/AMQDataBlock.h \ + qpid/framing/AMQFrame.h \ + qpid/framing/AMQHeaderBody.h \ + qpid/framing/AMQHeartbeatBody.h \ + qpid/framing/AMQMethodBody.h \ + qpid/framing/AMQP_HighestVersion.h \ + qpid/framing/AccumulatedAck.h \ + qpid/framing/Array.h \ + qpid/framing/Blob.h \ + qpid/framing/BodyHandler.h \ + qpid/framing/BodyHolder.h \ + qpid/framing/Buffer.h \ + qpid/framing/ChannelHandler.h \ + qpid/framing/Endian.h \ + qpid/framing/FieldTable.h \ + qpid/framing/FieldValue.h \ + qpid/framing/FrameDefaultVisitor.h \ + qpid/framing/FrameDecoder.h \ + qpid/framing/FrameHandler.h \ + qpid/framing/FrameSet.h \ + qpid/framing/Handler.h \ + qpid/framing/HeaderProperties.h \ + qpid/framing/Invoker.h \ + qpid/framing/InputHandler.h \ + qpid/framing/InitiationHandler.h \ + qpid/framing/MethodContent.h \ + qpid/framing/MaxMethodBodySize.h \ + qpid/framing/ModelMethod.h \ + qpid/framing/OutputHandler.h \ + qpid/framing/ProtocolInitiation.h \ + qpid/framing/ProtocolVersion.h \ + qpid/framing/Proxy.h \ + qpid/framing/SendContent.h \ + qpid/framing/SequenceNumber.h \ + qpid/framing/SequenceSet.h \ + qpid/framing/SequenceNumberSet.h \ + qpid/framing/StructHelper.h \ + qpid/framing/TransferContent.h \ + qpid/framing/TypeFilter.h \ + qpid/framing/Uuid.h \ + qpid/framing/Visitor.h \ + qpid/framing/amqp_framing.h \ + qpid/framing/amqp_types.h \ + qpid/framing/amqp_types_full.h \ + qpid/framing/frame_functors.h \ + qpid/framing/variant.h \ + qpid/log/Helpers.h \ + qpid/log/Options.h \ + qpid/log/Logger.h \ + qpid/log/OstreamOutput.h \ + qpid/log/Selector.h \ + qpid/log/SinkOptions.h \ + qpid/log/Statement.h \ + qpid/management/Args.h \ + qpid/management/Manageable.h \ + qpid/management/ManagementBroker.h \ + qpid/management/ManagementEvent.h \ + qpid/management/ManagementExchange.h \ + qpid/management/ManagementObject.h \ + qpid/sys/AggregateOutput.h \ + qpid/sys/AsynchIO.h \ + qpid/sys/AsynchIOHandler.h \ + qpid/sys/AtomicCount.h \ + qpid/sys/AtomicValue.h \ + qpid/sys/AtomicValue_gcc.h \ + qpid/sys/AtomicValue_mutex.h \ + qpid/sys/BlockingQueue.h \ + qpid/sys/CopyOnWriteArray.h \ + qpid/sys/Condition.h \ + qpid/sys/ConnectionCodec.h \ + qpid/sys/ConnectionInputHandler.h \ + qpid/sys/ConnectionInputHandlerFactory.h \ + qpid/sys/ConnectionOutputHandler.h \ + qpid/sys/ConnectionOutputHandlerPtr.h \ + qpid/sys/DeletionManager.h \ + qpid/sys/Dispatcher.h \ + qpid/sys/DispatchHandle.h \ + qpid/sys/FileSysDir.h \ + qpid/sys/IntegerTypes.h \ + qpid/sys/IOHandle.h \ + qpid/sys/LockFile.h \ + qpid/sys/LockPtr.h \ + qpid/sys/Monitor.h \ + qpid/sys/Mutex.h \ + qpid/sys/OutputControl.h \ + qpid/sys/OutputTask.h \ + qpid/sys/Poller.h \ + qpid/sys/ProtocolFactory.h \ + qpid/sys/Runnable.h \ + qpid/sys/Fork.h \ + qpid/sys/ScopedIncrement.h \ + qpid/sys/Semaphore.h \ + qpid/sys/SystemInfo.h \ + qpid/sys/Shlib.h \ + qpid/sys/ShutdownHandler.h \ + qpid/sys/Socket.h \ + qpid/sys/StateMonitor.h \ + qpid/sys/StrError.h \ + qpid/sys/Waitable.h \ + qpid/sys/Thread.h \ + qpid/sys/Time.h \ + qpid/sys/TimeoutHandler.h \ + qpid/sys/uuid.h + +# Force build of qpidd during dist phase so help2man will work. +dist-hook: $(BUILT_SOURCES) + $(MAKE) qpidd + +# Create the default data directory +install-data-local: + $(mkinstalldirs) $(DESTDIR)/$(localstatedir)/lib/qpidd + diff --git a/RC9/qpid/cpp/src/MaxMethodBodySize.vcproj b/RC9/qpid/cpp/src/MaxMethodBodySize.vcproj new file mode 100644 index 0000000000..1b1cf67433 --- /dev/null +++ b/RC9/qpid/cpp/src/MaxMethodBodySize.vcproj @@ -0,0 +1,396 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="MaxMethodBodySize"
+ ProjectGUID="{CB5C939B-FECA-1BAD-BB9C-8C3A688E2823}"
+ RootNamespace="MaxMethodBodySize"
+ Keyword="Win32Proj"
+ SignManifests="true"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="Static_Debug"
+ IntermediateDirectory="Static_Debug\MaxMethodBodySize\I386"
+ ConfigurationType="1"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="_DEBUG;WIN32;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX"
+ MinimalRebuild="false"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG;NOMINMAX"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies=""
+ OutputFile="$(OutDir)\generate_MaxMethodBodySize_h.exe"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories=".;$(BOOST_ROOT)\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="$(OutDir)\generate_MaxMethodBodySize_h"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="Static_Release"
+ IntermediateDirectory="Static_Release\MaxMethodBodySize\I386"
+ ConfigurationType="1"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="NDEBUG;WIN32;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX"
+ RuntimeLibrary="0"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG;NOMINMAX"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies=""
+ OutputFile="$(OutDir)\generate_MaxMethodBodySize_h.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories=".;$(BOOST_ROOT)\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="$(OutDir)\generate_MaxMethodBodySize_h"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="Static_Debug"
+ IntermediateDirectory="Static_Debug\MaxMethodBodySize\AMD64"
+ ConfigurationType="1"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="_DEBUG;WIN32;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_AMD64_;_WIN64;NOMINMAX"
+ MinimalRebuild="false"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG;NOMINMAX;_WIN64"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/machine:AMD64"
+ AdditionalDependencies=""
+ OutputFile="$(OutDir)\generate_MaxMethodBodySize_h.exe"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories=".;$(BOOST_ROOT)\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="$(OutDir)\generate_MaxMethodBodySize_h"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="Static_Release"
+ IntermediateDirectory="Static_Release\MaxMethodBodySize\AMD64"
+ ConfigurationType="1"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="NDEBUG;WIN32;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_AMD64_;_WIN64;NOMINMAX"
+ RuntimeLibrary="0"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG;NOMINMAX;_WIN64"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/machine:AMD64"
+ AdditionalDependencies=""
+ OutputFile="$(OutDir)\generate_MaxMethodBodySize_h.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories=".;$(BOOST_ROOT)\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="$(OutDir)\generate_MaxMethodBodySize_h"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;cxx;cc;C;c">
+ <File
+ RelativePath="gen\generate_MaxMethodBodySize_h.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hh">
+ <File
+ RelativePath="qpidd.h">
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/RC9/qpid/cpp/src/acl.mk b/RC9/qpid/cpp/src/acl.mk new file mode 100644 index 0000000000..2b3ab4dfd9 --- /dev/null +++ b/RC9/qpid/cpp/src/acl.mk @@ -0,0 +1,34 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# +# acl library makefile fragment, to be included in Makefile.am +# +dmodule_LTLIBRARIES += acl.la + +acl_la_SOURCES = \ + qpid/acl/Acl.cpp \ + qpid/acl/Acl.h \ + qpid/acl/AclData.cpp \ + qpid/acl/AclData.h \ + qpid/acl/AclPlugin.cpp \ + qpid/acl/AclReader.cpp \ + qpid/acl/AclReader.h + +acl_la_LIBADD = libqpidbroker.la +acl_la_LDFLAGS = $(PLUGINLDFLAGS) diff --git a/RC9/qpid/cpp/src/broker.vcproj b/RC9/qpid/cpp/src/broker.vcproj new file mode 100644 index 0000000000..aaa7c0ce54 --- /dev/null +++ b/RC9/qpid/cpp/src/broker.vcproj @@ -0,0 +1,1280 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="broker"
+ ProjectGUID="{09613D48-FECA-1BAD-9D20-8C37688E2823}"
+ RootNamespace="broker"
+ Keyword="Win32Proj"
+ SignManifests="true"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="Static_Debug"
+ IntermediateDirectory="Static_Debug\broker\I386"
+ ConfigurationType="1"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="_DEBUG;WIN32;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
+ MinimalRebuild="false"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4800;4290;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="qpidcommonsd.lib ws2_32.lib secur32.lib rpcrt4.lib"
+ OutputFile="$(OutDir)\qpidbroker.exe"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories=".;$(BOOST_ROOT)\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="Static_Release"
+ IntermediateDirectory="Static_Release\broker\I386"
+ ConfigurationType="1"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="NDEBUG;WIN32;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
+ RuntimeLibrary="0"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DisableSpecificWarnings="4244;4800;4290;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="qpidcommons.lib ws2_32.lib secur32.lib rpcrt4.lib"
+ OutputFile="$(OutDir)\qpidbroker.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories=".;$(BOOST_ROOT)\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="Static_Debug"
+ IntermediateDirectory="Static_Debug\broker\AMD64"
+ ConfigurationType="1"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="_DEBUG;WIN32;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_AMD64_;_WIN64;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
+ MinimalRebuild="false"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4800;4290;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS;_WIN64"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/machine:AMD64"
+ AdditionalDependencies="qpidcommonsd.lib ws2_32.lib secur32.lib rpcrt4.lib"
+ OutputFile="$(OutDir)\qpidbroker.exe"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories=".;$(BOOST_ROOT)\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="Static_Release"
+ IntermediateDirectory="Static_Release\broker\AMD64"
+ ConfigurationType="1"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="NDEBUG;WIN32;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_AMD64_;_WIN64;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
+ RuntimeLibrary="0"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DisableSpecificWarnings="4244;4800;4290;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS;_WIN64"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/machine:AMD64"
+ AdditionalDependencies="qpidcommons.lib ws2_32.lib secur32.lib rpcrt4.lib"
+ OutputFile="$(OutDir)\qpidbroker.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ AdditionalLibraryDirectories=".;$(BOOST_ROOT)\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;cxx;cc;C;c">
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\acl\Acl.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\acl\EventAllow.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\acl\EventDeny.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\acl\EventFileLoaded.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\acl\EventFileLoadFailed.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\acl\Package.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Agent.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Binding.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Bridge.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Broker.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Connection.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventBind.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventBrokerLinkDown.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventBrokerLinkUp.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventClientConnect.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventClientConnectFail.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventClientDisconnect.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventExchangeDeclare.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventExchangeDelete.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventQueueDeclare.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventQueueDelete.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventSubscribe.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventUnbind.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventUnsubscribe.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Exchange.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Link.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Package.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Queue.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Session.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\System.cpp">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Vhost.cpp">
+ </File>
+ <File
+ RelativePath="qpid\amqp_0_10\Connection.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="qpid\amqp_0_10\Connection.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\Bridge.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="qpid\broker\Broker.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="qpid\broker\BrokerSingleton.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\Connection.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)3.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)3.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)3.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)3.obj"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="qpid\broker\ConnectionFactory.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\ConnectionHandler.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\DeliverableMessage.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\DeliveryRecord.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\DirectExchange.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\DtxAck.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\DtxBuffer.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\DtxManager.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\DtxTimeout.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\DtxWorkRecord.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\Exchange.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="qpid\broker\ExchangeRegistry.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\FanOutExchange.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\HeadersExchange.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\IncompleteMessageList.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\Link.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="qpid\broker\LinkRegistry.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\Message.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\MessageAdapter.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\MessageBuilder.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\MessageStoreModule.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\NameGenerator.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\NullMessageStore.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\PersistableMessage.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\Queue.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="qpid\broker\QueueBindings.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\QueueCleaner.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\QueueListeners.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\QueuePolicy.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\QueueRegistry.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\RateTracker.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\RecoveredDequeue.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\RecoveredEnqueue.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\RecoveryManagerImpl.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\SemanticState.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\SemanticState.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\SessionAdapter.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\SessionContext.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\SessionHandler.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\SessionHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\SessionManager.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\SessionManager.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\SessionState.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\SessionState.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\System.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="qpid\broker\Timer.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\TopicExchange.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\TxAccept.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\TxBuffer.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\TxPublish.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\Vhost.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="qpid\broker\windows\BrokerDefaults.cpp">
+ </File>
+ <File
+ RelativePath="qpid\broker\windows\SaslAuthenticator.cpp">
+ </File>
+ <File
+ RelativePath="qpid\management\ManagementBroker.cpp">
+ </File>
+ <File
+ RelativePath="qpid\management\ManagementExchange.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\TCPIOPlugin.cpp">
+ </File>
+ <File
+ RelativePath="qpidd.cpp">
+ </File>
+ <File
+ RelativePath="windows\QpiddBroker.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hh">
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\acl\Acl.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\acl\EventAllow.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\acl\EventDeny.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\acl\EventFileLoaded.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\acl\EventFileLoadFailed.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\acl\Package.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Agent.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\ArgsBrokerConnect.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\ArgsBrokerEcho.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\ArgsBrokerQueueMoveMessages.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\ArgsLinkBridge.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\ArgsQueuePurge.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Binding.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Bridge.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Broker.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Connection.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventBind.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventBrokerLinkDown.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventBrokerLinkUp.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventClientConnect.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventClientConnectFail.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventClientDisconnect.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventExchangeDeclare.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventExchangeDelete.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventQueueDeclare.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventQueueDelete.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventSubscribe.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventUnbind.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\EventUnsubscribe.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Exchange.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Link.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Package.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Queue.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Session.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\System.h">
+ </File>
+ <File
+ RelativePath="gen\qmf\org\apache\qpid\broker\Vhost.h">
+ </File>
+ <File
+ RelativePath="qpid\amqp_0_10\Connection.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\AclModule.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\Bridge.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\Broker.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\BrokerSingleton.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\Connection.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\ConnectionFactory.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\ConnectionHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\ConnectionState.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\ConnectionToken.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\Consumer.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\Daemon.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\Deliverable.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\DeliverableMessage.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\DeliveryAdapter.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\DeliveryId.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\DeliveryRecord.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\DeliveryToken.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\DirectExchange.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\DtxAck.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\DtxBuffer.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\DtxManager.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\DtxTimeout.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\DtxWorkRecord.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\Exchange.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\ExchangeRegistry.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\FanOutExchange.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\HandlerImpl.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\HeadersExchange.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\IncompleteMessageList.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\Link.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\LinkRegistry.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\Message.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\MessageAdapter.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\MessageBuilder.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\MessageDelivery.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\MessageStore.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\MessageStoreModule.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\NameGenerator.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\NullMessageStore.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\OwnershipToken.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\Persistable.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\PersistableConfig.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\PersistableExchange.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\PersistableMessage.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\PersistableQueue.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\Queue.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\QueueBindings.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\QueueCleaner.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\QueueListeners.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\QueueMessage.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\QueuePolicy.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\QueueRegistry.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\RateTracker.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\RecoverableConfig.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\RecoverableExchange.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\RecoverableMessage.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\RecoverableQueue.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\RecoverableTransaction.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\RecoveredDequeue.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\RecoveredEnqueue.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\RecoveryManager.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\RecoveryManagerImpl.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\SaslAuthenticator.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\SemanticState.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\SessionAdapter.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\SessionContext.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\SessionHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\SessionManager.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\SessionState.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\SignalHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\System.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\Timer.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\TopicExchange.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\TransactionalStore.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\TxAccept.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\TxBuffer.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\TxOp.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\TxOpVisitor.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\TxPublish.h">
+ </File>
+ <File
+ RelativePath="qpid\broker\Vhost.h">
+ </File>
+ <File
+ RelativePath="qpid\management\Args.h">
+ </File>
+ <File
+ RelativePath="qpid\management\Manageable.h">
+ </File>
+ <File
+ RelativePath="qpid\management\ManagementBroker.h">
+ </File>
+ <File
+ RelativePath="qpid\management\ManagementExchange.h">
+ </File>
+ <File
+ RelativePath="qpid\management\ManagementObject.h">
+ </File>
+ <File
+ RelativePath="qpidd.h">
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/RC9/qpid/cpp/src/client.vcproj b/RC9/qpid/cpp/src/client.vcproj new file mode 100644 index 0000000000..a2d02e9ffc --- /dev/null +++ b/RC9/qpid/cpp/src/client.vcproj @@ -0,0 +1,574 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="client"
+ ProjectGUID="{6961DBA3-FECA-1BAD-F396-8C39688E2823}"
+ RootNamespace="client"
+ Keyword="Win32Proj"
+ SignManifests="true"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="."
+ IntermediateDirectory="Static_Debug\client\I386"
+ ConfigurationType="4"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;CONF_FILE=\"qpidc.conf\";MODULE_DIR=\".\";NOMINMAX;WIN32_LEAN_AND_MEAN"
+ MinimalRebuild="false"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG;CONF_FILE=\"qpidc.conf\";MODULE_DIR=\".\";NOMINMAX;WIN32_LEAN_AND_MEAN"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile=".\qpidclientsd.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="."
+ IntermediateDirectory="Static_Release\client\I386"
+ ConfigurationType="4"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;CONF_FILE=\"qpidc.conf\";MODULE_DIR=\".\";NOMINMAX;WIN32_LEAN_AND_MEAN"
+ RuntimeLibrary="0"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG;CONF_FILE=\"qpidc.conf\";MODULE_DIR=\".\";NOMINMAX;WIN32_LEAN_AND_MEAN"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile=".\qpidclients.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="."
+ IntermediateDirectory="Static_Debug\client\AMD64"
+ ConfigurationType="4"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_AMD64_;_WIN64;CONF_FILE=\"qpidc.conf\";MODULE_DIR=\".\";NOMINMAX;WIN32_LEAN_AND_MEAN"
+ MinimalRebuild="false"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG;CONF_FILE=\"qpidc.conf\";MODULE_DIR=\".\";NOMINMAX;WIN32_LEAN_AND_MEAN;_WIN64"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile=".\qpidclientsd.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="."
+ IntermediateDirectory="Static_Release\client\AMD64"
+ ConfigurationType="4"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_AMD64_;_WIN64;CONF_FILE=\"qpidc.conf\";MODULE_DIR=\".\";NOMINMAX;WIN32_LEAN_AND_MEAN"
+ RuntimeLibrary="0"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG;CONF_FILE=\"qpidc.conf\";MODULE_DIR=\".\";NOMINMAX;WIN32_LEAN_AND_MEAN;_WIN64"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile=".\qpidclients.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;cxx;cc;C;c">
+ <File
+ RelativePath="gen\qpid\client\no_keyword\AsyncSession_0_10.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\client\no_keyword\Session_0_10.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\Bounds.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\Connection.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\ConnectionAccess.h">
+ </File>
+ <File
+ RelativePath="qpid\client\ConnectionHandler.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\ConnectionImpl.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\ConnectionSettings.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\Connector.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\Demux.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\Dispatcher.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\FailoverListener.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\FailoverListener.h">
+ </File>
+ <File
+ RelativePath="qpid\client\FailoverManager.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\Future.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\FutureCompletion.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\FutureResult.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\LoadPlugins.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\LocalQueue.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\Message.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\MessageListener.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\MessageReplayTracker.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\QueueOptions.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\Results.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\SessionBase_0_10.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\SessionBase_0_10.h">
+ </File>
+ <File
+ RelativePath="qpid\client\SessionBase_0_10Access.h">
+ </File>
+ <File
+ RelativePath="qpid\client\SessionImpl.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\StateManager.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\Subscription.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\SubscriptionImpl.cpp">
+ </File>
+ <File
+ RelativePath="qpid\client\SubscriptionManager.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hh">
+ <File
+ RelativePath="gen\qpid\client\arg.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\client\AsyncSession_0_10.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\client\no_keyword\AsyncSession_0_10.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\client\no_keyword\Session_0_10.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\client\Session_0_10.h">
+ </File>
+ <File
+ RelativePath="qpid\client\AckMode.h">
+ </File>
+ <File
+ RelativePath="qpid\client\AckPolicy.h">
+ </File>
+ <File
+ RelativePath="qpid\client\AsyncSession.h">
+ </File>
+ <File
+ RelativePath="qpid\client\Bounds.h">
+ </File>
+ <File
+ RelativePath="qpid\client\ChainableFrameHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\client\Completion.h">
+ </File>
+ <File
+ RelativePath="qpid\client\Connection.h">
+ </File>
+ <File
+ RelativePath="qpid\client\ConnectionAccess.h">
+ </File>
+ <File
+ RelativePath="qpid\client\ConnectionHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\client\ConnectionImpl.h">
+ </File>
+ <File
+ RelativePath="qpid\client\ConnectionSettings.h">
+ </File>
+ <File
+ RelativePath="qpid\client\Connector.h">
+ </File>
+ <File
+ RelativePath="qpid\client\Demux.h">
+ </File>
+ <File
+ RelativePath="qpid\client\Dispatcher.h">
+ </File>
+ <File
+ RelativePath="qpid\client\Execution.h">
+ </File>
+ <File
+ RelativePath="qpid\client\FailoverListener.h">
+ </File>
+ <File
+ RelativePath="qpid\client\FailoverManager.h">
+ </File>
+ <File
+ RelativePath="qpid\client\FlowControl.h">
+ </File>
+ <File
+ RelativePath="qpid\client\Future.h">
+ </File>
+ <File
+ RelativePath="qpid\client\FutureCompletion.h">
+ </File>
+ <File
+ RelativePath="qpid\client\FutureResult.h">
+ </File>
+ <File
+ RelativePath="qpid\client\LocalQueue.h">
+ </File>
+ <File
+ RelativePath="qpid\client\Message.h">
+ </File>
+ <File
+ RelativePath="qpid\client\MessageListener.h">
+ </File>
+ <File
+ RelativePath="qpid\client\MessageReplayTracker.h">
+ </File>
+ <File
+ RelativePath="qpid\client\QueueOptions.h">
+ </File>
+ <File
+ RelativePath="qpid\client\Results.h">
+ </File>
+ <File
+ RelativePath="qpid\client\Session.h">
+ </File>
+ <File
+ RelativePath="qpid\client\SessionBase_0_10.h">
+ </File>
+ <File
+ RelativePath="qpid\client\SessionBase_0_10Access.h">
+ </File>
+ <File
+ RelativePath="qpid\client\SessionImpl.h">
+ </File>
+ <File
+ RelativePath="qpid\client\StateManager.h">
+ </File>
+ <File
+ RelativePath="qpid\client\Subscription.h">
+ </File>
+ <File
+ RelativePath="qpid\client\SubscriptionImpl.h">
+ </File>
+ <File
+ RelativePath="qpid\client\SubscriptionManager.h">
+ </File>
+ <File
+ RelativePath="qpid\client\SubscriptionSettings.h">
+ </File>
+ <File
+ RelativePath="qpid\client\TypedResult.h">
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/RC9/qpid/cpp/src/cluster.mk b/RC9/qpid/cpp/src/cluster.mk new file mode 100644 index 0000000000..5005004023 --- /dev/null +++ b/RC9/qpid/cpp/src/cluster.mk @@ -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. +# +# +# Cluster library makefile fragment, to be included in Makefile.am +# + +# Optional CMAN support + +# Distribute all sources. +EXTRA_DIST += qpid/cluster/Quorum_cman.h qpid/cluster/Quorum_cman.cpp qpid/cluster/Quorum_null.h + +if HAVE_LIBCMAN +CMAN_SOURCES = qpid/cluster/Quorum_cman.h qpid/cluster/Quorum_cman.cpp +libcman = -lcman +else +CMAN_SOURCES = qpid/cluster/Quorum_null.h +endif + +if HAVE_LIBCPG + +dmodule_LTLIBRARIES += cluster.la + +cluster_la_SOURCES = \ + $(CMAN_SOURCES) \ + qpid/cluster/types.h \ + qpid/cluster/Cluster.cpp \ + qpid/cluster/Cluster.h \ + qpid/cluster/Cpg.cpp \ + qpid/cluster/Cpg.h \ + qpid/cluster/Dispatchable.h \ + qpid/cluster/ClusterPlugin.cpp \ + qpid/cluster/ConnectionCodec.h \ + qpid/cluster/ConnectionCodec.cpp \ + qpid/cluster/Connection.h \ + qpid/cluster/Connection.cpp \ + qpid/cluster/ConnectionMap.h \ + qpid/cluster/NoOpConnectionOutputHandler.h \ + qpid/cluster/WriteEstimate.h \ + qpid/cluster/WriteEstimate.cpp \ + qpid/cluster/OutputInterceptor.h \ + qpid/cluster/OutputInterceptor.cpp \ + qpid/cluster/ProxyInputHandler.h \ + qpid/cluster/Event.h \ + qpid/cluster/Event.cpp \ + qpid/cluster/DumpClient.h \ + qpid/cluster/DumpClient.cpp \ + qpid/cluster/ClusterMap.h \ + qpid/cluster/ClusterMap.cpp \ + qpid/cluster/FailoverExchange.h \ + qpid/cluster/FailoverExchange.cpp \ + qpid/cluster/Multicaster.h \ + qpid/cluster/Multicaster.cpp \ + qpid/cluster/ClusterLeaveException.h \ + qpid/cluster/Quorum.h + +cluster_la_LIBADD= -lcpg $(libcman) libqpidbroker.la libqpidclient.la +cluster_la_LDFLAGS = $(PLUGINLDFLAGS) + +endif # HAVE_LIBCPG diff --git a/RC9/qpid/cpp/src/common.vcproj b/RC9/qpid/cpp/src/common.vcproj new file mode 100644 index 0000000000..39429078f3 --- /dev/null +++ b/RC9/qpid/cpp/src/common.vcproj @@ -0,0 +1,1870 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="common"
+ ProjectGUID="{C961EF23-FECA-1BAD-BB9C-8C3A688E2823}"
+ RootNamespace="common"
+ Keyword="Win32Proj"
+ SignManifests="true"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="."
+ IntermediateDirectory="Static_Debug\common\I386"
+ ConfigurationType="4"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
+ MinimalRebuild="false"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile=".\qpidcommonsd.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="."
+ IntermediateDirectory="Static_Release\common\I386"
+ ConfigurationType="4"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
+ RuntimeLibrary="0"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile=".\qpidcommons.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="."
+ IntermediateDirectory="Static_Debug\common\AMD64"
+ ConfigurationType="4"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_AMD64_;_WIN64;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
+ MinimalRebuild="false"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS;_WIN64"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile=".\qpidcommonsd.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="."
+ IntermediateDirectory="Static_Release\common\AMD64"
+ ConfigurationType="4"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_AMD64_;_WIN64;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS"
+ RuntimeLibrary="0"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;_SCL_SECURE_NO_WARNINGS;_WIN64"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile=".\qpidcommons.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;cxx;cc;C;c">
+ <File
+ RelativePath="gen\qpid\framing\AllInvoker.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\AMQP_AllProxy.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\AMQP_ClientProxy.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\AMQP_ServerProxy.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\BodyHolder_gen.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClientInvoker.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConfigChangeBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionAccumulatedAckBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionConsumerStateBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionDeliverCloseBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionDeliverDoOutputBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionDeliveryRecordBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionExchangeBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionMembershipBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionQueueBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionQueuePositionBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionSessionStateBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionShadowReadyBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionTxAcceptBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionTxDequeueBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionTxEndBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionTxEnqueueBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionTxPublishBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionTxStartBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterDumpOfferBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterDumpRequestBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterReadyBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterShutdownBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionCloseBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionCloseOkBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionHeartbeatBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionOpenBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionOpenOkBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionRedirectBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionSecureBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionSecureOkBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionStartBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionStartOkBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionTuneBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionTuneOkBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DeliveryProperties.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxCommitBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxEndBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxForgetBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxGetTimeoutBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxGetTimeoutResult.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxPrepareBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxRecoverBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxRecoverResult.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxRollbackBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxSelectBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxSetTimeoutBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxStartBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeBindBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeBoundBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeBoundResult.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeDeclareBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeDeleteBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeQueryBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeQueryResult.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeUnbindBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExecutionExceptionBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExecutionResultBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExecutionSyncBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileAckBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileCancelBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileConsumeBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileConsumeOkBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileDeliverBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileOpenBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileOpenOkBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileProperties.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FilePublishBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileQosBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileQosOkBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileRejectBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileReturnBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileStageBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FragmentProperties.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\Header.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageAcceptBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageAcquireBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageAcquireResult.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageCancelBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageFlowBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageFlushBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageProperties.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageRejectBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageReleaseBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageResumeBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageResumeResult.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageSetFlowModeBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageStopBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageSubscribeBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageTransferBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MethodBodyDefaultVisitor.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\QueueDeclareBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\QueueDeleteBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\QueuePurgeBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\QueueQueryBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\QueueQueryResult.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\reply_exceptions.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ReplyTo.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ServerInvoker.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionAttachBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionAttachedBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionCommandPointBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionCompletedBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionConfirmedBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionDetachBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionDetachedBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionExpectedBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionFlushBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionGapBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionKnownCompletedBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionRequestTimeoutBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionTimeoutBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamCancelBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamConsumeBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamConsumeOkBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamDeliverBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamProperties.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamPublishBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamQosBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamQosOkBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamReturnBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\TxCommitBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\TxRollbackBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\TxSelectBody.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\TypeCode.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\XaResult.cpp">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\Xid.cpp">
+ </File>
+ <File
+ RelativePath="qpid\Address.cpp">
+ </File>
+ <File
+ RelativePath="qpid\amqp_0_10\SessionHandler.cpp">
+ </File>
+ <File
+ RelativePath="qpid\amqp_0_10\SessionHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\assert.cpp">
+ </File>
+ <File
+ RelativePath="qpid\assert.h">
+ </File>
+ <File
+ RelativePath="qpid\DataDir.cpp">
+ </File>
+ <File
+ RelativePath="qpid\Exception.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\AccumulatedAck.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQBody.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQContentBody.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQFrame.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQHeaderBody.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQHeartbeatBody.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQMethodBody.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQP_HighestVersion.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\Array.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\Blob.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\BodyHandler.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\BodyHolder.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\Buffer.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\Endian.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\FieldTable.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\FieldValue.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\FrameDecoder.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\FrameSet.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\MaxMethodBodySize.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\ProtocolInitiation.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\ProtocolVersion.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\Proxy.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\SendContent.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\SequenceNumber.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\SequenceNumberSet.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\SequenceSet.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\TransferContent.cpp">
+ </File>
+ <File
+ RelativePath="qpid\framing\Uuid.cpp">
+ </File>
+ <File
+ RelativePath="qpid\log\Logger.cpp">
+ </File>
+ <File
+ RelativePath="qpid\log\Options.cpp">
+ </File>
+ <File
+ RelativePath="qpid\log\OstreamOutput.cpp">
+ </File>
+ <File
+ RelativePath="qpid\log\Selector.cpp">
+ </File>
+ <File
+ RelativePath="qpid\log\Statement.cpp">
+ </File>
+ <File
+ RelativePath="qpid\log\windows\SinkOptions.cpp">
+ </File>
+ <File
+ RelativePath="qpid\management\Manageable.cpp">
+ </File>
+ <File
+ RelativePath="qpid\management\ManagementObject.cpp">
+ </File>
+ <File
+ RelativePath="qpid\Modules.cpp">
+ </File>
+ <File
+ RelativePath="qpid\Options.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="qpid\Plugin.cpp">
+ </File>
+ <File
+ RelativePath="qpid\pointer_to_other.h">
+ </File>
+ <File
+ RelativePath="qpid\RefCountedBuffer.cpp">
+ </File>
+ <File
+ RelativePath="qpid\RefCountedBuffer.h">
+ </File>
+ <File
+ RelativePath="qpid\Serializer.h">
+ </File>
+ <File
+ RelativePath="qpid\SessionId.cpp">
+ </File>
+ <File
+ RelativePath="qpid\SessionState.cpp">
+ </File>
+ <File
+ RelativePath="qpid\SessionState.h">
+ </File>
+ <File
+ RelativePath="qpid\StringUtils.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\AggregateOutput.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\AsynchIOHandler.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\PollableCondition.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\PollableQueue.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\Runnable.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\Shlib.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\AsynchIO.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\FileSysDir.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\IocpDispatcher.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\IocpPoller.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\IOHandle.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\LockFile.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\Shlib.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\Socket.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\StrError.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\SystemInfo.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\Thread.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\Time.cpp">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\uuid.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64">
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)2.obj"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="qpid\Url.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hh">
+ <File
+ RelativePath="gen\qpid\framing\all_method_bodies.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\AllInvoker.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\AMQP_AllOperations.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\AMQP_AllProxy.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\AMQP_ClientOperations.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\AMQP_ClientProxy.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\AMQP_ServerOperations.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\AMQP_ServerProxy.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\amqp_structs.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClientInvoker.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConfigChangeBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionAccumulatedAckBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionConsumerStateBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionDeliverCloseBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionDeliverDoOutputBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionDeliveryRecordBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionExchangeBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionMembershipBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionQueueBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionQueuePositionBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionSessionStateBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionShadowReadyBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionTxAcceptBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionTxDequeueBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionTxEndBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionTxEnqueueBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionTxPublishBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterConnectionTxStartBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterDumpOfferBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterDumpRequestBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterReadyBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ClusterShutdownBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionCloseBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionCloseOkBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionHeartbeatBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionOpenBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionOpenOkBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionRedirectBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionSecureBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionSecureOkBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionStartBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionStartOkBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionTuneBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ConnectionTuneOkBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\constants.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DeliveryProperties.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxCommitBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxEndBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxForgetBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxGetTimeoutBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxGetTimeoutResult.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxPrepareBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxRecoverBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxRecoverResult.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxRollbackBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxSelectBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxSetTimeoutBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\DtxStartBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\enum.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeBindBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeBoundBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeBoundResult.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeDeclareBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeDeleteBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeQueryBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeQueryResult.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExchangeUnbindBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExecutionExceptionBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExecutionResultBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ExecutionSyncBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileAckBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileCancelBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileConsumeBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileConsumeOkBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileDeliverBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileOpenBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileOpenOkBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileProperties.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FilePublishBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileQosBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileQosOkBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileRejectBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileReturnBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FileStageBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\FragmentProperties.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\frame_body_lists.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\Header.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageAcceptBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageAcquireBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageAcquireResult.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageCancelBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageFlowBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageFlushBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageProperties.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageRejectBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageReleaseBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageResumeBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageResumeResult.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageSetFlowModeBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageStopBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageSubscribeBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MessageTransferBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MethodBodyConstVisitor.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\MethodBodyDefaultVisitor.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\QueueDeclareBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\QueueDeleteBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\QueuePurgeBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\QueueQueryBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\QueueQueryResult.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\reply_exceptions.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ReplyTo.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\ServerInvoker.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionAttachBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionAttachedBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionCommandPointBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionCompletedBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionConfirmedBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionDetachBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionDetachedBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionExpectedBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionFlushBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionGapBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionKnownCompletedBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionRequestTimeoutBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\SessionTimeoutBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamCancelBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamConsumeBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamConsumeOkBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamDeliverBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamProperties.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamPublishBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamQosBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamQosOkBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\StreamReturnBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\TxCommitBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\TxRollbackBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\TxSelectBody.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\TypeCode.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\XaResult.h">
+ </File>
+ <File
+ RelativePath="gen\qpid\framing\Xid.h">
+ </File>
+ <File
+ RelativePath="qpid\Address.h">
+ </File>
+ <File
+ RelativePath="qpid\amqp_0_10\apply.h">
+ </File>
+ <File
+ RelativePath="qpid\amqp_0_10\Exception.h">
+ </File>
+ <File
+ RelativePath="qpid\amqp_0_10\SessionHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\assert.h">
+ </File>
+ <File
+ RelativePath="qpid\DataDir.h">
+ </File>
+ <File
+ RelativePath="qpid\Exception.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\AccumulatedAck.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQBody.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQCommandControlBody.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQContentBody.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQDataBlock.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQFrame.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQHeaderBody.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQHeartbeatBody.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQMethodBody.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\amqp_framing.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\AMQP_HighestVersion.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\amqp_types.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\amqp_types_full.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\Array.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\Blob.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\BodyHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\BodyHolder.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\Buffer.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\ChannelHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\Endian.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\FieldTable.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\FieldValue.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\frame_functors.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\FrameDecoder.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\FrameDefaultVisitor.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\FrameHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\FrameSet.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\Handler.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\HeaderProperties.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\InitiationHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\InputHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\Invoker.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\MaxMethodBodySize.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\MethodContent.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\ModelMethod.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\OutputHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\ProtocolInitiation.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\ProtocolVersion.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\Proxy.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\SendContent.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\SequenceNumber.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\SequenceNumberSet.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\SequenceSet.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\StructHelper.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\TransferContent.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\TypeFilter.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\Uuid.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\variant.h qpid\log\Helpers.h">
+ </File>
+ <File
+ RelativePath="qpid\framing\Visitor.h">
+ </File>
+ <File
+ RelativePath="qpid\InlineAllocator.h">
+ </File>
+ <File
+ RelativePath="qpid\InlineVector.h">
+ </File>
+ <File
+ RelativePath="qpid\log\Logger.h">
+ </File>
+ <File
+ RelativePath="qpid\log\Options.h">
+ </File>
+ <File
+ RelativePath="qpid\log\OstreamOutput.h">
+ </File>
+ <File
+ RelativePath="qpid\log\Selector.h">
+ </File>
+ <File
+ RelativePath="qpid\log\SinkOptions.h">
+ </File>
+ <File
+ RelativePath="qpid\log\Statement.h">
+ </File>
+ <File
+ RelativePath="qpid\log\windows\SinkOptions.h">
+ </File>
+ <File
+ RelativePath="qpid\management\Manageable.h">
+ </File>
+ <File
+ RelativePath="qpid\management\ManagementObject.h">
+ </File>
+ <File
+ RelativePath="qpid\memory.h">
+ </File>
+ <File
+ RelativePath="qpid\Modules.h">
+ </File>
+ <File
+ RelativePath="qpid\Msg.h">
+ </File>
+ <File
+ RelativePath="qpid\Options.h">
+ </File>
+ <File
+ RelativePath="qpid\Plugin.h">
+ </File>
+ <File
+ RelativePath="qpid\pointer_to_other.h">
+ </File>
+ <File
+ RelativePath="qpid\ptr_map.h">
+ </File>
+ <File
+ RelativePath="qpid\RangeSet.h">
+ </File>
+ <File
+ RelativePath="qpid\RefCounted.h">
+ </File>
+ <File
+ RelativePath="qpid\RefCountedBuffer.h">
+ </File>
+ <File
+ RelativePath="qpid\Serializer.h">
+ </File>
+ <File
+ RelativePath="qpid\SessionId.h">
+ </File>
+ <File
+ RelativePath="qpid\SessionState.h">
+ </File>
+ <File
+ RelativePath="qpid\shared_ptr.h">
+ </File>
+ <File
+ RelativePath="qpid\SharedObject.h">
+ </File>
+ <File
+ RelativePath="qpid\StringUtils.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\AggregateOutput.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\AsynchIO.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\AsynchIOHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\AtomicCount.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\AtomicValue.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\BlockingQueue.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\Condition.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\ConnectionCodec.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\ConnectionInputHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\ConnectionInputHandlerFactory.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\ConnectionOutputHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\ConnectionOutputHandlerPtr.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\CopyOnWriteArray.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\DeletionManager.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\Dispatcher.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\ExceptionHolder.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\FileSysDir.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\IntegerTypes.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\IOHandle.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\LockFile.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\LockPtr.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\Monitor.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\Mutex.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\OutputControl.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\OutputTask.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\PollableCondition.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\PollableQueue.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\Poller.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\ProtocolFactory.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\Runnable.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\ScopedIncrement.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\Semaphore.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\Shlib.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\ShutdownHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\Socket.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\StateMonitor.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\StrError.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\SystemInfo.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\Thread.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\Time.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\TimeoutHandler.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\uuid.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\Waitable.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\AsynchIoResult.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\check.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\Condition.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\IntegerTypes.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\IoHandlePrivate.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\Mutex.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\Time.h">
+ </File>
+ <File
+ RelativePath="qpid\sys\windows\uuid.h">
+ </File>
+ <File
+ RelativePath="qpid\Url.h">
+ </File>
+ <File
+ RelativePath="qpid\Version.h">
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/RC9/qpid/cpp/src/generate.sh b/RC9/qpid/cpp/src/generate.sh new file mode 100755 index 0000000000..581a45ff7f --- /dev/null +++ b/RC9/qpid/cpp/src/generate.sh @@ -0,0 +1,67 @@ +# !/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Generate code from AMQP specification. +# specs and gentools_dir are set by Makefile +# +set -e + +test -z "$JAVA" && JAVA=java ; +test -z "$JAVAC" && JAVAC=javac ; + +srcdir=`dirname $0` +checkspecs() { + for s in $specs; do test -f $s || return 1; done + return 0 +} + +# Can we generate code? +if { test -d $gentools_dir && checkspecs && + which $JAVA && which $JAVAC; } > /dev/null; +then + echo "Generating code." + mkdir -p gen/qpid/framing + ( cd $gentools_dir/src && $JAVAC `find -name '*.java' -print` ; ) + $JAVA -cp $gentools_dir/src org.apache.qpid.gentools.Main \ + -c -o gen/qpid/framing -t $gentools_dir/templ.cpp $specs + GENERATED=yes +fi + +# Print a Makefile variable assignment. +make_assign() { + echo -n "$1 = "; shift + prefix=$1; shift + for f in $*; do echo "\\" ; echo -n " $prefix$f "; done + echo +} + +# Generate a Makefile fragment +( + make_assign "generated_cpp" "" `find gen -name '*.cpp' -print` + make_assign "generated_h" "" `find gen -name '*.h' -print` + if test x$GENERATED = xyes; then + make_assign "generator" "" $specs \ + `find ../gentools \( -name '*.java' -o -name '*.tmpl' \) -print` + fi +) > generate.mk-t +mv generate.mk-t $srcdir/generate.mk + + + diff --git a/RC9/qpid/cpp/src/posix/QpiddBroker.cpp b/RC9/qpid/cpp/src/posix/QpiddBroker.cpp new file mode 100644 index 0000000000..ba1ef640df --- /dev/null +++ b/RC9/qpid/cpp/src/posix/QpiddBroker.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 "qpidd.h" +#include "qpid/Exception.h" +#include "qpid/broker/Broker.h" +#include "qpid/broker/Daemon.h" +#include "qpid/broker/SignalHandler.h" +#include "qpid/log/Logger.h" + +#include <iostream> +#include <signal.h> +#include <unistd.h> +#include <sys/utsname.h> + +using namespace std; +using namespace qpid; +using qpid::broker::Broker; +using qpid::broker::Daemon; + +BootstrapOptions::BootstrapOptions(const char* argv0) + : qpid::Options("Options"), + common("", CONF_FILE), + module(MODULE_DIR), + log(argv0) +{ + add(common); + add(module); + add(log); +} + +namespace { +const std::string TCP = "tcp"; +} + +struct DaemonOptions : public qpid::Options { + bool daemon; + bool quit; + bool check; + int wait; + std::string piddir; + std::string transport; + + DaemonOptions() : qpid::Options("Daemon options"), daemon(false), quit(false), check(false), wait(10), transport(TCP) + { + char *home = ::getenv("HOME"); + + if (home == 0) + piddir += "/tmp"; + else + piddir += home; + piddir += "/.qpidd"; + + addOptions() + ("daemon,d", optValue(daemon), "Run as a daemon. Logs to syslog by default in this mode.") + ("transport", optValue(transport, "TRANSPORT"), "The transport for which to return the port") + ("pid-dir", optValue(piddir, "DIR"), "Directory where port-specific PID file is stored") + ("wait,w", optValue(wait, "SECONDS"), "Sets the maximum wait time to initialize the daemon. If the daemon fails to initialize, prints an error and returns 1") + ("check,c", optValue(check), "Prints the daemon's process ID to stdout and returns 0 if the daemon is running, otherwise returns 1") + ("quit,q", optValue(quit), "Tells the daemon to shut down"); + } +}; + +struct QpiddPosixOptions : public QpiddOptionsPrivate { + DaemonOptions daemon; + QpiddOptions *parent; + + QpiddPosixOptions(QpiddOptions *parent_) : parent(parent_) { + parent->add(daemon); + } +}; + +QpiddOptions::QpiddOptions(const char* argv0) + : qpid::Options("Options"), + common("", CONF_FILE), + module(MODULE_DIR), + log(argv0) +{ + add(common); + add(module); + add(broker); + add(log); + + platform.reset(new QpiddPosixOptions(this)); + qpid::Plugin::addOptions(*this); +} + +void QpiddOptions::usage() const { + cout << "Usage: qpidd [OPTIONS]" << endl << endl << *this << endl; +} + +struct QpiddDaemon : public Daemon { + QpiddPosixOptions *options; + + QpiddDaemon(std::string pidDir, QpiddPosixOptions *opts) + : Daemon(pidDir), options(opts) {} + + /** Code for parent process */ + void parent() { + uint16_t port = wait(options->daemon.wait); + if (options->parent->broker.port == 0 || options->daemon.transport != TCP) + cout << port << endl; + } + + /** Code for forked child process */ + void child() { + boost::intrusive_ptr<Broker> brokerPtr(new Broker(options->parent->broker)); + qpid::broker::SignalHandler::setBroker(brokerPtr); + uint16_t port=brokerPtr->getPort(options->daemon.transport); + ready(port); // Notify parent. + brokerPtr->run(); + } +}; + +int QpiddBroker::execute (QpiddOptions *options) { + // Options that affect a running daemon. + QpiddPosixOptions *myOptions = + static_cast<QpiddPosixOptions *>(options->platform.get()); + if (myOptions == 0) + throw Exception("Internal error obtaining platform options"); + + if (myOptions->daemon.check || myOptions->daemon.quit) { + pid_t pid = Daemon::getPid(myOptions->daemon.piddir, + options->broker.port); + if (pid < 0) + return 1; + if (myOptions->daemon.check) + cout << pid << endl; + if (myOptions->daemon.quit && kill(pid, SIGINT) < 0) + throw Exception("Failed to stop daemon: " + qpid::sys::strError(errno)); + return 0; + } + + // Starting the broker. + if (myOptions->daemon.daemon) { + // For daemon mode replace default stderr with syslog. + options->log.sinkOptions->detached(); + qpid::log::Logger::instance().configure(options->log); + // Fork the daemon + QpiddDaemon d(myOptions->daemon.piddir, myOptions); + d.fork(); // Broker is stared in QpiddDaemon::child() + } + else { // Non-daemon broker. + boost::intrusive_ptr<Broker> brokerPtr(new Broker(options->broker)); + broker::SignalHandler::setBroker(brokerPtr); + if (options->broker.port == 0 || myOptions->daemon.transport != TCP) + cout << uint16_t(brokerPtr->getPort(myOptions->daemon.transport)) << endl; + brokerPtr->run(); + } + return 0; +} diff --git a/RC9/qpid/cpp/src/prof b/RC9/qpid/cpp/src/prof new file mode 100755 index 0000000000..acfbaff2d4 --- /dev/null +++ b/RC9/qpid/cpp/src/prof @@ -0,0 +1,39 @@ +#!/bin/bash +# +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# + + +rm /var/lib/oprofile/oprofiled.log + +opcontrol --reset +opcontrol --setup --no-vmlinux --separate=library +opcontrol --start +# -- Do stuff here -- +./qpidd +# -- End of stuff -- +opcontrol --stop +opcontrol --dump +opcontrol --shutdown +opreport -l ./.libs/lt-qpidd > stats.txt +opannotate --source --output-dir=qpidd-prof ./.libs/lt-qpidd + +# clear the relusts +#opcontrol --reset diff --git a/RC9/qpid/cpp/src/protocol_gen.mak b/RC9/qpid/cpp/src/protocol_gen.mak new file mode 100644 index 0000000000..5c61eb90d5 --- /dev/null +++ b/RC9/qpid/cpp/src/protocol_gen.mak @@ -0,0 +1,28 @@ +# This nmake file generates the Qpid protocol files from the AMQP XML spec
+# and the management sources from the management XML specs.
+#
+# The Visual Studio projects assume the existence of the generated files.
+# The generated files are valid in Apache-released kits but must be generated
+# using this makefile for Windows developers working from the source
+# repository.
+
+specdir = ..\..\specs
+specs = $(specdir)\amqp.0-10-qpid-errata.xml ..\xml\cluster.xml
+
+rgen_dir=..\rubygen
+
+mgen_dir=..\managementgen
+mgmt_specs=$(specdir)\management-schema.xml \
+ .\qpid\acl\management-schema.xml \
+ .\qpid\cluster\management-schema.xml
+
+all: rubygen.mk gen\generate_MaxMethodBodySize_h.cpp managementgen.mk
+
+rubygen.mk gen\generate_MaxMethodBodySize_h.cpp: $(specs)
+ ruby -I $(rgen_dir) $(rgen_dir)\generate gen $(specs) all rubygen.mk
+
+# Management code generation... uses Python
+
+managementgen.mk: $(mgmt_specs)
+ python $(mgen_dir)\qmf-gen -m managementgen.mk -o gen\qmf $(mgmt_specs)
+
diff --git a/RC9/qpid/cpp/src/qmf.mk b/RC9/qpid/cpp/src/qmf.mk new file mode 100644 index 0000000000..f97d10e06f --- /dev/null +++ b/RC9/qpid/cpp/src/qmf.mk @@ -0,0 +1,35 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# +# qmf agent library makefile fragment, to be included in Makefile.am +# +lib_LTLIBRARIES += libqmfagent.la + +module_hdr += \ + qpid/agent/ManagementAgent.h \ + qpid/agent/ManagementAgentImpl.h + +libqmfagent_la_SOURCES = \ + qpid/agent/ManagementAgent.h \ + qpid/agent/ManagementAgentImpl.cpp \ + qpid/agent/ManagementAgentImpl.h + +libqmfagent_la_LIBADD = libqpidclient.la + diff --git a/RC9/qpid/cpp/src/qmfc.mk b/RC9/qpid/cpp/src/qmfc.mk new file mode 100644 index 0000000000..46e8d82f35 --- /dev/null +++ b/RC9/qpid/cpp/src/qmfc.mk @@ -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. +# + +# +# qmf console library makefile fragment, to be included in Makefile.am +# +lib_LTLIBRARIES += libqmfconsole.la + +module_hdr += \ + qpid/console/Agent.h \ + qpid/console/Broker.h \ + qpid/console/ClassKey.h \ + qpid/console/ConsoleListener.h \ + qpid/console/Event.h \ + qpid/console/Object.h \ + qpid/console/ObjectId.h \ + qpid/console/Package.h \ + qpid/console/Schema.h \ + qpid/console/SequenceManager.h \ + qpid/console/SessionManager.h \ + qpid/console/Value.h + +libqmfconsole_la_SOURCES = \ + qpid/console/Agent.h \ + qpid/console/Agent.cpp \ + qpid/console/Broker.h \ + qpid/console/Broker.cpp \ + qpid/console/ClassKey.h \ + qpid/console/ClassKey.cpp \ + qpid/console/ConsoleListener.h \ + qpid/console/Event.h \ + qpid/console/Event.cpp \ + qpid/console/Object.h \ + qpid/console/Object.cpp \ + qpid/console/ObjectId.h \ + qpid/console/ObjectId.cpp \ + qpid/console/Package.h \ + qpid/console/Package.cpp \ + qpid/console/Schema.h \ + qpid/console/Schema.cpp \ + qpid/console/SequenceManager.h \ + qpid/console/SequenceManager.cpp \ + qpid/console/SessionManager.h \ + qpid/console/SessionManager.cpp \ + qpid/console/Value.h \ + qpid/console/Value.cpp + +libqmfconsole_la_LIBADD = libqpidclient.la + diff --git a/RC9/qpid/cpp/src/qmfconsole.vcproj b/RC9/qpid/cpp/src/qmfconsole.vcproj new file mode 100644 index 0000000000..a9dd414c0f --- /dev/null +++ b/RC9/qpid/cpp/src/qmfconsole.vcproj @@ -0,0 +1,418 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="qmfconsole"
+ ProjectGUID="{C95DE177-FECA-1BAD-5EDC-8FFA4564ADCF}"
+ RootNamespace="qmfconsole"
+ Keyword="Win32Proj"
+ SignManifests="true"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="."
+ IntermediateDirectory="Static_Debug\qmfconsole\I386"
+ ConfigurationType="4"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN"
+ MinimalRebuild="false"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile=".\qmfconsolesd.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="."
+ IntermediateDirectory="Static_Release\qmfconsole\I386"
+ ConfigurationType="4"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN"
+ RuntimeLibrary="0"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile=".\qmfconsoles.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="."
+ IntermediateDirectory="Static_Debug\qmfconsole\AMD64"
+ ConfigurationType="4"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_AMD64_;_WIN64;NOMINMAX;WIN32_LEAN_AND_MEAN"
+ MinimalRebuild="false"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;_WIN64"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile=".\qmfconsolesd.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="."
+ IntermediateDirectory="Static_Release\qmfconsole\AMD64"
+ ConfigurationType="4"
+ CharacterSet="0"
+
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ AdditionalOptions=""
+ AdditionalIncludeDirectories=""
+ TypeLibraryName="$(InputName).tlb"
+ HeaderFileName="$(InputName).h"
+ InterfaceIdentifierFileName="$(InputName)_i.c"
+ ProxyFileName="$(InputName)_p.c"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_AMD64_;_WIN64;NOMINMAX;WIN32_LEAN_AND_MEAN"
+ RuntimeLibrary="0"
+ RuntimeTypeInfo="true"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DisableSpecificWarnings="4244;4800;4355"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG;NOMINMAX;WIN32_LEAN_AND_MEAN;_WIN64"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(BOOST_ROOT)\include\$(BOOST_VERSION),$(BOOST_ROOT)\.,.,gen"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile=".\qmfconsoles.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;cxx;cc;C;c">
+ <File
+ RelativePath="qpid\console\Agent.cpp">
+ </File>
+ <File
+ RelativePath="qpid\console\Broker.cpp">
+ </File>
+ <File
+ RelativePath="qpid\console\ClassKey.cpp">
+ </File>
+ <File
+ RelativePath="qpid\console\Object.cpp">
+ </File>
+ <File
+ RelativePath="qpid\console\ObjectId.cpp">
+ </File>
+ <File
+ RelativePath="qpid\console\Package.cpp">
+ </File>
+ <File
+ RelativePath="qpid\console\Schema.cpp">
+ </File>
+ <File
+ RelativePath="qpid\console\SequenceManager.cpp">
+ </File>
+ <File
+ RelativePath="qpid\console\SessionManager.cpp">
+ </File>
+ <File
+ RelativePath="qpid\console\Value.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hh">
+ <File
+ RelativePath="qpid\console\Agent.h">
+ </File>
+ <File
+ RelativePath="qpid\console\Broker.h">
+ </File>
+ <File
+ RelativePath="qpid\console\ClassKey.h">
+ </File>
+ <File
+ RelativePath="qpid\console\ConsoleListener.h">
+ </File>
+ <File
+ RelativePath="qpid\console\Event.h">
+ </File>
+ <File
+ RelativePath="qpid\console\Object.h">
+ </File>
+ <File
+ RelativePath="qpid\console\ObjectId.h">
+ </File>
+ <File
+ RelativePath="qpid\console\Package.h">
+ </File>
+ <File
+ RelativePath="qpid\console\Schema.h">
+ </File>
+ <File
+ RelativePath="qpid\console\SequenceManager.h">
+ </File>
+ <File
+ RelativePath="qpid\console\SessionManager.h">
+ </File>
+ <File
+ RelativePath="qpid\console\Value.h">
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/RC9/qpid/cpp/src/qpid.sln b/RC9/qpid/cpp/src/qpid.sln new file mode 100644 index 0000000000..8e886cf7a4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid.sln @@ -0,0 +1,72 @@ +
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+# $Id: VC9WorkspaceCreator.pm 1439 2008-07-10 14:31:19Z elliott_c $
+#
+# This file was generated by MPC. Any changes made directly to
+# this file will be lost the next time it is generated.
+#
+# MPC Command:
+# C:\ace\MPC\mwc.pl -type vc9 -features boost=1 -static qpid.mwc
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MaxMethodBodySize", "MaxMethodBodySize.vcproj", "{CB5C939B-FECA-1BAD-BB9C-8C3A688E2823}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "broker", "broker.vcproj", "{09613D48-FECA-1BAD-9D20-8C37688E2823}"
+ ProjectSection(ProjectDependencies) = postProject
+ {C961EF23-FECA-1BAD-BB9C-8C3A688E2823} = {C961EF23-FECA-1BAD-BB9C-8C3A688E2823}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "client", "client.vcproj", "{6961DBA3-FECA-1BAD-F396-8C39688E2823}"
+ ProjectSection(ProjectDependencies) = postProject
+ {C961EF23-FECA-1BAD-BB9C-8C3A688E2823} = {C961EF23-FECA-1BAD-BB9C-8C3A688E2823}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "common.vcproj", "{C961EF23-FECA-1BAD-BB9C-8C3A688E2823}"
+ ProjectSection(ProjectDependencies) = postProject
+ {CB5C939B-FECA-1BAD-BB9C-8C3A688E2823} = {CB5C939B-FECA-1BAD-BB9C-8C3A688E2823}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CB5C939B-FECA-1BAD-BB9C-8C3A688E2823}.Debug|Win32.ActiveCfg = Debug|Win32
+ {CB5C939B-FECA-1BAD-BB9C-8C3A688E2823}.Debug|Win32.Build.0 = Debug|Win32
+ {CB5C939B-FECA-1BAD-BB9C-8C3A688E2823}.Debug|x64.ActiveCfg = Debug|x64
+ {CB5C939B-FECA-1BAD-BB9C-8C3A688E2823}.Debug|x64.Build.0 = Debug|x64
+ {CB5C939B-FECA-1BAD-BB9C-8C3A688E2823}.Release|Win32.ActiveCfg = Release|Win32
+ {CB5C939B-FECA-1BAD-BB9C-8C3A688E2823}.Release|Win32.Build.0 = Release|Win32
+ {CB5C939B-FECA-1BAD-BB9C-8C3A688E2823}.Release|x64.ActiveCfg = Release|x64
+ {CB5C939B-FECA-1BAD-BB9C-8C3A688E2823}.Release|x64.Build.0 = Release|x64
+ {09613D48-FECA-1BAD-9D20-8C37688E2823}.Debug|Win32.ActiveCfg = Debug|Win32
+ {09613D48-FECA-1BAD-9D20-8C37688E2823}.Debug|Win32.Build.0 = Debug|Win32
+ {09613D48-FECA-1BAD-9D20-8C37688E2823}.Debug|x64.ActiveCfg = Debug|x64
+ {09613D48-FECA-1BAD-9D20-8C37688E2823}.Debug|x64.Build.0 = Debug|x64
+ {09613D48-FECA-1BAD-9D20-8C37688E2823}.Release|Win32.ActiveCfg = Release|Win32
+ {09613D48-FECA-1BAD-9D20-8C37688E2823}.Release|Win32.Build.0 = Release|Win32
+ {09613D48-FECA-1BAD-9D20-8C37688E2823}.Release|x64.ActiveCfg = Release|x64
+ {09613D48-FECA-1BAD-9D20-8C37688E2823}.Release|x64.Build.0 = Release|x64
+ {6961DBA3-FECA-1BAD-F396-8C39688E2823}.Debug|Win32.ActiveCfg = Debug|Win32
+ {6961DBA3-FECA-1BAD-F396-8C39688E2823}.Debug|Win32.Build.0 = Debug|Win32
+ {6961DBA3-FECA-1BAD-F396-8C39688E2823}.Debug|x64.ActiveCfg = Debug|x64
+ {6961DBA3-FECA-1BAD-F396-8C39688E2823}.Debug|x64.Build.0 = Debug|x64
+ {6961DBA3-FECA-1BAD-F396-8C39688E2823}.Release|Win32.ActiveCfg = Release|Win32
+ {6961DBA3-FECA-1BAD-F396-8C39688E2823}.Release|Win32.Build.0 = Release|Win32
+ {6961DBA3-FECA-1BAD-F396-8C39688E2823}.Release|x64.ActiveCfg = Release|x64
+ {6961DBA3-FECA-1BAD-F396-8C39688E2823}.Release|x64.Build.0 = Release|x64
+ {C961EF23-FECA-1BAD-BB9C-8C3A688E2823}.Debug|Win32.ActiveCfg = Debug|Win32
+ {C961EF23-FECA-1BAD-BB9C-8C3A688E2823}.Debug|Win32.Build.0 = Debug|Win32
+ {C961EF23-FECA-1BAD-BB9C-8C3A688E2823}.Debug|x64.ActiveCfg = Debug|x64
+ {C961EF23-FECA-1BAD-BB9C-8C3A688E2823}.Debug|x64.Build.0 = Debug|x64
+ {C961EF23-FECA-1BAD-BB9C-8C3A688E2823}.Release|Win32.ActiveCfg = Release|Win32
+ {C961EF23-FECA-1BAD-BB9C-8C3A688E2823}.Release|Win32.Build.0 = Release|Win32
+ {C961EF23-FECA-1BAD-BB9C-8C3A688E2823}.Release|x64.ActiveCfg = Release|x64
+ {C961EF23-FECA-1BAD-BB9C-8C3A688E2823}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/RC9/qpid/cpp/src/qpid/Address.cpp b/RC9/qpid/cpp/src/qpid/Address.cpp new file mode 100644 index 0000000000..ac8c7f30b1 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/Address.cpp @@ -0,0 +1,58 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "Address.h" + +#include <ostream> + +using namespace std; + +namespace qpid { + +TcpAddress::TcpAddress(const std::string& h, uint16_t p): host(h), port(p) {} + +struct AddressOstreamVisitor : public boost::static_visitor<ostream&> { + ostream& out; + AddressOstreamVisitor(ostream& o) : out(o) {} + template <class T> ostream& operator()(const T& data) { return out << data; } +}; + +ostream& operator<<(ostream& os, const Address& addr) { + AddressOstreamVisitor visitor(os); + return boost::apply_visitor(visitor, addr.value); +} + +bool operator==(const TcpAddress& x, const TcpAddress& y) { + return y.host==x.host && y.port == x.port; +} + +ostream& operator<<(ostream& os, const TcpAddress& a) { + return os << "tcp:" << a.host << ":" << a.port; +} + +ExampleAddress::ExampleAddress(const char c) : data(c) {} + +bool operator==(const ExampleAddress& x, const ExampleAddress& y) { + return x.data == y.data; +} + +ostream& operator<<(ostream& os, const ExampleAddress& ex) { + return os << "example:" << ex.data; +} + +} // namespace qpid diff --git a/RC9/qpid/cpp/src/qpid/Address.h b/RC9/qpid/cpp/src/qpid/Address.h new file mode 100755 index 0000000000..9669985165 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/Address.h @@ -0,0 +1,85 @@ +#ifndef QPID_ADDRESS_H +#define QPID_ADDRESS_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/sys/IntegerTypes.h" + +#include <boost/variant.hpp> +#include <iosfwd> +#include <string> +#include <vector> + +namespace qpid { + +/** TCP address of a broker - host:port */ +struct TcpAddress { + static const uint16_t DEFAULT_PORT=5672; + explicit TcpAddress(const std::string& host_=std::string(),uint16_t port_=DEFAULT_PORT); + std::string host; + uint16_t port; +}; +bool operator==(const TcpAddress& x, const TcpAddress& y); +std::ostream& operator<<(std::ostream& os, const TcpAddress& a); + +/**@internal Not a real address type, this is a placeholder to + * demonstrate and validate multi-protocol Urls for unit tests and + * developer education only. An example address holds just a char. + */ +struct ExampleAddress { + explicit ExampleAddress(char data); + char data; +}; +bool operator==(const ExampleAddress& x, const ExampleAddress& y); +std::ostream& operator<<(std::ostream& os, const ExampleAddress& a); + +/** + * Contains the address of an AMQP broker. Can any supported type of + * broker address. Currently only TcpAddress is supported. + */ +struct Address { +public: + Address(const Address& a) : value(a.value) {} + Address(const TcpAddress& tcp) : value(tcp) {} + Address(const ExampleAddress& eg) : value(eg) {} ///<@internal + + template <class AddressType> Address& operator=(const AddressType& t) { value=t; return *this; } + + /** Get the address of type AddressType. + *@return AddressType* pointing to the contained address or 0 if + *contained address is not of type AddressType. + */ + template <class AddressType> AddressType* get() { return boost::get<AddressType>(&value); } + + /** Get the address of type AddressType. + *@return AddressType* pointing to the contained address or 0 if + *contained address is not of type AddressType. + */ + template <class AddressType> const AddressType* get() const { return boost::get<AddressType>(&value); } + +private: + boost::variant<TcpAddress,ExampleAddress> value; + friend std::ostream& operator<<(std::ostream& os, const Address& addr); +}; + + + +} // namespace qpid + +#endif /*!QPID_ADDRESS_H*/ diff --git a/RC9/qpid/cpp/src/qpid/DataDir.cpp b/RC9/qpid/cpp/src/qpid/DataDir.cpp new file mode 100644 index 0000000000..4d61d22219 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/DataDir.cpp @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Exception.h" +#include "DataDir.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/FileSysDir.h" + +namespace qpid { + +DataDir::DataDir (std::string path) : + enabled (!path.empty ()), + dirPath (path) +{ + if (!enabled) + { + QPID_LOG (info, "No data directory - Disabling persistent configuration"); + return; + } + + sys::FileSysDir dir(dirPath); + if (!dir.exists()) + dir.mkdir(); + std::string lockFileName(path); + lockFileName += "/lock"; + lockFile = std::auto_ptr<sys::LockFile>(new sys::LockFile(lockFileName, true)); +} + +DataDir::~DataDir () {} + +} // namespace qpid + diff --git a/RC9/qpid/cpp/src/qpid/DataDir.h b/RC9/qpid/cpp/src/qpid/DataDir.h new file mode 100644 index 0000000000..6b45d8747b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/DataDir.h @@ -0,0 +1,50 @@ +#ifndef QPID_DATADIR_H +#define QPID_DATADIR_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <string> +#include <memory> +#include "qpid/sys/LockFile.h" + +namespace qpid { + +/** + * DataDir class. + */ +class DataDir +{ + const bool enabled; + const std::string dirPath; + std::auto_ptr<qpid::sys::LockFile> lockFile; + + public: + + DataDir (std::string path); + ~DataDir (); + + bool isEnabled() { return enabled; } + const std::string& getPath() { return dirPath; } +}; + +} // namespace qpid + +#endif /*!QPID_DATADIR_H*/ diff --git a/RC9/qpid/cpp/src/qpid/Exception.cpp b/RC9/qpid/cpp/src/qpid/Exception.cpp new file mode 100644 index 0000000000..05d1a26f57 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/Exception.cpp @@ -0,0 +1,55 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/log/Statement.h" +#include "Exception.h" +#include <typeinfo> +#include <assert.h> +#include <string.h> + +namespace qpid { + +Exception::Exception(const std::string& msg) throw() : message(msg) { + QPID_LOG_IF(debug, !msg.empty(), "Exception constructed: " << message); +} + +Exception::~Exception() throw() {} + +std::string Exception::getPrefix() const { return ""; } + +std::string Exception::getMessage() const { return message; } + +const char* Exception::what() const throw() { + // Construct the what string the first time it is needed. + if (whatStr.empty()) { + whatStr = getPrefix(); + if (!whatStr.empty()) whatStr += ": "; + whatStr += message; + } + return whatStr.c_str(); +} + +ClosedException::ClosedException(const std::string& msg) + : Exception(msg) {} + +std::string ClosedException::getPrefix() const { return "Closed"; } + +} // namespace qpid diff --git a/RC9/qpid/cpp/src/qpid/Exception.h b/RC9/qpid/cpp/src/qpid/Exception.h new file mode 100644 index 0000000000..86bf8fbc4a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/Exception.h @@ -0,0 +1,93 @@ +#ifndef _Exception_ +#define _Exception_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/framing/amqp_types.h" +#include "qpid/framing/constants.h" +#include "qpid/framing/enum.h" +#include "qpid/sys/StrError.h" +#include "qpid/Msg.h" + +#include <memory> +#include <string> +#include <errno.h> + +namespace qpid +{ + +/** + * Base class for Qpid runtime exceptions. + */ +class Exception : public std::exception +{ + public: + explicit Exception(const std::string& message=std::string()) throw(); + virtual ~Exception() throw(); + virtual const char* what() const throw(); // prefix: message + virtual std::string getMessage() const; // Unprefixed message + virtual std::string getPrefix() const; // Prefix + + private: + std::string message; + mutable std::string whatStr; +}; + +/** Exception that includes an errno message. */ +struct ErrnoException : public Exception { + ErrnoException(const std::string& msg, int err) : Exception(msg+": "+qpid::sys::strError(err)) {} + ErrnoException(const std::string& msg) : Exception(msg+": "+qpid::sys::strError(errno)) {} +}; + +struct SessionException : public Exception { + const framing::execution::ErrorCode code; + SessionException(framing::execution::ErrorCode code_, const std::string& message) + : Exception(message), code(code_) {} +}; + +struct ChannelException : public Exception { + const framing::session::DetachCode code; + ChannelException(framing::session::DetachCode _code, const std::string& message) + : Exception(message), code(_code) {} +}; + +struct ConnectionException : public Exception { + const framing::connection::CloseCode code; + ConnectionException(framing::connection::CloseCode _code, const std::string& message) + : Exception(message), code(_code) {} +}; + +struct ClosedException : public Exception { + ClosedException(const std::string& msg=std::string()); + std::string getPrefix() const; +}; + +/** + * Exception representing transport failure + */ +struct TransportFailure : public Exception { + TransportFailure(const std::string& msg=std::string()) : Exception(msg) {} +}; + +} // namespace qpid + +#endif /*!_Exception_*/ diff --git a/RC9/qpid/cpp/src/qpid/InlineAllocator.h b/RC9/qpid/cpp/src/qpid/InlineAllocator.h new file mode 100644 index 0000000000..bf2f570599 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/InlineAllocator.h @@ -0,0 +1,84 @@ +#ifndef QPID_INLINEALLOCATOR_H +#define QPID_INLINEALLOCATOR_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <memory> +#include <assert.h> +#include <boost/type_traits/type_with_alignment.hpp> +#include <boost/type_traits/alignment_of.hpp> + +namespace qpid { + +/** + * An allocator that has inline storage for up to Max objects + * of type BaseAllocator::value_type. + */ +template <class BaseAllocator, size_t Max> +class InlineAllocator : public BaseAllocator { + public: + typedef typename BaseAllocator::pointer pointer; + typedef typename BaseAllocator::size_type size_type; + typedef typename BaseAllocator::value_type value_type; + + InlineAllocator() : allocated(false) {} + InlineAllocator(const InlineAllocator& x) : BaseAllocator(x), allocated(false) {} + + pointer allocate(size_type n) { + if (n <= Max && !allocated) { + allocated=true; + return reinterpret_cast<value_type*>(address()); + } + else + return BaseAllocator::allocate(n, 0); + } + + void deallocate(pointer p, size_type n) { + if (p == address()) { + assert(allocated); + allocated=false; + } + else + BaseAllocator::deallocate(p, n); + } + + template<typename T1> + struct rebind { + typedef typename BaseAllocator::template rebind<T1>::other BaseOther; + typedef InlineAllocator<BaseOther, Max> other; + }; + + private: + // POD object with alignment and size to hold Max value_types. + static const size_t ALIGNMENT=boost::alignment_of<value_type>::value; + typedef typename boost::type_with_alignment<ALIGNMENT>::type Aligner; + union Store { + Aligner aligner_; + char sizer_[sizeof(value_type)*Max]; + } store; + value_type* address() { return reinterpret_cast<value_type*>(&store); } + bool allocated; +}; + +} // namespace qpid + +#endif /*!QPID_INLINEALLOCATOR_H*/ diff --git a/RC9/qpid/cpp/src/qpid/InlineVector.h b/RC9/qpid/cpp/src/qpid/InlineVector.h new file mode 100644 index 0000000000..551b9912e7 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/InlineVector.h @@ -0,0 +1,68 @@ +#ifndef QPID_INLINEVECTOR_H +#define QPID_INLINEVECTOR_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "InlineAllocator.h" +#include <vector> + +namespace qpid { + +/** + * A vector that stores up to Max elements in inline storage, + * otherwise uses normal vector allocation. + * + * NOTE: depends on some non-standard but highly probably assumptions + * about how std::vector uses its allocator, they are true for g++. + * - default constructor does not allocate. + * - reserve(N) does not allocate more than N elements. + * - vector never re-allocates when size() < capacity() + */ +template <class T, size_t Max, class Alloc=std::allocator<T> > +class InlineVector : public std::vector<T, InlineAllocator<Alloc, Max> > +{ + typedef std::vector<T, InlineAllocator<Alloc, Max> > Base; + public: + typedef typename Base::allocator_type allocator_type; + typedef typename Base::value_type value_type; + typedef typename Base::size_type size_type; + + explicit InlineVector(const allocator_type& a=allocator_type()) : Base(a) { + this->reserve(Max); + } + + explicit InlineVector(size_type n, const value_type& x = value_type(), + const allocator_type& a=allocator_type()) : Base(a) + { + this->reserve(std::max(n, Max)); + this->insert(this->end(), n, x); + } + + InlineVector(const InlineVector& x) : Base() { + this->reserve(std::max(x.size(), Max)); + *this = x; + } +}; + +} // namespace qpid + +#endif /*!QPID_INLINEVECTOR_H*/ diff --git a/RC9/qpid/cpp/src/qpid/Modules.cpp b/RC9/qpid/cpp/src/qpid/Modules.cpp new file mode 100644 index 0000000000..1b802f0019 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/Modules.cpp @@ -0,0 +1,78 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Modules.h" +#include "Exception.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/Shlib.h" + +#include <boost/filesystem/operations.hpp> +#include <boost/filesystem/path.hpp> + +namespace fs=boost::filesystem; + +namespace qpid { + +ModuleOptions::ModuleOptions(const std::string& defaultModuleDir) + : qpid::Options("Module options"), loadDir(defaultModuleDir), noLoad(false) +{ + addOptions() + ("module-dir", optValue(loadDir, "DIR"), "Load all shareable modules in this directory") + ("load-module", optValue(load, "FILE"), "Specifies additional module(s) to be loaded") + ("no-module-dir", optValue(noLoad), "Don't load modules from module directory"); +} + +void tryShlib(const char* libname, bool noThrow) { + try { + sys::Shlib shlib(libname); + QPID_LOG (info, "Loaded Module: " << libname); + } + catch (const std::exception& /*e*/) { + if (!noThrow) + throw; + } +} + +void loadModuleDir (std::string dirname, bool isDefault) +{ + fs::path dirPath (dirname, fs::native); + + if (!fs::exists (dirPath)) + { + if (isDefault) + return; + throw Exception ("Directory not found: " + dirname); + } + if (!fs::is_directory(dirPath)) + { + throw Exception ("Invalid value for module-dir: " + dirname + " is not a directory"); + } + + fs::directory_iterator endItr; + for (fs::directory_iterator itr (dirPath); itr != endItr; ++itr) + { + if (!fs::is_directory(*itr) && + itr->string().find (".so") == itr->string().length() - 3) + tryShlib (itr->string().data(), true); + } +} + +} // namespace qpid diff --git a/RC9/qpid/cpp/src/qpid/Modules.h b/RC9/qpid/cpp/src/qpid/Modules.h new file mode 100644 index 0000000000..2a3b24f359 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/Modules.h @@ -0,0 +1,43 @@ +#ifndef QPID_MODULES_H +#define QPID_MODULES_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Options.h" +#include <string> +#include <vector> + +namespace qpid { + +struct ModuleOptions : public qpid::Options { + std::string loadDir; + std::vector<std::string> load; + bool noLoad; + ModuleOptions(const std::string& defaultModuleDir); +}; + +void tryShlib(const char* libname, bool noThrow); +void loadModuleDir (std::string dirname, bool isDefault); + +} // namespace qpid + +#endif /*!QPID_MODULES_H*/ diff --git a/RC9/qpid/cpp/src/qpid/Msg.h b/RC9/qpid/cpp/src/qpid/Msg.h new file mode 100644 index 0000000000..7214db611f --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/Msg.h @@ -0,0 +1,61 @@ +#ifndef QPID_MSG_H +#define QPID_MSG_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <sstream> +#include <iostream> + +namespace qpid { + +/** A simple wrapper for std::ostringstream that allows + * in place construction of a message and automatic conversion + * to string. + * E.g. + *@code + * void foo(const std::string&); + * foo(Msg() << "hello " << 32); + *@endcode + * Will construct the string "hello 32" and pass it to foo() + */ +struct Msg { + std::ostringstream os; + Msg() {} + Msg(const Msg& m) : os(m.str()) {} + std::string str() const { return os.str(); } + operator std::string() const { return str(); } +}; + +template <class T> const Msg& operator<<(const Msg& m, const T& t) { + const_cast<std::ostringstream&>(m.os)<<t; return m; +} + +inline std::ostream& operator<<(std::ostream& o, const Msg& m) { + return o<<m.str(); +} + +/** Construct a message using operator << and append (file:line) */ +#define QPID_MSG(message) ::qpid::Msg() << message << " (" << __FILE__ << ":" << __LINE__ << ")" + +} // namespace qpid + +#endif /*!QPID_MSG_H*/ diff --git a/RC9/qpid/cpp/src/qpid/Options.cpp b/RC9/qpid/cpp/src/qpid/Options.cpp new file mode 100644 index 0000000000..e521b1220a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/Options.cpp @@ -0,0 +1,468 @@ +/* + * + * 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 "Options.h" +#include "qpid/Exception.h" + +#include <boost/bind.hpp> + +#include <fstream> +#include <algorithm> +#include <iostream> + +namespace qpid { + +using namespace std; + + +/* + * --------------------------------------------- + * Explanation for Boost 103200 conditional code + * --------------------------------------------- + * + * Please see large comment in Options.h . + * + */ + +#if ( BOOST_VERSION == 103200 ) +std::vector<std::string> Options::long_names; +std::vector<std::string> Options::short_names; +#endif + + + + +namespace { + +struct EnvOptMapper { + static bool matchChar(char env, char opt) { + return (env==toupper(opt)) || (strchr("-.", opt) && env=='_'); + } + + static bool matchStr(const string& env, boost::shared_ptr<po::option_description> desc) { + return std::equal(env.begin(), env.end(), desc->long_name().begin(), &matchChar); + } + + static bool matchCase(const string& env, boost::shared_ptr<po::option_description> desc) { + return env == desc->long_name(); + } + + EnvOptMapper(const Options& o) : opts(o) {} + + string operator()(const string& envVar) { + static const std::string prefix("QPID_"); + if (envVar.substr(0, prefix.size()) == prefix) { + string env = envVar.substr(prefix.size()); +#if (BOOST_VERSION >= 103300) + typedef const std::vector< boost::shared_ptr<po::option_description> > OptDescs; + OptDescs::const_iterator i = + find_if(opts.options().begin(), opts.options().end(), boost::bind(matchStr, env, _1)); + if (i != opts.options().end()) + return (*i)->long_name(); +#else + /* + * For Boost version 103200 and below. + * + * In Boost version 103200, the options_description::options member, + * used above, is private. So what I will do here is use the + * count() funtion, which returns a 1 or 0 indicating presence or + * absence of the environment variable. + * + * If it is present, I will return its name. Env vars do not have + * short and long forms, so the name is synonymous with the long + * name. (This would not work for command line args.) + * And if it's absent -- an empty string. + */ + + + /* + * The env vars come in unaltered, i.e. QPID_FOO, but the + * options are stored normalized as "qpid-foo". Change the + * local variable "env" so it can be found by "opts". + */ + for (std::string::iterator i = env.begin(); i != env.end(); ++i) + { + *i = (*i == '_') + ? '-' + : ::tolower(*i); + } + + if ( opts.count(env.c_str()) > 0 ) + { + return env.c_str(); + } + else + { + return string(); + } +#endif + } + return string(); + } + + + bool + isComment ( string const & str ) + { + size_t i = str.find_first_not_of ( " \t" ); + + if ( i == string::npos ) + return true; + + return str[i] == '#'; + } + + + string configFileLine (string& line) { + + if ( isComment ( line ) ) + return string(); + + size_t pos = line.find ('='); + if (pos == string::npos) + return string(); + string key = line.substr (0, pos); +#if (BOOST_VERSION >= 103300) + typedef const std::vector< boost::shared_ptr<po::option_description> > OptDescs; + OptDescs::const_iterator i = + find_if(opts.options().begin(), opts.options().end(), boost::bind(matchCase, key, _1)); + if (i != opts.options().end()) + return string (line) + "\n"; + else + return string(); +#else + // Use 'count' to see if this option exists. Using 'find' will SEGV or hang + // if the option has not been defined yet. + if ( opts.count(key.c_str()) > 0 ) + return string ( line ) + "\n"; + else + return string ( ); +#endif + } + + const Options& opts; +}; + +} +std::string prettyArg(const std::string& name, const std::string& value) { + return value.empty() ? name+" " : name+" ("+value+") "; +} + +Options::Options(const string& name) : + po::options_description(name) + +#if ( BOOST_VERSION == 103200 ) + , m_less_easy(this, this) +#endif +{ +} + + + + + +void Options::parse(int argc, char const* const* argv, const std::string& configFile, bool allowUnknown) +{ + string defaultConfigFile = configFile; // May be changed by env/cmdline + string parsing; + try { + po::variables_map vm; + parsing="command line options"; + if (argc > 0 && argv != 0) { + if (allowUnknown) { +#if (BOOST_VERSION >= 103300) + // This hideous workaround is required because boost 1.33 has a bug + // that causes 'allow_unregistered' to not work. + po::command_line_parser clp = po::command_line_parser(argc, const_cast<char**>(argv)). + options(*this).allow_unregistered(); + po::parsed_options opts = clp.run(); + po::parsed_options filtopts = clp.run(); + filtopts.options.clear (); + for (std::vector< po::basic_option<char> >::iterator i = opts.options.begin(); + i != opts.options.end(); i++) + if (!i->unregistered) + filtopts.options.push_back (*i); + po::store(filtopts, vm); + +#elif ( BOOST_VERSION == 103200 ) + + /* + * "Tokenize" the argv to get rid of equals signs. + */ + vector<string> tokenized_argv; + vector<string>::iterator iter; + + for ( int i = 0; i < argc; ++ i ) + { + string s = argv[i]; + size_t equals_pos = s.find_first_of ( '=' ); + + if ( string::npos == equals_pos ) // There's no equals sign. This is a token. + { + tokenized_argv.push_back(s); + } + else + { + // Two tokens -- before and after the equals position. + tokenized_argv.push_back ( s.substr(0, equals_pos) ); + tokenized_argv.push_back ( s.substr(equals_pos+1) ); + } + } + + + /* + * Now "filter" the tokenized argv, to get rid of all + * unrecognized options. Because Boost 103200 has no + * facility for dealing gracefully with "unregistered" + * options. + */ + vector<string> filtered_argv; + vector<string>::iterator the_end = tokenized_argv.end(); + + // The program-name gets in for free... + iter = tokenized_argv.begin(); + filtered_argv.push_back ( * iter ); + ++ iter; + + // ...but all other args get checked. + while ( iter < the_end ) + { + /* + * If this is an argument that is registered, + * copy it to filtered_argv and also copy all + * of its arguments. + */ + if ( is_registered_option ( * iter ) ) + { + // Store this recognized arg. + filtered_argv.push_back ( * iter ); + ++ iter; + + // Copy all values for the above arg. + // Args are tokens that do not start with a minus. + while ( (iter < the_end) && ((* iter)[0] != '-') ) + { + filtered_argv.push_back ( * iter ); + ++ iter; + } + } + else + { + // Skip this unrecognized arg. + ++ iter; + + // Copy all values for the above arg. + // Values are tokens that do not start with a minus. + while ( (iter < the_end) && ( '-' != (*iter)[0] ) ) + { + ++ iter; + } + } + } + + // Make an array of temporary C strings, because + // the interface I can use wants it that way. + int new_argc = filtered_argv.size(); + char ** new_argv = new char * [ new_argc ]; + int i = 0; + + // cout << "NEW ARGV: |"; + for ( iter = filtered_argv.begin(); + iter < filtered_argv.end(); + ++ iter, ++ i + ) + { + new_argv[i] = strdup( (* iter).c_str() ); + // cout << " " << new_argv[i] ; + } + // cout << "|\n"; + + + // Use the array of C strings. + po::basic_parsed_options<char> bpo = po::parse_command_line(new_argc, const_cast<char**>(new_argv), *this); + po::store(bpo, vm); + + + // Now free the temporary C strings. + for ( i = 0; i < new_argc; ++ i ) + { + free ( new_argv[i] ); + } + delete[] new_argv; + +#endif + } + else + po::store(po::parse_command_line(argc, const_cast<char**>(argv), *this), vm); + } + parsing="environment variables"; + po::store(po::parse_environment(*this, EnvOptMapper(*this)), vm); + po::notify(vm); // configFile may be updated from arg/env options. + if (!configFile.empty()) { + parsing="configuration file "+configFile; + ifstream conf(configFile.c_str()); + if (conf.good()) { + // Remove this hack when we get a stable version of boost that + // can allow unregistered options in config files. + EnvOptMapper mapper(*this); + stringstream filtered; + + while (!conf.eof()) { + string line; + getline (conf, line); + filtered << mapper.configFileLine (line); + } + + po::store(po::parse_config_file(filtered, *this), vm); + // End of hack + } + else { + // No error if default configfile is missing/unreadable + // but complain for non-default config file. + if (configFile != defaultConfigFile) + throw Exception("cannot read configuration file " + +configFile); + } + } + po::notify(vm); + } + catch (const std::exception& e) { + ostringstream msg; + msg << "Error in " << parsing << ": " << e.what() << endl; +#if (BOOST_VERSION >= 103300) + if (find_nothrow("help", false)) + msg << "Use --help to see valid options" << endl; +#endif + throw Exception(msg.str()); + } +} + +CommonOptions::CommonOptions(const string& name, const string& configfile) + : Options(name), config(configfile) +{ + addOptions() + ("help,h", optValue(help), "Displays the help message") + ("version,v", optValue(version), "Displays version information") + ("config", optValue(config, "FILE"), "Reads configuration from FILE"); +} + + + + +#if ( BOOST_VERSION == 103200 ) +options_description_less_easy_init& +options_description_less_easy_init::operator()(char const * name, + char const * description) +{ + // Snoop on the arguments.... + owner->register_names ( name ); + // ... then call parent function explicitly. + po::options_description_easy_init::operator() ( name, description ); + return * this; +} + + +options_description_less_easy_init& +options_description_less_easy_init::operator()(char const * name, + const po::value_semantic* s) +{ + // Snoop on the arguments.... + owner->register_names ( name ); + // ... then call parent function explicitly. + po::options_description_easy_init::operator() ( name, s ); + return * this; +} + + +options_description_less_easy_init& +options_description_less_easy_init::operator()(const char* name, + const po::value_semantic* s, + const char* description) +{ + // Snoop on the arguments.... + owner->register_names ( name ); + // ... then call parent function explicitly. + po::options_description_easy_init::operator() ( name, s, description ); + return * this; +} + + + + + +void +Options::register_names ( std::string s ) +{ + + std::string::size_type comma_pos = s.find_first_of ( ',' ); + + if ( std::string::npos == comma_pos ) + { + // There is no short-name. + long_names.push_back ( s ); + } + else + { + std::string long_name = s.substr(0, comma_pos), + short_name = s.substr(comma_pos+1); + long_names .push_back ( long_name ); + short_names.push_back ( short_name ); + } + + /* + * There is no way to tell when the adding of new options is finished, + * so I re-sort after each one. + */ + std::sort ( long_names .begin(), long_names .end() ); + std::sort ( short_names.begin(), short_names.end() ); +} + + + + + +bool +Options::is_registered_option ( std::string s ) +{ + std::string without_dashes = s.substr ( s.find_first_not_of ( '-' ) ); + std::vector<std::string>::iterator i; + + // Look among the long names. + i = std::find ( long_names.begin(), + long_names.end(), + without_dashes + ); + if ( i != long_names.end() ) + return true; + + // Look among the short names. + i = std::find ( short_names.begin(), + short_names.end(), + without_dashes + ); + if ( i != short_names.end() ) + return true; + + + return false; +} +#endif + + +} // namespace qpid + diff --git a/RC9/qpid/cpp/src/qpid/Options.h b/RC9/qpid/cpp/src/qpid/Options.h new file mode 100644 index 0000000000..cb86d27241 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/Options.h @@ -0,0 +1,257 @@ +#ifndef QPID_COMMONOPTIONS_H +#define QPID_COMMONOPTIONS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Exception.h" +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <sstream> +#include <iterator> +#include <algorithm> +#include <string> + + +namespace qpid { +namespace po=boost::program_options; + + + +///@internal +std::string prettyArg(const std::string&, const std::string&); + +/** @internal Normally only constructed by optValue() */ +template <class T> +class OptionValue : public po::typed_value<T> { + public: + OptionValue(T& value, const std::string& arg) + : po::typed_value<T>(&value), argName(arg) {} + std::string name() const { return argName; } + + private: + std::string argName; +}; + + +/** Create an option value. + * name, value appear after the option name in help like this: + * <name> (=<value>) + * T must support operator <<. + *@see Options for example of use. + */ +template<class T> +po::value_semantic* optValue(T& value, const char* name) { + std::string valstr(boost::lexical_cast<std::string>(value)); + return new OptionValue<T>(value, prettyArg(name, valstr)); +} + +/** Create a vector value. Multiple occurences of the option are + * accumulated into the vector + */ +template <class T> +po::value_semantic* optValue(std::vector<T>& value, const char* name) { + using namespace std; + ostringstream os; + copy(value.begin(), value.end(), ostream_iterator<T>(os, " ")); + string val=os.str(); + if (!val.empty()) + val.erase(val.end()-1); // Remove trailing " " + return (new OptionValue<vector<T> >(value, prettyArg(name, val))); +} + +/** Create a boolean switch value. Presence of the option sets the value. */ +inline po::value_semantic* optValue(bool& value) { return po::bool_switch(&value); } + +/** + * Base class for options. + * Example of use: + @code + struct MySubOptions : public Options { + int x; + string y; + MySubOptions() : Options("Sub options") { + addOptions() + ("x", optValue(x,"XUNIT"), "Option X") + ("y", optValue(y, "YUNIT"), "Option Y"); + } + }; + + struct MyOptions : public Options { + bool z; + vector<string> foo; + MySubOptions subOptions; + MyOptions() : Options("My Options") { + addOptions() + ("z", boolSwitch(z), "Option Z") + ("foo", optValue(foo), "Multiple option foo"); + add(subOptions); + } + + main(int argc, char** argv) { + Options opts; + opts.parse(argc, char** argv); + // Use values + dosomething(opts.subOptions.x); + if (error) + cout << opts << end; // Help message. + } + + @endcode + */ + + + + +/* + * --------------------------------------------- + * Explanation for Boost 103200 conditional code + * --------------------------------------------- + * + * This boost version has an implementation of the program_options library + * that has no provision for allowing unregistered options to pass by. + * + * But that means that, if you have a program that loads optional modules + * after start-up, and those modules each have their own set of options, + * then if you parse the command line too soon, you will get spurious + * reports of unrecognized options -- and the program will exit! + * + * And we must process the command-line before module-loading, because we + * need to look at the "bootstrap" options. + * + * This conditional code: + * + * 1. implements it's own functor class, derived from the Boost + * "options_description_easy_init" class. This functor is used + * to process added options and do the functor chaining, so that + * I can snoop on the arguments before doing an explicit call + * to its parent. + * + * 2. It implements two static vectors, one to hold long names, and + * one for short names, so that options declared by modules are + * not forgotten when their options_description goes out of scope. + * + * I will be thrilled to personally delete this code if we ever decide + * that qpid doesn't really need to support this antique version of Boost. + * + */ + +#if ( BOOST_VERSION == 103200 ) +struct Options; + + +struct +options_description_less_easy_init + : public po::options_description_easy_init +{ + options_description_less_easy_init ( Options * my_owner, + po::options_description * my_parents_owner + ) + : po::options_description_easy_init(my_parents_owner) + { + owner = my_owner; + } + + + options_description_less_easy_init& + operator()(char const * name, + char const * description); + + + options_description_less_easy_init& + operator()(char const * name, + const po::value_semantic* s); + + + options_description_less_easy_init& + operator()(const char* name, + const po::value_semantic* s, + const char* description); + + + Options * owner; +}; +#endif + + + + + + +struct Options : public po::options_description { + + struct Exception : public qpid::Exception { + Exception(const std::string& msg) : qpid::Exception(msg) {} + }; + + Options(const std::string& name=std::string()); + + /** + * Parses options from argc/argv, environment variables and config file. + * Note the filename argument can reference an options variable that + * is updated by argc/argv or environment variable parsing. + */ + void parse(int argc, char const* const* argv, + const std::string& configfile=std::string(), + bool allowUnknown = false); + + + #if ( BOOST_VERSION == 103200 ) + options_description_less_easy_init m_less_easy; + + options_description_less_easy_init addOptions() { + return m_less_easy; + } + + bool + is_registered_option ( std::string s ); + + void + register_names ( std::string s ); + + static std::vector<std::string> long_names; + static std::vector<std::string> short_names; + #else + boost::program_options::options_description_easy_init addOptions() { + return add_options(); + } + #endif +}; + + + +/** + * Standard options for configuration + */ +struct CommonOptions : public Options { + CommonOptions(const std::string& name=std::string(), + const std::string& configfile=std::string()); + bool help; + bool version; + std::string config; +}; + + + + +} // namespace qpid + +#endif /*!QPID_COMMONOPTIONS_H*/ diff --git a/RC9/qpid/cpp/src/qpid/Plugin.cpp b/RC9/qpid/cpp/src/qpid/Plugin.cpp new file mode 100644 index 0000000000..e4b76db28a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/Plugin.cpp @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Plugin.h" +#include "qpid/Options.h" +#include <boost/bind.hpp> +#include <algorithm> + +namespace qpid { + +namespace { + +Plugin::Plugins& thePlugins() { + // This is a single threaded singleton implementation so + // it is important to be sure that the first use of this + // singleton is when the program is still single threaded + static Plugin::Plugins plugins; + return plugins; +} + +void invoke(boost::function<void()> f) { f(); } + +} // namespace + +Plugin::Target::~Target() { finalize(); } + +void Plugin::Target::finalize() { + for_each(finalizers.begin(), finalizers.end(), invoke); + finalizers.clear(); +} + +void Plugin::Target::addFinalizer(const boost::function<void()>& f) { + finalizers.push_back(f); +} + +Plugin::Plugin() { + // Register myself. + thePlugins().push_back(this); +} + +Plugin::~Plugin() {} + +Options* Plugin::getOptions() { return 0; } + +const Plugin::Plugins& Plugin::getPlugins() { return thePlugins(); } + +namespace { +template <class F> void each_plugin(const F& f) { + std::for_each(Plugin::getPlugins().begin(), Plugin::getPlugins().end(), f); +} +} + +void Plugin::addOptions(Options& opts) { + for (Plugins::const_iterator i = getPlugins().begin(); i != getPlugins().end(); ++i) { + if ((*i)->getOptions()) + opts.add(*(*i)->getOptions()); + } +} + +void Plugin::earlyInitAll(Target& t) { each_plugin(boost::bind(&Plugin::earlyInitialize, _1, boost::ref(t))); } +void Plugin::initializeAll(Target& t) { each_plugin(boost::bind(&Plugin::initialize, _1, boost::ref(t))); } + +} // namespace qpid diff --git a/RC9/qpid/cpp/src/qpid/Plugin.h b/RC9/qpid/cpp/src/qpid/Plugin.h new file mode 100644 index 0000000000..50d8e01227 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/Plugin.h @@ -0,0 +1,118 @@ +#ifndef QPID_PLUGIN_H +#define QPID_PLUGIN_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/noncopyable.hpp> +#include <boost/function.hpp> +#include <vector> + +/**@file Generic plug-in framework. */ + +namespace qpid { +struct Options; + +/** + * Plug-in base class. + */ +class Plugin : private boost::noncopyable { + public: + typedef std::vector<Plugin*> Plugins; + + /** + * Base interface for targets that can receive plug-ins. + * Also allows plug-ins to attach a a function to be called + * when the target is 'finalized'. + */ + class Target : private boost::noncopyable + { + public: + /** Calls finalize() if not already called. */ + virtual ~Target(); + + /** Run all the finalizers */ + void finalize(); + + /** Add a function to run when finalize() is called */ + void addFinalizer(const boost::function<void()>&); + + private: + std::vector<boost::function<void()> > finalizers; + }; + + /** + * Constructor registers the plug-in to appear in getPlugins(). + * + * A concrete Plugin is instantiated as a global or static + * member variable in a library so it is registered during + * initialization when the library is loaded. + */ + Plugin(); + + virtual ~Plugin(); + + /** + * Configuration options for the plugin. + * Then will be updated during option parsing by the host program. + * + * @return An options group or 0 for no options. Default returns 0. + * Plugin retains ownership of return value. + */ + virtual Options* getOptions(); + + /** + * Initialize Plugin functionality on a Target, called before + * initializing the target. + * + * Plugins should ignore targets they don't recognize. + * + * Called before the target itself is initialized. + */ + virtual void earlyInitialize(Target&) = 0; + + /** + * Initialize Plugin functionality on a Target. Called after + * initializing the target. + * + * Plugins should ignore targets they don't recognize. + * + * Called after the target is fully initialized. + */ + virtual void initialize(Target&) = 0; + + /** List of registered Plugin objects. + * Caller must not delete plugin pointers. + */ + static const Plugins& getPlugins(); + + /** Call earlyInitialize() on all registered plugins */ + static void earlyInitAll(Target&); + + /** Call initialize() on all registered plugins */ + static void initializeAll(Target&); + + /** For each registered plugin, add plugin.getOptions() to opts. */ + static void addOptions(Options& opts); +}; + +} // namespace qpid + +#endif /*!QPID_PLUGIN_H*/ diff --git a/RC9/qpid/cpp/src/qpid/RangeSet.h b/RC9/qpid/cpp/src/qpid/RangeSet.h new file mode 100644 index 0000000000..1ba4fbbcef --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/RangeSet.h @@ -0,0 +1,330 @@ +#ifndef QPID_RANGESET_H +#define QPID_RANGESET_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/InlineVector.h" +#include <boost/iterator/iterator_facade.hpp> +#include <boost/operators.hpp> +#include <boost/bind.hpp> +#include <algorithm> +#include <numeric> + +namespace qpid { + +/** A range of values, used in RangeSet. + * Range(begin, end) includes begin but excludes end. + * Range::makeClosed(first,last) includes both first and last. + */ +template <class T> +class Range { + public: + static Range makeClosed(const T& first, T last) { return Range(first, ++last); } + + Range() : begin_(), end_() {} + explicit Range(const T& t) : begin_(t), end_(t) { ++end_; } + Range(const T& b, const T& e) : begin_(b), end_(e) { assert(b <= e); } + + T begin() const { return begin_; } + /** End of _open_ range, i.e. !contains(end()) */ + T end() const { return end_; } + + T first() const { assert(!empty()); return begin_; } + /** Last in closed range, i.e. contains(end()) */ + T last() const { assert(!empty()); T ret=end_; return --ret; } + + void begin(const T& t) { begin_ = t; } + void end(const T& t) { end_ = t; } + size_t size() const { return end_ - begin_; } + bool empty() const { return begin_ == end_; } + + bool contains(const T& x) const { return begin_ <= x && x < end_; } + bool contains(const Range& r) const { return begin_ <= r.begin_ && r.end_ <= end_; } + bool strictContains(const Range& r) const { return begin_ < r.begin_ && r.end_ < end_; } + + bool operator==(const Range& x) { return begin_ == x.begin_ && end_== x.end_; } + + bool operator<(const T& t) const { return end_ < t; } + bool operator<(const Range<T>& r) const { return end_ < r.begin_; } + + /** touching ranges can be merged into a single range. */ + bool touching(const Range& r) const { + return std::max(begin_, r.begin_) <= std::min(end_, r.end_); + } + + /** @pre touching */ + void merge(const Range& r) { + assert(touching(r)); + begin_ = std::min(begin_, r.begin_); + end_ = std::max(end_, r.end_); + } + + operator bool() const { return !empty(); } + + template <class S> void serialize(S& s) { s(begin_)(end_); } + + private: + T begin_, end_; +}; + + +/** + * A set implemented as a list of [begin, end) ranges. + * T must be LessThanComparable and Incrementable. + * RangeSet only provides const iterators. + */ +template <class T> +class RangeSet + : boost::additive1<RangeSet<T>, + boost::additive2<RangeSet<T>, Range<T>, + boost::additive2<RangeSet<T>, T> > > +{ + typedef InlineVector<Range<T>, 3> Ranges; // TODO aconway 2008-04-21: what's the optimial inlined value? + + public: + + class iterator : public boost::iterator_facade< + iterator, + const T, + boost::forward_traversal_tag> + { + public: + iterator() : ranges(), iter(), value() {} + + private: + typedef typename Ranges::const_iterator RangesIter; + iterator(const Ranges& r, const RangesIter& i, const T& t) + : ranges(&r), iter(i), value(t) {} + + void increment(); + bool equal(const iterator& i) const; + const T& dereference() const { return value; } + + const Ranges* ranges; + RangesIter iter; + T value; + + friend class RangeSet<T>; + friend class boost::iterator_core_access; + }; + + typedef iterator const_iterator; + + RangeSet() {} + explicit RangeSet(const Range<T>& r) { *this += r; } + RangeSet(const T& a, const T& b) { *this += Range<T>(a,b); } + + bool contiguous() const { return ranges.size() <= 1; } + + bool contains(const T& t) const; + bool contains(const Range<T>&) const; + + /**@pre contiguous() */ + Range<T> toRange() const; + + bool operator==(const RangeSet<T>&) const; + + void addRange (const Range<T>&); + void addSet (const RangeSet<T>&); + + RangeSet<T>& operator+=(const T& t) { return *this += Range<T>(t); } + RangeSet<T>& operator+=(const Range<T>& r) { addRange(r); return *this; } + RangeSet<T>& operator+=(const RangeSet<T>& s) { addSet(s); return *this; } + + void removeRange (const Range<T>&); + void removeSet (const RangeSet<T>&); + + RangeSet<T>& operator-=(const T& t) { return *this -= Range<T>(t); } + RangeSet<T>& operator-=(const Range<T>& r) { removeRange(r); return *this; } + RangeSet<T>& operator-=(const RangeSet<T>& s) { removeSet(s); return *this; } + + T front() const { return ranges.front().begin(); } + T back() const { return ranges.back().end(); } + + // Iterate over elements in the set. + iterator begin() const; + iterator end() const; + + // Iterate over ranges in the set. + typedef typename Ranges::const_iterator RangeIterator; + RangeIterator rangesBegin() const { return ranges.begin(); } + RangeIterator rangesEnd() const { return ranges.end(); } + size_t rangesSize() const { return ranges.size(); } + + // The difference between the start and end of this range set + uint32_t span() const; + + size_t size() const; + bool empty() const { return ranges.empty(); } + void clear() { ranges.clear(); } + + /** Return the largest contiguous range containing x. + * Returns the empty range [x,x) if x is not in the set. + */ + Range<T> rangeContaining(const T&) const; + + template <class S> void serialize(S& s) { s.split(*this); s(ranges.begin(), ranges.end()); } + template <class S> void encode(S& s) const { s(uint16_t(ranges.size()*sizeof(Range<T>))); } + template <class S> void decode(S& s) { uint16_t sz; s(sz); ranges.resize(sz/sizeof(Range<T>)); } + + private: + static size_t accumulateSize(size_t s, const Range<T>& r) { return s+r.size(); } + Ranges ranges; + + template <class U> friend std::ostream& operator<<(std::ostream& o, const RangeSet<U>& r); + + friend class iterator; +}; + +template <class T> +std::ostream& operator<<(std::ostream& o, const Range<T>& r) { + return o << "[" << r.begin() << "," << r.end() << ")"; +} + +template <class T> +std::ostream& operator<<(std::ostream& o, const RangeSet<T>& rs) { + std::ostream_iterator<Range<T> > i(o, " "); + o << "{ "; + std::copy(rs.ranges.begin(), rs.ranges.end(), i); + return o << "}"; +} + +template <class T> +bool RangeSet<T>::contains(const T& t) const { + typename Ranges::const_iterator i = + std::lower_bound(ranges.begin(), ranges.end(), Range<T>(t)); + return i != ranges.end() && i->contains(t); +} + +template <class T> +bool RangeSet<T>::contains(const Range<T>& r) const { + typename Ranges::const_iterator i = + std::lower_bound(ranges.begin(), ranges.end(), r); + return i != ranges.end() && i->contains(r); +} + +template <class T> void RangeSet<T>::addRange(const Range<T>& r) { + if (r.empty()) return; + typename Ranges::iterator i = + std::lower_bound(ranges.begin(), ranges.end(), r); + if (i == ranges.end() || !i->touching(r)) + ranges.insert(i, r); + else { + i->merge(r); + typename Ranges::iterator j = i; + if (++j != ranges.end() && i->touching(*j)) { + i->merge(*j); + ranges.erase(j); + } + } +} + + +template <class T> void RangeSet<T>::addSet(const RangeSet<T>& s) { + typedef RangeSet<T>& (RangeSet<T>::*RangeSetRangeOp)(const Range<T>&); + std::for_each(s.ranges.begin(), s.ranges.end(), + boost::bind((RangeSetRangeOp)&RangeSet<T>::operator+=, this, _1)); +} + +template <class T> void RangeSet<T>::removeRange(const Range<T>& r) { + if (r.empty()) return; + typename Ranges::iterator i,j; + i = std::lower_bound(ranges.begin(), ranges.end(), r); + if (i == ranges.end() || i->begin() >= r.end()) + return; // Outside of set + if (*i == r) // Erase i + ranges.erase(i); + else if (i->strictContains(r)) { // Split i + Range<T> i1(i->begin(), r.begin()); + Range<T> i2(r.end(), i->end()); + *i = i2; + ranges.insert(i, i1); + } else { + if (i->begin() < r.begin()) { // Truncate i + i->end(r.begin()); + ++i; + } + for (j = i; j != ranges.end() && r.contains(*j); ++j) + ; // Ranges to erase. + if (j != ranges.end() && j->end() > r.end()) + j->begin(r.end()); // Truncate j + ranges.erase(i,j); + } +} + +template <class T> void RangeSet<T>::removeSet(const RangeSet<T>& r) { + std::for_each( + r.ranges.begin(), r.ranges.end(), + boost::bind(&RangeSet<T>::removeRange, this, _1)); +} + +template <class T> Range<T> RangeSet<T>::toRange() const { + assert(contiguous()); + return empty() ? Range<T>() : ranges.front(); +} + +template <class T> void RangeSet<T>::iterator::increment() { + assert(ranges && iter != ranges->end()); + if (!iter->contains(++value)) { + ++iter; + if (iter == ranges->end()) + *this=iterator(); // end() iterator + else + value=iter->begin(); + } +} + +template <class T> bool RangeSet<T>::operator==(const RangeSet<T>& r) const { + return ranges.size() == r.ranges.size() && std::equal(ranges.begin(), ranges.end(), r.ranges.begin()); +} + +template <class T> typename RangeSet<T>::iterator RangeSet<T>::begin() const { + return empty() ? end() : iterator(ranges, ranges.begin(), front()); +} + +template <class T> typename RangeSet<T>::iterator RangeSet<T>::end() const { + return iterator(); +} + +template <class T> bool RangeSet<T>::iterator::equal(const iterator& i) const { + return ranges==i.ranges && (ranges==0 || value==i.value); +} + +template <class T> Range<T> RangeSet<T>::rangeContaining(const T& t) const { + typename Ranges::const_iterator i = + std::lower_bound(ranges.begin(), ranges.end(), Range<T>(t)); + return (i != ranges.end() && i->contains(t)) ? *i : Range<T>(t,t); +} + +template <class T> uint32_t RangeSet<T>::span() const { + if (ranges.empty()) return 0; + return ranges.back().last() - ranges.front().first(); +} + +template <class T> size_t RangeSet<T>::size() const { + return std::accumulate(rangesBegin(), rangesEnd(), 0, &RangeSet<T>::accumulateSize); +} + +} // namespace qpid + + +#endif /*!QPID_RANGESET_H*/ diff --git a/RC9/qpid/cpp/src/qpid/RefCounted.h b/RC9/qpid/cpp/src/qpid/RefCounted.h new file mode 100644 index 0000000000..10b5e4afcc --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/RefCounted.h @@ -0,0 +1,59 @@ +#ifndef QPID_REFCOUNTED_H +#define QPID_REFCOUNTED_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/utility.hpp> +#include <boost/detail/atomic_count.hpp> + +namespace qpid { + +/** + * Reference-counted base class. + * Note: this class isn't copyable - you must copy the intrusive_ptr that points + * to the class that has mixed this in not the class itself (as that would sidestep + * the reference counting) + */ +class RefCounted : boost::noncopyable { + mutable boost::detail::atomic_count count; + +public: + RefCounted() : count(0) {} + void addRef() const { ++count; } + void release() const { if (--count==0) delete this; } + long refCount() { return count; } + +protected: + virtual ~RefCounted() {}; +}; + + +} // namespace qpid + +// intrusive_ptr support. +namespace boost { +inline void intrusive_ptr_add_ref(const qpid::RefCounted* p) { p->addRef(); } +inline void intrusive_ptr_release(const qpid::RefCounted* p) { p->release(); } +} + + +#endif /*!QPID_REFCOUNTED_H*/ diff --git a/RC9/qpid/cpp/src/qpid/RefCountedBuffer.cpp b/RC9/qpid/cpp/src/qpid/RefCountedBuffer.cpp new file mode 100644 index 0000000000..57ef48ac42 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/RefCountedBuffer.cpp @@ -0,0 +1,53 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "RefCountedBuffer.h" +#include <new> + +namespace qpid { + +RefCountedBuffer::RefCountedBuffer() : count(0) {} + +void RefCountedBuffer::destroy() const { + this->~RefCountedBuffer(); + ::delete[] reinterpret_cast<const char*>(this); +} + +char* RefCountedBuffer::addr() const { + return const_cast<char*>(reinterpret_cast<const char*>(this)+sizeof(RefCountedBuffer)); +} + +RefCountedBuffer::pointer RefCountedBuffer::create(size_t n) { + char* store=::new char[n+sizeof(RefCountedBuffer)]; + new(store) RefCountedBuffer; + return pointer(reinterpret_cast<RefCountedBuffer*>(store)); +} + +RefCountedBuffer::pointer::pointer() {} +RefCountedBuffer::pointer::pointer(RefCountedBuffer* x) : p(x) {} +RefCountedBuffer::pointer::pointer(const pointer& x) : p(x.p) {} +RefCountedBuffer::pointer::~pointer() {} +RefCountedBuffer::pointer& RefCountedBuffer::pointer::operator=(const RefCountedBuffer::pointer& x) { p = x.p; return *this; } + +char* RefCountedBuffer::pointer::cp() const { return p ? p->get() : 0; } +} // namespace qpid + + diff --git a/RC9/qpid/cpp/src/qpid/RefCountedBuffer.h b/RC9/qpid/cpp/src/qpid/RefCountedBuffer.h new file mode 100644 index 0000000000..c332325378 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/RefCountedBuffer.h @@ -0,0 +1,89 @@ +#ifndef QPID_REFCOUNTEDBUFFER_H +#define QPID_REFCOUNTEDBUFFER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/utility.hpp> +#include <boost/detail/atomic_count.hpp> +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +// FIXME aconway 2008-09-06: easy to add alignment +/** + * Reference-counted byte buffer. + * No alignment guarantees. + */ +class RefCountedBuffer : boost::noncopyable { + mutable boost::detail::atomic_count count; + RefCountedBuffer(); + void destroy() const; + char* addr() const; + +public: + /** Smart char pointer to a reference counted buffer */ + class pointer { + boost::intrusive_ptr<RefCountedBuffer> p; + char* cp() const; + pointer(RefCountedBuffer* x); + friend class RefCountedBuffer; + + public: + pointer(); + pointer(const pointer&); + ~pointer(); + pointer& operator=(const pointer&); + + char* get() { return cp(); } + operator char*() { return cp(); } + char& operator*() { return *cp(); } + char& operator[](size_t i) { return cp()[i]; } + + const char* get() const { return cp(); } + operator const char*() const { return cp(); } + const char& operator*() const { return *cp(); } + const char& operator[](size_t i) const { return cp()[i]; } + }; + + /** Create a reference counted buffer of size n */ + static pointer create(size_t n); + + /** Get a pointer to the start of the buffer. */ + char* get() { return addr(); } + const char* get() const { return addr(); } + char& operator[](size_t i) { return get()[i]; } + const char& operator[](size_t i) const { return get()[i]; } + + void addRef() const { ++count; } + void release() const { if (--count==0) destroy(); } + long refCount() { return count; } +}; + +} // namespace qpid + +// intrusive_ptr support. +namespace boost { +inline void intrusive_ptr_add_ref(const qpid::RefCountedBuffer* p) { p->addRef(); } +inline void intrusive_ptr_release(const qpid::RefCountedBuffer* p) { p->release(); } +} + + +#endif /*!QPID_REFCOUNTEDBUFFER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/Serializer.h b/RC9/qpid/cpp/src/qpid/Serializer.h new file mode 100644 index 0000000000..a8ded9f5e0 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/Serializer.h @@ -0,0 +1,197 @@ +#ifndef QPID_SERIALIZER_H +#define QPID_SERIALIZER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <limits> +#include <algorithm> +#include "qpid/Exception.h" // FIXME aconway 2008-04-03: proper exception class. + +namespace qpid { + +/** + * Overload for types that do not provide a serialize() member. + * It should retrun a wrapper holding a reference to t that implements + * serialize() + */ +template <class T> T& serializable(T& t) { return t; } + +/** Serialize std::pair */ +template <class T, class U> struct SerializablePair { + std::pair<T,U>& value; + SerializablePair(std::pair<T,U>& x) : value(x) {} + template <class S> void serialize(S& s) { s(value.first)(value.second); } +}; + +template <class T, class U> +SerializablePair<T,U> serializable(std::pair<T,U>& p) { + return SerializablePair<T,U>(p); +} + +/** + * Base class for all serializers. + * Derived serializers inherit from either Encoder or Decoder. + * Serializers can be used as functors or static_visitors. + */ +template <class Derived> class Serializer { + public: + /** Temporarily set a lower relative limit on the serializer */ + class ScopedLimit { + public: + ScopedLimit(Serializer& s, size_t l) + : serializer(s), save(serializer.setLimit(l)) {} + + ~ScopedLimit() { serializer.setAbsLimit(save); } + + private: + Serializer& serializer; + size_t save; + }; + + static size_t maxLimit() { return std::numeric_limits<size_t>::max(); } + + Serializer() : bytes(0), limit(maxLimit()) {} + + typedef Derived& result_type; // unary functor requirement. + + /** Wrapper functor to pass serializer functors by reference. */ + template <class S> struct Ref { + typedef typename S::result_type result_type; + S& s; + Ref(S& ss) : s(ss) {} + template <class T> result_type operator()(T& x) { return s(x); } + template <class T> result_type operator()(const T& x) { return s(x); } + }; + + /** Reference wrapper to pass serializers by reference, + * e.g. to std:: functions that take functors. + */ + template <class S> static Ref<S> ref(S& s) { return Ref<S>(s); } + + /** Generic rule to serialize an iterator range */ + template <class Iter> Derived& operator()(Iter begin, Iter end) { + std::for_each(begin, end, ref(this->self())); + return self(); + } + + /** Set limit relative to current position. + * @return old absolute limit. + */ + size_t setLimit(size_t n) { + size_t l=limit; + limit = bytes+n; + return l; + } + + /** Get the max number of bytes that can be processed under the + * current limit. + */ + size_t bytesRemaining() const { + return limit - bytes; + } + /** Set absolute limit. */ + void setAbsLimit(size_t n) { + limit = n; + if (bytes > limit) + throw Exception("Framing error: data overrun"); // FIXME aconway 2008-04-03: proper exception. + } + + protected: + Derived& self() { return *static_cast<Derived*>(this); } + void addBytes(size_t n) { + size_t newBytes=bytes+n; + if (newBytes > limit) + throw Exception("Framing error: data overrun"); // FIXME aconway 2008-04-03: proper exception. + bytes = newBytes; + } + + private: + void checkLimit() { + } + + size_t bytes; // how many bytes serialized. + size_t limit; // bytes may not exceed this limit. +}; + +/** + * Base class for encoders, provides generic encode functions. + * + * A derived encoder must provide operator(const T&) to encode all + * primitive types T. + */ +template <class Derived> class EncoderBase : public Serializer<Derived> { + public: + using Serializer<Derived>::operator(); + using Serializer<Derived>::self; + + /** Default op() for non-primitive types. */ + template <class T> Derived& operator()(const T& t) { + serializable(const_cast<T&>(t)).serialize(self()); return self(); + } + + /** Split serialize() into encode()/decode() */ + template <class T> Derived& split(const T& t) { + t.encode(self()); return self(); + } +}; + +/** + * Base class for decoders, provides generic decode functions. + * + * A derived encoder must provide operator(T&) to encode all + * primitive types T. + */ +template <class Derived> class DecoderBase : public Serializer<Derived> { + public: + using Serializer<Derived>::operator(); + using Serializer<Derived>::self; + + /** Default op() for non-primitive types. */ + template <class T> Derived& operator()(T& t) { + + serializable(t).serialize(self()); return self(); + } + + /** Split serialize() into encode()/decode() */ + template <class T> Derived& split(T& t) { + t.decode(self()); return self(); + } +}; + +/** Serialize a type by converting it to/from another type. + * To serialize type Foo by converting to/from type Bar create + * a serializable() overload like this: + * + * SerializeAs<Foo,Bar> serializable(Foo& t) { return SerializeAs<Foo,Bar>(t); } + */ +template <class Type, class AsType> +struct SerializeAs { + Type& value; + SerializeAs(Type & t) : value(t) {} + template <class S> void serialize(S& s) { s.split(*this); } + template <class S> void encode(S& s) const { s(AsType(value)); } + template <class S> void decode(S& s) { AsType x; s(x); value=Type(x); } +}; + +} // namespace qpid + +#endif /*!QPID_SERIALIZER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/SessionId.cpp b/RC9/qpid/cpp/src/qpid/SessionId.cpp new file mode 100644 index 0000000000..fce6619f5d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/SessionId.cpp @@ -0,0 +1,47 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SessionId.h" +#include <sstream> + +namespace qpid { + +SessionId::SessionId(const std::string& u, const std::string& n) : userId(u), name(n) {} + +bool SessionId::operator<(const SessionId& id) const { + return userId < id.userId || (userId == id.userId && name < id.name); +} + +bool SessionId::operator==(const SessionId& id) const { + return id.name == name && id.userId == userId; +} + +std::ostream& operator<<(std::ostream& o, const SessionId& id) { + return o << id.getName() << "@" << id.getUserId(); +} + +std::string SessionId::str() const { + std::ostringstream o; + o << *this; + return o.str(); +} + +} // namespace qpid diff --git a/RC9/qpid/cpp/src/qpid/SessionId.h b/RC9/qpid/cpp/src/qpid/SessionId.h new file mode 100644 index 0000000000..291c42a2bb --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/SessionId.h @@ -0,0 +1,59 @@ +#ifndef QPID_SESSIONID_H +#define QPID_SESSIONID_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/operators.hpp> +#include <string> + +namespace qpid { + +/** Identifier for a session. + * There are two parts to a session identifier: + * + * getUserId() returns the authentication principal associated with + * the session's connection. + * + * getName() returns the session name. + * + * The name must be unique among sessions with the same authentication + * principal. + */ +class SessionId : boost::totally_ordered1<SessionId> { + std::string userId; + std::string name; + public: + SessionId(const std::string& userId=std::string(), const std::string& name=std::string()); + std::string getUserId() const { return userId; } + std::string getName() const { return name; } + bool operator<(const SessionId&) const ; + bool operator==(const SessionId& id) const; + // Convert to a string + std::string str() const; +}; + +std::ostream& operator<<(std::ostream&, const SessionId&); + + +} // namespace qpid + +#endif /*!QPID_SESSIONID_H*/ diff --git a/RC9/qpid/cpp/src/qpid/SessionState.cpp b/RC9/qpid/cpp/src/qpid/SessionState.cpp new file mode 100644 index 0000000000..ac75b5c5ff --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/SessionState.cpp @@ -0,0 +1,278 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SessionState.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/framing/enum.h" +#include "qpid/log/Statement.h" +#include <boost/bind.hpp> +#include <numeric> + +namespace qpid { +using framing::AMQFrame; +using framing::NotImplementedException; +using framing::InvalidArgumentException; +using framing::IllegalStateException; +using framing::ResourceLimitExceededException; +using framing::InternalErrorException; +using framing::FramingErrorException; + +namespace { +bool isControl(const AMQFrame& f) { + return f.getMethod() && f.getMethod()->type() == framing::SEGMENT_TYPE_CONTROL; +} +bool isCommand(const AMQFrame& f) { + return f.getMethod() && f.getMethod()->type() == framing::SEGMENT_TYPE_COMMAND; +} +} // namespace + +SessionPoint::SessionPoint(SequenceNumber c, uint64_t o) : command(c), offset(o) {} + +// TODO aconway 2008-05-22: Do complete frame sequence validity check here, +// currently duplicated betwen broker and client session impl. +// +void SessionPoint::advance(const AMQFrame& f) { + if (isControl(f)) return; // Ignore controls. + if (f.isFirstSegment() && f.isFirstFrame()) { + if (offset != 0) + throw FramingErrorException(QPID_MSG("Unexpected command start frame.")); + if (!isCommand(f)) + throw FramingErrorException( + QPID_MSG("Command start frame has invalid type" << f.getBody()->type())); + if (f.isLastSegment() && f.isLastFrame()) + ++command; // Single-frame command. + else + offset += f.encodedSize(); + } + else { // continuation frame for partial command + if (offset == 0) + throw FramingErrorException(QPID_MSG("Unexpected command continuation frame.")); + if (f.isLastSegment() && f.isLastFrame()) { + ++command; + offset = 0; + } + else { + // TODO aconway 2008-04-24: if we go to support for partial + // command replay, then it may be better to record the unframed + // data size in a command point rather than the framed size so + // that the relationship of fragment offsets to the replay + // list can be computed more easily. + // + offset += f.encodedSize(); + } + } +} + +bool SessionPoint::operator<(const SessionPoint& x) const { + return command < x.command || (command == x.command && offset < x.offset); +} + +bool SessionPoint::operator==(const SessionPoint& x) const { + return command == x.command && offset == x.offset; +} + + +SessionState::SendState::SendState() : unflushedSize(), replaySize(), bytesSinceKnownCompleted() {} + +SessionState::ReceiveState::ReceiveState() : bytesSinceKnownCompleted() {} + +SessionPoint SessionState::senderGetCommandPoint() { return sender.sendPoint; } +SequenceSet SessionState::senderGetIncomplete() const { return sender.incomplete; } +SessionPoint SessionState::senderGetReplayPoint() const { return sender.replayPoint; } + +SessionState::ReplayRange SessionState::senderExpected(const SessionPoint& expect) { + if (expect < sender.replayPoint || sender.sendPoint < expect) + throw InvalidArgumentException(QPID_MSG(getId() << ": expected command-point out of range.")); + QPID_LOG(debug, getId() << ": sender expected point moved to " << expect); + ReplayList::iterator i = sender.replayList.begin(); + SessionPoint p = sender.replayPoint; + while (i != sender.replayList.end() && p.command < expect.command) + p.advance(*i++); + assert(p.command == expect.command); + return boost::make_iterator_range(i, sender.replayList.end()); +} + +void SessionState::senderRecord(const AMQFrame& f) { + if (isControl(f)) return; // Ignore control frames. + QPID_LOG_IF(debug, f.getMethod(), getId() << ": sent cmd " << sender.sendPoint.command << ": " << *f.getMethod()); + stateful = true; + if (timeout) sender.replayList.push_back(f); + sender.unflushedSize += f.encodedSize(); + sender.bytesSinceKnownCompleted += f.encodedSize(); + sender.replaySize += f.encodedSize(); + sender.incomplete += sender.sendPoint.command; + sender.sendPoint.advance(f); + if (config.replayHardLimit && config.replayHardLimit < sender.replaySize) + throw ResourceLimitExceededException("Replay buffer exceeeded hard limit"); +} + +static const uint32_t SPONTANEOUS_REQUEST_INTERVAL = 65536; + +bool SessionState::senderNeedFlush() const { + return (sender.sendPoint.command % SPONTANEOUS_REQUEST_INTERVAL == 0) || + (config.replayFlushLimit && sender.unflushedSize >= config.replayFlushLimit); +} + +void SessionState::senderRecordFlush() { + sender.flushPoint = sender.sendPoint; + sender.unflushedSize = 0; +} + +bool SessionState::senderNeedKnownCompleted() const { + return config.replayFlushLimit && sender.bytesSinceKnownCompleted >= config.replayFlushLimit; +} + +void SessionState::senderRecordKnownCompleted() { + sender.bytesSinceKnownCompleted = 0; +} + +void SessionState::senderConfirmed(const SessionPoint& confirmed) { + if (confirmed > sender.sendPoint) + throw InvalidArgumentException(QPID_MSG(getId() << ": confirmed < " << confirmed << " but only sent < " << sender.sendPoint)); + QPID_LOG(debug, getId() << ": sender confirmed point moved to " << confirmed); + ReplayList::iterator i = sender.replayList.begin(); + while (i != sender.replayList.end() && sender.replayPoint.command < confirmed.command) { + sender.replayPoint.advance(*i); + assert(sender.replayPoint <= sender.sendPoint); + sender.replaySize -= i->encodedSize(); + if (sender.replayPoint > sender.flushPoint) + sender.unflushedSize -= i->encodedSize(); + ++i; + } + if (sender.replayPoint > sender.flushPoint) + sender.flushPoint = sender.replayPoint; + sender.replayList.erase(sender.replayList.begin(), i); + assert(sender.replayPoint.offset == 0); +} + +void SessionState::senderCompleted(const SequenceSet& commands) { + if (commands.empty()) return; + QPID_LOG(debug, getId() << ": sender marked completed: " << commands); + sender.incomplete -= commands; + // Completion implies confirmation but we don't handle out-of-order + // confirmation, so confirm up to the end of the first contiguous range of commands. + senderConfirmed(SessionPoint(commands.rangesBegin()->end())); +} + +void SessionState::receiverSetCommandPoint(const SessionPoint& point) { + if (hasState() && point > receiver.received) + throw InvalidArgumentException(QPID_MSG(getId() << ": Command-point out of range.")); + QPID_LOG(debug, getId() << ": receiver command-point set to: " << point); + receiver.expected = point; + if (receiver.expected > receiver.received) + receiver.received = receiver.expected; +} + +bool SessionState::receiverRecord(const AMQFrame& f) { + if (isControl(f)) return true; // Ignore control frames. + stateful = true; + receiver.expected.advance(f); + receiver.bytesSinceKnownCompleted += f.encodedSize(); + bool firstTime = receiver.expected > receiver.received; + if (firstTime) { + receiver.received = receiver.expected; + receiver.incomplete += receiverGetCurrent(); + } + QPID_LOG_IF(debug, f.getMethod(), getId() << ": recv cmd " << receiverGetCurrent() << ": " << *f.getMethod()); + QPID_LOG_IF(debug, !firstTime, "Ignoring duplicate frame: " << receiverGetCurrent() << ": " << f); + return firstTime; +} + +void SessionState::receiverCompleted(SequenceNumber command, bool cumulative) { + assert(receiver.incomplete.contains(command)); // Internal error to complete command twice. + SequenceNumber first =cumulative ? receiver.incomplete.front() : command; + SequenceNumber last = command; + receiver.unknownCompleted.add(first, last); + receiver.incomplete.remove(first, last); + QPID_LOG(debug, getId() << ": receiver marked completed: " << command + << " incomplete: " << receiver.incomplete + << " unknown-completed: " << receiver.unknownCompleted); +} + +void SessionState::receiverKnownCompleted(const SequenceSet& commands) { + if (!commands.empty() && commands.back() > receiver.received.command) + throw InvalidArgumentException(QPID_MSG(getId() << ": Known-completed has invalid commands.")); + receiver.bytesSinceKnownCompleted=0; + receiver.unknownCompleted -= commands; + QPID_LOG(debug, getId() << ": receiver known completed: " << commands << " unknown: " << receiver.unknownCompleted); +} + +bool SessionState::receiverNeedKnownCompleted() const { + return (receiver.expected.command % SPONTANEOUS_REQUEST_INTERVAL == 0) || + (config.replayFlushLimit && receiver.bytesSinceKnownCompleted >= config.replayFlushLimit); +} + +const SessionPoint& SessionState::receiverGetExpected() const { return receiver.expected; } +const SessionPoint& SessionState::receiverGetReceived() const { return receiver.received; } +const SequenceSet& SessionState::receiverGetUnknownComplete() const { return receiver.unknownCompleted; } +const SequenceSet& SessionState::receiverGetIncomplete() const { return receiver.incomplete; } + +SequenceNumber SessionState::receiverGetCurrent() const { + SequenceNumber current = receiver.expected.command; + if (receiver.expected.offset == 0) + --current; + return current; +} + +SessionState::Configuration::Configuration(size_t flush, size_t hard) : + replayFlushLimit(flush), replayHardLimit(hard) {} + +SessionState::SessionState(const SessionId& i, const Configuration& c) + : id(i), timeout(), config(c), stateful() +{ + QPID_LOG(debug, "SessionState::SessionState " << id << ": " << this); +} + +bool SessionState::hasState() const { return stateful; } + +SessionState::~SessionState() {} + +std::ostream& operator<<(std::ostream& o, const SessionPoint& p) { + return o << "(" << p.command.getValue() << "+" << p.offset << ")"; +} + +void SessionState::setState( + const SequenceNumber& replayStart, + const SequenceNumber& sendCommandPoint, + const SequenceSet& sentIncomplete, + const SequenceNumber& expected, + const SequenceNumber& received, + const SequenceSet& unknownCompleted, + const SequenceSet& receivedIncomplete +) +{ + sender.replayPoint = replayStart; + sender.flushPoint = sendCommandPoint; + sender.sendPoint = sendCommandPoint; + sender.unflushedSize = 0; + sender.replaySize = 0; // Replay list will be updated separately. + sender.incomplete = sentIncomplete; + sender.bytesSinceKnownCompleted = 0; + + receiver.expected = expected; + receiver.received = received; + receiver.unknownCompleted = unknownCompleted; + receiver.incomplete = receivedIncomplete; + receiver.bytesSinceKnownCompleted = 0; +} + +} // namespace qpid diff --git a/RC9/qpid/cpp/src/qpid/SessionState.h b/RC9/qpid/cpp/src/qpid/SessionState.h new file mode 100644 index 0000000000..bf4ff6d326 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/SessionState.h @@ -0,0 +1,219 @@ +#ifndef QPID_SESSIONSTATE_H +#define QPID_SESSIONSTATE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <qpid/SessionId.h> +#include <qpid/framing/SequenceNumber.h> +#include <qpid/framing/SequenceSet.h> +#include <qpid/framing/AMQFrame.h> +#include <qpid/framing/FrameHandler.h> +#include <boost/operators.hpp> +#include <boost/range/iterator_range.hpp> +#include <vector> +#include <iosfwd> + +namespace qpid { +using framing::SequenceNumber; +using framing::SequenceSet; + +/** A point in the session. Points to command id + offset */ +struct SessionPoint : boost::totally_ordered1<SessionPoint> { + SessionPoint(SequenceNumber command = 0, uint64_t offset = 0); + + SequenceNumber command; + uint64_t offset; + + /** Advance past frame f */ + void advance(const framing::AMQFrame& f); + + bool operator<(const SessionPoint&) const; + bool operator==(const SessionPoint&) const; +}; + +std::ostream& operator<<(std::ostream&, const SessionPoint&); + +/** + * Support for session idempotence barrier and resume as defined in + * AMQP 0-10. + * + * We only issue/use contiguous confirmations, out-of-order confirmation + * is ignored. Out of order completion is fully supported. + * + * Raises NotImplemented if the command point is set greater than the + * max currently received command data, either explicitly via + * session.command-point or implicitly via session.gap. + * + * Partial replay is not supported, replay always begins on a command + * boundary, and we never confirm partial commands. + * + * The SessionPoint data structure does store offsets so this class + * could be extended to support partial replay without + * source-incompatbile API changes. + */ +class SessionState { + typedef std::vector<framing::AMQFrame> ReplayList; + + public: + + typedef boost::iterator_range<ReplayList::iterator> ReplayRange; + + struct Configuration { + Configuration(size_t flush=1024*1024, size_t hard=0); + size_t replayFlushLimit; // Flush when the replay list >= N bytes. 0 disables. + size_t replayHardLimit; // Kill session if replay list > N bytes. 0 disables. + }; + + SessionState(const SessionId& =SessionId(), const Configuration& =Configuration()); + + virtual ~SessionState(); + + bool hasState() const; + + const SessionId& getId() const { return id; } + + uint32_t getTimeout() const { return timeout; } + void setTimeout(uint32_t seconds) { timeout = seconds; } + + bool operator==(const SessionId& other) const { return id == other; } + bool operator==(const SessionState& other) const { return id == other.id; } + + // ==== Functions for sender state. + + /** Record frame f for replay. Should not be called during replay. */ + virtual void senderRecord(const framing::AMQFrame& f); + + /** @return true if we should send flush for confirmed and completed commands. */ + virtual bool senderNeedFlush() const; + + /** Called when flush for confirmed and completed commands is sent to peer. */ + virtual void senderRecordFlush(); + + /** True if we should reply to the next incoming completed command */ + virtual bool senderNeedKnownCompleted() const; + + /** Called when knownCompleted is sent to peer. */ + virtual void senderRecordKnownCompleted(); + + /** Called when the peer confirms up to comfirmed. */ + virtual void senderConfirmed(const SessionPoint& confirmed); + + /** Called when the peer indicates commands completed */ + virtual void senderCompleted(const SequenceSet& commands); + + /** Point from which the next new (not replayed) data will be sent. */ + virtual SessionPoint senderGetCommandPoint(); + + /** Set of outstanding incomplete commands */ + virtual SequenceSet senderGetIncomplete() const; + + /** Point from which we can replay. */ + virtual SessionPoint senderGetReplayPoint() const; + + /** Peer expecting commands from this point. + *@return Range of frames to be replayed. + */ + virtual ReplayRange senderExpected(const SessionPoint& expected); + + // ==== Functions for receiver state + + /** Set the command point. */ + virtual void receiverSetCommandPoint(const SessionPoint& point); + + /** Returns true if frame should be be processed, false if it is a duplicate. */ + virtual bool receiverRecord(const framing::AMQFrame& f); + + /** Command completed locally */ + virtual void receiverCompleted(SequenceNumber command, bool cumulative=false); + + /** Peer has indicated commands are known completed */ + virtual void receiverKnownCompleted(const SequenceSet& commands); + + /** True if the next completed control should set the timely-reply argument + * to request a knonw-completed response. + */ + virtual bool receiverNeedKnownCompleted() const; + + /** Get the incoming command point */ + virtual const SessionPoint& receiverGetExpected() const; + + /** Get the received high-water-mark, may be > getExpected() during replay */ + virtual const SessionPoint& receiverGetReceived() const; + + /** Completed received commands that the peer may not know about. */ + virtual const SequenceSet& receiverGetUnknownComplete() const; + + /** Incomplete received commands. */ + virtual const SequenceSet& receiverGetIncomplete() const; + + /** ID of the command currently being handled. */ + virtual SequenceNumber receiverGetCurrent() const; + + /** Set the state variables, used to create a session that will resume + * from some previously established point. + */ + virtual void setState( + const SequenceNumber& replayStart, + const SequenceNumber& sendCommandPoint, + const SequenceSet& sentIncomplete, + const SequenceNumber& expected, + const SequenceNumber& received, + const SequenceSet& unknownCompleted, + const SequenceSet& receivedIncomplete + ); + + private: + + struct SendState { + SendState(); + // invariant: replayPoint <= flushPoint <= sendPoint + SessionPoint replayPoint; // Can replay from this point + SessionPoint flushPoint; // Point of last flush + SessionPoint sendPoint; // Send from this point + ReplayList replayList; // Starts from replayPoint. + size_t unflushedSize; // Un-flushed bytes in replay list. + size_t replaySize; // Total bytes in replay list. + SequenceSet incomplete; // Commands sent and not yet completed. + size_t bytesSinceKnownCompleted; // Bytes sent since we last issued a knownCompleted. + } sender; + + struct ReceiveState { + ReceiveState(); + SessionPoint expected; // Expected from here + SessionPoint received; // Received to here. Invariant: expected <= received. + SequenceSet unknownCompleted; // Received & completed, may not not known-complete by peer. + SequenceSet incomplete; // Incomplete received commands. + size_t bytesSinceKnownCompleted; // Bytes sent since we last issued a knownCompleted. + } receiver; + + SessionId id; + uint32_t timeout; + Configuration config; + bool stateful; +}; + +inline bool operator==(const SessionId& id, const SessionState& s) { return s == id; } + +} // namespace qpid + + +#endif /*!QPID_SESSIONSTATE_H*/ diff --git a/RC9/qpid/cpp/src/qpid/SharedObject.h b/RC9/qpid/cpp/src/qpid/SharedObject.h new file mode 100644 index 0000000000..852a036ab9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/SharedObject.h @@ -0,0 +1,55 @@ +#ifndef _SharedObject_ +#define _SharedObject_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/shared_ptr.hpp> +#include <boost/noncopyable.hpp> + +namespace qpid { + /** + * Template to enforce shared object conventions. + * Shared object classes should inherit : public qpid::SharedObject + * That ensures Foo: + * - has typedef boost::shared_ptr<T> shared_ptr + * - has virtual destructor + * - is boost::noncopyable (no default copy or assign) + * - has a protected default constructor. + * + * Shared objects should not have public constructors. + * Make constructors protected and provide public statc create() + * functions that return a shared_ptr. + */ + template <class T> + class SharedObject : private boost::noncopyable + { + public: + typedef boost::shared_ptr<T> shared_ptr; + + virtual ~SharedObject() {}; + + protected: + SharedObject() {} + }; +} + +#endif /*!_SharedObject_*/ diff --git a/RC9/qpid/cpp/src/qpid/StringUtils.cpp b/RC9/qpid/cpp/src/qpid/StringUtils.cpp new file mode 100644 index 0000000000..17eb141e12 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/StringUtils.cpp @@ -0,0 +1,50 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "StringUtils.h" + +namespace qpid { + +using std::string; +using std::vector; + +void split(vector<string>& out, const string& in, const string& delims) +{ + string::size_type start = in.find_first_not_of(delims); + if (start == string::npos) return; + + string::size_type end = in.find_first_of(delims, start); + while (end != string::npos) { + out.push_back(in.substr(start, end - start)); + start = in.find_first_not_of(delims, end); + if (start == string::npos) return; + end = in.find_first_of(delims, start); + } + out.push_back(in.substr(start)); +} + +vector<string> split(const string& in, const string& delims) +{ + vector<string> out; + split(out, in, delims); + return out; +} + +} // namespace qpid diff --git a/RC9/qpid/cpp/src/qpid/StringUtils.h b/RC9/qpid/cpp/src/qpid/StringUtils.h new file mode 100644 index 0000000000..3120e43334 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/StringUtils.h @@ -0,0 +1,43 @@ +#ifndef QPID_STRINGUTILS_H +#define QPID_STRINGUTILS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <string> +#include <vector> + +namespace qpid { + +/** + * Split 'in' into words using delimiters in 'delims' and put + * resulting strings into 'out' vector. + */ +void split(std::vector<std::string>& out, const std::string& in, const std::string& delims); +/** + * Split 'in' into words using delimiters in 'delims' and return the + * resulting strings in a vector. + */ +std::vector<std::string> split(const std::string& in, const std::string& delims); + +} // namespace qpid + +#endif /*!QPID_STRINGUTILS_H*/ diff --git a/RC9/qpid/cpp/src/qpid/Url.cpp b/RC9/qpid/cpp/src/qpid/Url.cpp new file mode 100644 index 0000000000..f831167dd8 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/Url.cpp @@ -0,0 +1,212 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/Url.h" +#include "qpid/Exception.h" +#include "qpid/Msg.h" +#include "qpid/sys/SystemInfo.h" +#include "qpid/sys/StrError.h" + +#include <boost/lexical_cast.hpp> + +#include <algorithm> + +#include <string.h> + +using namespace std; +using boost::lexical_cast; + +namespace qpid { + +Url::Invalid::Invalid(const string& s) : Exception(s) {} + +Url Url::getHostNameUrl(uint16_t port) { + TcpAddress address(std::string(), port); + if (!sys::SystemInfo::getLocalHostname(address)) + throw Url::Invalid(QPID_MSG("Cannot get host name: " << qpid::sys::strError(errno))); + return Url(address); +} + +Url Url::getIpAddressesUrl(uint16_t port) { + Url url; + sys::SystemInfo::getLocalIpAddresses(port, url); + return url; +} + +string Url::str() const { + if (cache.empty() && !this->empty()) { + ostringstream os; + os << *this; + cache = os.str(); + } + return cache; +} + +ostream& operator<<(ostream& os, const Url& url) { + Url::const_iterator i = url.begin(); + os << "amqp:"; + if (i!=url.end()) { + os << *i++; + while (i != url.end()) + os << "," << *i++; + } + return os; +} + + +/** Simple recursive-descent parser for url grammar in AMQP 0-10 spec: + + amqp_url = "amqp:" prot_addr_list + prot_addr_list = [prot_addr ","]* prot_addr + prot_addr = tcp_prot_addr | tls_prot_addr + + tcp_prot_addr = tcp_id tcp_addr + tcp_id = "tcp:" | "" + tcp_addr = [host [":" port] ] + host = <as per http://www.ietf.org/rfc/rfc3986.txt> + port = number]]> +*/ +class UrlParser { + public: + UrlParser(Url& u, const char* s) : url(u), text(s), end(s+strlen(s)), i(s) {} + bool parse() { return literal("amqp:") && list(&UrlParser::protAddr, &UrlParser::comma) && i == end; } + + private: + typedef bool (UrlParser::*Rule)(); + + bool comma() { return literal(","); } + + // NOTE: tcpAddr must be last since it is allowed to omit it's tcp: tag. + bool protAddr() { return exampleAddr() || tcpAddr(); } + + bool tcpAddr() { + TcpAddress addr; + literal("tcp:"); // Don't check result, allowed to be absent. + return addIf(host(addr.host) && (literal(":") ? port(addr.port) : true), addr); + } + + // Placeholder address type till we have multiple address types. Address is a single char. + bool exampleAddr () { + if (literal("example:") && i < end) { + ExampleAddress ex(*i++); + url.push_back(ex); + return true; + } + return false; + } + + // FIXME aconway 2008-11-20: this does not implement http://www.ietf.org/rfc/rfc3986.txt. + // Works for DNS names and ipv4 literals but won't handle ipv6. + bool host(string& h) { + const char* start=i; + while (unreserved() || pctEncoded()) + ; + if (start == i) h = LOCALHOST; // Default + else h.assign(start, i); + return true; + } + + bool unreserved() { return (::isalnum(*i) || ::strchr("-._~", *i)) && advance(); } + + bool pctEncoded() { return literal("%") && hexDigit() && hexDigit(); } + + bool hexDigit() { return i < end && ::strchr("01234567890abcdefABCDEF", *i) && advance(); } + + bool port(uint16_t& p) { return decimalInt(p); } + + template <class AddrType> bool addIf(bool ok, const AddrType& addr) { if (ok) url.push_back(addr); return ok; } + + template <class IntType> bool decimalInt(IntType& n) { + const char* start = i; + while (decDigit()) + ; + try { + n = lexical_cast<IntType>(string(start, i)); + return true; + } catch(...) { return false; } + } + + bool decDigit() { return i < end && ::isdigit(*i) && advance(); } + + bool literal(const char* s) { + int n = ::strlen(s); + if (n <= end-i && equal(s, s+n, i)) return advance(n); + return false; + }; + + bool noop() { return true; } + + /** List of item, separated by separator, with min & max bounds. */ + bool list(Rule item, Rule separator, size_t min=0, size_t max=UNLIMITED) { + assert(max > 0); + assert(max >= min); + if (!(this->*item)()) return min == 0; // Empty list. + size_t n = 1; + while (n < max && i < end) { + if (!(this->*separator)()) break; + if (i == end || !(this->*item)()) return false; // Separator with no item. + ++n; + } + return n >= min; + } + + /** List of items with no separator */ + bool list(Rule item, size_t min=0, size_t max=UNLIMITED) { return list(item, &UrlParser::noop, min, max); } + + bool advance(size_t n=1) { + if (i+n > end) return false; + i += n; + return true; + } + + static const size_t UNLIMITED = size_t(~1); + static const std::string LOCALHOST; + + Url& url; + const char* text; + const char* end; + const char* i; +}; + +const string UrlParser::LOCALHOST("127.0.0.1"); + +void Url::parse(const char* url) { + parseNoThrow(url); + if (empty()) + throw Url::Invalid(QPID_MSG("Invalid URL: " << url)); +} + +void Url::parseNoThrow(const char* url) { + cache.clear(); + if (!UrlParser(*this, url).parse()) + clear(); +} + +void Url::throwIfEmpty() const { + if (empty()) + throw Url::Invalid("URL contains no addresses"); +} + +std::istream& operator>>(std::istream& is, Url& url) { + std::string s; + is >> s; + url.parse(s); + return is; +} + +} // namespace qpid diff --git a/RC9/qpid/cpp/src/qpid/Url.h b/RC9/qpid/cpp/src/qpid/Url.h new file mode 100644 index 0000000000..07ca46e70c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/Url.h @@ -0,0 +1,92 @@ +#ifndef QPID_URL_H +#define QPID_URL_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/Address.h" +#include "qpid/Exception.h" +#include <string> +#include <vector> +#include <new> +#include <ostream> + +namespace qpid { + +std::ostream& operator<<(std::ostream& os, const TcpAddress& a); + +/** An AMQP URL contains a list of addresses */ +struct Url : public std::vector<Address> { + + /** Url with the hostname as returned by gethostname(2) */ + static Url getHostNameUrl(uint16_t port); + + /** Url with local IP address(es), may be more than one address + * on a multi-homed host. */ + static Url getIpAddressesUrl(uint16_t port); + + struct Invalid : public Exception { Invalid(const std::string& s); }; + + /** Convert to string form. */ + std::string str() const; + + /** Empty URL. */ + Url() {} + + /** URL containing a single address */ + explicit Url(const Address& addr) { push_back(addr); } + + /** Parse url, throw Invalid if invalid. */ + explicit Url(const std::string& url) { parse(url.c_str()); } + + /** Parse url, throw Invalid if invalid. */ + explicit Url(const char* url) { parse(url); } + + Url& operator=(const Url& u) { this->std::vector<Address>::operator=(u); cache=u.cache; return *this; } + Url& operator=(const char* s) { parse(s); return *this; } + Url& operator=(const std::string& s) { parse(s); return *this; } + + /** Throw Invalid if the URL does not contain any addresses. */ + void throwIfEmpty() const; + + /** Replace contents with parsed URL as defined in + * https://wiki.108.redhat.com/jira/browse/AMQP-95 + *@exception Invalid if the url is invalid. + */ + void parse(const char* url); + void parse(const std::string& url) { parse(url.c_str()); } + + /** Replace contesnts with parsed URL as defined in + * https://wiki.108.redhat.com/jira/browse/AMQP-95 + * url.empty() will be true if url is invalid. + */ + void parseNoThrow(const char* url); + + private: + mutable std::string cache; // cache string form for efficiency. +}; + +inline bool operator==(const Url& a, const Url& b) { return a.str()==b.str(); } +inline bool operator!=(const Url& a, const Url& b) { return a.str()!=b.str(); } + +std::ostream& operator<<(std::ostream& os, const Url& url); +std::istream& operator>>(std::istream& is, Url& url); + +} // namespace qpid + +#endif /*!QPID_URL_H*/ diff --git a/RC9/qpid/cpp/src/qpid/Version.h b/RC9/qpid/cpp/src/qpid/Version.h new file mode 100755 index 0000000000..9bd561b7a9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/Version.h @@ -0,0 +1,44 @@ +#ifndef QPID_VERSION_H +#define QPID_VERSION_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <string> + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +namespace qpid { +#ifdef HAVE_CONFIG_H + const std::string product = PACKAGE_NAME; + const std::string version = PACKAGE_VERSION; +# if HAVE_SASL + const std::string saslName = BROKER_SASL_NAME; +# else + const std::string saslName = "qpidd-no-sasl"; +# endif +#else + const std::string product = "qpidc"; + const std::string version = "0.3"; + const std::string saslName = "qpid-broker"; +#endif +} + +#endif /*!QPID_VERSION_H*/ diff --git a/RC9/qpid/cpp/src/qpid/acl/Acl.cpp b/RC9/qpid/cpp/src/qpid/acl/Acl.cpp new file mode 100644 index 0000000000..238ab9df6c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/acl/Acl.cpp @@ -0,0 +1,164 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/acl/Acl.h" +#include "qpid/acl/AclData.h" + +#include "qpid/broker/Broker.h" +#include "qpid/Plugin.h" +#include "qpid/Options.h" +#include "qpid/shared_ptr.h" +#include "qpid/log/Logger.h" +#include "qmf/org/apache/qpid/acl/Package.h" +#include "qmf/org/apache/qpid/acl/EventAllow.h" +#include "qmf/org/apache/qpid/acl/EventDeny.h" +#include "qmf/org/apache/qpid/acl/EventFileLoaded.h" +#include "qmf/org/apache/qpid/acl/EventFileLoadFailed.h" + +#include <map> + +#include <boost/utility/in_place_factory.hpp> + +using namespace std; +using namespace qpid::acl; +using qpid::management::ManagementAgent; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; +namespace _qmf = qmf::org::apache::qpid::acl; + +Acl::Acl (AclValues& av, broker::Broker& b): aclValues(av), broker(&b), transferAcl(false) +{ + + agent = ManagementAgent::Singleton::getInstance(); + + if (agent != 0){ + _qmf::Package packageInit(agent); + mgmtObject = new _qmf::Acl (agent, this, broker); + agent->addObject (mgmtObject); + } + + std::string errorString; + if (!readAclFile(errorString)){ + throw Exception("Could not read ACL file " + errorString); + if (mgmtObject!=0) mgmtObject->set_enforcingAcl(0); + } + QPID_LOG(info, "ACL Plugin loaded"); + if (mgmtObject!=0) mgmtObject->set_enforcingAcl(1); +} + + bool Acl::authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& name, std::map<Property, std::string>* params) + { + boost::shared_ptr<AclData> dataLocal = data; //rcu copy + + // add real ACL check here... + AclResult aclreslt = dataLocal->lookup(id,action,objType,name,params); + + + return result(aclreslt, id, action, objType, name); + } + + bool Acl::authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& ExchangeName, const std::string& RoutingKey) + { + boost::shared_ptr<AclData> dataLocal = data; //rcu copy + + // only use dataLocal here... + AclResult aclreslt = dataLocal->lookup(id,action,objType,ExchangeName,RoutingKey); + + return result(aclreslt, id, action, objType, ExchangeName); + } + + + bool Acl::result(const AclResult& aclreslt, const std::string& id, const Action& action, const ObjectType& objType, const std::string& name) + { + switch (aclreslt) + { + case ALLOWLOG: + QPID_LOG(info, "ACL Allow id:" << id <<" action:" << AclHelper::getActionStr(action) << + " ObjectType:" << AclHelper::getObjectTypeStr(objType) << " Name:" << name ); + agent->raiseEvent(_qmf::EventAllow(id, AclHelper::getActionStr(action), + AclHelper::getObjectTypeStr(objType), + name, framing::FieldTable())); + case ALLOW: + return true; + case DENY: + if (mgmtObject!=0) mgmtObject->inc_aclDenyCount(); + return false; + case DENYLOG: + if (mgmtObject!=0) mgmtObject->inc_aclDenyCount(); + default: + QPID_LOG(info, "ACL Deny id:" << id << " action:" << AclHelper::getActionStr(action) << " ObjectType:" << AclHelper::getObjectTypeStr(objType) << " Name:" << name); + agent->raiseEvent(_qmf::EventDeny(id, AclHelper::getActionStr(action), + AclHelper::getObjectTypeStr(objType), + name, framing::FieldTable())); + return false; + } + return false; + } + + bool Acl::readAclFile(std::string& errorText) + { + // only set transferAcl = true if a rule implies the use of ACL on transfer, else keep false for permormance reasons. + return readAclFile(aclValues.aclFile, errorText); + } + + bool Acl::readAclFile(std::string& aclFile, std::string& errorText) { + boost::shared_ptr<AclData> d(new AclData); + AclReader ar; + if (ar.read(aclFile, d)){ + agent->raiseEvent(_qmf::EventFileLoadFailed("", ar.getError())); + errorText = ar.getError(); + QPID_LOG(error,ar.getError()); + return false; + } + + data = d; + transferAcl = data->transferAcl; // any transfer ACL + if (mgmtObject!=0){ + mgmtObject->set_transferAcl(transferAcl?1:0); + mgmtObject->set_policyFile(aclFile); + sys::AbsTime now = sys::AbsTime::now(); + int64_t ns = sys::Duration(now); + mgmtObject->set_lastAclLoad(ns); + agent->raiseEvent(_qmf::EventFileLoaded("")); + } + return true; + } + + Acl::~Acl(){} + + ManagementObject* Acl::GetManagementObject(void) const + { + return (ManagementObject*) mgmtObject; + } + + Manageable::status_t Acl::ManagementMethod (uint32_t methodId, Args& /*args*/, string& text) + { + Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; + QPID_LOG (debug, "Queue::ManagementMethod [id=" << methodId << "]"); + + switch (methodId) + { + case _qmf::Acl::METHOD_RELOADACLFILE : + readAclFile(text); + status = Manageable::STATUS_USER; + break; + } + + return status; +} diff --git a/RC9/qpid/cpp/src/qpid/acl/Acl.h b/RC9/qpid/cpp/src/qpid/acl/Acl.h new file mode 100644 index 0000000000..2a522bc56d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/acl/Acl.h @@ -0,0 +1,85 @@ +#ifndef QPID_ACL_ACL_H +#define QPID_ACL_ACL_H + + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + + +#include "qpid/acl/AclReader.h" +#include "qpid/shared_ptr.h" +#include "qpid/RefCounted.h" +#include "qpid/broker/AclModule.h" +#include "qpid/management/Manageable.h" +#include "qpid/agent/ManagementAgent.h" +#include "qmf/org/apache/qpid/acl/Acl.h" + +#include <map> +#include <string> + + +namespace qpid { +namespace broker { +class Broker; +} + +namespace acl { + +struct AclValues { + std::string aclFile; +}; + + +class Acl : public broker::AclModule, public RefCounted, public management::Manageable +{ + +private: + acl::AclValues aclValues; + broker::Broker* broker; + bool transferAcl; + boost::shared_ptr<AclData> data; + qmf::org::apache::qpid::acl::Acl* mgmtObject; // mgnt owns lifecycle + qpid::management::ManagementAgent* agent; + +public: + Acl (AclValues& av, broker::Broker& b); + + void initialize(); + + inline virtual bool doTransferAcl() {return transferAcl;}; + + // create specilied authorise methods for cases that need faster matching as needed. + virtual bool authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& name, std::map<Property, std::string>* params=0); + virtual bool authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& ExchangeName,const std::string& RoutingKey); + + virtual ~Acl(); +private: + bool result(const AclResult& aclreslt, const std::string& id, const Action& action, const ObjectType& objType, const std::string& name); + bool readAclFile(std::string& errorText); + bool readAclFile(std::string& aclFile, std::string& errorText); + virtual qpid::management::ManagementObject* GetManagementObject(void) const; + virtual management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); + +}; + + + +}} // namespace qpid::acl + +#endif // QPID_ACL_ACL_H diff --git a/RC9/qpid/cpp/src/qpid/acl/AclData.cpp b/RC9/qpid/cpp/src/qpid/acl/AclData.cpp new file mode 100644 index 0000000000..d2a55c0027 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/acl/AclData.cpp @@ -0,0 +1,162 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/acl/AclData.h" +#include "qpid/log/Statement.h" + + +namespace qpid { +namespace acl { + +AclData::AclData():decisionMode(qpid::acl::DENY),transferAcl(false) +{ + for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++){ + actionList[cnt]=0; + } + +} + +void AclData::clear () +{ + for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++){ + if (actionList[cnt]){ + for (unsigned int cnt1=0; cnt1< qpid::acl::OBJECTSIZE; cnt1++) + delete actionList[cnt][cnt1]; + } + delete[] actionList[cnt]; + } + +} + +bool AclData::matchProp(const std::string & src, const std::string& src1) +{ + // allow wildcard on the end of strings... + if (src.data()[src.size()-1]=='*') { + return (src.compare(0, src.size()-1, src1, 0,src.size()-1 ) == 0); + } else { + return (src.compare(src1)==0) ; + } +} + +AclResult AclData::lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& name, std::map<Property, std::string>* params) +{ + AclResult aclresult = decisionMode; + + if (actionList[action] && actionList[action][objType]){ + AclData::actObjItr itrRule = actionList[action][objType]->find(id); + if (itrRule == actionList[action][objType]->end()) + itrRule = actionList[action][objType]->find("*"); + if (itrRule != actionList[action][objType]->end() ) { + + //loop the vector + for (ruleSetItr i=itrRule->second.begin(); i<itrRule->second.end(); i++) { + + // loop the names looking for match + bool match =true; + for (propertyMapItr pMItr = i->props.begin(); (pMItr != i->props.end()) && match; pMItr++) + { + //match name is exists first + if (pMItr->first == acl::PROP_NAME){ + if (!matchProp(pMItr->second, name)){ + match= false; + } + }else if (params){ //match pMItr against params + propertyMapItr paramItr = params->find (pMItr->first); + if (paramItr == params->end()){ + match = false; + }else if (!matchProp(paramItr->second, pMItr->second)){ + match = false; + } + } + } + if (match) return getACLResult(i->logOnly, i->log); + } + } + } + return aclresult; +} + +AclResult AclData::lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& /*Exchange*/ name, const std::string& RoutingKey) +{ + AclResult aclresult = decisionMode; + + if (actionList[action] && actionList[action][objType]){ + AclData::actObjItr itrRule = actionList[action][objType]->find(id); + if (itrRule == actionList[action][objType]->end()) + itrRule = actionList[action][objType]->find("*"); + if (itrRule != actionList[action][objType]->end() ) { + + //loop the vector + for (ruleSetItr i=itrRule->second.begin(); i<itrRule->second.end(); i++) { + + // loop the names looking for match + bool match =true; + for (propertyMapItr pMItr = i->props.begin(); (pMItr != i->props.end()) && match; pMItr++) + { + //match name is exists first + if (pMItr->first == acl::PROP_NAME){ + if (!matchProp(pMItr->second, name)){ + match= false; + } + }else if (pMItr->first == acl::PROP_ROUTINGKEY){ + if (!matchProp(pMItr->second, RoutingKey)){ + match= false; + } + } + } + if (match) return getACLResult(i->logOnly, i->log); + } + } + } + return aclresult; + +} + + +AclResult AclData::getACLResult(bool logOnly, bool log) +{ + switch (decisionMode) + { + case qpid::acl::ALLOWLOG: + case qpid::acl::ALLOW: + if (logOnly) return qpid::acl::ALLOWLOG; + if (log) + return qpid::acl::DENYLOG; + else + return qpid::acl::DENY; + + + case qpid::acl::DENYLOG: + case qpid::acl::DENY: + if (logOnly) return qpid::acl::DENYLOG; + if (log) + return qpid::acl::ALLOWLOG; + else + return qpid::acl::ALLOW; + } + + QPID_LOG(error, "ACL Decision Failed, setting DENY"); + return qpid::acl::DENY; +} + +AclData::~AclData() +{ + clear(); +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/acl/AclData.h b/RC9/qpid/cpp/src/qpid/acl/AclData.h new file mode 100644 index 0000000000..249c3523eb --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/acl/AclData.h @@ -0,0 +1,73 @@ +#ifndef QPID_ACL_ACLDATA_H +#define QPID_ACL_ACLDATA_H + + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/broker/AclModule.h" +#include <vector> + + +namespace qpid { +namespace acl { + +class AclData { + + +public: + + typedef std::map<qpid::acl::Property, std::string> propertyMap; + typedef propertyMap::const_iterator propertyMapItr; + struct rule { + + bool log; + bool logOnly; // this is a rule is to log only + + // key value map + //?? + propertyMap props; + + + rule (propertyMap& p):log(false),logOnly(false),props(p) {}; + }; + typedef std::vector<rule> ruleSet; + typedef ruleSet::const_iterator ruleSetItr; + typedef std::map<std::string, ruleSet > actionObject; // user + typedef actionObject::iterator actObjItr; + typedef actionObject* aclAction; + + // Action*[] -> Object*[] -> map<user -> set<Rule> > + aclAction* actionList[qpid::acl::ACTIONSIZE]; + qpid::acl::AclResult decisionMode; // determines if the rule set is an deny or accept basis. + bool transferAcl; + + AclResult lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& name, std::map<Property, std::string>* params=0); + AclResult lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& ExchangeName, const std::string& RoutingKey); + AclResult getACLResult(bool logOnly, bool log); + + bool matchProp(const std::string & src, const std::string& src1); + void clear (); + + AclData(); + virtual ~AclData(); +}; + +}} // namespace qpid::acl + +#endif // QPID_ACL_ACLDATA_H diff --git a/RC9/qpid/cpp/src/qpid/acl/AclPlugin.cpp b/RC9/qpid/cpp/src/qpid/acl/AclPlugin.cpp new file mode 100644 index 0000000000..7310139041 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/acl/AclPlugin.cpp @@ -0,0 +1,101 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <sstream> +#include "qpid/acl/Acl.h" +#include "qpid/broker/Broker.h" +#include "qpid/Plugin.h" +#include "qpid/Options.h" +#include "qpid/shared_ptr.h" +#include "qpid/log/Statement.h" + +#include <boost/utility/in_place_factory.hpp> + +namespace qpid { +namespace acl { + +using namespace std; + +/** Note separating options from values to work around boost version differences. + * Old boost takes a reference to options objects, but new boost makes a copy. + * New boost allows a shared_ptr but that's not compatible with old boost. + */ +struct AclOptions : public Options { + AclValues& values; + + AclOptions(AclValues& v) : Options("ACL Options"), values(v) { + addOptions() + ("acl-file", optValue(values.aclFile, "FILE"), "The policy file to load from, loaded from data dir"); + } +}; + +struct AclPlugin : public Plugin { + + AclValues values; + AclOptions options; + boost::intrusive_ptr<Acl> acl; + + AclPlugin() : options(values) {} + + Options* getOptions() { return &options; } + + void init(broker::Broker& b) { + if (values.aclFile.empty()){ + QPID_LOG(info, "Policy file not specified. ACL Disabled, no ACL checking being done!"); + return; + } + + if (acl) throw Exception("ACL plugin cannot be initialized twice in one process."); + + if (values.aclFile.at(0) == '/') + { + values.aclFile = values.aclFile; + } + else + { + std::ostringstream oss; + oss << b.getDataDir().getPath() << "/" << values.aclFile; + values.aclFile = oss.str(); + } + + acl = new Acl(values, b); + b.setAcl(acl.get()); + b.addFinalizer(boost::bind(&AclPlugin::shutdown, this)); + } + + template <class T> bool init(Plugin::Target& target) { + T* t = dynamic_cast<T*>(&target); + if (t) init(*t); + return t; + } + + void earlyInitialize(Plugin::Target&) {} + + void initialize(Plugin::Target& target) { + init<broker::Broker>(target); + } + + void shutdown() { acl = 0; } +}; + +static AclPlugin instance; // Static initialization. + +// For test purposes. +boost::intrusive_ptr<Acl> getGlobalAcl() { return instance.acl; } + +}} // namespace qpid::acl diff --git a/RC9/qpid/cpp/src/qpid/acl/AclReader.cpp b/RC9/qpid/cpp/src/qpid/acl/AclReader.cpp new file mode 100644 index 0000000000..c407339390 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/acl/AclReader.cpp @@ -0,0 +1,511 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/acl/AclReader.h" + +#include <cctype> +#include <cstring> +#include <fstream> +#include <sstream> +#include "qpid/log/Statement.h" +#include "qpid/Exception.h" + +#include <iomanip> // degug +#include <iostream> // debug + +#define ACL_FORMAT_ERR_LOG_PREFIX "ACL format error: " << fileName << ":" << lineNumber << ": " + +namespace qpid { +namespace acl { + +AclReader::aclRule::aclRule(const AclResult r, const std::string n, const groupMap& groups) : res(r), actionAll(true), objStatus(NONE) { + processName(n, groups); +} +AclReader::aclRule::aclRule(const AclResult r, const std::string n, const groupMap& groups, const Action a) : res(r), actionAll(false), action(a), objStatus(NONE) { + processName(n, groups); +} + +void AclReader::aclRule::setObjectType(const ObjectType o) { + objStatus = VALUE; + object = o; +} + +void AclReader::aclRule::setObjectTypeAll() { + objStatus = ALL; +} + +bool AclReader::aclRule::addProperty(const Property p, const std::string v) { + return props.insert(propNvPair(p, v)).second; +} + +bool AclReader::aclRule::validate(const AclHelper::objectMapPtr& /*validationMap*/) { + // TODO - invalid rules won't ever be called in real life... + return true; +} + +// Debug aid +std::string AclReader::aclRule::toString() { + std::ostringstream oss; + oss << AclHelper::getAclResultStr(res) << " ["; + for (nsCitr itr = names.begin(); itr != names.end(); itr++) { + if (itr != names.begin()) oss << ", "; + oss << *itr; + } + oss << "]"; + if (actionAll) { + oss << " *"; + } else { + oss << " " << AclHelper::getActionStr(action); + } + if (objStatus == ALL) { + oss << " *"; + } else if (objStatus == VALUE) { + oss << " " << AclHelper::getObjectTypeStr(object); + } + for (pmCitr i=props.begin(); i!=props.end(); i++) { + oss << " " << AclHelper::getPropertyStr(i->first) << "=" << i->second; + } + return oss.str(); +} + +void AclReader::loadDecisionData( boost::shared_ptr<AclData> d ) +{ + d->clear(); + QPID_LOG(debug, "ACL Load Rules"); + int cnt = rules.size(); + bool foundmode = false; + for (rlCitr i=rules.end()-1; cnt; i--,cnt--) { + QPID_LOG(debug, "ACL Processing " << std::setfill(' ') << std::setw(2) << cnt << " " << (*i)->toString()); + + if (!foundmode && (*i)->actionAll && (*i)->names.size()==1 && (*((*i)->names.begin())).compare("*")==0 ){ + d->decisionMode = (*i)->res; + QPID_LOG(debug, "ACL FoundMode " << AclHelper::getAclResultStr(d->decisionMode)); + foundmode=true; + }else{ + AclData::rule rule((*i)->props); + bool addrule= true; + + switch ((*i)->res) + { + case qpid::acl::ALLOWLOG: + rule.log = true; + if (d->decisionMode == qpid::acl::ALLOW || d->decisionMode == qpid::acl::ALLOWLOG) + rule.logOnly = true; + break; + case qpid::acl::ALLOW: + if (d->decisionMode == qpid::acl::ALLOW || d->decisionMode == qpid::acl::ALLOWLOG) + addrule = false; + break; + case qpid::acl::DENYLOG: + rule.log = true; + if (d->decisionMode == qpid::acl::DENY || d->decisionMode == qpid::acl::DENYLOG) + rule.logOnly = true; + break; + case qpid::acl::DENY: + if (d->decisionMode == qpid::acl::DENY || d->decisionMode == qpid::acl::DENYLOG) + addrule = false; + break; + default: + throw Exception("Invalid ACL Result loading rules."); + } + + + // Action -> Object -> map<user -> set<Rule> > + if (addrule){ + for (int acnt= ((*i)->actionAll?0:(*i)->action); + acnt< acl::ACTIONSIZE; (*i)->actionAll?acnt++:acnt=acl::ACTIONSIZE ) { + + if (acnt == acl::ACT_PUBLISH) d->transferAcl = true; // we have transfer ACL + + QPID_LOG(debug, "ACL Adding action:" << AclHelper::getActionStr((Action)acnt) ); + + //find the Action, create if not exist + if (d->actionList[acnt]==NULL) { + d->actionList[acnt] = new AclData::aclAction[qpid::acl::OBJECTSIZE]; + for (int j=0;j<qpid::acl::OBJECTSIZE; j++) + d->actionList[acnt][j] = NULL; + } + + // optimize this loop to limit to valid options only!! + for (int ocnt= ((*i)->objStatus!=aclRule::VALUE ?0:(*i)->object); + ocnt< acl::OBJECTSIZE; + (*i)->objStatus!=aclRule::VALUE?ocnt++:ocnt=acl::OBJECTSIZE ) { + + QPID_LOG(debug, "ACL Adding object:" << AclHelper::getObjectTypeStr((ObjectType)ocnt) ); + + //find the Object, create if not exist + if (d->actionList[acnt][ocnt] == NULL) + d->actionList[acnt][ocnt] = new AclData::actionObject; + + // add users and Rule to object set + bool allNames=false; + // check to see if names.begin is '*' + if ( (*(*i)->names.begin()).compare("*")==0 ) allNames = true; + + for (nsCitr itr = (allNames?names.begin():(*i)->names.begin()); + itr != (allNames?names.end():(*i)->names.end()); itr++) { + AclData::actObjItr itrRule = d->actionList[acnt][ocnt]->find(*itr); + if (itrRule == d->actionList[acnt][ocnt]->end()) { + QPID_LOG(debug, "ACL Adding rule & user:" << *itr); + AclData::ruleSet rSet; + rSet.push_back(rule); + d->actionList[acnt][ocnt]->insert(make_pair( std::string(*itr) , rSet) ); + }else{ + + // TODO add code to check for dead rules + // allow peter create queue name=tmp <-- dead rule!! + // allow peter create queue + + itrRule->second.push_back(rule); + QPID_LOG(debug, "ACL Adding rule to user:" << *itr); + } + } + + } + + } + }else{ + QPID_LOG(debug, "ACL Skipping based on Mode:" << AclHelper::getAclResultStr(d->decisionMode) ); + } + } + + } + + +} + + + + +void AclReader::aclRule::processName(const std::string& name, const groupMap& groups) { + if (name.compare("all") == 0) { + names.insert("*"); + } else { + gmCitr itr = groups.find(name); + if (itr == groups.end()) { + names.insert(name); + } else { + names.insert(itr->second->begin(), itr->second->end()); + } + } +} + +AclReader::AclReader() : lineNumber(0), contFlag(false), validationMap(new AclHelper::objectMap) { + AclHelper::loadValidationMap(validationMap); + names.insert("*"); +} + +AclReader::~AclReader() {} + +std::string AclReader::getError() { + return errorStream.str(); +} + +int AclReader::read(const std::string& fn, boost::shared_ptr<AclData> d) { + fileName = fn; + lineNumber = 0; + char buff[1024]; + std::ifstream ifs(fn.c_str(), std::ios_base::in); + if (!ifs.good()) { + errorStream << "Unable to open ACL file \"" << fn << "\": eof=" << (ifs.eof()?"T":"F") << "; fail=" << (ifs.fail()?"T":"F") << "; bad=" << (ifs.bad()?"T":"F"); + return -1; + } + try { + bool err = false; + while (ifs.good()) { + ifs.getline(buff, 1024); + lineNumber++; + if (std::strlen(buff) > 0 && buff[0] != '#') // Ignore blank lines and comments + err |= !processLine(buff); + } + if (!ifs.eof()) + { + errorStream << "Unable to read ACL file \"" << fn << "\": eof=" << (ifs.eof()?"T":"F") << "; fail=" << (ifs.fail()?"T":"F") << "; bad=" << (ifs.bad()?"T":"F"); + ifs.close(); + return -2; + } + ifs.close(); + if (err) return -3; + QPID_LOG(notice, "Read ACL file \"" << fn << "\""); + } catch (const std::exception& e) { + errorStream << "Unable to read ACL file \"" << fn << "\": " << e.what(); + ifs.close(); + return -4; + } catch (...) { + errorStream << "Unable to read ACL file \"" << fn << "\": Unknown exception"; + ifs.close(); + return -5; + } + printNames(); + printRules(); + loadDecisionData(d); + + return 0; +} + +bool AclReader::processLine(char* line) { + bool ret = false; + std::vector<std::string> toks; + + // Check for continuation + char* contCharPtr = std::strrchr(line, '\\'); + bool cont = contCharPtr != 0; + if (cont) *contCharPtr = 0; + + int numToks = tokenize(line, toks); + if (numToks && (toks[0].compare("group") == 0 || contFlag)) { + ret = processGroupLine(toks, cont); + } else if (numToks && toks[0].compare("acl") == 0) { + ret = processAclLine(toks); + } else { + // Check for whitespace only line, ignore these + bool ws = true; + for (unsigned i=0; i<std::strlen(line) && ws; i++) { + if (!std::isspace(line[i])) ws = false; + } + if (ws) { + ret = true; + } else { + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Non-continuation line must start with \"group\" or \"acl\"."; + ret = false; + } + } + contFlag = cont; + return ret; +} + +int AclReader::tokenize(char* line, std::vector<std::string>& toks) { + const char* tokChars = " \t\n\f\v\r"; + int cnt = 0; + char* cp = std::strtok(line, tokChars); + while (cp != 0) { + toks.push_back(std::string(cp)); + cnt++; + cp = std::strtok(0, tokChars); + } + return cnt; +} + +// Return true if the line is successfully processed without errors +// If cont is true, then groupName must be set to the continuation group name +bool AclReader::processGroupLine(tokList& toks, const bool cont) { + const unsigned toksSize = toks.size(); + if (contFlag) { + gmCitr citr = groups.find(groupName); + for (unsigned i = 0; i < toksSize; i++) { + if (!checkName(toks[i])) { + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Name \"" << toks[i] << "\" contains illegal characters."; + return false; + } + addName(toks[i], citr->second); + } + } else { + if (toksSize < (cont ? 2 : 3)) { + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Insufficient tokens for group definition."; + return false; + } + if (!checkName(toks[1])) { + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Group name \"" << toks[1] << "\" contains illegal characters."; + return false; + } + gmCitr citr = addGroup(toks[1]); + if (citr == groups.end()) return false; + for (unsigned i = 2; i < toksSize; i++) { + if (!checkName(toks[i])) { + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Name \"" << toks[i] << "\" contains illegal characters."; + return false; + } + addName(toks[i], citr->second); + } + } + return true; +} + +// Return true if sucessfully added group +AclReader::gmCitr AclReader::addGroup(const std::string& newGroupName) { + gmCitr citr = groups.find(newGroupName); + if (citr != groups.end()) { + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Duplicate group name \"" << newGroupName << "\"."; + return groups.end(); + } + groupPair p(newGroupName, nameSetPtr(new nameSet)); + gmRes res = groups.insert(p); + assert(res.second); + groupName = newGroupName; + return res.first; +} + +void AclReader::addName(const std::string& name, nameSetPtr groupNameSet) { + gmCitr citr = groups.find(name); + if (citr != groups.end() && citr->first != name){ + // This is a previously defined group: add all the names in that group to this group + groupNameSet->insert(citr->second->begin(), citr->second->end()); + } else { + // Not a known group name + groupNameSet->insert(name); + addName(name); + } +} + +void AclReader::addName(const std::string& name) { + names.insert(name); +} + +// Debug aid +void AclReader::printNames() const { + QPID_LOG(debug, "Group list: " << groups.size() << " groups found:" ); + std::string tmp; + for (gmCitr i=groups.begin(); i!= groups.end(); i++) { + tmp += " \""; + tmp += i->first; + tmp += "\":"; + for (nsCitr j=i->second->begin(); j!=i->second->end(); j++) { + tmp += " "; + tmp += *j; + } + QPID_LOG(debug, tmp); + tmp.clear(); + } + QPID_LOG(debug, "Name list: " << names.size() << " names found:" ); + tmp.clear(); + for (nsCitr k=names.begin(); k!=names.end(); k++) { + tmp += " "; + tmp += *k; + } + QPID_LOG(debug, tmp); +} + +bool AclReader::processAclLine(tokList& toks) { + const unsigned toksSize = toks.size(); + if (toksSize < 4) { + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Insufficient tokens for acl definition."; + return false; + } + + AclResult res; + try { + res = AclHelper::getAclResult(toks[1]); + } catch (...) { + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Unknown ACL permission \"" << toks[1] << "\"."; + return false; + } + + bool actionAllFlag = toks[3].compare("all") == 0; + bool userAllFlag = toks[2].compare("all") == 0; + Action action; + if (actionAllFlag) { + + if (userAllFlag && toksSize > 4) { + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Tokens found after action \"all\"."; + return false; + } + action = ACT_CONSUME; // dummy; compiler must initialize action for this code path + } else { + try { + action = AclHelper::getAction(toks[3]); + } catch (...) { + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Unknown action \"" << toks[3] << "\"."; + return false; + } + } + + // Create rule obj; then add object (if any) and properties (if any) + aclRulePtr rule; + if (actionAllFlag) { + rule.reset(new aclRule(res, toks[2], groups)); + } else { + rule.reset(new aclRule(res, toks[2], groups, action)); + } + + if (toksSize >= 5) { // object name-value pair + if (toks[4].compare("all") == 0) { + rule->setObjectTypeAll(); + } else { + try { + rule->setObjectType(AclHelper::getObjectType(toks[4])); + } catch (...) { + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Unknown object \"" << toks[4] << "\"."; + return false; + } + } + } + + if (toksSize >= 6) { // property name-value pair(s) + for (unsigned i=5; i<toksSize; i++) { + nvPair propNvp = splitNameValuePair(toks[i]); + if (propNvp.second.size() == 0) { + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Badly formed property name-value pair \"" << propNvp.first << "\". (Must be name=value)"; + return false; + } + Property prop; + try { + prop = AclHelper::getProperty(propNvp.first); + } catch (...) { + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Unknown property \"" << propNvp.first << "\"."; + return false; + } + rule->addProperty(prop, propNvp.second); + } + } + // Check if name (toks[2]) is group; if not, add as name of individual + if (toks[2].compare("all") != 0) { + if (groups.find(toks[2]) == groups.end()) { + addName(toks[2]); + } + } + + // If rule validates, add to rule list + if (!rule->validate(validationMap)) { + errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Invalid object/action/property combination."; + return false; + } + rules.push_back(rule); + + return true; +} + +// Debug aid +void AclReader::printRules() const { + QPID_LOG(debug, "Rule list: " << rules.size() << " ACL rules found:"); + int cnt = 0; + for (rlCitr i=rules.begin(); i<rules.end(); i++,cnt++) { + QPID_LOG(debug, " " << std::setfill(' ') << std::setw(2) << cnt << " " << (*i)->toString()); + } +} + +// Static function +// Return true if the name is well-formed (ie contains legal characters) +bool AclReader::checkName(const std::string& name) { + for (unsigned i=0; i<name.size(); i++) { + const char ch = name.at(i); + if (!std::isalnum(ch) && ch != '-' && ch != '_' && ch != '@') return false; + } + return true; +} + +// Static function +// Split name-value pair around '=' char of the form "name=value" +AclReader::nvPair AclReader::splitNameValuePair(const std::string& nvpString) { + std::size_t pos = nvpString.find("="); + if (pos == std::string::npos || pos == nvpString.size() - 1) { + return nvPair(nvpString, ""); + } + return nvPair(nvpString.substr(0, pos), nvpString.substr(pos+1)); +} + +}} // namespace qpid::acl diff --git a/RC9/qpid/cpp/src/qpid/acl/AclReader.h b/RC9/qpid/cpp/src/qpid/acl/AclReader.h new file mode 100644 index 0000000000..d85dbeef6b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/acl/AclReader.h @@ -0,0 +1,117 @@ +#ifndef QPID_ACL_ACLREADER_H +#define QPID_ACL_ACLREADER_H + + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/shared_ptr.hpp> +#include <map> +#include <set> +#include <string> +#include <vector> +#include <sstream> +#include "qpid/acl/AclData.h" +#include "qpid/broker/AclModule.h" + +namespace qpid { +namespace acl { + +class AclReader { + typedef std::set<std::string> nameSet; + typedef nameSet::const_iterator nsCitr; + typedef boost::shared_ptr<nameSet> nameSetPtr; + + typedef std::pair<std::string, nameSetPtr> groupPair; + typedef std::map<std::string, nameSetPtr> groupMap; + typedef groupMap::const_iterator gmCitr; + typedef std::pair<gmCitr, bool> gmRes; + + typedef std::pair<Property, std::string> propNvPair; + typedef std::map<Property, std::string> propMap; + typedef propMap::const_iterator pmCitr; + + class aclRule { + public: + enum objectStatus {NONE, VALUE, ALL}; + AclResult res; + nameSet names; + bool actionAll; // True if action is set to keyword "all" + Action action; // Ignored if action is set to keyword "all" + objectStatus objStatus; + ObjectType object; // Ignored for all status values except VALUE + propMap props; + public: + aclRule(const AclResult r, const std::string n, const groupMap& groups); // action = "all" + aclRule(const AclResult r, const std::string n, const groupMap& groups, const Action a); + void setObjectType(const ObjectType o); + void setObjectTypeAll(); + bool addProperty(const Property p, const std::string v); + bool validate(const AclHelper::objectMapPtr& validationMap); + std::string toString(); // debug aid + private: + void processName(const std::string& name, const groupMap& groups); + }; + typedef boost::shared_ptr<aclRule> aclRulePtr; + typedef std::vector<aclRulePtr> ruleList; + typedef ruleList::const_iterator rlCitr; + + typedef std::vector<std::string> tokList; + typedef tokList::const_iterator tlCitr; + + typedef std::set<std::string> keywordSet; + typedef keywordSet::const_iterator ksCitr; + typedef std::pair<std::string, std::string> nvPair; // Name-Value pair + + std::string fileName; + int lineNumber; + bool contFlag; + std::string groupName; + nameSet names; + groupMap groups; + ruleList rules; + AclHelper::objectMapPtr validationMap; + std::ostringstream errorStream; + + public: + AclReader(); + virtual ~AclReader(); + int read(const std::string& fn, boost::shared_ptr<AclData> d); + std::string getError(); + + private: + bool processLine(char* line); + void loadDecisionData( boost::shared_ptr<AclData> d); + int tokenize(char* line, tokList& toks); + + bool processGroupLine(tokList& toks, const bool cont); + gmCitr addGroup(const std::string& groupName); + void addName(const std::string& name, nameSetPtr groupNameSet); + void addName(const std::string& name); + void printNames() const; // debug aid + + bool processAclLine(tokList& toks); + void printRules() const; // debug aid + + static bool checkName(const std::string& name); + static nvPair splitNameValuePair(const std::string& nvpString); +}; + +}} // namespace qpid::acl + +#endif // QPID_ACL_ACLREADER_H diff --git a/RC9/qpid/cpp/src/qpid/acl/management-schema.xml b/RC9/qpid/cpp/src/qpid/acl/management-schema.xml new file mode 100644 index 0000000000..f4637253d0 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/acl/management-schema.xml @@ -0,0 +1,44 @@ +<schema package="org.apache.qpid.acl"> + +<!-- + * Copyright (c) 2008 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> + + <class name="Acl"> + <property name="brokerRef" type="objId" references="org.apache.qpid.broker:Broker" access="RO" index="y" parentRef="y"/> + <property name="policyFile" type="sstr" access="RO" desc="Name of the policy file"/> + <property name="enforcingAcl" type="bool" access="RO" desc="Currently Enforcing ACL"/> + <property name="transferAcl" type="bool" access="RO" desc="Any transfer ACL rules in force"/> + <property name="lastAclLoad" type="absTime" access="RO" desc="Timestamp of last successful load of ACL"/> + <statistic name="aclDenyCount" type="count64" unit="request" desc="Number of ACL requests denied"/> + + <method name="reloadACLFile" desc="Reload the ACL file"/> + </class> + + <eventArguments> + <arg name="action" type="sstr"/> + <arg name="arguments" type="map"/> + <arg name="objectName" type="sstr"/> + <arg name="objectType" type="sstr"/> + <arg name="reason" type="sstr"/> + <arg name="userId" type="sstr"/> + </eventArguments> + + <event name="allow" sev="inform" args="userId, action, objectType, objectName, arguments"/> + <event name="deny" sev="notice" args="userId, action, objectType, objectName, arguments"/> + <event name="fileLoaded" sev="inform" args="userId"/> + <event name="fileLoadFailed" sev="error" args="userId, reason"/> + +</schema> diff --git a/RC9/qpid/cpp/src/qpid/agent/ManagementAgent.h b/RC9/qpid/cpp/src/qpid/agent/ManagementAgent.h new file mode 100644 index 0000000000..c94291c9e7 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/agent/ManagementAgent.h @@ -0,0 +1,162 @@ +#ifndef _qpid_agent_ManagementAgent_ +#define _qpid_agent_ManagementAgent_ + +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include "qpid/management/ManagementObject.h" +#include "qpid/management/ManagementEvent.h" +#include "qpid/management/Manageable.h" +#include "qpid/sys/Mutex.h" +#include "qpid/client/ConnectionSettings.h" + +namespace qpid { +namespace management { + +class ManagementAgent +{ + public: + + class Singleton { + public: + Singleton(bool disableManagement = false); + ~Singleton(); + static ManagementAgent* getInstance(); + private: + static sys::Mutex lock; + static bool disabled; + static int refCount; + static ManagementAgent* agent; + }; + + typedef enum { + SEV_EMERG = 0, + SEV_ALERT = 1, + SEV_CRIT = 2, + SEV_ERROR = 3, + SEV_WARN = 4, + SEV_NOTE = 5, + SEV_INFO = 6, + SEV_DEBUG = 7, + SEV_DEFAULT = 8 + } severity_t; + + ManagementAgent() {} + virtual ~ManagementAgent() {} + + virtual int getMaxThreads() = 0; + + // Connect to a management broker + // + // brokerHost - Hostname or IP address (dotted-quad) of broker. + // + // brokerPort - TCP port of broker. + // + // intervalSeconds - The interval (in seconds) that this agent shall use + // between broadcast updates to the broker. + // + // useExternalThread - If true, the thread of control used for callbacks + // must be supplied by the user of the object (via the + // pollCallbacks method). + // + // If false, callbacks shall be invoked on the management + // agent's thread. In this case, the callback implementations + // MUST be thread safe. + // + // storeFile - File where this process has read and write access. This + // file shall be used to store persistent state. + // + virtual void init(const std::string& brokerHost = "localhost", + uint16_t brokerPort = 5672, + uint16_t intervalSeconds = 10, + bool useExternalThread = false, + const std::string& storeFile = "", + const std::string& uid = "guest", + const std::string& pwd = "guest", + const std::string& mech = "PLAIN", + const std::string& proto = "tcp") = 0; + + virtual void init(const client::ConnectionSettings& settings, + uint16_t intervalSeconds = 10, + bool useExternalThread = false, + const std::string& storeFile = "") = 0; + + // Register a schema with the management agent. This is normally called by the + // package initializer generated by the management code generator. + // + virtual void + registerClass(const std::string& packageName, + const std::string& className, + uint8_t* md5Sum, + management::ManagementObject::writeSchemaCall_t schemaCall) = 0; + + virtual void + registerEvent(const std::string& packageName, + const std::string& eventName, + uint8_t* md5Sum, + management::ManagementEvent::writeSchemaCall_t schemaCall) = 0; + + // Add a management object to the agent. Once added, this object shall be visible + // in the greater management context. + // + // Please note that ManagementObject instances are not explicitly deleted from + // the management agent. When the core object represented by a management object + // is deleted, the "resourceDestroy" method on the management object must be called. + // It will then be reclaimed in due course by the management agent. + // + // Once a ManagementObject instance is added to the agent, the agent then owns the + // instance. The caller MUST NOT free the resources of the instance at any time. + // When it is no longer needed, invoke its "resourceDestroy" method and discard the + // pointer. This allows the management agent to report the deletion of the object + // in an orderly way. + // + virtual ObjectId addObject(ManagementObject* objectPtr, uint64_t persistId = 0) = 0; + + // + // + virtual void raiseEvent(const ManagementEvent& event, + severity_t severity = SEV_DEFAULT) = 0; + + // If "useExternalThread" was set to true in init, this method must + // be called to provide a thread for any pending method calls that have arrived. + // The method calls for ManagementObject instances shall be invoked synchronously + // during the execution of this method. + // + // callLimit may optionally be used to limit the number of callbacks invoked. + // if 0, no limit is imposed. + // + // The return value is the number of callbacks that remain queued after this + // call is complete. It can be used to determine whether or not further calls + // to pollCallbacks are necessary to clear the backlog. If callLimit is zero, + // the return value will also be zero. + // + virtual uint32_t pollCallbacks(uint32_t callLimit = 0) = 0; + + // If "useExternalThread" was set to true in the constructor, this method provides + // a standard file descriptor that can be used in a select statement to signal that + // there are method callbacks ready (i.e. that "pollCallbacks" will result in at + // least one method call). When this fd is ready-for-read, pollCallbacks may be + // invoked. Calling pollCallbacks shall reset the ready-to-read state of the fd. + // + virtual int getSignalFd() = 0; +}; + +}} + +#endif /*!_qpid_agent_ManagementAgent_*/ diff --git a/RC9/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp b/RC9/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp new file mode 100644 index 0000000000..1e2f4cdd78 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp @@ -0,0 +1,906 @@ + +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include "qpid/management/Manageable.h" +#include "qpid/management/ManagementObject.h" +#include "qpid/log/Statement.h" +#include "ManagementAgentImpl.h" +#include <list> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include <fcntl.h> +#include <iostream> +#include <fstream> + + +using namespace qpid::client; +using namespace qpid::framing; +using namespace qpid::management; +using namespace qpid::sys; +using namespace std; +using std::stringstream; +using std::ofstream; +using std::ifstream; +using std::string; +using std::cout; +using std::endl; + +Mutex ManagementAgent::Singleton::lock; +bool ManagementAgent::Singleton::disabled = false; +ManagementAgent* ManagementAgent::Singleton::agent = 0; +int ManagementAgent::Singleton::refCount = 0; + +ManagementAgent::Singleton::Singleton(bool disableManagement) +{ + Mutex::ScopedLock _lock(lock); + if (disableManagement && !disabled) { + disabled = true; + assert(refCount == 0); // can't disable after agent has been allocated + } + if (refCount == 0 && !disabled) + agent = new ManagementAgentImpl(); + refCount++; +} + +ManagementAgent::Singleton::~Singleton() +{ + Mutex::ScopedLock _lock(lock); + refCount--; + if (refCount == 0 && !disabled) { + delete agent; + agent = 0; + } +} + +ManagementAgent* ManagementAgent::Singleton::getInstance() +{ + return agent; +} + +const string ManagementAgentImpl::storeMagicNumber("MA02"); + +ManagementAgentImpl::ManagementAgentImpl() : + extThread(false), writeFd(-1), readFd(-1), + initialized(false), connected(false), lastFailure("never connected"), + clientWasAdded(true), requestedBrokerBank(0), requestedAgentBank(0), + assignedBrokerBank(0), assignedAgentBank(0), bootSequence(0), + connThreadBody(*this), connThread(connThreadBody), + pubThreadBody(*this), pubThread(pubThreadBody) +{ +} + +ManagementAgentImpl::~ManagementAgentImpl() +{ + connThreadBody.close(); + + // If the thread is doing work on the connection, we must wait for it to + // complete before shutting down. + if (!connThreadBody.isSleeping()) { + connThread.join(); + } + + // Release the memory associated with stored management objects. + { + Mutex::ScopedLock lock(agentLock); + + moveNewObjectsLH(); + for (ManagementObjectMap::iterator iter = managementObjects.begin (); + iter != managementObjects.end (); + iter++) { + ManagementObject* object = iter->second; + delete object; + } + managementObjects.clear(); + } +} + +void ManagementAgentImpl::init(const string& brokerHost, + uint16_t brokerPort, + uint16_t intervalSeconds, + bool useExternalThread, + const string& _storeFile, + const string& uid, + const string& pwd, + const string& mech, + const string& proto) +{ + client::ConnectionSettings settings; + settings.protocol = proto; + settings.host = brokerHost; + settings.port = brokerPort; + settings.username = uid; + settings.password = pwd; + settings.mechanism = mech; + init(settings, intervalSeconds, useExternalThread, _storeFile); +} + +void ManagementAgentImpl::init(const client::ConnectionSettings& settings, + uint16_t intervalSeconds, + bool useExternalThread, + const std::string& _storeFile) +{ + interval = intervalSeconds; + extThread = useExternalThread; + storeFile = _storeFile; + nextObjectId = 1; + + QPID_LOG(info, "QMF Agent Initialized: broker=" << settings.host << ":" << settings.port << + " interval=" << intervalSeconds << " storeFile=" << _storeFile); + connectionSettings = settings; + + // TODO: Abstract the socket calls for portability + if (extThread) { + int pair[2]; + int result = socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); + if (result == -1) { + return; + } + writeFd = pair[0]; + readFd = pair[1]; + + // Set the readFd to non-blocking + int flags = fcntl(readFd, F_GETFL); + fcntl(readFd, F_SETFL, flags | O_NONBLOCK); + } + + retrieveData(); + bootSequence++; + if ((bootSequence & 0xF000) != 0) + bootSequence = 1; + storeData(true); + + initialized = true; +} + +void ManagementAgentImpl::registerClass(const string& packageName, + const string& className, + uint8_t* md5Sum, + management::ManagementObject::writeSchemaCall_t schemaCall) +{ + Mutex::ScopedLock lock(agentLock); + PackageMap::iterator pIter = findOrAddPackage(packageName); + addClassLocal(ManagementItem::CLASS_KIND_TABLE, pIter, className, md5Sum, schemaCall); +} + +void ManagementAgentImpl::registerEvent(const string& packageName, + const string& eventName, + uint8_t* md5Sum, + management::ManagementObject::writeSchemaCall_t schemaCall) +{ + Mutex::ScopedLock lock(agentLock); + PackageMap::iterator pIter = findOrAddPackage(packageName); + addClassLocal(ManagementItem::CLASS_KIND_EVENT, pIter, eventName, md5Sum, schemaCall); +} + +ObjectId ManagementAgentImpl::addObject(ManagementObject* object, + uint64_t persistId) +{ + Mutex::ScopedLock lock(addLock); + uint16_t sequence = persistId ? 0 : bootSequence; + uint64_t objectNum = persistId ? persistId : nextObjectId++; + + ObjectId objectId(&attachment, 0, sequence, objectNum); + + // TODO: fix object-id handling + object->setObjectId(objectId); + newManagementObjects[objectId] = object; + return objectId; +} + +void ManagementAgentImpl::raiseEvent(const ManagementEvent& event, severity_t severity) +{ + Mutex::ScopedLock lock(agentLock); + Buffer outBuffer(eventBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + uint8_t sev = (severity == SEV_DEFAULT) ? event.getSeverity() : (uint8_t) severity; + stringstream key; + + key << "console.event." << assignedBrokerBank << "." << assignedAgentBank << "." << + event.getPackageName() << "." << event.getEventName(); + + encodeHeader(outBuffer, 'e'); + outBuffer.putShortString(event.getPackageName()); + outBuffer.putShortString(event.getEventName()); + outBuffer.putBin128(event.getMd5Sum()); + outBuffer.putLongLong(uint64_t(Duration(now()))); + outBuffer.putOctet(sev); + event.encode(outBuffer); + outLen = MA_BUFFER_SIZE - outBuffer.available(); + outBuffer.reset(); + connThreadBody.sendBuffer(outBuffer, outLen, "qpid.management", key.str()); +} + +uint32_t ManagementAgentImpl::pollCallbacks(uint32_t callLimit) +{ + Mutex::ScopedLock lock(agentLock); + + for (uint32_t idx = 0; callLimit == 0 || idx < callLimit; idx++) { + if (methodQueue.empty()) + break; + + QueuedMethod* item = methodQueue.front(); + methodQueue.pop_front(); + { + Mutex::ScopedUnlock unlock(agentLock); + Buffer inBuffer(const_cast<char*>(item->body.c_str()), item->body.size()); + invokeMethodRequest(inBuffer, item->sequence, item->replyTo); + delete item; + } + } + + uint8_t rbuf[100]; + while (read(readFd, rbuf, 100) > 0) ; // Consume all signaling bytes + return methodQueue.size(); +} + +int ManagementAgentImpl::getSignalFd(void) +{ + return readFd; +} + +void ManagementAgentImpl::startProtocol() +{ + char rawbuffer[512]; + Buffer buffer(rawbuffer, 512); + + connected = true; + encodeHeader(buffer, 'A'); + buffer.putShortString("RemoteAgent [C++]"); + systemId.encode (buffer); + buffer.putLong(requestedBrokerBank); + buffer.putLong(requestedAgentBank); + uint32_t length = buffer.getPosition(); + buffer.reset(); + connThreadBody.sendBuffer(buffer, length, "qpid.management", "broker"); + QPID_LOG(trace, "SENT AttachRequest: reqBroker=" << requestedBrokerBank << + " reqAgent=" << requestedAgentBank); +} + +void ManagementAgentImpl::storeData(bool requested) +{ + if (!storeFile.empty()) { + ofstream outFile(storeFile.c_str()); + uint32_t brokerBankToWrite = requested ? requestedBrokerBank : assignedBrokerBank; + uint32_t agentBankToWrite = requested ? requestedAgentBank : assignedAgentBank; + + if (outFile.good()) { + outFile << storeMagicNumber << " " << brokerBankToWrite << " " << + agentBankToWrite << " " << bootSequence << endl; + outFile.close(); + } + } +} + +void ManagementAgentImpl::retrieveData() +{ + if (!storeFile.empty()) { + ifstream inFile(storeFile.c_str()); + string mn; + + if (inFile.good()) { + inFile >> mn; + if (mn == storeMagicNumber) { + inFile >> requestedBrokerBank; + inFile >> requestedAgentBank; + inFile >> bootSequence; + } + inFile.close(); + } + } +} + +void ManagementAgentImpl::sendCommandComplete(string replyToKey, uint32_t sequence, + uint32_t code, string text) +{ + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader(outBuffer, 'z', sequence); + outBuffer.putLong(code); + outBuffer.putShortString(text); + outLen = MA_BUFFER_SIZE - outBuffer.available(); + outBuffer.reset(); + connThreadBody.sendBuffer(outBuffer, outLen, "amq.direct", replyToKey); + QPID_LOG(trace, "SENT CommandComplete: seq=" << sequence << " code=" << code << " text=" << text); +} + +void ManagementAgentImpl::handleAttachResponse(Buffer& inBuffer) +{ + Mutex::ScopedLock lock(agentLock); + + assignedBrokerBank = inBuffer.getLong(); + assignedAgentBank = inBuffer.getLong(); + + QPID_LOG(trace, "RCVD AttachResponse: broker=" << assignedBrokerBank << " agent=" << assignedAgentBank); + + if ((assignedBrokerBank != requestedBrokerBank) || + (assignedAgentBank != requestedAgentBank)) { + if (requestedAgentBank == 0) { + QPID_LOG(notice, "Initial object-id bank assigned: " << assignedBrokerBank << "." << + assignedAgentBank); + } else { + QPID_LOG(warning, "Collision in object-id! New bank assigned: " << assignedBrokerBank << + "." << assignedAgentBank); + } + storeData(); + requestedBrokerBank = assignedBrokerBank; + requestedAgentBank = assignedAgentBank; + } + + attachment.setBanks(assignedBrokerBank, assignedAgentBank); + + // Bind to qpid.management to receive commands + connThreadBody.bindToBank(assignedBrokerBank, assignedAgentBank); + + // Send package indications for all local packages + for (PackageMap::iterator pIter = packages.begin(); + pIter != packages.end(); + pIter++) { + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader(outBuffer, 'p'); + encodePackageIndication(outBuffer, pIter); + outLen = MA_BUFFER_SIZE - outBuffer.available(); + outBuffer.reset(); + connThreadBody.sendBuffer(outBuffer, outLen, "qpid.management", "broker"); + + // Send class indications for all local classes + ClassMap cMap = pIter->second; + for (ClassMap::iterator cIter = cMap.begin(); cIter != cMap.end(); cIter++) { + outBuffer.reset(); + encodeHeader(outBuffer, 'q'); + encodeClassIndication(outBuffer, pIter, cIter); + outLen = MA_BUFFER_SIZE - outBuffer.available(); + outBuffer.reset(); + connThreadBody.sendBuffer(outBuffer, outLen, "qpid.management", "broker"); + } + } +} + +void ManagementAgentImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence) +{ + Mutex::ScopedLock lock(agentLock); + string packageName; + SchemaClassKey key; + + inBuffer.getShortString(packageName); + inBuffer.getShortString(key.name); + inBuffer.getBin128(key.hash); + + QPID_LOG(trace, "RCVD SchemaRequest: package=" << packageName << " class=" << key.name); + + PackageMap::iterator pIter = packages.find(packageName); + if (pIter != packages.end()) { + ClassMap& cMap = pIter->second; + ClassMap::iterator cIter = cMap.find(key); + if (cIter != cMap.end()) { + SchemaClass& schema = cIter->second; + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader(outBuffer, 's', sequence); + schema.writeSchemaCall(outBuffer); + outLen = MA_BUFFER_SIZE - outBuffer.available(); + outBuffer.reset(); + connThreadBody.sendBuffer(outBuffer, outLen, "qpid.management", "broker"); + + QPID_LOG(trace, "SENT SchemaInd: package=" << packageName << " class=" << key.name); + } + } +} + +void ManagementAgentImpl::handleConsoleAddedIndication() +{ + Mutex::ScopedLock lock(agentLock); + clientWasAdded = true; + + QPID_LOG(trace, "RCVD ConsoleAddedInd"); +} + +void ManagementAgentImpl::invokeMethodRequest(Buffer& inBuffer, uint32_t sequence, string replyTo) +{ + string methodName; + string packageName; + string className; + uint8_t hash[16]; + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + ObjectId objId(inBuffer); + inBuffer.getShortString(packageName); + inBuffer.getShortString(className); + inBuffer.getBin128(hash); + inBuffer.getShortString(methodName); + + encodeHeader(outBuffer, 'm', sequence); + + ManagementObjectMap::iterator iter = managementObjects.find(objId); + if (iter == managementObjects.end() || iter->second->isDeleted()) { + outBuffer.putLong (Manageable::STATUS_UNKNOWN_OBJECT); + outBuffer.putMediumString(Manageable::StatusText(Manageable::STATUS_UNKNOWN_OBJECT)); + } else { + if ((iter->second->getPackageName() != packageName) || + (iter->second->getClassName() != className)) { + outBuffer.putLong (Manageable::STATUS_INVALID_PARAMETER); + outBuffer.putMediumString(Manageable::StatusText (Manageable::STATUS_INVALID_PARAMETER)); + } + else + try { + outBuffer.record(); + iter->second->doMethod(methodName, inBuffer, outBuffer); + } catch(exception& e) { + outBuffer.restore(); + outBuffer.putLong(Manageable::STATUS_EXCEPTION); + outBuffer.putMediumString(e.what()); + } + } + + outLen = MA_BUFFER_SIZE - outBuffer.available(); + outBuffer.reset(); + connThreadBody.sendBuffer(outBuffer, outLen, "amq.direct", replyTo); +} + +void ManagementAgentImpl::handleGetQuery(Buffer& inBuffer, uint32_t sequence, string replyTo) +{ + FieldTable ft; + FieldTable::ValuePtr value; + + moveNewObjectsLH(); + + ft.decode(inBuffer); + + QPID_LOG(trace, "RCVD GetQuery: map=" << ft); + + value = ft.get("_class"); + if (value.get() == 0 || !value->convertsTo<string>()) { + value = ft.get("_objectid"); + if (value.get() == 0 || !value->convertsTo<string>()) + return; + + ObjectId selector(value->get<string>()); + ManagementObjectMap::iterator iter = managementObjects.find(selector); + if (iter != managementObjects.end()) { + ManagementObject* object = iter->second; + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader(outBuffer, 'g', sequence); + object->writeProperties(outBuffer); + object->writeStatistics(outBuffer, true); + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + connThreadBody.sendBuffer(outBuffer, outLen, "amq.direct", replyTo); + + QPID_LOG(trace, "SENT ObjectInd"); + } + sendCommandComplete(replyTo, sequence); + return; + } + + string className(value->get<string>()); + + for (ManagementObjectMap::iterator iter = managementObjects.begin(); + iter != managementObjects.end(); + iter++) { + ManagementObject* object = iter->second; + if (object->getClassName() == className) { + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader(outBuffer, 'g', sequence); + object->writeProperties(outBuffer); + object->writeStatistics(outBuffer, true); + outLen = MA_BUFFER_SIZE - outBuffer.available(); + outBuffer.reset(); + connThreadBody.sendBuffer(outBuffer, outLen, "amq.direct", replyTo); + + QPID_LOG(trace, "SENT ObjectInd"); + } + } + + sendCommandComplete(replyTo, sequence); +} + +void ManagementAgentImpl::handleMethodRequest(Buffer& inBuffer, uint32_t sequence, string replyTo) +{ + if (extThread) { + Mutex::ScopedLock lock(agentLock); + string body; + + inBuffer.getRawData(body, inBuffer.available()); + methodQueue.push_back(new QueuedMethod(sequence, replyTo, body)); + write(writeFd, "X", 1); + } else { + invokeMethodRequest(inBuffer, sequence, replyTo); + } + + QPID_LOG(trace, "RCVD MethodRequest"); +} + +void ManagementAgentImpl::received(Message& msg) +{ + string data = msg.getData(); + Buffer inBuffer(const_cast<char*>(data.c_str()), data.size()); + uint8_t opcode; + uint32_t sequence; + string replyToKey; + + framing::MessageProperties p = msg.getMessageProperties(); + if (p.hasReplyTo()) { + const framing::ReplyTo& rt = p.getReplyTo(); + replyToKey = rt.getRoutingKey(); + } + + if (checkHeader(inBuffer, &opcode, &sequence)) + { + if (opcode == 'a') handleAttachResponse(inBuffer); + else if (opcode == 'S') handleSchemaRequest(inBuffer, sequence); + else if (opcode == 'x') handleConsoleAddedIndication(); + else if (opcode == 'G') handleGetQuery(inBuffer, sequence, replyToKey); + else if (opcode == 'M') handleMethodRequest(inBuffer, sequence, replyToKey); + } +} + +void ManagementAgentImpl::encodeHeader(Buffer& buf, uint8_t opcode, uint32_t seq) +{ + buf.putOctet('A'); + buf.putOctet('M'); + buf.putOctet('2'); + buf.putOctet(opcode); + buf.putLong (seq); +} + +bool ManagementAgentImpl::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq) +{ + if (buf.getSize() < 8) + return false; + + uint8_t h1 = buf.getOctet(); + uint8_t h2 = buf.getOctet(); + uint8_t h3 = buf.getOctet(); + + *opcode = buf.getOctet(); + *seq = buf.getLong(); + + return h1 == 'A' && h2 == 'M' && h3 == '2'; +} + +ManagementAgentImpl::PackageMap::iterator ManagementAgentImpl::findOrAddPackage(const string& name) +{ + PackageMap::iterator pIter = packages.find(name); + if (pIter != packages.end()) + return pIter; + + // No such package found, create a new map entry. + pair<PackageMap::iterator, bool> result = + packages.insert(pair<string, ClassMap>(name, ClassMap())); + + if (connected) { + // Publish a package-indication message + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader(outBuffer, 'p'); + encodePackageIndication(outBuffer, result.first); + outLen = MA_BUFFER_SIZE - outBuffer.available(); + outBuffer.reset(); + connThreadBody.sendBuffer(outBuffer, outLen, "qpid.management", "schema.package"); + } + + return result.first; +} + +void ManagementAgentImpl::moveNewObjectsLH() +{ + Mutex::ScopedLock lock(addLock); + for (ManagementObjectMap::iterator iter = newManagementObjects.begin(); + iter != newManagementObjects.end(); + iter++) + managementObjects[iter->first] = iter->second; + newManagementObjects.clear(); +} + +void ManagementAgentImpl::addClassLocal(uint8_t classKind, + PackageMap::iterator pIter, + const string& className, + uint8_t* md5Sum, + management::ManagementObject::writeSchemaCall_t schemaCall) +{ + SchemaClassKey key; + ClassMap& cMap = pIter->second; + + key.name = className; + memcpy(&key.hash, md5Sum, 16); + + ClassMap::iterator cIter = cMap.find(key); + if (cIter != cMap.end()) + return; + + // No such class found, create a new class with local information. + cMap.insert(pair<SchemaClassKey, SchemaClass>(key, SchemaClass(schemaCall, classKind))); +} + +void ManagementAgentImpl::encodePackageIndication(Buffer& buf, + PackageMap::iterator pIter) +{ + buf.putShortString((*pIter).first); + + QPID_LOG(trace, "SENT PackageInd: package=" << (*pIter).first); +} + +void ManagementAgentImpl::encodeClassIndication(Buffer& buf, + PackageMap::iterator pIter, + ClassMap::iterator cIter) +{ + SchemaClassKey key = (*cIter).first; + + buf.putOctet((*cIter).second.kind); + buf.putShortString((*pIter).first); + buf.putShortString(key.name); + buf.putBin128(key.hash); + + QPID_LOG(trace, "SENT ClassInd: package=" << (*pIter).first << " class=" << key.name); +} + +void ManagementAgentImpl::periodicProcessing() +{ +#define BUFSIZE 65536 + Mutex::ScopedLock lock(agentLock); + char msgChars[BUFSIZE]; + uint32_t contentSize; + list<pair<ObjectId, ManagementObject*> > deleteList; + + if (!connected) + return; + + moveNewObjectsLH(); + + // + // Clear the been-here flag on all objects in the map. + // + for (ManagementObjectMap::iterator iter = managementObjects.begin(); + iter != managementObjects.end(); + iter++) { + ManagementObject* object = iter->second; + object->setFlags(0); + if (clientWasAdded) { + object->setForcePublish(true); + } + } + + clientWasAdded = false; + + // + // Process the entire object map. + // + for (ManagementObjectMap::iterator baseIter = managementObjects.begin(); + baseIter != managementObjects.end(); + baseIter++) { + ManagementObject* baseObject = baseIter->second; + + // + // Skip until we find a base object requiring a sent message. + // + if (baseObject->getFlags() == 1 || + (!baseObject->getConfigChanged() && + !baseObject->getInstChanged() && + !baseObject->getForcePublish() && + !baseObject->isDeleted())) + continue; + + Buffer msgBuffer(msgChars, BUFSIZE); + for (ManagementObjectMap::iterator iter = baseIter; + iter != managementObjects.end(); + iter++) { + ManagementObject* object = iter->second; + if (baseObject->isSameClass(*object) && object->getFlags() == 0) { + object->setFlags(1); + if (object->getConfigChanged() || object->getInstChanged()) + object->setUpdateTime(); + + if (object->getConfigChanged() || object->getForcePublish() || object->isDeleted()) { + encodeHeader(msgBuffer, 'c'); + object->writeProperties(msgBuffer); + } + + if (object->hasInst() && (object->getInstChanged() || object->getForcePublish())) { + encodeHeader(msgBuffer, 'i'); + object->writeStatistics(msgBuffer); + } + + if (object->isDeleted()) + deleteList.push_back(pair<ObjectId, ManagementObject*>(iter->first, object)); + object->setForcePublish(false); + + if (msgBuffer.available() < (BUFSIZE / 2)) + break; + } + } + + contentSize = BUFSIZE - msgBuffer.available(); + if (contentSize > 0) { + msgBuffer.reset(); + stringstream key; + key << "console.obj." << assignedBrokerBank << "." << assignedAgentBank << "." << + baseObject->getPackageName() << "." << baseObject->getClassName(); + connThreadBody.sendBuffer(msgBuffer, contentSize, "qpid.management", key.str()); + } + } + + // Delete flagged objects + for (list<pair<ObjectId, ManagementObject*> >::reverse_iterator iter = deleteList.rbegin(); + iter != deleteList.rend(); + iter++) { + delete iter->second; + managementObjects.erase(iter->first); + } + + deleteList.clear(); + + { + Buffer msgBuffer(msgChars, BUFSIZE); + encodeHeader(msgBuffer, 'h'); + msgBuffer.putLongLong(uint64_t(Duration(now()))); + stringstream key; + key << "console.heartbeat." << assignedBrokerBank << "." << assignedAgentBank; + + contentSize = BUFSIZE - msgBuffer.available(); + msgBuffer.reset(); + connThreadBody.sendBuffer(msgBuffer, contentSize, "qpid.management", key.str()); + } +} + +void ManagementAgentImpl::ConnectionThread::run() +{ + static const int delayMin(1); + static const int delayMax(128); + static const int delayFactor(2); + int delay(delayMin); + string dest("qmfagent"); + + sessionId.generate(); + queueName << "qmfagent-" << sessionId; + + while (true) { + try { + if (agent.initialized) { + QPID_LOG(debug, "QMF Agent attempting to connect to the broker..."); + connection.open(agent.connectionSettings); + session = connection.newSession(queueName.str()); + subscriptions = new client::SubscriptionManager(session); + + session.queueDeclare(arg::queue=queueName.str(), arg::autoDelete=true, + arg::exclusive=true); + session.exchangeBind(arg::exchange="amq.direct", arg::queue=queueName.str(), + arg::bindingKey=queueName.str()); + + subscriptions->subscribe(agent, queueName.str(), dest); + QPID_LOG(info, "Connection established with broker"); + { + Mutex::ScopedLock _lock(connLock); + if (shutdown) + return; + operational = true; + agent.startProtocol(); + try { + Mutex::ScopedUnlock _unlock(connLock); + subscriptions->run(); + } catch (exception) {} + + QPID_LOG(warning, "Connection to the broker has been lost"); + + operational = false; + agent.connected = false; + } + delay = delayMin; + connection.close(); + delete subscriptions; + subscriptions = 0; + } + } catch (exception &e) { + if (delay < delayMax) + delay *= delayFactor; + QPID_LOG(debug, "Connection failed: exception=" << e.what()); + } + + { + Mutex::ScopedLock _lock(connLock); + if (shutdown) + return; + sleeping = true; + { + Mutex::ScopedUnlock _unlock(connLock); + ::sleep(delay); + } + sleeping = false; + if (shutdown) + return; + } + } +} + +ManagementAgentImpl::ConnectionThread::~ConnectionThread() +{ +} + +void ManagementAgentImpl::ConnectionThread::sendBuffer(Buffer& buf, + uint32_t length, + const string& exchange, + const string& routingKey) +{ + { + Mutex::ScopedLock _lock(connLock); + if (!operational) + return; + } + + Message msg; + string data; + + buf.getRawData(data, length); + msg.getDeliveryProperties().setRoutingKey(routingKey); + msg.getMessageProperties().setReplyTo(ReplyTo("amq.direct", queueName.str())); + msg.setData(data); + try { + session.messageTransfer(arg::content=msg, arg::destination=exchange); + } catch(exception& e) { + QPID_LOG(error, "Exception caught in sendBuffer: " << e.what()); + // Bounce the connection + if (subscriptions) + subscriptions->stop(); + } +} + +void ManagementAgentImpl::ConnectionThread::bindToBank(uint32_t brokerBank, uint32_t agentBank) +{ + stringstream key; + key << "agent." << brokerBank << "." << agentBank; + session.exchangeBind(arg::exchange="qpid.management", arg::queue=queueName.str(), + arg::bindingKey=key.str()); +} + +void ManagementAgentImpl::ConnectionThread::close() +{ + { + Mutex::ScopedLock _lock(connLock); + shutdown = true; + } + if (subscriptions) + subscriptions->stop(); +} + +bool ManagementAgentImpl::ConnectionThread::isSleeping() const +{ + Mutex::ScopedLock _lock(connLock); + return sleeping; +} + + +void ManagementAgentImpl::PublishThread::run() +{ + while (true) { + ::sleep(agent.getInterval()); + agent.periodicProcessing(); + } +} diff --git a/RC9/qpid/cpp/src/qpid/agent/ManagementAgentImpl.h b/RC9/qpid/cpp/src/qpid/agent/ManagementAgentImpl.h new file mode 100644 index 0000000000..53eb690ba8 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/agent/ManagementAgentImpl.h @@ -0,0 +1,237 @@ +#ifndef _qpid_agent_ManagementAgentImpl_ +#define _qpid_agent_ManagementAgentImpl_ + +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include "ManagementAgent.h" +#include "qpid/client/Connection.h" +#include "qpid/client/ConnectionSettings.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Session.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/client/Message.h" +#include "qpid/client/MessageListener.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Mutex.h" +#include "qpid/framing/Uuid.h" +#include <iostream> +#include <sstream> +#include <deque> + +namespace qpid { +namespace management { + +class ManagementAgentImpl : public ManagementAgent, public client::MessageListener +{ + public: + + ManagementAgentImpl(); + virtual ~ManagementAgentImpl(); + + // + // Methods from ManagementAgent + // + int getMaxThreads() { return 1; } + void init(const std::string& brokerHost = "localhost", + uint16_t brokerPort = 5672, + uint16_t intervalSeconds = 10, + bool useExternalThread = false, + const std::string& storeFile = "", + const std::string& uid = "guest", + const std::string& pwd = "guest", + const std::string& mech = "PLAIN", + const std::string& proto = "tcp"); + void init(const client::ConnectionSettings& settings, + uint16_t intervalSeconds = 10, + bool useExternalThread = false, + const std::string& storeFile = ""); + bool isConnected() { return connected; } + std::string& getLastFailure() { return lastFailure; } + void registerClass(const std::string& packageName, + const std::string& className, + uint8_t* md5Sum, + management::ManagementObject::writeSchemaCall_t schemaCall); + void registerEvent(const std::string& packageName, + const std::string& eventName, + uint8_t* md5Sum, + management::ManagementObject::writeSchemaCall_t schemaCall); + ObjectId addObject(management::ManagementObject* objectPtr, uint64_t persistId = 0); + void raiseEvent(const management::ManagementEvent& event, severity_t severity = SEV_DEFAULT); + uint32_t pollCallbacks(uint32_t callLimit = 0); + int getSignalFd(); + + uint16_t getInterval() { return interval; } + void periodicProcessing(); + + private: + + struct SchemaClassKey { + std::string name; + uint8_t hash[16]; + }; + + struct SchemaClassKeyComp { + bool operator() (const SchemaClassKey& lhs, const SchemaClassKey& rhs) const + { + if (lhs.name != rhs.name) + return lhs.name < rhs.name; + else + for (int i = 0; i < 16; i++) + if (lhs.hash[i] != rhs.hash[i]) + return lhs.hash[i] < rhs.hash[i]; + return false; + } + }; + + struct SchemaClass { + management::ManagementObject::writeSchemaCall_t writeSchemaCall; + uint8_t kind; + + SchemaClass(const management::ManagementObject::writeSchemaCall_t call, + const uint8_t _kind) : writeSchemaCall(call), kind(_kind) {} + }; + + struct QueuedMethod { + QueuedMethod(uint32_t _seq, std::string _reply, std::string _body) : + sequence(_seq), replyTo(_reply), body(_body) {} + + uint32_t sequence; + std::string replyTo; + std::string body; + }; + + typedef std::deque<QueuedMethod*> MethodQueue; + typedef std::map<SchemaClassKey, SchemaClass, SchemaClassKeyComp> ClassMap; + typedef std::map<std::string, ClassMap> PackageMap; + + PackageMap packages; + AgentAttachment attachment; + management::ManagementObjectMap managementObjects; + management::ManagementObjectMap newManagementObjects; + MethodQueue methodQueue; + + void received (client::Message& msg); + + uint16_t interval; + bool extThread; + int writeFd; + int readFd; + uint64_t nextObjectId; + std::string storeFile; + sys::Mutex agentLock; + sys::Mutex addLock; + framing::Uuid systemId; + client::ConnectionSettings connectionSettings; + bool initialized; + bool connected; + std::string lastFailure; + + bool clientWasAdded; + uint32_t requestedBrokerBank; + uint32_t requestedAgentBank; + uint32_t assignedBrokerBank; + uint32_t assignedAgentBank; + uint16_t bootSequence; + + static const uint8_t DEBUG_OFF = 0; + static const uint8_t DEBUG_CONN = 1; + static const uint8_t DEBUG_PROTO = 2; + static const uint8_t DEBUG_PUBLISH = 3; + +# define MA_BUFFER_SIZE 65536 + char outputBuffer[MA_BUFFER_SIZE]; + char eventBuffer[MA_BUFFER_SIZE]; + + friend class ConnectionThread; + class ConnectionThread : public sys::Runnable + { + bool operational; + ManagementAgentImpl& agent; + framing::Uuid sessionId; + client::Connection connection; + client::Session session; + client::SubscriptionManager* subscriptions; + std::stringstream queueName; + mutable sys::Mutex connLock; + bool shutdown; + bool sleeping; + void run(); + public: + ConnectionThread(ManagementAgentImpl& _agent) : + operational(false), agent(_agent), subscriptions(0), + shutdown(false), sleeping(false) {} + ~ConnectionThread(); + void sendBuffer(qpid::framing::Buffer& buf, + uint32_t length, + const std::string& exchange, + const std::string& routingKey); + void bindToBank(uint32_t brokerBank, uint32_t agentBank); + void close(); + bool isSleeping() const; + }; + + class PublishThread : public sys::Runnable + { + ManagementAgentImpl& agent; + void run(); + public: + PublishThread(ManagementAgentImpl& _agent) : agent(_agent) {} + }; + + ConnectionThread connThreadBody; + sys::Thread connThread; + PublishThread pubThreadBody; + sys::Thread pubThread; + + static const std::string storeMagicNumber; + + void startProtocol(); + void storeData(bool requested=false); + void retrieveData(); + PackageMap::iterator findOrAddPackage(const std::string& name); + void moveNewObjectsLH(); + void addClassLocal (uint8_t classKind, + PackageMap::iterator pIter, + const std::string& className, + uint8_t* md5Sum, + management::ManagementObject::writeSchemaCall_t schemaCall); + void encodePackageIndication (framing::Buffer& buf, + PackageMap::iterator pIter); + void encodeClassIndication (framing::Buffer& buf, + PackageMap::iterator pIter, + ClassMap::iterator cIter); + void encodeHeader (framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0); + bool checkHeader (framing::Buffer& buf, uint8_t *opcode, uint32_t *seq); + void sendCommandComplete (std::string replyToKey, uint32_t sequence, + uint32_t code = 0, std::string text = std::string("OK")); + void handleAttachResponse (qpid::framing::Buffer& inBuffer); + void handlePackageRequest (qpid::framing::Buffer& inBuffer); + void handleClassQuery (qpid::framing::Buffer& inBuffer); + void handleSchemaRequest (qpid::framing::Buffer& inBuffer, uint32_t sequence); + void invokeMethodRequest (qpid::framing::Buffer& inBuffer, uint32_t sequence, std::string replyTo); + void handleGetQuery (qpid::framing::Buffer& inBuffer, uint32_t sequence, std::string replyTo); + void handleMethodRequest (qpid::framing::Buffer& inBuffer, uint32_t sequence, std::string replyTo); + void handleConsoleAddedIndication(); +}; + +}} + +#endif /*!_qpid_agent_ManagementAgentImpl_*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Array.cpp b/RC9/qpid/cpp/src/qpid/amqp_0_10/Array.cpp new file mode 100644 index 0000000000..380e0f1f36 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Array.cpp @@ -0,0 +1,34 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Array.h" + +namespace qpid { +namespace amqp_0_10 { + +std::ostream& operator<<(std::ostream& o, const Array& a) { + std::ostream_iterator<UnknownType> i(o, " "); + o << "Array<" << typeName(a.getType()) << "["; + std::copy(a.begin(), a.end(), i); + o << "]"; + return o; +} + +}} // namespace qpid::amqp_0_10 diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Array.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/Array.h new file mode 100644 index 0000000000..6e8a419df7 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Array.h @@ -0,0 +1,124 @@ +#ifndef QPID_AMQP_0_10_ARRAY_H +#define QPID_AMQP_0_10_ARRAY_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/amqp_0_10/TypeForCode.h" +#include "qpid/amqp_0_10/CodeForType.h" +#include "qpid/amqp_0_10/UnknownType.h" +#include "qpid/amqp_0_10/exceptions.h" +#include "qpid/amqp_0_10/Codec.h" +#include <vector> +#include <ostream> + +namespace qpid { +namespace amqp_0_10 { + +template <class T> class ArrayDomain : public std::vector<T> { + public: + template <class S> void serialize(S& s) { s.split(*this); } + + template <class S> void encode(S& s) const { + s(contentSize())(CodeForType<T>::value)(uint32_t(this->size())); + s(this->begin(), this->end()); + } + + void encode(Codec::Size& s) const { s.raw(0, contentSize() + 4/*size*/); } + + template <class S> void decode(S& s) { + uint32_t size; uint8_t type; uint32_t count; + s(size); + typename S::ScopedLimit l(s, size); + s(type); + if (type != CodeForType<T>::value) + throw InvalidArgumentException(QPID_MSG("Array domain expected type " << CodeForType<T>::value << " but found " << type)); + s(count); + this->resize(count); + s(this->begin(), this->end()); + } + + private: + uint32_t contentSize() const { + return Codec::size(this->begin(), this->end()) + sizeof(uint32_t) /*count*/ + sizeof(uint8_t) /*type*/; + } +}; + +template <class T> +std::ostream& operator<<(std::ostream& o, const ArrayDomain<T>& ad) { + std::ostream_iterator<T> i(o, " "); + o << "Array<" << typeName(CodeForType<T>::value) << ">["; + std::copy(ad.begin(), ad.end(), i); + o << "]"; + return o; +} + +/** A non-domain array is represented as and array of UnknownType. + * Special case templat. + */ +template<> class ArrayDomain<UnknownType> : public std::vector<UnknownType> { + public: + ArrayDomain(uint8_t type_=0) : type(type_) {} + + template <class S> void serialize(S& s) { s.split(*this); } + + template <class S> void encode(S& s) const { + s(contentSize())(type)(uint32_t(this->size())); + s(this->begin(), this->end()); + } + + void encode(Codec::Size& s) const { s.raw(0, contentSize() + 4/*size*/); } + + template <class S> void decode(S& s) { + uint32_t size; uint32_t count; + s(size); + typename S::ScopedLimit l(s, size); + s(type)(count); + this->clear(); + this->resize(count, UnknownType(type)); + s(this->begin(), this->end()); + } + + uint8_t getType() const { return type; } + + private: + uint32_t contentSize() const { + return Codec::size(this->begin(), this->end()) + sizeof(uint32_t) /*count*/ + sizeof(uint8_t) /*type*/; + } + uint8_t type; +}; + +std::ostream& operator<<(std::ostream& o, const Array& a); + +// FIXME aconway 2008-04-08: hack to supress encoding of +// command-fragments and in-doubt as there is a problem with the spec +// (command-fragments does not have a one byte type code.) +namespace session { class CommandFragment; } +namespace dtx { class Xid; } + +template <> struct ArrayDomain<session::CommandFragment> : public Void {}; +template <> struct ArrayDomain<dtx::Xid> : public Void {}; +inline std::ostream& operator<<(std::ostream& o, const ArrayDomain<session::CommandFragment>&) { return o; } +inline std::ostream& operator<<(std::ostream& o, const ArrayDomain<dtx::Xid>&) { return o; } + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_ARRAY_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Body.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/Body.h new file mode 100644 index 0000000000..c96931551c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Body.h @@ -0,0 +1,55 @@ +#ifndef QPID_AMQP_0_10_BODY_H +#define QPID_AMQP_0_10_BODY_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <string> +#include <ostream> + +namespace qpid { +namespace amqp_0_10 { + +/** Holds data from a body frame. */ +class Body { + public: + Body() {} + Body(size_t size_) : str(size_, '\0') {} + Body(const char* data_, size_t size_) : str(data_, size_) {} + + size_t size() const { return str.size(); }; + const char* data() const { return str.data(); } + char* data() { return const_cast<char*>(str.data()); } + + template <class S> void serialize(S& s) { s.raw(data(), size()); } + + private: + std::string str; + + friend std::ostream& operator<<(std::ostream&, const Body&); +}; + +inline std::ostream& operator<<(std::ostream& o, const Body& b) { + return o << b.str.substr(0, 16) << "... (" << b.size() << ")"; +} + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_BODY_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Codec.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/Codec.h new file mode 100644 index 0000000000..5cad5cf4ed --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Codec.h @@ -0,0 +1,213 @@ +#ifndef QPID_AMQP_0_10_CODEC_H +#define QPID_AMQP_0_10_CODEC_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "built_in_types.h" +#include "qpid/Serializer.h" +#include <boost/type_traits/is_integral.hpp> +#include <boost/type_traits/is_float.hpp> +#include <boost/type_traits/is_arithmetic.hpp> +#include <boost/detail/endian.hpp> +#include <boost/static_assert.hpp> +#include <iterator> + +namespace qpid { +namespace amqp_0_10 { + +template <class T> void reverse(T& t) { + char*p =reinterpret_cast<char*>(&t); + std::reverse(p, p+sizeof(T)); +} + +#ifdef BOOST_LITTLE_ENDIAN +template <class T> void bigEndian(T& t) { reverse(t); } +template <class T> void littleEndian(T&) {} +#else +template <class T> void littleEndian(T& t) { reverse(t); } +template <class T> void bigEndian(T&) {} +#endif + +/** + * AMQP 0-10 encoding and decoding. + */ +struct Codec { + /** Encode to an output byte iterator */ + template <class OutIter> + class Encoder : public EncoderBase<Encoder<OutIter> > + { + public: + typedef EncoderBase<Encoder<OutIter> > Base; + typedef OutIter Iterator; + + Encoder(OutIter o, size_t limit=Base::maxLimit()) : out(o) { + this->setLimit(limit); + } + + using EncoderBase<Encoder<OutIter> >::operator(); + + Encoder& operator()(bool x) { raw(x); return *this;} + Encoder& operator()(char x) { raw(x); return *this; } + Encoder& operator()(int8_t x) { raw(x); return *this; } + Encoder& operator()(uint8_t x) { raw(x); return *this; } + + Encoder& operator()(int16_t x) { return networkByteOrder(x); } + Encoder& operator()(int32_t x) { return networkByteOrder(x); } + Encoder& operator()(int64_t x) { return networkByteOrder(x); } + + Encoder& operator()(uint16_t x) { return networkByteOrder(x); } + Encoder& operator()(uint32_t x) { return networkByteOrder(x); } + Encoder& operator()(uint64_t x) { return networkByteOrder(x); } + + Encoder& operator()(float x) { return networkByteOrder(x); } + Encoder& operator()(double x) { return networkByteOrder(x); } + + void raw(const void* p, size_t n) { + this->addBytes(n); + out = std::copy((const char*)p, (const char*)p+n, out); + } + + void raw(char b) { this->addBytes(1); *out++=b; } + + template <class T> Encoder& littleEnd(T x) { + littleEndian(x); raw(&x, sizeof(x)); return *this; + } + + OutIter pos() const { return out; } + + private: + + template <class T> Encoder& networkByteOrder(T x) { + bigEndian(x); raw(&x, sizeof(x)); return *this; + } + + OutIter out; + }; + + template <class InIter> + class Decoder : public DecoderBase<Decoder<InIter> > { + public: + typedef DecoderBase<Decoder<InIter> > Base; + typedef InIter Iterator; + + Decoder(InIter i, size_t limit=Base::maxLimit()) : in(i) { + this->setLimit(limit); + } + + using DecoderBase<Decoder<InIter> >::operator(); + + // FIXME aconway 2008-03-10: wrong encoding, need packing support + Decoder& operator()(bool& x) { raw((char&)x); return *this; } + + Decoder& operator()(char& x) { raw((char&)x); return *this; } + Decoder& operator()(int8_t& x) { raw((char&)x); return *this; } + Decoder& operator()(uint8_t& x) { raw((char&)x); return *this; } + + Decoder& operator()(int16_t& x) { return networkByteOrder(x); } + Decoder& operator()(int32_t& x) { return networkByteOrder(x); } + Decoder& operator()(int64_t& x) { return networkByteOrder(x); } + + Decoder& operator()(uint16_t& x) { return networkByteOrder(x); } + Decoder& operator()(uint32_t& x) { return networkByteOrder(x); } + Decoder& operator()(uint64_t& x) { return networkByteOrder(x); } + + Decoder& operator()(float& x) { return networkByteOrder(x); } + Decoder& operator()(double& x) { return networkByteOrder(x); } + + void raw(void *p, size_t n) { + this->addBytes(n); + std::copy(in, in+n, (char*)p); + std::advance(in, n); + } + + void raw(char &b) { this->addBytes(1); b=*in++; } + + template <class T> Decoder& littleEnd(T& x) { + raw(&x, sizeof(x)); littleEndian(x); return *this; + } + + InIter pos() const { return in; } + + private: + + template <class T> Decoder& networkByteOrder(T& x) { + raw(&x, sizeof(x)); bigEndian(x); return *this; + } + + InIter in; + }; + + + class Size : public EncoderBase<Size> { + public: + Size() : size(0) {} + + operator size_t() const { return size; } + + using EncoderBase<Size>::operator(); + + // FIXME aconway 2008-03-10: wrong encoding, need packing support + Size& operator()(bool x) { size += sizeof(x); return *this; } + + Size& operator()(char x) { size += sizeof(x); return *this; } + Size& operator()(int8_t x) { size += sizeof(x); return *this; } + Size& operator()(uint8_t x) { size += sizeof(x); return *this; } + + Size& operator()(int16_t x) { size += sizeof(x); return *this; } + Size& operator()(int32_t x) { size += sizeof(x); return *this; } + Size& operator()(int64_t x) { size += sizeof(x); return *this; } + + Size& operator()(uint16_t x) { size += sizeof(x); return *this; } + Size& operator()(uint32_t x) { size += sizeof(x); return *this; } + Size& operator()(uint64_t x) { size += sizeof(x); return *this; } + + Size& operator()(float x) { size += sizeof(x); return *this; } + Size& operator()(double x) { size += sizeof(x); return *this; } + + // FIXME aconway 2008-04-03: optimize op()(Iter,Iter) + // for Iter with fixed-size value_type: + // distance(begin,end)*sizeof(value_type) + + void raw(const void*, size_t n){ size += n; } + + template <class T> Size& littleEnd(T) { size+= sizeof(T); return *this; } + + private: + size_t size; + }; + + // FIXME aconway 2008-03-11: rename to encoder(), decoder() + template <class InIter> static Decoder<InIter> decode(const InIter &i) { + return Decoder<InIter>(i); + } + + template <class OutIter> static Encoder<OutIter> encode(OutIter i) { + return Encoder<OutIter>(i); + } + + template <class T> static size_t size(const T& x) { return Size()(x); } + template <class Iter> static size_t size(const Iter& a, const Iter& z) { return Size()(a,z); } +}; + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_CODEC_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Command.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/Command.h new file mode 100644 index 0000000000..0fe023e520 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Command.h @@ -0,0 +1,62 @@ +#ifndef QPID_AMQP_0_10_COMMAND_H +#define QPID_AMQP_0_10_COMMAND_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Control.h" +#include "qpid/amqp_0_10/structs.h" + +namespace qpid { +namespace amqp_0_10 { + +struct CommandVisitor; +struct ConstCommandVisitor; +struct CommandHolder; +struct Command + : public Action, + public Visitable<CommandVisitor, ConstCommandVisitor, CommandHolder> +{ + using Action::getCommand; + Command* getCommand() { return this; } + uint8_t getCode() const; + uint8_t getClassCode() const; + const char* getName() const; + const char* getClassName() const; + + session::Header sessionHeader; +}; + +std::ostream& operator<<(std::ostream&, const Command&); + +template <class T> +struct CommandPacker : Packer<T> { + CommandPacker(T& t) : Packer<T>(t) {} + + template <class S> void serialize(S& s) { + s(this->data.sessionHeader); + Packer<T>::serialize(s); + } +}; + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_COMMAND_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/CommmandPacker.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/CommmandPacker.h new file mode 100644 index 0000000000..51ebfe8186 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/CommmandPacker.h @@ -0,0 +1,60 @@ +#ifndef QPID_AMQP_0_10_COMMMANDPACKER_H +#define QPID_AMQP_0_10_COMMMANDPACKER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/amqp_0_10/structs.h" + +namespace qpid { +namespace amqp_0_10 { + +/** + * Packer for commands - serialize session.header before pack bits. + */ +template <class T> +class CommmandPacker : public Packer<T> +{ + public: + CommmandPacker(T& t) : Packer<T>(t) {} + template <class S> void serialize(S& s) { s.split(*this); } + + template <class S> void encode(S& s) const { + s.sessionHeader( + Packer<T>::encode(s); + } + + template <class S> void decode(S& s) { + Bits bits; + s.littleEnd(bits); + PackedDecoder<S, Bits> decode(s, bits); + data.serialize(decode); + } + + + protected: + T& data; + + +}; +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_COMMMANDPACKER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Connection.cpp b/RC9/qpid/cpp/src/qpid/amqp_0_10/Connection.cpp new file mode 100644 index 0000000000..5b14d60ff5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Connection.cpp @@ -0,0 +1,138 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Connection.h" +#include "qpid/log/Statement.h" +#include "qpid/amqp_0_10/exceptions.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/ProtocolInitiation.h" + +namespace qpid { +namespace amqp_0_10 { + +using sys::Mutex; + +Connection::Connection(sys::OutputControl& o, const std::string& id, bool _isClient) + : frameQueueClosed(false), output(o), identifier(id), initialized(false), isClient(_isClient), buffered(0) +{} + +void Connection::setInputHandler(std::auto_ptr<sys::ConnectionInputHandler> c) { + connection = c; +} + +size_t Connection::decode(const char* buffer, size_t size) { + framing::Buffer in(const_cast<char*>(buffer), size); + if (isClient && !initialized) { + //read in protocol header + framing::ProtocolInitiation pi; + if (pi.decode(in)) { + //TODO: check the version is correct + QPID_LOG(trace, "RECV " << identifier << " INIT(" << pi << ")"); + } + initialized = true; + } + framing::AMQFrame frame; + while(frame.decode(in)) { + QPID_LOG(trace, "RECV [" << identifier << "]: " << frame); + connection->received(frame); + } + return in.getPosition(); +} + +bool Connection::canEncode() { + if (!frameQueueClosed) connection->doOutput(); + Mutex::ScopedLock l(frameQueueLock); + return (!isClient && !initialized) || !frameQueue.empty(); +} + +bool Connection::isClosed() const { + Mutex::ScopedLock l(frameQueueLock); + return frameQueueClosed; +} + +size_t Connection::encode(const char* buffer, size_t size) { + { // Swap frameQueue data into workQueue to avoid holding lock while we encode. + Mutex::ScopedLock l(frameQueueLock); + assert(workQueue.empty()); + workQueue.swap(frameQueue); + } + framing::Buffer out(const_cast<char*>(buffer), size); + if (!isClient && !initialized) { + framing::ProtocolInitiation pi(getVersion()); + pi.encode(out); + initialized = true; + QPID_LOG(trace, "SENT " << identifier << " INIT(" << pi << ")"); + } + size_t frameSize=0; + size_t encoded=0; + while (!workQueue.empty() && ((frameSize=workQueue.front().encodedSize()) <= out.available())) { + workQueue.front().encode(out); + QPID_LOG(trace, "SENT [" << identifier << "]: " << workQueue.front()); + workQueue.pop_front(); + encoded += frameSize; + if (workQueue.empty() && out.available() > 0) connection->doOutput(); + } + assert(workQueue.empty() || workQueue.front().encodedSize() <= size); + if (!workQueue.empty() && workQueue.front().encodedSize() > size) + throw InternalErrorException(QPID_MSG("Frame too large for buffer.")); + { + Mutex::ScopedLock l(frameQueueLock); + buffered -= encoded; + // Put back any frames we did not encode. + frameQueue.insert(frameQueue.begin(), workQueue.begin(), workQueue.end()); + workQueue.clear(); + } + return out.getPosition(); +} + +void Connection::activateOutput() { output.activateOutput(); } +void Connection::giveReadCredit(int32_t credit) { output.giveReadCredit(credit); } + +void Connection::close() { + // Close the output queue. + Mutex::ScopedLock l(frameQueueLock); + frameQueueClosed = true; +} + +void Connection::closed() { + connection->closed(); +} + +void Connection::send(framing::AMQFrame& f) { + { + Mutex::ScopedLock l(frameQueueLock); + if (!frameQueueClosed) + frameQueue.push_back(f); + buffered += f.encodedSize(); + } + activateOutput(); +} + +framing::ProtocolVersion Connection::getVersion() const { + return framing::ProtocolVersion(0,10); +} + +size_t Connection::getBuffered() const { + Mutex::ScopedLock l(frameQueueLock); + return buffered; +} + +}} // namespace qpid::amqp_0_10 diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Connection.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/Connection.h new file mode 100644 index 0000000000..743a7de3aa --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Connection.h @@ -0,0 +1,76 @@ +#ifndef QPID_AMQP_0_10_CONNECTION_H +#define QPID_AMQP_0_10_CONNECTION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/framing/AMQFrame.h" +#include "qpid/sys/ConnectionCodec.h" +#include "qpid/sys/ConnectionInputHandler.h" +#include "qpid/sys/ConnectionOutputHandler.h" +#include "qpid/sys/Mutex.h" +#include <boost/intrusive_ptr.hpp> +#include <memory> +#include <deque> + +namespace qpid { + +namespace sys { +class ConnectionInputHandlerFactory; +} + +namespace amqp_0_10 { + +class Connection : public sys::ConnectionCodec, + public sys::ConnectionOutputHandler +{ + typedef std::deque<framing::AMQFrame> FrameQueue; + + FrameQueue frameQueue; + FrameQueue workQueue; + bool frameQueueClosed; + mutable sys::Mutex frameQueueLock; + sys::OutputControl& output; + std::auto_ptr<sys::ConnectionInputHandler> connection; + std::string identifier; + bool initialized; + bool isClient; + size_t buffered; + + public: + Connection(sys::OutputControl&, const std::string& id, bool isClient); + void setInputHandler(std::auto_ptr<sys::ConnectionInputHandler> c); + size_t decode(const char* buffer, size_t size); + size_t encode(const char* buffer, size_t size); + bool isClosed() const; + bool canEncode(); + void 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; +}; + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_CONNECTION_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Control.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/Control.h new file mode 100644 index 0000000000..226f6f92a6 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Control.h @@ -0,0 +1,70 @@ +#ifndef QPID_AMQP_0_10_CONTROL_H +#define QPID_AMQP_0_10_CONTROL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Struct.h" + +namespace qpid { +namespace amqp_0_10 { + +struct Command; +struct Control; + +struct Action { // Base for commands & controls + virtual ~Action() {} + virtual Command* getCommand() { return 0; } + virtual Control* getControl() { return 0; } + + virtual const Command* getCommand() const { + return const_cast<Action*>(this)->getCommand(); + } + virtual const Control* getControl() const { + return const_cast<Action*>(this)->getControl(); + } + static const uint8_t SIZE=0; + static const uint8_t PACK=2; +}; + +struct ControlVisitor; +struct ConstControlVisitor; +struct ControlHolder; +struct Control + : public Action, + public Visitable<ControlVisitor, ConstControlVisitor, ControlHolder> +{ + using Action::getControl; + Control* getControl() { return this; } + uint8_t getCode() const; + uint8_t getClassCode() const; + const char* getName() const; + const char* getClassName() const; +}; +std::ostream& operator<<(std::ostream&, const Control&); + +template <SegmentType E> struct ActionType; +template <> struct ActionType<CONTROL> { typedef Control type; }; +template <> struct ActionType<COMMAND> { typedef Command type; }; + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_CONTROL_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Decimal.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/Decimal.h new file mode 100644 index 0000000000..50fc457c76 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Decimal.h @@ -0,0 +1,51 @@ +#ifndef TESTS_DECIMAL_H +#define TESTS_DECIMAL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <ostream> + +namespace qpid { +namespace amqp_0_10 { + +template <class E, class M> struct Decimal { + E exponent; + M mantissa; + + Decimal(E exp=0, M man=0) : exponent(exp), mantissa(man) {} + + bool operator==(const Decimal& d) const { + return exponent == d.exponent && mantissa == d.mantissa; + } + + // TODO aconway 2008-02-20: We could provide arithmetic operators + // if anybody really cares about this type. + + template <class S> void serialize(S& s) { s(exponent)(mantissa); } +}; + +template<class E, class M> +inline std::ostream& operator<<(std::ostream& o, const Decimal<E,M>& d) { + return o << "Decimal{" << d.mantissa << "/10^" << (int)d.exponent << "}"; +} +}} + +#endif /*!TESTS_DECIMAL_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Exception.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/Exception.h new file mode 100644 index 0000000000..6d526c1706 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Exception.h @@ -0,0 +1,96 @@ +#ifndef QPID_AMQP_0_10_EXCEPTION_H +#define QPID_AMQP_0_10_EXCEPTION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Exception.h" +#include "qpid/amqp_0_10/specification_fwd.h" + +namespace qpid { +namespace amqp_0_10 { + +/** + * Raised when the connection is unexpectedly closed. Sessions with + * non-0 timeout may be available for re-attachment on another connection. + */ +struct ConnectionException : public qpid::Exception { + // FIXME aconway 2008-04-04: Merge qpid::ConnectionException + // into this when the old code is removed. + typedef connection::CloseCode Code; + ConnectionException(Code c, const std::string m) + : qpid::Exception(m), code(c) {} + Code code; +}; + +/** + * Raised when a session is unexpectedly detached for any reason, or + * if an attempt is made to use a session that is not attached. + */ +struct SessionException : public qpid::Exception { + // FIXME aconway 2008-04-04: should not have a code at this level. + // Leave in place till old preview code is gone. + SessionException(int /*code*/, const std::string& msg) : qpid::Exception(msg) {} +}; + +/** Raised when the state of a session has been destroyed */ +struct SessionDestroyedException : public SessionException { + // FIXME aconway 2008-04-04: should not have a code at this level. + // Leave in place till old preview code is gone. + SessionDestroyedException(int code, const std::string& msg) : SessionException(code, msg){} +}; + +/** Raised when a session is destroyed due to an execution.exception */ +struct SessionAbortedException : public SessionDestroyedException { + typedef execution::ErrorCode Code; + SessionAbortedException(Code c, const std::string m) + : SessionDestroyedException(c, m), code(c) {} + Code code; +}; + +/** + * Raised when a session with 0 timeout is unexpectedly detached + * and therefore expires and is destroyed. + */ +struct SessionExpiredException : public SessionDestroyedException { + typedef session::DetachCode Code; + SessionExpiredException(Code c, const std::string m) + : SessionDestroyedException(c, m), code(c) {} + Code code; +}; + +/** + * Raised when a session with non-0 timeout is unexpectedly detached + * or if an attempt is made to use a session that is not attached. + * + * The session is not necessarily destroyed, it may be possible to + * re-attach. + */ +struct SessionDetachedException : public SessionException { + typedef session::DetachCode Code; + SessionDetachedException(Code c, const std::string m) + : SessionException(c, m), code(c) {} + Code code; +}; + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_EXCEPTION_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.cpp b/RC9/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.cpp new file mode 100644 index 0000000000..f1a59b9e27 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.cpp @@ -0,0 +1,50 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "FrameHeader.h" +#include <ios> +#include <iomanip> +#include <ostream> + +using namespace std; + +namespace qpid { +namespace amqp_0_10 { + +bool FrameHeader::operator==(const FrameHeader& x) const { + return flags == x.flags && + type == x.type && + size == x.size && + track == x.track && + channel == x.channel; +} + +std::ostream& operator<<(std::ostream& o, const FrameHeader& f) { + std::ios::fmtflags saveFlags = o.flags(); + return o << "Frame[" + << "flags=" << std::hex << std::showbase << int(f.getFlags()) << std::setiosflags(saveFlags) + << " type=" << f.getType() + << " size=" << f.getSize() + << " track=" << int(f.getTrack()) + << " channel=" << f.getChannel() + << "]"; +} + +}} // namespace qpid::amqp_0_10 diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.h new file mode 100644 index 0000000000..b2f0619f9b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.h @@ -0,0 +1,90 @@ +#ifndef QPID_AMQP_0_10_FRAMEHEADER_H +#define QPID_AMQP_0_10_FRAMEHEADER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/amqp_0_10/built_in_types.h" +#include <boost/shared_array.hpp> +#include <string.h> +#include <assert.h> +#include <iosfwd> + +namespace qpid { +namespace amqp_0_10 { + +enum FrameFlags { FIRST_SEGMENT=8, LAST_SEGMENT=4, FIRST_FRAME=2, LAST_FRAME=1 }; + +class FrameHeader { + public: + static const size_t SIZE=12; + static uint8_t trackFor(SegmentType type) { return type == 0 ? 0 : 1; } + + FrameHeader(uint8_t flags_=0, SegmentType type_=SegmentType(), uint16_t size_=0, uint8_t track_=0, uint16_t channel_=0) + : flags(flags_), type(type_), size(size_), track(track_), channel(channel_) + {} + + uint8_t getFlags() const { return flags; } + SegmentType getType() const { return type; } + /** @return size total size of of frame, including frame header. */ + uint16_t getSize() const { return size; } + /** @return size of frame data, excluding frame header. */ + uint16_t getDataSize() const { return size - SIZE; } + uint8_t getTrack() const { return track; } + uint16_t getChannel() const { return channel; } + + void setFlags(uint8_t flags_) { flags=flags_; } + /** Also sets the track. There is no setTrack() */ + void setType(SegmentType type_) { type=type_; track=trackFor(type); } + /** @param size total size of of frame, including frame header. */ + void setSize(uint16_t size_) { size = size_; } + /** @param size size of frame data, excluding frame header. */ + void setDataSize(uint16_t size_) { size = size_+SIZE; } + void setChannel(uint8_t channel_) { channel=channel_; } + + bool allFlags(uint8_t f) const { return (flags & f) == f; } + bool anyFlags(uint8_t f) const { return (flags & f); } + + void raiseFlags(uint8_t f) { flags |= f; } + void clearFlags(uint8_t f) { flags &= ~f; } + + bool isComplete() const { return allFlags(FIRST_FRAME | LAST_FRAME); } + + bool operator==(const FrameHeader&) const; + + template <class S> void serialize(S& s) { + uint8_t pad8=0; uint32_t pad32=0; + s(flags)(type)(size)(pad8)(track)(channel)(pad32); + } + + private: + uint8_t flags; + SegmentType type; + uint16_t size; + uint8_t track; + uint16_t channel; +}; + +std::ostream& operator<<(std::ostream&, const FrameHeader&); + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_FRAMEHEADER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Header.cpp b/RC9/qpid/cpp/src/qpid/amqp_0_10/Header.cpp new file mode 100644 index 0000000000..669c960e7f --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Header.cpp @@ -0,0 +1,34 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Header.h" + +namespace qpid { +namespace amqp_0_10 { + +std::ostream& operator<<(std::ostream& o, const Header& h) { + o << "Header["; + std::ostream_iterator<Struct32> i(o, " "); + std::copy(h.begin(), h.end(), i); + o << "]"; + return o; +} + +}} // namespace qpid::amqp_0_10 diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Header.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/Header.h new file mode 100644 index 0000000000..0ce6ad9135 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Header.h @@ -0,0 +1,53 @@ +#ifndef QPID_AMQP_0_10_HEADER_H +#define QPID_AMQP_0_10_HEADER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp_0_10/built_in_types.h" +#include "qpid/amqp_0_10/Struct32.h" +#include <vector> +#include <ostream> + +namespace qpid { +namespace amqp_0_10 { + +class Header : public std::vector<Struct32> { + public: + Header() {} + + template <class S> void serialize(S& s) { s.split(*this); } + template <class S> void encode(S& s) const { s(this->begin(), this->end()); } + template <class S> void decode(S& s); +}; + +template <class S> void Header::decode(S& s) { + this->clear(); + while (s.bytesRemaining() > 0) { + this->push_back(Struct32()); + s(this->back()); + } +} + +std::ostream& operator<<(std::ostream& o, const Header&); + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_HEADER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Holder.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/Holder.h new file mode 100644 index 0000000000..8712db6c86 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Holder.h @@ -0,0 +1,103 @@ +#ifndef QPID_AMQP_0_10_HOLDER_H +#define QPID_AMQP_0_10_HOLDER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/framing/Blob.h" +#include "apply.h" + +namespace qpid { +namespace amqp_0_10 { + +using framing::in_place; + +template <class Invokable> struct InvokeVisitor { + typedef void result_type; + Invokable& target; + InvokeVisitor(Invokable& i) : target(i) {} + + template <class Action> + void operator()(const Action& action) { action.invoke(target); } +}; + +template <class DerivedHolder, class BaseHeld, size_t Size> +class Holder : public framing::Blob<Size, BaseHeld> { + typedef framing::Blob<Size, BaseHeld> Base; + + public: + + Holder() {} + template <class T> explicit Holder(const T& value) : Base(value) {} + + using Base::operator=; + Holder& operator=(const BaseHeld& rhs); + + uint8_t getCode() const { return this->get()->getCode(); } + uint8_t getClassCode() const { return this->get()->getClassCode(); } + + template <class Invokable> void invoke(Invokable& i) const { + InvokeVisitor<Invokable> v(i); + apply(v, *this->get()); + } + + template <class S> void encode(S& s) const { + s(getClassCode())(getCode()); + } + + template <class S> void decode(S& s) { + uint8_t code, classCode; + s(classCode)(code); + static_cast<DerivedHolder*>(this)->set(classCode, code); + } + + template <class S> void serialize(S& s) { + s.split(*this); + qpid::amqp_0_10::apply(s, *this->get()); + } + + template <class T> T* getIf() { + return (getClassCode()==T::CLASS_CODE && getCode()==T::CODE) ? static_cast<T*>(this->get()) : 0; + } + + template <class T> const T* getIf() const { + return (getClassCode()==T::CLASS_CODE && getCode()==T::CODE) ? static_cast<T*>(this->get()) : 0; + } + + private: + struct Assign : public ApplyFunctor<void> { + Holder& holder; + Assign(Holder& x) : holder(x) {} + template <class T> void operator()(const T& rhs) { holder=rhs; } + }; +}; + +template <class D, class B, size_t S> +Holder<D,B,S>& Holder<D,B,S>::operator=(const B& rhs) { + Assign assign(*this); + qpid::amqp_0_10::apply(assign, rhs); + return *this; +} + + + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_HOLDER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Map.cpp b/RC9/qpid/cpp/src/qpid/amqp_0_10/Map.cpp new file mode 100644 index 0000000000..b517b8baba --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Map.cpp @@ -0,0 +1,66 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Map.h" +#include "qpid/amqp_0_10/Struct32.h" +#include "qpid/amqp_0_10/Array.h" +#include <ostream> + +namespace qpid { +namespace amqp_0_10 { + +MapValue::MapValue() : code(codeFor(uint8_t(0))), blob(in_place<uint8_t>(0)) {} + +MapValue::MapValue(const MapValue& x) : code(x.code), blob(x.blob) {} + +bool MapValue::operator==(const MapValue& x) const { + return code == x.code; // FIXME aconway 2008-04-01: incomplete +} + +struct OstreamVisitor : public MapValue::Visitor<std::ostream&> { + std::ostream& out; + OstreamVisitor(std::ostream& o) : out(o) {} + template <class T> std::ostream& operator()(const T& t) { + return out << t; + } +}; + +std::ostream& operator<<(std::ostream& o, const MapValue& m) { + o << typeName(m.getCode()) << ":"; + const_cast<MapValue&>(m).apply_visitor(OstreamVisitor(o)); + return o; +} + +std::ostream& operator<<(std::ostream& o, const Map::value_type& v) { + return o << v.first << "=" << v.second; +} +std::ostream& operator<<(std::ostream& o, const Map& map) { + o << "map["; + std::ostream_iterator<Map::value_type> i(o, " "); + std::copy(map.begin(), map.end(), i); + return o << "]"; +} + +uint32_t Map::contentSize() const { + // FIXME aconway 2008-04-03: preview to 0-10 mapping: +4 for count. + return /*4 +*/ Codec::Size()(begin(), end()); +} + +}} // namespace qpid::amqp_0_10 diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Map.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/Map.h new file mode 100644 index 0000000000..4093b1a0aa --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Map.h @@ -0,0 +1,188 @@ +#ifndef QPID_AMQP_0_10_MAP_H +#define QPID_AMQP_0_10_MAP_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on ang + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Exception.h" +#include "qpid/amqp_0_10/built_in_types.h" +#include "qpid/amqp_0_10/UnknownType.h" +#include "qpid/amqp_0_10/CodeForType.h" +#include "qpid/amqp_0_10/TypeForCode.h" +#include "qpid/amqp_0_10/Codec.h" +#include "qpid/framing/Blob.h" +#include <map> +#include <string> +#include <iosfwd> + +namespace qpid { +namespace amqp_0_10 { + +class Map; + +class MapValue { + public: + struct BadTypeException : public Exception {}; + + template <class R> struct Visitor { typedef R result_type; }; + + MapValue(); + MapValue(const MapValue& x); + template <class T> explicit MapValue(const T& t); + template <class T> MapValue& operator=(const T& t); + + template <class T> T* get(); + template <class T> const T* get() const; + + template <class V> typename V::result_type apply_visitor(V&); + template <class V> typename V::result_type apply_visitor(const V&); + + uint8_t getCode() const { return code; } + + bool operator==(const MapValue&) const; + + template <class S> void serialize(S& s) { s(code); s.split(*this); } + template <class S> void encode(S& s) const { + const_cast<MapValue*>(this)->apply_visitor(s); + } + template <class S> void decode(S& s) { + DecodeVisitor<S> dv(blob, s); + qpid::amqp_0_10::apply_visitor(dv, code); + } + + + private: + // TODO aconway 2008-04-15: Estimate required size, we will get a + // compile error from static_assert in Blob.h if the estimate is too + // low. We can't use sizeof() directly because #include Struct32.h + // creates a circular dependency. Needs a better solution. + static const size_t SIZE=256; + typedef framing::Blob<SIZE> Blob; + + template <class V> struct VisitVisitor; + template <class T> struct GetVisitor; + template <class D> struct DecodeVisitor; + + uint8_t code; + Blob blob; +}; + +class Map : public std::map<Str8, MapValue> { + public: + template <class S> void serialize(S& s) { s.split(*this); } + template <class S> void encode(S& s) const; + // Shortcut calculation for size. + void encode(Codec::Size& s) const { s.raw(0, contentSize() + 4/*size*/); } + + template <class S> void decode(S& s); + + private: + uint32_t contentSize() const; +}; + +std::ostream& operator<<(std::ostream&, const MapValue&); +std::ostream& operator<<(std::ostream&, const Map::value_type&); +std::ostream& operator<<(std::ostream&, const Map&); + +using framing::in_place; + +template <class T> MapValue::MapValue(const T& t) : code(codeFor(t)), blob(in_place<t>()) {} + +template <class T> MapValue& MapValue::operator=(const T& t) { + code=codeFor(t); + blob=t; + return *this; +} + +template <class V> struct MapValue::VisitVisitor { + typedef typename V::result_type result_type; + V& visitor; + Blob& blob; + VisitVisitor(V& v, Blob& b) : visitor(v), blob(b) {} + + template <class T> result_type operator()(T*) { + return visitor(*reinterpret_cast<T*>(blob.get())); + } +}; + +template <class V> typename V::result_type MapValue::apply_visitor(V& v) { + VisitVisitor<V> visitor(v, blob); + return qpid::amqp_0_10::apply_visitor(visitor, code); +} + +template <class R> struct MapValue::GetVisitor { + typedef R* result_type; + const MapValue::Blob& blob; + + GetVisitor(const MapValue::Blob& b) : blob(b) {} + + R* operator()(R& r) { return &r; } + template <class T> R* operator()(T&) { return 0; } +}; + +template <class D> struct MapValue::DecodeVisitor { + typedef void result_type; + MapValue::Blob& blob; + D& decoder; + DecodeVisitor(Blob& b, D& d) : blob(b), decoder(d) {} + + template <class T> void operator()(T*) { + T t; + decoder(t); + blob = t; + } +}; + +template <class T> T* MapValue::get() { return apply_visitor(GetVisitor<T>(blob)); } +template <class T> const T* MapValue::get() const { return apply_visitor(GetVisitor<const T>()); } + +template <class V> typename V::result_type MapValue::apply_visitor(const V& v) { + return apply_visitor(const_cast<V&>(v)); +} + +template <class S> void Map::encode(S& s) const { + // FIXME aconway 2008-04-03: replace preview mapping with 0-10 mapping: + // s(contentSize())(uint32_t(size())); // size, count + s(contentSize()); + for (const_iterator i = begin(); i != end(); ++i) + s(i->first)(i->second); // key (type value) +} + +template <class S> void Map::decode(S& s) { + uint32_t decodedSize /*, count*/; + // FIXME aconway 2008-04-03: replace preview mapping with 0-10 mapping: + // s(contentSize())(uint32_t(size())); // size, count + // s(decodedSize)(count); + s(decodedSize); + typename S::ScopedLimit l(s, decodedSize); // Make sure we don't overrun. + // FIXME aconway 2008-04-03: replace preview with 0-10: + // for ( ; count > 0; --count) { + while (s.bytesRemaining() > 0) { + key_type k; MapValue v; + s(k)(v); + insert(value_type(k,v)); + } +} + + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_MAP_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Packer.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/Packer.h new file mode 100644 index 0000000000..c38e3a7efa --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Packer.h @@ -0,0 +1,195 @@ +#ifndef QPID_PACKER_H +#define QPID_PACKER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/optional.hpp> +#include <boost/none.hpp> +#include "qpid/amqp_0_10/built_in_types.h" + +namespace qpid { +namespace amqp_0_10 { + +/** Serialization for optional values */ +template <class T> struct SerializableOptional { + boost::optional<T>& optional; + SerializableOptional(boost::optional<T>& x) : optional(x) {} + template <class S> void serialize(S& s) { + if (optional) + s(*optional); + } +}; + +}} + + +namespace boost { // For argument dependent lookup. + +template <class T> +qpid::amqp_0_10::SerializableOptional<T> serializable(boost::optional<T>& x) { + return qpid::amqp_0_10::SerializableOptional<T>(x); +} + +} // namespace boost + +namespace qpid { +namespace amqp_0_10 { + +/** "Encoder" that encodes a struct as a set of bit flags + * for all non-empty members. + */ +class PackBits { + public: + PackBits() : bit(1), bits(0) {} + + void setBit(bool b) { if (b) bits |= bit; bit <<= 1; } + uint32_t getBits() { return bits; } + + /** The bit is always set for non-optional values. */ + template <class T> + PackBits& operator()(const T&) { setBit(1); return *this; } + + /** For optional values the bit is set if the value is present. */ + template <class T> PackBits& operator()(const boost::optional<T>& opt) { + setBit(opt); return *this; + } + + /** Bits are special optional values */ + PackBits& operator()(Bit b) { setBit(b); return *this; } + + private: + uint32_t bit; + uint32_t bits; +}; + +/** Bit mask to encode a packable struct */ +template<class T> uint32_t packBits(const T& t) { + PackBits pack; + const_cast<T&>(t).serialize(pack); + return pack.getBits(); +} + +/** Decode members enabled by Bits */ +template <class Decoder, class Bits> +class PackedDecoder { + public: + PackedDecoder(Decoder& d, Bits b) : decode(d), bits(b) {} + + template <class T> PackedDecoder& operator()(T& t) { + if (bits & 1) + decode(t); + else + t = T(); + // FIXME aconway 2008-04-10: When we have all optionals + // represented by boost::optional the line above should be: + // throw CommandInvalidException("A required value was omitted."); + bits >>= 1; + return *this; + } + + template <class T> PackedDecoder& operator()(boost::optional<T>& opt) { + if (bits & 1) { + opt = T(); + decode(*opt); + } + else + opt = boost::none; + bits >>= 1; + return *this; + } + + private: + Decoder& decode; + Bits bits; +}; + +/** Metafunction to compute type to contain pack bits. */ +template <int Bytes> struct UintOfSize; +template <> struct UintOfSize<1> { typedef uint8_t type; }; +template <> struct UintOfSize<2> { typedef uint16_t type; }; +template <> struct UintOfSize<4> { typedef uint32_t type; }; + +/** + * Helper to serialize packed structs. + */ +template <class T> class Packer +{ + public: + typedef typename UintOfSize<T::PACK>::type Bits; + + Packer(T& t) : data(t) {} + + template <class S> void serialize(S& s) { s.split(*this); } + + template <class S> void encode(S& s) const { + Bits bits = packBits(data); + s.littleEnd(bits); + data.serialize(s); + } + + template <class S> void decode(S& s) { + Bits bits; + s.littleEnd(bits); + PackedDecoder<S, Bits> decode(s, bits); + data.serialize(decode); + } + + + protected: + T& data; +}; + +template <class T, uint8_t=T::SIZE> struct SizedPacker : public Packer<T> { + typedef typename UintOfSize<T::SIZE>::type Size; + + SizedPacker(T& t) : Packer<T>(t) {} + + template <class S> void serialize(S& s) { + s.split(*this); + } + + template <class S> void encode(S& s) const { + Codec::Size sizer; + this->data.serialize(sizer); + Size size=size_t(sizer)+T::PACK; // Size with pack bits. + s(size); + Packer<T>::encode(s); + } + + template <class S> void decode(S& s) { + Size size; + s(size); + typename S::ScopedLimit l(s, size); + Packer<T>::decode(s); + } + +}; + +template <class T> struct SizedPacker<T,0> : public Packer<T> { + SizedPacker(T& t) : Packer<T>(t) {} +}; + +}} // namespace qpid::amqp_0_10 + + + +#endif /*!QPID_PACKER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h new file mode 100644 index 0000000000..485b7ca6a8 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h @@ -0,0 +1,62 @@ +#ifndef QPID_AMQP_0_10_SERIALIZABLESTRING_H +#define QPID_AMQP_0_10_SERIALIZABLESTRING_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +namespace qpid { +namespace amqp_0_10 { + +/** Template for length-prefixed strings/arrays. + * Unique parameter allows creation of distinct SerializableString + * types with the smae T/SizeType + */ +template <class T, class SizeType, int Unique=0> +struct SerializableString : public std::basic_string<T> { + SerializableString() {} + template <class U> SerializableString(const U& u) : std::basic_string<T>(u) {} + template <class I> SerializableString(const I& i, const I& j) : std::basic_string<T>(i,j) {} + + using std::basic_string<T>::operator=; + + template <class S> void serialize(S& s) { s.split(*this); } + + template <class S> void encode(S& s) const { + s(SizeType(this->size()))(this->begin(), this->end()); + } + + template <class S> void decode(S& s) { + SizeType newSize; + s(newSize); + this->resize(newSize); + s(this->begin(), this->end()); + } +}; + +// TODO aconway 2008-02-29: separate ostream ops +template <class T, class SizeType> +std::ostream& operator<<(std::ostream& o, const SerializableString<T,SizeType>& s) { + const std::basic_string<T> str(s); + return o << str.c_str(); // TODO aconway 2008-02-29: why doesn't o<<str work? +} + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_SERIALIZABLESTRING_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/SessionHandler.cpp b/RC9/qpid/cpp/src/qpid/amqp_0_10/SessionHandler.cpp new file mode 100644 index 0000000000..0e57e4b3f1 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/SessionHandler.cpp @@ -0,0 +1,316 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +#include "SessionHandler.h" +#include "qpid/SessionState.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/AllInvoker.h" +#include "qpid/framing/enum.h" +#include "qpid/log/Statement.h" + + +#include <boost/bind.hpp> + +namespace qpid { +namespace amqp_0_10 { +using namespace framing; +using namespace std; + +SessionHandler::SessionHandler(FrameHandler* out, ChannelId ch) + : channel(ch, out), peer(channel), ignoring(false), sendReady(), receiveReady() {} + +SessionHandler::~SessionHandler() {} + +namespace { +bool isSessionControl(AMQMethodBody* m) { + return m && + m->amqpClassId() == SESSION_CLASS_ID; +} +bool isSessionDetachedControl(AMQMethodBody* m) { + return isSessionControl(m) && + m->amqpMethodId() == SESSION_DETACHED_METHOD_ID; +} + +session::DetachCode convert(uint8_t code) { + switch(code) { + case 0: return session::DETACH_CODE_NORMAL; + case 1: return session::DETACH_CODE_SESSION_BUSY; + case 2: return session::DETACH_CODE_TRANSPORT_BUSY; + case 3: return session::DETACH_CODE_NOT_ATTACHED; + case 4: default: return session::DETACH_CODE_UNKNOWN_IDS; + } +} + +} // namespace + +void SessionHandler::checkAttached() { + if (!getState()) + throw NotAttachedException( + QPID_MSG("Channel " << channel.get() << " is not attached")); + assert(getInHandler()); + assert(channel.next); +} + +void SessionHandler::invoke(const AMQMethodBody& m) { + framing::invoke(*this, m); +} + +void SessionHandler::handleIn(AMQFrame& f) { + // Note on channel states: a channel is attached if session != 0 + AMQMethodBody* m = f.getBody()->getMethod(); + try { + if (ignoring && !isSessionDetachedControl(m)) + return; + else if (isSessionControl(m)) + invoke(*m); + else { + checkAttached(); + if (!receiveReady) + throw IllegalStateException(QPID_MSG(getState()->getId() << ": Not ready to receive data")); + if (!getState()->receiverRecord(f)) + return; // Ignore duplicates. + if (getState()->receiverNeedKnownCompleted()) + sendCompletion(); + getInHandler()->handle(f); + } + } + catch(const SessionException& e) { + QPID_LOG(error, "Execution exception: " << e.what()); + framing::AMQP_AllProxy::Execution execution(channel); + AMQMethodBody* m = f.getMethod(); + SequenceNumber commandId; + if (getState()) commandId = getState()->receiverGetCurrent(); + execution.exception(e.code, commandId, m ? m->amqpClassId() : 0, m ? m->amqpMethodId() : 0, 0, e.what(), FieldTable()); + detaching(); + sendDetach(); + } + catch(const ChannelException& e){ + QPID_LOG(error, "Channel exception: " << e.what()); + peer.detached(name, e.code); + } + catch(const ConnectionException& e) { + QPID_LOG(error, "Connection exception: " << e.what()); + connectionException(e.code, e.getMessage()); + } + catch(const std::exception& e) { + QPID_LOG(error, "Unexpected exception: " << e.what()); + connectionException(connection::CLOSE_CODE_FRAMING_ERROR, e.what()); + } +} + +namespace { +bool isControl(const AMQFrame& f) { + return f.getMethod() && f.getMethod()->type() == framing::SEGMENT_TYPE_CONTROL; +} +bool isCommand(const AMQFrame& f) { + return f.getMethod() && f.getMethod()->type() == framing::SEGMENT_TYPE_COMMAND; +} +} // namespace + +void SessionHandler::handleOut(AMQFrame& f) { + checkAttached(); + if (!sendReady) + throw IllegalStateException(QPID_MSG(getState()->getId() << ": Not ready to send data")); + getState()->senderRecord(f); + if (isCommand(f) && getState()->senderNeedFlush()) { + peer.flush(false, false, true); + getState()->senderRecordFlush(); + } + channel.handle(f); +} + +void SessionHandler::checkName(const std::string& name) { + checkAttached(); + if (name != getState()->getId().getName()) + throw InvalidArgumentException( + QPID_MSG("Incorrect session name: " << name + << ", expecting: " << getState()->getId().getName())); +} + +void SessionHandler::attach(const std::string& name_, bool force) { + // Save the name for possible session-busy exception. Session-busy + // can be thrown before we have attached the handler to a valid + // SessionState, and in that case we need the name to send peer.detached + name = name_; + if (getState() && name == getState()->getId().getName()) + return; // Idempotent + if (getState()) + throw TransportBusyException( + QPID_MSG("Channel " << channel.get() << " already attached to " << getState()->getId())); + setState(name, force); + QPID_LOG(debug, "Attached channel " << channel.get() << " to " << getState()->getId()); + peer.attached(name); + if (getState()->hasState()) + peer.flush(true, true, true); + else + sendCommandPoint(getState()->senderGetCommandPoint()); +} + +void SessionHandler::attached(const std::string& name) { + checkName(name); +} + +void SessionHandler::detach(const std::string& name) { + checkName(name); + peer.detached(name, session::DETACH_CODE_NORMAL); + handleDetach(); +} + +void SessionHandler::detached(const std::string& name, uint8_t code) { + checkName(name); + ignoring = false; + if (code != session::DETACH_CODE_NORMAL) + channelException(convert(code), "session.detached from peer."); + else { + handleDetach(); + } +} + +void SessionHandler::handleDetach() { + sendReady = receiveReady = false; +} + +void SessionHandler::requestTimeout(uint32_t t) { + checkAttached(); + getState()->setTimeout(t); + peer.timeout(t); +} + +void SessionHandler::timeout(uint32_t t) { + checkAttached(); + getState()->setTimeout(t); +} + +void SessionHandler::commandPoint(const SequenceNumber& id, uint64_t offset) { + checkAttached(); + getState()->receiverSetCommandPoint(SessionPoint(id, offset)); + if (!receiveReady) { + receiveReady = true; + readyToReceive(); + } +} + +void SessionHandler::expected(const SequenceSet& commands, const Array& /*fragments*/) { + checkAttached(); + if (getState()->hasState()) { // Replay + if (commands.empty()) throw IllegalStateException( + QPID_MSG(getState()->getId() << ": has state but client is attaching as new session.")); + // TODO aconway 2008-05-12: support replay of partial commands. + // Here we always round down to the last command boundary. + SessionPoint expectedPoint = commands.empty() ? SequenceNumber(0) : SessionPoint(commands.front(),0); + SessionState::ReplayRange replay = getState()->senderExpected(expectedPoint); + sendCommandPoint(expectedPoint); + std::for_each(replay.begin(), replay.end(), out); // replay + } + else + sendCommandPoint(getState()->senderGetCommandPoint()); +} + +void SessionHandler::confirmed(const SequenceSet& commands, const Array& /*fragments*/) { + checkAttached(); + // Ignore non-contiguous confirmations. + if (!commands.empty() && commands.front() >= getState()->senderGetReplayPoint()) + getState()->senderConfirmed(commands.rangesBegin()->last()); +} + +void SessionHandler::completed(const SequenceSet& commands, bool timelyReply) { + checkAttached(); + getState()->senderCompleted(commands); + if (getState()->senderNeedKnownCompleted() || timelyReply) { + peer.knownCompleted(commands); + getState()->senderRecordKnownCompleted(); + } +} + +void SessionHandler::knownCompleted(const SequenceSet& commands) { + checkAttached(); + getState()->receiverKnownCompleted(commands); +} + +void SessionHandler::flush(bool expected, bool confirmed, bool completed) { + checkAttached(); + if (expected) { + SequenceSet expectSet; + if (getState()->hasState()) + expectSet.add(getState()->receiverGetExpected().command); + peer.expected(expectSet, Array()); + } + if (confirmed) { + SequenceSet confirmSet; + if (!getState()->receiverGetUnknownComplete().empty()) + confirmSet.add(getState()->receiverGetUnknownComplete().front(), + getState()->receiverGetReceived().command); + peer.confirmed(confirmSet, Array()); + } + if (completed) + peer.completed(getState()->receiverGetUnknownComplete(), true); +} + +void SessionHandler::gap(const SequenceSet& /*commands*/) { + throw NotImplementedException("session.gap not supported"); +} + +void SessionHandler::sendDetach() +{ + checkAttached(); + ignoring = true; + peer.detach(getState()->getId().getName()); +} + +void SessionHandler::sendCompletion() { + checkAttached(); + const SequenceSet& c = getState()->receiverGetUnknownComplete(); + peer.completed(c, getState()->receiverNeedKnownCompleted()); +} + +void SessionHandler::sendAttach(bool force) { + checkAttached(); + QPID_LOG(debug, "SessionHandler::sendAttach attach id=" << getState()->getId()); + peer.attach(getState()->getId().getName(), force); + if (getState()->hasState()) + peer.flush(true, true, true); + else + sendCommandPoint(getState()->senderGetCommandPoint()); +} + +void SessionHandler::sendCommandPoint(const SessionPoint& point) { + peer.commandPoint(point.command, point.offset); + if (!sendReady) { + sendReady = true; + readyToSend(); + } +} + +void SessionHandler::sendTimeout(uint32_t t) { + checkAttached(); + peer.requestTimeout(t); +} + +void SessionHandler::sendFlush() { + peer.flush(false, true, true); +} + +bool SessionHandler::ready() const { + return sendReady && receiveReady; +} + + +}} // namespace qpid::broker diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/SessionHandler.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/SessionHandler.h new file mode 100644 index 0000000000..016de454cc --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/SessionHandler.h @@ -0,0 +1,114 @@ +#ifndef QPID_AMQP_0_10_SESSIONHANDLER_H +#define QPID_AMQP_0_10_SESSIONHANDLER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/framing/ChannelHandler.h" +#include "qpid/framing/AMQP_AllProxy.h" +#include "qpid/framing/AMQP_AllOperations.h" +#include "qpid/SessionState.h" + +namespace qpid { + + +namespace amqp_0_10 { + +/** + * Base SessionHandler with logic common to both client and broker. + * + * A SessionHandler is associated with a channel and can be attached + * to a session state. + */ + +class SessionHandler : public framing::AMQP_AllOperations::SessionHandler, + public framing::FrameHandler::InOutHandler +{ + public: + SessionHandler(framing::FrameHandler* out=0, uint16_t channel=0); + ~SessionHandler(); + + void setChannel(uint16_t ch) { channel = ch; } + uint16_t getChannel() const { return channel.get(); } + + void setOutHandler(framing::FrameHandler& h) { channel.next = &h; } + + virtual SessionState* getState() = 0; + virtual framing::FrameHandler* getInHandler() = 0; + + // Non-protocol methods, called locally to initiate some action. + void sendDetach(); + void sendCompletion(); + void sendAttach(bool force); + void sendTimeout(uint32_t t); + void sendFlush(); + + /** True if the handler is ready to send and receive */ + bool ready() const; + + // Protocol methods + void attach(const std::string& name, bool force); + void attached(const std::string& name); + void detach(const std::string& name); + void detached(const std::string& name, uint8_t code); + + void requestTimeout(uint32_t t); + void timeout(uint32_t t); + + void commandPoint(const framing::SequenceNumber& id, uint64_t offset); + void expected(const framing::SequenceSet& commands, const framing::Array& fragments); + void confirmed(const framing::SequenceSet& commands,const framing::Array& fragments); + void completed(const framing::SequenceSet& commands, bool timelyReply); + void knownCompleted(const framing::SequenceSet& commands); + void flush(bool expected, bool confirmed, bool completed); + void gap(const framing::SequenceSet& commands); + + protected: + virtual void invoke(const framing::AMQMethodBody& m); + + virtual void setState(const std::string& sessionName, bool force) = 0; + virtual void channelException(framing::session::DetachCode code, const std::string& msg) = 0; + virtual void connectionException(framing::connection::CloseCode code, const std::string& msg) = 0; + virtual void detaching() = 0; + + // Notification of events + virtual void readyToSend() {} + virtual void readyToReceive() {} + + virtual void handleDetach(); + virtual void handleIn(framing::AMQFrame&); + virtual void handleOut(framing::AMQFrame&); + + void checkAttached(); + void checkName(const std::string& name); + + framing::ChannelHandler channel; + framing::AMQP_AllProxy::Session peer; + bool ignoring; + bool sendReady, receiveReady; + std::string name; + + private: + void sendCommandPoint(const SessionPoint&); +}; +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_SESSIONHANDLER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Struct.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/Struct.h new file mode 100644 index 0000000000..c0cea09c60 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Struct.h @@ -0,0 +1,60 @@ +#ifndef QPID_AMQP_0_10_STRUCT_H +#define QPID_AMQP_0_10_STRUCT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "built_in_types.h" +#include <iosfwd> + +namespace qpid { +namespace amqp_0_10 { + +// Base classes for complex types. + +template <class V, class CV, class H> struct Visitable { + typedef V Visitor; + typedef CV ConstVisitor; + typedef H Holder; + + virtual ~Visitable() {} + virtual void accept(Visitor&) = 0; + virtual void accept(ConstVisitor&) const = 0; +}; + + +// Note: only coded structs inherit from Struct. +struct StructVisitor; +struct ConstStructVisitor; +struct StructHolder; +struct Struct + : public Visitable<StructVisitor, ConstStructVisitor, StructHolder> +{ + uint8_t getCode() const; + uint8_t getPack() const; + uint8_t getSize() const; + uint8_t getClassCode() const; +}; +std::ostream& operator<<(std::ostream&, const Struct&); + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_STRUCT_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Struct32.cpp b/RC9/qpid/cpp/src/qpid/amqp_0_10/Struct32.cpp new file mode 100644 index 0000000000..541f02bcc4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Struct32.cpp @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Struct32.h" + +namespace qpid { +namespace amqp_0_10 { + +Struct32::Struct32() { + // FIXME aconway 2008-04-16: this is only here to force a valid + // default-constructed Struct32 for serialize tests, clean up. + *this = in_place<message::MessageResumeResult>(); +} + +std::ostream& operator<<(std::ostream& o, const Struct32& s) { + return o << static_cast<const StructHolder&>(s); +} + +}} // namespace qpid::amqp_0_10 diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Struct32.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/Struct32.h new file mode 100644 index 0000000000..2ed73e0b4c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Struct32.h @@ -0,0 +1,64 @@ +#ifndef QPID_AMQP_0_10_STRUCT32_H +#define QPID_AMQP_0_10_STRUCT32_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/amqp_0_10/StructHolder.h" + +namespace qpid { +namespace amqp_0_10 { + +class Struct32 : public StructHolder +{ + public: + Struct32(); + + template <class T> explicit Struct32(const T& t) : StructHolder(t) {} + + template <class S> void serialize(S& s) { s.split(*this); } + + using StructHolder::operator=; + + template <class S> void encode(S& s) const { + s(contentSize()); + const_cast<Struct32*>(this)->StructHolder::serialize(s); + } + + template <class S> void decode(S& s) { + uint32_t contentSz; + s(contentSz); + typename S::ScopedLimit l(s, contentSz); + StructHolder::serialize(s); + } + + private: + uint32_t contentSize() const { + return Codec::size(static_cast<const StructHolder&>(*this)); + } + +}; + +std::ostream& operator<<(std::ostream&, const Struct32&); + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_STRUCT32_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Unit.cpp b/RC9/qpid/cpp/src/qpid/amqp_0_10/Unit.cpp new file mode 100644 index 0000000000..75ea1c1b30 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Unit.cpp @@ -0,0 +1,65 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Unit.h" +#include "Codec.h" + +namespace qpid { +namespace amqp_0_10 { + +void Unit::updateVariant() { + switch (header.getType()) { + case CONTROL: variant=ControlHolder(); break; + case COMMAND: variant=CommandHolder(); break; + case HEADER: variant=Header(); break; + case BODY: variant=Body(header.getDataSize()); break; + default: assert(0); // FIXME aconway 2008-04-14: exception? + } +} + +struct GetTypeVisitor : public boost::static_visitor<SegmentType> { + SegmentType operator()(const CommandHolder& ) const { return COMMAND; } + SegmentType operator()(const ControlHolder& ) const { return CONTROL; } + SegmentType operator()(const Header& ) const { return HEADER; } + SegmentType operator()(const Body&) const { return BODY; } +}; + +struct GetFlagsVisitor : public boost::static_visitor<uint8_t> { + uint8_t operator()(const CommandHolder& ) const { return FIRST_FRAME|LAST_FRAME|FIRST_SEGMENT; } + uint8_t operator()(const ControlHolder& ) const { return FIRST_FRAME|LAST_FRAME|FIRST_SEGMENT; } + uint8_t operator()(const Header& ) const { return FIRST_FRAME|LAST_FRAME; } + uint8_t operator()(const Body&) const { return 0; } +}; + +void Unit::updateHeader(uint8_t flags) { + GetFlagsVisitor flagger; + header.setFlags(flags | variant.apply_visitor(flagger)); + GetTypeVisitor getter; + header.setType(variant.apply_visitor(getter)); + header.setDataSize(Codec::size(*this)); + // track automatically set from type. + // no channel specified at this point. +} + +std::ostream& operator<<(std::ostream& o, const Unit& u) { + return o << u.getHeader() << " " << u.variant.type().name() << "[" << u.variant << "]"; +} + +}} // namespace qpid::amqp_0_10 diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/Unit.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/Unit.h new file mode 100644 index 0000000000..0229e07419 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/Unit.h @@ -0,0 +1,82 @@ +#ifndef QPID_AMQP_0_10_UNIT_H +#define QPID_AMQP_0_10_UNIT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/amqp_0_10/ControlHolder.h" +#include "qpid/amqp_0_10/CommandHolder.h" +#include "qpid/amqp_0_10/Header.h" +#include "qpid/amqp_0_10/Body.h" +#include "qpid/amqp_0_10/FrameHeader.h" + +#include <boost/variant.hpp> +#include <ostream> + +namespace qpid { +namespace amqp_0_10 { + +/** + * A Unit contains a frame header and associated value. + * For all types except BODY the frame header is for a complete segment. + */ +class Unit { + public: + explicit Unit(const FrameHeader& h=FrameHeader()) : header(h) { updateVariant(); } + + /** + *@param flags: is ORed with the required flags for type T. + */ + template <class T> + explicit Unit(const T& t, uint8_t flags=0) : variant(t) { updateHeader(flags); } + + void setHeader(FrameHeader& h) { header = h; updateVariant(); } + const FrameHeader& getHeader() const { return header; } + + template<class T> const T* get() const { return boost::get<T>(&variant); } + template<class T> T* get() { return boost::get<T>(&variant); } + template<class T> Unit& operator=(const T& t) { variant=t; return *this; } + + template <class V> typename V::result_type applyVisitor(V& v) const { + variant.apply_visitor(v); + } + + template <class S> void serialize(S& s) { variant.apply_visitor(s); s.split(*this); } + template <class S> void encode(S&) const {} + template <class S> void decode(S&) { updateHeader(header.getFlags()); } + + private: + typedef boost::variant<ControlHolder, CommandHolder, Header, Body> Variant; + + void updateHeader(uint8_t flags); + void updateVariant(); + + Variant variant; + FrameHeader header; + + friend std::ostream& operator<<(std::ostream& o, const Unit& u); +}; + +std::ostream& operator<<(std::ostream& o, const Unit& u); + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_UNIT_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/UnitHandler.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/UnitHandler.h new file mode 100644 index 0000000000..93a8ce573a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/UnitHandler.h @@ -0,0 +1,35 @@ +#ifndef QPID_AMQP_0_10_UNITHANDLER_H +#define QPID_AMQP_0_10_UNITHANDLER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/framing/Handler.h" + +namespace qpid { +namespace amqp_0_10 { + +class Unit; +typedef framing::Handler<const Unit&> UnitHandler; + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_UNITHANDLER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/UnknownStruct.cpp b/RC9/qpid/cpp/src/qpid/amqp_0_10/UnknownStruct.cpp new file mode 100644 index 0000000000..35445054c9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/UnknownStruct.cpp @@ -0,0 +1,34 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/amqp_0_10/StructVisitor.h" +#include "qpid/amqp_0_10/UnknownStruct.h" + +namespace qpid { +namespace amqp_0_10 { + +void UnknownStruct::accept(Visitor& v) { v.visit(*this); } +void UnknownStruct::accept(ConstVisitor& v) const { v.visit(*this); } +std::ostream& operator<<(std::ostream& o, const UnknownStruct& u) { + return o << "UnknownStruct[class=" << u.getClassCode() << " code=" << u.getCode() << "]"; +} + +}} // namespace qpid::amqp_0_10 diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/UnknownStruct.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/UnknownStruct.h new file mode 100644 index 0000000000..1c66d8e6af --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/UnknownStruct.h @@ -0,0 +1,55 @@ +#ifndef QPID_AMQP_0_10_UNKNOWNSTRUCT_H +#define QPID_AMQP_0_10_UNKNOWNSTRUCT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp_0_10/Struct.h" +#include <string> + +namespace qpid { +namespace amqp_0_10 { + +class UnknownStruct : public Struct { + public: + static const uint8_t SIZE=4; + static const uint8_t PACK=2; + + template <class S> void serialize(S& s) { s.split(*this); s(data.begin(), data.end()); } + template <class S> void encode(S&) const { } + template <class S> void decode(S& s) { data.resize(s.bytesRemaining()); } + + UnknownStruct(uint8_t cc=0, uint8_t c=0) : classCode(cc), code(c) {} + void accept(Visitor&); + void accept(ConstVisitor&) const; + + uint8_t getClassCode() const { return classCode; } + uint8_t getCode() const { return code; } + + private: + uint8_t classCode, code; + std::string data; +}; + +std::ostream& operator<<(std::ostream&, const UnknownStruct&); + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_UNKNOWNSTRUCT_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp b/RC9/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp new file mode 100644 index 0000000000..844891d732 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp @@ -0,0 +1,56 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "UnknownType.h" +#include <boost/range/iterator_range.hpp> +#include <ostream> + +namespace qpid { +namespace amqp_0_10 { + +UnknownType::Width UnknownType::WidthTable[16] = { + { 1, 0 }, + { 2, 0 }, + { 4, 0 }, + { 8, 0 }, + { 16, 0 }, + { 32, 0 }, + { 64, 0 }, + { 128, 0 }, + { 0, 1 }, + { 0, 2 }, + { 0, 4 }, + { -1, -1 }, // Invalid + { 5, 0 }, + { 9, 0 }, + { -1, -1 }, // Invalid + { 0, 0 } +}; + +int UnknownType::fixed() const { return WidthTable[code>>4].fixed; } +int UnknownType::variable() const { return WidthTable[code>>4].variable; } +UnknownType::UnknownType(uint8_t c) : code(c) { data.resize(fixed()); } + +std::ostream& operator<<(std::ostream& o, const UnknownType& u) { + return o << boost::make_iterator_range(u.begin(), u.end()) << std::endl; +} + +}} // namespace qpid::amqp_0_10 + diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h new file mode 100644 index 0000000000..1e4aa04bf4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h @@ -0,0 +1,87 @@ +#ifndef QPID_AMQP_0_10_UNKNOWNTYPE_H +#define QPID_AMQP_0_10_UNKNOWNTYPE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <vector> +#include <iosfwd> +#include <stdint.h> + +namespace qpid { +namespace amqp_0_10 { + +/** Encode/decode an unknown type based on typecode. */ +class UnknownType { + public: + UnknownType(uint8_t code=0); + uint8_t getCode() const { return code; } + /** Size of fixed type or 0 if not fixed/0-length. -1 invalid */ + int fixed() const; + /** Bytes in size type for variable width. -1 invalid */ + int variable() const; + + typedef std::vector<char>::const_iterator const_iterator; + const_iterator begin() const { return data.begin(); } + const_iterator end() const { return data.end(); } + size_t size() const { return data.size(); } + + template <class S> void serialize(S& s) { s.split(*this); } + template <class S> void encode(S& s) const; + template <class S> void decode(S& s); + + private: + uint8_t code; + struct Width { int fixed; int variable; }; + static Width WidthTable[16]; + + std::vector<char> data; +}; + +template <class S> void UnknownType::encode(S& s) const { + switch (variable()) { + case 0: break; + case 1: s(uint8_t(data.size())); break; + case 2: s(uint16_t(data.size())); break; + case 4: s(uint32_t(data.size())); break; + } + s(data.begin(), data.end()); +} + +template <class S> void UnknownType::decode(S& s) { + uint32_t s8; + uint32_t s16; + uint32_t s32; + switch (variable()) { + case 0: break; + case 1: s(s8); data.resize(s8); break; + case 2: s(s16); data.resize(s16); break; + case 4: s(s32); data.resize(s32); break; + } + s(data.begin(), data.end()); +} + +inline uint8_t codeFor(const UnknownType& u) { return u.getCode(); } + +std::ostream& operator<<(std::ostream&, const UnknownType&); + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_UNKNOWNTYPE_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/apply.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/apply.h new file mode 100644 index 0000000000..f32b3482ef --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/apply.h @@ -0,0 +1,86 @@ +#ifndef QPID_AMQP_0_10_APPLY_H +#define QPID_AMQP_0_10_APPLY_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <boost/optional.hpp> + +namespace qpid { +namespace amqp_0_10 { + +template <class F, class R=typename F::result_type> struct FunctionAndResult { + F* functor; + boost::optional<R> result; + + FunctionAndResult() : functor(0) {} + template <class T> void invoke(T& t) { result=(*functor)(t); } + template <class T> void invoke(const T& t) { result=(*functor)(t); } + R getResult() { return *result; } +}; + +// void result is special case. +template <class F> struct FunctionAndResult<F, void> { + F* functor; + + FunctionAndResult() : functor(0) {} + template <class T> void invoke(T& t) { (*functor)(t); } + void getResult() {} +}; + +// Metafunction returning correct abstract visitor for Visitable type. +template <class Visitable> struct VisitorType { + typedef typename Visitable::Visitor type; +}; +template <class Visitable> struct VisitorType<const Visitable> { + typedef typename Visitable::ConstVisitor type; +}; + +template <class Visitor, class F> +struct ApplyVisitorBase : public Visitor, public FunctionAndResult<F> {}; + +// Specialize for each visitor type +template <class Visitable, class F> struct ApplyVisitor; + +/** Apply a functor to a visitable object. + * The functor can have operator() overloads for each visitable type + * and/or templated operator(). + */ +template <class F, class Visitable> +typename F::result_type apply(F& functor, Visitable& visitable) { + ApplyVisitor<typename VisitorType<Visitable>::type, F> visitor; + visitor.functor=&functor; + visitable.accept(visitor); + return visitor.getResult(); +} + +template <class F, class Visitable> +typename F::result_type apply(const F& functor, Visitable& visitable) { + ApplyVisitor<typename VisitorType<Visitable>::type, const F> visitor; + visitor.functor=&functor; + visitable.accept(visitor); + return visitor.getResult(); +} + +template <class R> struct ApplyFunctor { typedef R result_type; }; + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_APPLY_H*/ diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h b/RC9/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h new file mode 100644 index 0000000000..196e02a302 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h @@ -0,0 +1,171 @@ +#ifndef QPID_AMQP_0_10_BUILT_IN_TYPES_H +#define QPID_AMQP_0_10_BUILT_IN_TYPES_H +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Serializer.h" +#include "qpid/framing/Uuid.h" +#include "qpid/sys/IntegerTypes.h" +#include "qpid/sys/Time.h" +#include "Decimal.h" +#include "SerializableString.h" +#include <boost/array.hpp> +#include <boost/range/iterator_range.hpp> +#include <string> +#include <ostream> +#include <vector> + +/**@file Mapping from built-in AMQP types to C++ types */ + +namespace qpid { + +namespace framing { +class SequenceNumber; +class SequenceSet; +} + +namespace amqp_0_10 { + +/** Wrapper that behaves like type T but is a distinct type for + * overloading purposes. Unique allows multiple distinc wrappers. + */ +template <class T, int Unique=0> struct Wrapper { + T value; + Wrapper() {} + Wrapper(const T& x) : value(x) {} + Wrapper& operator=(const T& x) { value=x; return *this; } + operator T&() { return value; } + operator const T&() const { return value; } + template <class S> void serialize(S& s) { s(value); } +}; + +template<class T> +inline std::ostream& operator<<(std::ostream& o, const Wrapper<T>& w) { + return o << w.value; +} + +/** Void type */ +struct Void { template <class S> void serialize(S&) {} }; +inline std::ostream& operator<<(std::ostream& o, const Void&) { return o; } + +/** Bit is a presence indicator - an optional value with no encoding. */ +struct Bit : public Wrapper<bool> { + Bit(bool b=false) : Wrapper<bool>(b) {} + using Wrapper<bool>::operator=; + template <class S> void serialize(S& s) { s.split(*this); } + template <class S> void encode(S&) const { } + template <class S> void decode(S&) { *this = true; } +}; + +inline std::ostream& operator<<(std::ostream& o, const Bit& b) { + return o << bool(b); +} + +// Fixed size types +typedef bool Boolean; +typedef char Char; +typedef int8_t Int8; +typedef int16_t Int16; +typedef int32_t Int32; +typedef int64_t Int64; +typedef uint8_t Uint8; +typedef uint16_t Uint16; +typedef uint32_t Uint32; +typedef uint64_t Uint64; +typedef Wrapper<uint32_t> CharUtf32; + +template <size_t N> struct Bin : public boost::array<char, N> { + template <class S> void serialize(S& s) { s.raw(this->begin(), this->size()); } +}; + +template <size_t N> std::ostream& operator<<(std::ostream& o, const Bin<N>& b) { + return o << boost::make_iterator_range(b.begin(), b.end()); +} + +template <> struct Bin<1> : public boost::array<char, 1> { + Bin(char c=0) { this->front() = c; } + operator char() { return this->front(); } + template <class S> void serialize(S& s) { s(front()); } +}; + +typedef Bin<1> Bin8; +typedef Bin<128> Bin1024; +typedef Bin<16> Bin128; +typedef Bin<2> Bin16; +typedef Bin<32> Bin256; +typedef Bin<4> Bin32; +typedef Bin<5> Bin40; +typedef Bin<64> Bin512; +typedef Bin<8> Bin64; +typedef Bin<9> Bin72; + +typedef double Double; +typedef float Float; +typedef framing::SequenceNumber SequenceNo; +using framing::Uuid; +typedef sys::AbsTime Datetime; + +typedef Decimal<Uint8, Int32> Dec32; +typedef Decimal<Uint8, Int64> Dec64; + +// Variable width types + +typedef SerializableString<Uint8, Uint8> Vbin8; +typedef SerializableString<char, Uint8, 1> Str8Latin; +typedef SerializableString<char, Uint8> Str8; +typedef SerializableString<Uint16, Uint8> Str8Utf16; + +typedef SerializableString<Uint8, Uint16> Vbin16; +typedef SerializableString<char, Uint16, 1> Str16Latin; +typedef SerializableString<char, Uint16> Str16; +typedef SerializableString<Uint16, Uint16> Str16Utf16; + +typedef SerializableString<Uint8, Uint32> Vbin32; + +typedef framing::SequenceSet SequenceSet; + +// Forward declare class types. +class Map; +class Struct32; +class UnknownType; + +template <class T> struct ArrayDomain; +typedef ArrayDomain<UnknownType> Array; + +// FIXME aconway 2008-04-08: TODO +struct ByteRanges { template <class S> void serialize(S&) {} }; +struct List { template <class S> void serialize(S&) {} }; + +// FIXME aconway 2008-03-10: dummy ostream operators +inline std::ostream& operator<<(std::ostream& o, const ByteRanges&) { return o; } +inline std::ostream& operator<<(std::ostream& o, const SequenceSet&) { return o; } +inline std::ostream& operator<<(std::ostream& o, const List&) { return o; } + +enum SegmentType { CONTROL, COMMAND, HEADER, BODY }; + +inline SerializeAs<SegmentType, uint8_t> serializable(SegmentType& st) { + return SerializeAs<SegmentType, uint8_t>(st); +} + + +}} // namespace qpid::amqp_0_10 + +#endif diff --git a/RC9/qpid/cpp/src/qpid/amqp_0_10/complex_types.cpp b/RC9/qpid/cpp/src/qpid/amqp_0_10/complex_types.cpp new file mode 100644 index 0000000000..656d363ba6 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/amqp_0_10/complex_types.cpp @@ -0,0 +1,84 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/amqp_0_10/UnknownStruct.h" +#include "qpid/amqp_0_10/ApplyCommand.h" +#include "qpid/amqp_0_10/ApplyControl.h" +#include "qpid/amqp_0_10/ApplyStruct.h" +#include "qpid/amqp_0_10/apply.h" +#include <iostream> + +namespace qpid { +namespace amqp_0_10 { +// Functors for getting static values from a visitable base type. + +#define QPID_STATIC_VALUE_GETTER(NAME, TYPE, VALUE) \ + struct NAME : public ApplyFunctor<TYPE> { \ + template <class T> TYPE operator()(const T&) const { return T::VALUE; }\ + } + +QPID_STATIC_VALUE_GETTER(GetCode, uint8_t, CODE); +QPID_STATIC_VALUE_GETTER(GetSize, uint8_t, SIZE); +QPID_STATIC_VALUE_GETTER(GetPack, uint8_t, PACK); +QPID_STATIC_VALUE_GETTER(GetClassCode, uint8_t, CLASS_CODE); +QPID_STATIC_VALUE_GETTER(GetName, const char*, NAME); +QPID_STATIC_VALUE_GETTER(GetClassName, const char*, CLASS_NAME); + + +uint8_t Command::getCode() const { return apply(GetCode(), *this); } +uint8_t Command::getClassCode() const { return apply(GetClassCode(), *this); } +const char* Command::getName() const { return apply(GetName(), *this); } +const char* Command::getClassName() const { return apply(GetClassName(), *this); } + +uint8_t Control::getCode() const { return apply(GetCode(), *this); } +uint8_t Control::getClassCode() const { return apply(GetClassCode(), *this); } +const char* Control::getName() const { return apply(GetName(), *this); } +const char* Control::getClassName() const { return apply(GetClassName(), *this); } + +// Special cases for UnknownStruct +struct GetStructCode : public GetCode { + using GetCode::operator(); + uint8_t operator()(const UnknownStruct& u) const { return u.getCode(); } +}; + +struct GetStructClassCode : public GetClassCode { + using GetClassCode::operator(); + uint8_t operator()(const UnknownStruct& u) const { return u.getClassCode(); } +}; + +uint8_t Struct::getCode() const { return apply(GetStructCode(), *this); } +uint8_t Struct::getClassCode() const { return apply(GetStructClassCode(), *this); } +uint8_t Struct::getPack() const { return apply(GetPack(), *this); } +uint8_t Struct::getSize() const { return apply(GetSize(), *this); } + +struct PrintVisitor { + typedef std::ostream& result_type; + std::ostream& out; + PrintVisitor(std::ostream& o) : out(o) {} + template <class T> result_type operator()(const T& t) const { return out << t; } +}; + +std::ostream& operator<<(std::ostream& o, const Command& x) { return apply(PrintVisitor(o), x); } +std::ostream& operator<<(std::ostream& o, const Control& x) { return apply(PrintVisitor(o), x); } +std::ostream& operator<<(std::ostream& o, const Struct& x) { return apply(PrintVisitor(o), x); } + +}} // namespace qpid::amqp_0_10 + diff --git a/RC9/qpid/cpp/src/qpid/assert.cpp b/RC9/qpid/cpp/src/qpid/assert.cpp new file mode 100644 index 0000000000..5d039da528 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/assert.cpp @@ -0,0 +1,45 @@ +#ifndef QPID_ASSERT_CPP +#define QPID_ASSERT_CPP + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <sstream> +#include <iostream> +#include "qpid/framing/reply_exceptions.h" +#include <stdlib.h> + +namespace qpid { + +void assert_fail(char const * expr, char const * function, char const * file, long line) { + std::ostringstream msg; + msg << "Internal error: " << expr << " in function " << function + << "(" << file << ":" << line << ")"; +#ifdef NDEBUG + throw framing::InternalErrorException(msg.str()); +#else + std::cerr << msg << std::endl; + abort(); +#endif +} + +} // namespace qpid + +#endif /*!QPID_ASSERT_CPP*/ diff --git a/RC9/qpid/cpp/src/qpid/assert.h b/RC9/qpid/cpp/src/qpid/assert.h new file mode 100644 index 0000000000..49e7c5355d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/assert.h @@ -0,0 +1,38 @@ +#ifndef QPID_ASSERT_H +#define QPID_ASSERT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/current_function.hpp> + +/** + * Abort if !expr in debug mode, throw an exception if NDEBUG is set. + */ +#define QPID_ASSERT(expr) ((expr) ? static_cast<void>(0) : ::qpid::assert_fail(#expr, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__)) + +namespace qpid { + +void assert_fail(char const * expr, char const * function, char const * file, long line); + +} // namespace qpid + +#endif /*!QPID_ASSERT_H*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/AclModule.h b/RC9/qpid/cpp/src/qpid/broker/AclModule.h new file mode 100644 index 0000000000..4bb6ca12b4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/AclModule.h @@ -0,0 +1,257 @@ +#ifndef QPID_ACLMODULE_ACL_H +#define QPID_ACLMODULE_ACL_H + + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + + +#include "qpid/shared_ptr.h" +#include "qpid/RefCounted.h" +#include <map> +#include <set> +#include <string> + + +namespace qpid { + +namespace acl { + +enum ObjectType {OBJ_QUEUE, OBJ_EXCHANGE, OBJ_BROKER, OBJ_LINK, + OBJ_METHOD, OBJECTSIZE}; // OBJECTSIZE must be last in list +enum Action {ACT_CONSUME, ACT_PUBLISH, ACT_CREATE, ACT_ACCESS, ACT_BIND, + ACT_UNBIND, ACT_DELETE, ACT_PURGE, ACT_UPDATE, + ACTIONSIZE}; // ACTIONSIZE must be last in list +enum Property {PROP_NAME, PROP_DURABLE, PROP_OWNER, PROP_ROUTINGKEY, + PROP_PASSIVE, PROP_AUTODELETE, PROP_EXCLUSIVE, PROP_TYPE, + PROP_ALTERNATE, PROP_QUEUENAME, PROP_SCHEMAPACKAGE, + PROP_SCHEMACLASS}; +enum AclResult {ALLOW, ALLOWLOG, DENY, DENYLOG}; + +} // namespace acl + +namespace broker { + + +class AclModule +{ + +public: + + // effienty turn off ACL on message transfer. + virtual bool doTransferAcl()=0; + + virtual bool authorise(const std::string& id, const acl::Action& action, const acl::ObjectType& objType, const std::string& name, + std::map<acl::Property, std::string>* params=0)=0; + virtual bool authorise(const std::string& id, const acl::Action& action, const acl::ObjectType& objType, const std::string& ExchangeName, + const std::string& RoutingKey)=0; + // create specilied authorise methods for cases that need faster matching as needed. + + virtual ~AclModule() {}; +}; + +} // namespace broker + +namespace acl { + +class AclHelper { + private: + AclHelper(){} + public: + static inline ObjectType getObjectType(const std::string& str) { + if (str.compare("queue") == 0) return OBJ_QUEUE; + if (str.compare("exchange") == 0) return OBJ_EXCHANGE; + if (str.compare("broker") == 0) return OBJ_BROKER; + if (str.compare("link") == 0) return OBJ_LINK; + if (str.compare("method") == 0) return OBJ_METHOD; + throw str; + } + static inline std::string getObjectTypeStr(const ObjectType o) { + switch (o) { + case OBJ_QUEUE: return "queue"; + case OBJ_EXCHANGE: return "exchange"; + case OBJ_BROKER: return "broker"; + case OBJ_LINK: return "link"; + case OBJ_METHOD: return "method"; + default: assert(false); // should never get here + } + return ""; + } + static inline Action getAction(const std::string& str) { + if (str.compare("consume") == 0) return ACT_CONSUME; + if (str.compare("publish") == 0) return ACT_PUBLISH; + if (str.compare("create") == 0) return ACT_CREATE; + if (str.compare("access") == 0) return ACT_ACCESS; + if (str.compare("bind") == 0) return ACT_BIND; + if (str.compare("unbind") == 0) return ACT_UNBIND; + if (str.compare("delete") == 0) return ACT_DELETE; + if (str.compare("purge") == 0) return ACT_PURGE; + if (str.compare("update") == 0) return ACT_UPDATE; + throw str; + } + static inline std::string getActionStr(const Action a) { + switch (a) { + case ACT_CONSUME: return "consume"; + case ACT_PUBLISH: return "publish"; + case ACT_CREATE: return "create"; + case ACT_ACCESS: return "access"; + case ACT_BIND: return "bind"; + case ACT_UNBIND: return "unbind"; + case ACT_DELETE: return "delete"; + case ACT_PURGE: return "purge"; + case ACT_UPDATE: return "update"; + default: assert(false); // should never get here + } + return ""; + } + static inline Property getProperty(const std::string& str) { + if (str.compare("name") == 0) return PROP_NAME; + if (str.compare("durable") == 0) return PROP_DURABLE; + if (str.compare("owner") == 0) return PROP_OWNER; + if (str.compare("routingkey") == 0) return PROP_ROUTINGKEY; + if (str.compare("passive") == 0) return PROP_PASSIVE; + if (str.compare("autodelete") == 0) return PROP_AUTODELETE; + if (str.compare("exclusive") == 0) return PROP_EXCLUSIVE; + if (str.compare("type") == 0) return PROP_TYPE; + if (str.compare("alternate") == 0) return PROP_ALTERNATE; + if (str.compare("queuename") == 0) return PROP_QUEUENAME; + if (str.compare("schemapackage") == 0) return PROP_SCHEMAPACKAGE; + if (str.compare("schemaclass") == 0) return PROP_SCHEMACLASS; + throw str; + } + static inline std::string getPropertyStr(const Property p) { + switch (p) { + case PROP_NAME: return "name"; + case PROP_DURABLE: return "durable"; + case PROP_OWNER: return "owner"; + case PROP_ROUTINGKEY: return "routingkey"; + case PROP_PASSIVE: return "passive"; + case PROP_AUTODELETE: return "autodelete"; + case PROP_EXCLUSIVE: return "exclusive"; + case PROP_TYPE: return "type"; + case PROP_ALTERNATE: return "alternate"; + case PROP_QUEUENAME: return "queuename"; + case PROP_SCHEMAPACKAGE: return "schemapackage"; + case PROP_SCHEMACLASS: return "schemaclass"; + default: assert(false); // should never get here + } + return ""; + } + static inline AclResult getAclResult(const std::string& str) { + if (str.compare("allow") == 0) return ALLOW; + if (str.compare("allow-log") == 0) return ALLOWLOG; + if (str.compare("deny") == 0) return DENY; + if (str.compare("deny-log") == 0) return DENYLOG; + throw str; + } + static inline std::string getAclResultStr(const AclResult r) { + switch (r) { + case ALLOW: return "allow"; + case ALLOWLOG: return "allow-log"; + case DENY: return "deny"; + case DENYLOG: return "deny-log"; + default: assert(false); // should never get here + } + return ""; + } + + typedef std::set<Property> propSet; + typedef boost::shared_ptr<propSet> propSetPtr; + typedef std::pair<Action, propSetPtr> actionPair; + typedef std::map<Action, propSetPtr> actionMap; + typedef boost::shared_ptr<actionMap> actionMapPtr; + typedef std::pair<ObjectType, actionMapPtr> objectPair; + typedef std::map<ObjectType, actionMapPtr> objectMap; + typedef objectMap::const_iterator omCitr; + typedef boost::shared_ptr<objectMap> objectMapPtr; + + // This map contains the legal combinations of object/action/properties found in an ACL file + static void loadValidationMap(objectMapPtr& map) { + if (!map.get()) return; + map->clear(); + propSetPtr p0; // empty ptr, used for no properties + + // == Exchanges == + + propSetPtr p1(new propSet); + p1->insert(PROP_TYPE); + p1->insert(PROP_ALTERNATE); + p1->insert(PROP_PASSIVE); + p1->insert(PROP_DURABLE); + + propSetPtr p2(new propSet); + p2->insert(PROP_ROUTINGKEY); + + propSetPtr p3(new propSet); + p3->insert(PROP_QUEUENAME); + p3->insert(PROP_ROUTINGKEY); + + actionMapPtr a0(new actionMap); + a0->insert(actionPair(ACT_CREATE, p1)); + a0->insert(actionPair(ACT_DELETE, p0)); + a0->insert(actionPair(ACT_ACCESS, p0)); + a0->insert(actionPair(ACT_BIND, p2)); + a0->insert(actionPair(ACT_UNBIND, p2)); + a0->insert(actionPair(ACT_ACCESS, p3)); + a0->insert(actionPair(ACT_PUBLISH, p0)); + + map->insert(objectPair(OBJ_EXCHANGE, a0)); + + // == Queues == + + propSetPtr p4(new propSet); + p3->insert(PROP_ALTERNATE); + p3->insert(PROP_PASSIVE); + p3->insert(PROP_DURABLE); + p3->insert(PROP_EXCLUSIVE); + p3->insert(PROP_AUTODELETE); + + actionMapPtr a1(new actionMap); + a1->insert(actionPair(ACT_ACCESS, p0)); + a1->insert(actionPair(ACT_CREATE, p4)); + a1->insert(actionPair(ACT_PURGE, p0)); + a1->insert(actionPair(ACT_DELETE, p0)); + a1->insert(actionPair(ACT_CONSUME, p0)); + + map->insert(objectPair(OBJ_QUEUE, a1)); + + // == Links == + + actionMapPtr a2(new actionMap); + a2->insert(actionPair(ACT_CREATE, p0)); + + map->insert(objectPair(OBJ_LINK, a2)); + + // == Method == + + propSetPtr p5(new propSet); + p5->insert(PROP_SCHEMAPACKAGE); + p5->insert(PROP_SCHEMACLASS); + + actionMapPtr a4(new actionMap); + a4->insert(actionPair(ACT_ACCESS, p5)); + + map->insert(objectPair(OBJ_METHOD, a4)); + } +}; + + +}} // namespace qpid::acl + +#endif // QPID_ACLMODULE_ACL_H diff --git a/RC9/qpid/cpp/src/qpid/broker/Bridge.cpp b/RC9/qpid/cpp/src/qpid/broker/Bridge.cpp new file mode 100644 index 0000000000..f9cb7ccd3c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Bridge.cpp @@ -0,0 +1,294 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Bridge.h" +#include "ConnectionState.h" +#include "Connection.h" +#include "LinkRegistry.h" + +#include "qpid/agent/ManagementAgent.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/Uuid.h" +#include "qpid/log/Statement.h" +#include <iostream> + +using qpid::framing::FieldTable; +using qpid::framing::Uuid; +using qpid::framing::Buffer; +using qpid::management::ManagementAgent; +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace +{ +const std::string qpidFedOp("qpid.fed.op"); +const std::string qpidFedTags("qpid.fed.tags"); +const std::string qpidFedOrigin("qpid.fed.origin"); + +const std::string fedOpBind("B"); +const std::string fedOpUnbind("U"); +const std::string fedOpReorigin("R"); +const std::string fedOpHello("H"); +} + +namespace qpid { +namespace broker { + +void Bridge::PushHandler::handle(framing::AMQFrame& frame) +{ + conn->received(frame); +} + +Bridge::Bridge(Link* _link, framing::ChannelId _id, CancellationListener l, + const _qmf::ArgsLinkBridge& _args) : + link(_link), id(_id), args(_args), mgmtObject(0), + listener(l), name(Uuid(true).str()), queueName("bridge_queue_"), persistenceId(0) +{ + std::stringstream title; + title << id << "_" << link->getBroker()->getFederationTag(); + queueName += title.str(); + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + if (agent != 0) { + mgmtObject = new _qmf::Bridge + (agent, this, link, id, args.i_durable, args.i_src, args.i_dest, + args.i_key, args.i_srcIsQueue, args.i_srcIsLocal, + args.i_tag, args.i_excludes, args.i_dynamic); + if (!args.i_durable) + agent->addObject(mgmtObject); + } +} + +Bridge::~Bridge() +{ + mgmtObject->resourceDestroy(); +} + +void Bridge::create(ConnectionState& c) +{ + connState = &c; + if (args.i_srcIsLocal) { + if (args.i_dynamic) + throw Exception("Dynamic routing not supported for push routes"); + // Point the bridging commands at the local connection handler + Connection* conn = dynamic_cast<Connection*>(&c); + if (conn == 0) + return; + pushHandler.reset(new PushHandler(conn)); + channelHandler.reset(new framing::ChannelHandler(id, pushHandler.get())); + } else { + // Point the bridging commands at the remote peer broker + channelHandler.reset(new framing::ChannelHandler(id, &(connState->getOutput()))); + } + + session.reset(new framing::AMQP_ServerProxy::Session(*channelHandler)); + peer.reset(new framing::AMQP_ServerProxy(*channelHandler)); + + session->attach(name, false); + session->commandPoint(0,0); + + if (args.i_srcIsQueue) { + peer->getMessage().subscribe(args.i_src, args.i_dest, 1, 0, false, "", 0, FieldTable()); + peer->getMessage().flow(args.i_dest, 0, 0xFFFFFFFF); + peer->getMessage().flow(args.i_dest, 1, 0xFFFFFFFF); + } else { + FieldTable queueSettings; + + if (args.i_tag.size()) { + queueSettings.setString("qpid.trace.id", args.i_tag); + } else { + const string& peerTag = connState->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 be use srcIsQueue for durable queues? + bool autoDelete = !durable;//auto delete transient queues? + peer->getQueue().declare(queueName, "", false, durable, true, autoDelete, queueSettings); + if (!args.i_dynamic) + peer->getExchange().bind(queueName, args.i_src, args.i_key, FieldTable()); + peer->getMessage().subscribe(queueName, args.i_dest, 1, 0, false, "", 0, FieldTable()); + peer->getMessage().flow(args.i_dest, 0, 0xFFFFFFFF); + peer->getMessage().flow(args.i_dest, 1, 0xFFFFFFFF); + + if (args.i_dynamic) { + Exchange::shared_ptr exchange = link->getBroker()->getExchanges().get(args.i_src); + if (exchange.get() == 0) + throw Exception("Exchange not found for dynamic route"); + exchange->registerDynamicBridge(this); + } + } +} + +void Bridge::cancel() +{ + peer->getMessage().cancel(args.i_dest); + peer->getSession().detach(name); + if (args.i_dynamic) { + Exchange::shared_ptr exchange = link->getBroker()->getExchanges().get(args.i_src); + if (exchange.get() != 0) + exchange->removeDynamicBridge(this); + } +} + +void Bridge::destroy() +{ + listener(this); +} + +void Bridge::setPersistenceId(uint64_t id) const +{ + if (mgmtObject != 0 && persistenceId == 0) { + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + agent->addObject (mgmtObject, id); + } + persistenceId = id; +} + +const string& Bridge::getName() const +{ + return name; +} + +Bridge::shared_ptr Bridge::decode(LinkRegistry& links, Buffer& buffer) +{ + string host; + uint16_t port; + string src; + string dest; + string key; + string id; + string excludes; + + buffer.getShortString(host); + port = buffer.getShort(); + bool durable(buffer.getOctet()); + buffer.getShortString(src); + buffer.getShortString(dest); + buffer.getShortString(key); + bool is_queue(buffer.getOctet()); + bool is_local(buffer.getOctet()); + buffer.getShortString(id); + buffer.getShortString(excludes); + bool dynamic(buffer.getOctet()); + + return links.declare(host, port, durable, src, dest, key, + is_queue, is_local, id, excludes, dynamic).first; +} + +void Bridge::encode(Buffer& buffer) const +{ + buffer.putShortString(string("bridge")); + buffer.putShortString(link->getHost()); + buffer.putShort(link->getPort()); + buffer.putOctet(args.i_durable ? 1 : 0); + buffer.putShortString(args.i_src); + buffer.putShortString(args.i_dest); + buffer.putShortString(args.i_key); + buffer.putOctet(args.i_srcIsQueue ? 1 : 0); + buffer.putOctet(args.i_srcIsLocal ? 1 : 0); + buffer.putShortString(args.i_tag); + buffer.putShortString(args.i_excludes); + buffer.putOctet(args.i_dynamic ? 1 : 0); +} + +uint32_t Bridge::encodedSize() const +{ + return link->getHost().size() + 1 // short-string (host) + + 7 // short-string ("bridge") + + 2 // port + + 1 // durable + + args.i_src.size() + 1 + + args.i_dest.size() + 1 + + args.i_key.size() + 1 + + 1 // srcIsQueue + + 1 // srcIsLocal + + args.i_tag.size() + 1 + + args.i_excludes.size() + 1 + + 1; // dynamic +} + +management::ManagementObject* Bridge::GetManagementObject (void) const +{ + return (management::ManagementObject*) mgmtObject; +} + +management::Manageable::status_t Bridge::ManagementMethod(uint32_t methodId, + management::Args& /*args*/, + string&) +{ + if (methodId == _qmf::Bridge::METHOD_CLOSE) { + //notify that we are closed + destroy(); + return management::Manageable::STATUS_OK; + } else { + return management::Manageable::STATUS_UNKNOWN_METHOD; + } +} + +void Bridge::propagateBinding(const string& key, const string& tagList, + const string& op, const string& origin) +{ + const string& localTag = link->getBroker()->getFederationTag(); + const string& peerTag = connState->getFederationPeerTag(); + + if (tagList.find(peerTag) == tagList.npos) { + FieldTable bindArgs; + string newTagList(tagList + string(tagList.empty() ? "" : ",") + localTag); + + bindArgs.setString(qpidFedOp, op); + bindArgs.setString(qpidFedTags, newTagList); + if (origin.empty()) + bindArgs.setString(qpidFedOrigin, localTag); + else + bindArgs.setString(qpidFedOrigin, origin); + + peer->getExchange().bind(queueName, args.i_src, key, bindArgs); + } +} + +void Bridge::sendReorigin() +{ + FieldTable bindArgs; + + bindArgs.setString(qpidFedOp, fedOpReorigin); + bindArgs.setString(qpidFedTags, link->getBroker()->getFederationTag()); + + peer->getExchange().bind(queueName, args.i_src, args.i_key, bindArgs); +} + +bool Bridge::containsLocalTag(const string& tagList) const +{ + const string& localTag = link->getBroker()->getFederationTag(); + return (tagList.find(localTag) != tagList.npos); +} + +const string& Bridge::getLocalTag() const +{ + return link->getBroker()->getFederationTag(); +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/broker/Bridge.h b/RC9/qpid/cpp/src/qpid/broker/Bridge.h new file mode 100644 index 0000000000..c530a5d696 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Bridge.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. + * + */ +#ifndef _Bridge_ +#define _Bridge_ + +#include "PersistableConfig.h" +#include "qpid/framing/AMQP_ServerProxy.h" +#include "qpid/framing/ChannelHandler.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/FrameHandler.h" +#include "qpid/management/Manageable.h" +#include "Exchange.h" +#include "qmf/org/apache/qpid/broker/ArgsLinkBridge.h" +#include "qmf/org/apache/qpid/broker/Bridge.h" + +#include <boost/function.hpp> +#include <memory> + +namespace qpid { +namespace broker { + +class Connection; +class ConnectionState; +class Link; +class LinkRegistry; + +class Bridge : public PersistableConfig, public management::Manageable, public Exchange::DynamicBridge +{ +public: + typedef boost::shared_ptr<Bridge> shared_ptr; + typedef boost::function<void(Bridge*)> CancellationListener; + + Bridge(Link* link, framing::ChannelId id, CancellationListener l, + const qmf::org::apache::qpid::broker::ArgsLinkBridge& args); + ~Bridge(); + + void create(ConnectionState& c); + void cancel(); + void destroy(); + bool isDurable() { return args.i_durable; } + + management::ManagementObject* GetManagementObject() const; + management::Manageable::status_t ManagementMethod(uint32_t methodId, + management::Args& args, + std::string& text); + + // PersistableConfig: + void setPersistenceId(uint64_t id) const; + uint64_t getPersistenceId() const { return persistenceId; } + uint32_t encodedSize() const; + void encode(framing::Buffer& buffer) const; + const std::string& getName() const; + static Bridge::shared_ptr decode(LinkRegistry& links, framing::Buffer& buffer); + + // Exchange::DynamicBridge methods + void propagateBinding(const std::string& key, const std::string& tagList, const std::string& op, const std::string& origin); + void sendReorigin(); + bool containsLocalTag(const std::string& tagList) const; + const std::string& getLocalTag() const; + +private: + struct PushHandler : framing::FrameHandler { + PushHandler(Connection* c) { conn = c; } + void handle(framing::AMQFrame& frame); + Connection* conn; + }; + + std::auto_ptr<PushHandler> pushHandler; + std::auto_ptr<framing::ChannelHandler> channelHandler; + std::auto_ptr<framing::AMQP_ServerProxy::Session> session; + std::auto_ptr<framing::AMQP_ServerProxy> peer; + + Link* link; + framing::ChannelId id; + qmf::org::apache::qpid::broker::ArgsLinkBridge args; + qmf::org::apache::qpid::broker::Bridge* mgmtObject; + CancellationListener listener; + std::string name; + std::string queueName; + mutable uint64_t persistenceId; + ConnectionState* connState; +}; + + +}} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/Broker.cpp b/RC9/qpid/cpp/src/qpid/broker/Broker.cpp new file mode 100644 index 0000000000..64be104b98 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Broker.cpp @@ -0,0 +1,450 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Broker.h" +#include "DirectExchange.h" +#include "FanOutExchange.h" +#include "HeadersExchange.h" +#include "MessageStoreModule.h" +#include "NullMessageStore.h" +#include "RecoveryManagerImpl.h" +#include "SaslAuthenticator.h" +#include "TopicExchange.h" +#include "Link.h" + +#include "qmf/org/apache/qpid/broker/Package.h" +#include "qmf/org/apache/qpid/broker/ArgsBrokerEcho.h" +#include "qmf/org/apache/qpid/broker/ArgsBrokerQueueMoveMessages.h" +#include "qpid/management/ManagementExchange.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/framing/Uuid.h" +#include "qpid/sys/ProtocolFactory.h" +#include "qpid/sys/Poller.h" +#include "qpid/sys/Dispatcher.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Time.h" +#include "qpid/sys/ConnectionInputHandler.h" +#include "qpid/sys/ConnectionInputHandlerFactory.h" +#include "qpid/sys/TimeoutHandler.h" +#include "qpid/sys/SystemInfo.h" +#include "qpid/Address.h" +#include "qpid/Url.h" +#include "qpid/Version.h" + +#include <boost/bind.hpp> + +#include <iostream> +#include <memory> +#include <stdlib.h> + +using qpid::sys::ProtocolFactory; +using qpid::sys::Poller; +using qpid::sys::Dispatcher; +using qpid::sys::Thread; +using qpid::framing::FrameHandler; +using qpid::framing::ChannelId; +using qpid::management::ManagementBroker; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace qpid { +namespace broker { + +Broker::Options::Options(const std::string& name) : + qpid::Options(name), + noDataDir(0), + port(DEFAULT_PORT), + workerThreads(5), + maxConnections(500), + connectionBacklog(10), + stagingThreshold(5000000), + enableMgmt(1), + mgmtPubInterval(10), + queueCleanInterval(60*10),//10 minutes + auth(SaslAuthenticator::available()), + realm("QPID"), + replayFlushLimit(0), + replayHardLimit(0), + queueLimit(100*1048576/*100M default limit*/), + tcpNoDelay(false), + requireEncrypted(false) +{ + int c = sys::SystemInfo::concurrency(); + workerThreads=c+1; + char *home = ::getenv("HOME"); + + if (home == 0) + dataDir += DEFAULT_DATA_DIR_LOCATION; + else + dataDir += home; + dataDir += DEFAULT_DATA_DIR_NAME; + + addOptions() + ("data-dir", optValue(dataDir,"DIR"), "Directory to contain persistent data generated by the broker") + ("no-data-dir", optValue(noDataDir), "Don't use a data directory. No persistent configuration will be loaded or stored") + ("port,p", optValue(port,"PORT"), "Tells the broker to listen on PORT") + ("worker-threads", optValue(workerThreads, "N"), "Sets the broker thread pool size") + ("max-connections", optValue(maxConnections, "N"), "Sets the maximum allowed connections") + ("connection-backlog", optValue(connectionBacklog, "N"), "Sets the connection backlog limit for the server socket") + ("staging-threshold", optValue(stagingThreshold, "N"), "Stages messages over N bytes to disk") + ("mgmt-enable,m", optValue(enableMgmt,"yes|no"), "Enable Management") + ("mgmt-pub-interval", optValue(mgmtPubInterval, "SECONDS"), "Management Publish Interval") + ("queue-purge-interval", optValue(queueCleanInterval, "SECONDS"), + "Interval between attempts to purge any expired messages from queues") + ("auth", optValue(auth, "yes|no"), "Enable authentication, if disabled all incoming connections will be trusted") + ("realm", optValue(realm, "REALM"), "Use the given realm when performing authentication") + ("default-queue-limit", optValue(queueLimit, "BYTES"), "Default maximum size for queues (in bytes)") + ("tcp-nodelay", optValue(tcpNoDelay), "Set TCP_NODELAY on TCP connections") + ("require-encryption", optValue(requireEncrypted), "Only accept connections that are encrypted"); +} + +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"); + +Broker::Broker(const Broker::Options& conf) : + poller(new Poller), + config(conf), + managementAgentSingleton(!config.enableMgmt), + store(0), + acl(0), + dataDir(conf.noDataDir ? std::string() : conf.dataDir), + links(this), + factory(new ConnectionFactory(*this)), + dtxManager(timer), + sessionManager( + qpid::SessionState::Configuration( + conf.replayFlushLimit*1024, // convert kb to bytes. + conf.replayHardLimit*1024), + *this), + queueCleaner(queues, timer), + getKnownBrokers(boost::bind(&Broker::getKnownBrokersImpl, this)) +{ + if (conf.enableMgmt) { + QPID_LOG(info, "Management enabled"); + managementAgent = managementAgentSingleton.getInstance(); + ((ManagementBroker*) managementAgent)->configure + (dataDir.isEnabled() ? dataDir.getPath() : string(), + conf.mgmtPubInterval, this, conf.workerThreads + 3); + _qmf::Package packageInitializer(managementAgent); + + System* system = new System (dataDir.isEnabled() ? dataDir.getPath() : string()); + systemObject = System::shared_ptr(system); + + mgmtObject = new _qmf::Broker(managementAgent, this, system, conf.port); + mgmtObject->set_workerThreads(conf.workerThreads); + mgmtObject->set_maxConns(conf.maxConnections); + mgmtObject->set_connBacklog(conf.connectionBacklog); + mgmtObject->set_stagingThreshold(conf.stagingThreshold); + mgmtObject->set_mgmtPubInterval(conf.mgmtPubInterval); + mgmtObject->set_version(qpid::version); + if (dataDir.isEnabled()) + mgmtObject->set_dataDir(dataDir.getPath()); + else + mgmtObject->clr_dataDir(); + + managementAgent->addObject(mgmtObject, 0x1000000000000002LL); + + // Since there is currently no support for virtual hosts, a placeholder object + // representing the implied single virtual host is added here to keep the + // management schema correct. + Vhost* vhost = new Vhost(this); + vhostObject = Vhost::shared_ptr(vhost); + framing::Uuid uuid(((ManagementBroker*) managementAgent)->getUuid()); + federationTag = uuid.str(); + vhostObject->setFederationTag(federationTag); + + queues.setParent(vhost); + exchanges.setParent(vhost); + links.setParent(vhost); + } else { + // Management is disabled so there is no broker management ID. + // Create a unique uuid to use as the federation tag. + framing::Uuid uuid(true); + federationTag = uuid.str(); + } + + QueuePolicy::setDefaultMaxSize(conf.queueLimit); + + // Early-Initialize plugins + const Plugin::Plugins& plugins=Plugin::getPlugins(); + for (Plugin::Plugins::const_iterator i = plugins.begin(); + i != plugins.end(); + i++) + (*i)->earlyInitialize(*this); + + // If no plugin store module registered itself, set up the null store. + if (store.get() == 0) + setStore (new NullMessageStore()); + + exchanges.declare(empty, DirectExchange::typeName); // Default exchange. + + if (store.get() != 0) { + RecoveryManagerImpl recoverer(queues, exchanges, links, dtxManager, + conf.stagingThreshold); + store->recover(recoverer); + } + + //ensure standard exchanges exist (done after recovery from store) + declareStandardExchange(amq_direct, DirectExchange::typeName); + declareStandardExchange(amq_topic, TopicExchange::typeName); + declareStandardExchange(amq_fanout, FanOutExchange::typeName); + declareStandardExchange(amq_match, HeadersExchange::typeName); + + if(conf.enableMgmt) { + exchanges.declare(qpid_management, ManagementExchange::typeName); + Exchange::shared_ptr mExchange = exchanges.get (qpid_management); + Exchange::shared_ptr dExchange = exchanges.get (amq_direct); + ((ManagementBroker*) managementAgent)->setExchange (mExchange, dExchange); + dynamic_pointer_cast<ManagementExchange>(mExchange)->setManagmentAgent + ((ManagementBroker*) managementAgent); + } + else + QPID_LOG(info, "Management not enabled"); + + /** + * SASL setup, can fail and terminate startup + */ + if (conf.auth) { + SaslAuthenticator::init(qpid::saslName); + QPID_LOG(info, "SASL enabled"); + } + + // Initialize plugins + for (Plugin::Plugins::const_iterator i = plugins.begin(); + i != plugins.end(); + i++) + (*i)->initialize(*this); + + if (conf.queueCleanInterval) { + queueCleaner.start(conf.queueCleanInterval * qpid::sys::TIME_SEC); + } + + //initialize known broker urls (TODO: add support for urls for other transports (SSL, RDMA)): + boost::shared_ptr<ProtocolFactory> factory = getProtocolFactory(TCP_TRANSPORT); + if (factory) { + knownBrokers.push_back ( qpid::Url::getIpAddressesUrl ( factory->getPort() ) ); + } +} + +void Broker::declareStandardExchange(const std::string& name, const std::string& type) +{ + bool storeEnabled = store.get() != NULL; + std::pair<Exchange::shared_ptr, bool> status = exchanges.declare(name, type, storeEnabled); + if (status.second && storeEnabled) { + store->create(*status.first, framing::FieldTable ()); + } +} + + +boost::intrusive_ptr<Broker> Broker::create(int16_t port) +{ + Options config; + config.port=port; + return create(config); +} + +boost::intrusive_ptr<Broker> Broker::create(const Options& opts) +{ + return boost::intrusive_ptr<Broker>(new Broker(opts)); +} + +void Broker::setStore (MessageStore* _store) +{ + store.reset(new MessageStoreModule (_store)); + queues.setStore (store.get()); + dtxManager.setStore (store.get()); + links.setStore (store.get()); +} + +void Broker::run() { + accept(); + QPID_LOG(notice, "Broker running"); + Dispatcher d(poller); + int numIOThreads = config.workerThreads; + std::vector<Thread> t(numIOThreads-1); + + // Run n-1 io threads + for (int i=0; i<numIOThreads-1; ++i) + t[i] = Thread(d); + + // Run final thread + d.run(); + + // Now wait for n-1 io threads to exit + for (int i=0; i<numIOThreads-1; ++i) { + t[i].join(); + } +} + +void Broker::shutdown() { + // NB: this function must be async-signal safe, it must not + // call any function that is not async-signal safe. + // Any unsafe shutdown actions should be done in the destructor. + poller->shutdown(); +} + +Broker::~Broker() { + shutdown(); + finalize(); // Finalize any plugins. + if (config.auth) + SaslAuthenticator::fini(); + QPID_LOG(notice, "Shut down"); +} + +ManagementObject* Broker::GetManagementObject(void) const +{ + return (ManagementObject*) mgmtObject; +} + +Manageable* Broker::GetVhostObject(void) const +{ + return vhostObject.get(); +} + +Manageable::status_t Broker::ManagementMethod (uint32_t methodId, + Args& args, + string&) +{ + Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; + + QPID_LOG (debug, "Broker::ManagementMethod [id=" << methodId << "]"); + + switch (methodId) + { + case _qmf::Broker::METHOD_ECHO : + status = Manageable::STATUS_OK; + break; + case _qmf::Broker::METHOD_CONNECT : { + _qmf::ArgsBrokerConnect& hp= + dynamic_cast<_qmf::ArgsBrokerConnect&>(args); + + string transport = hp.i_transport.empty() ? TCP_TRANSPORT : hp.i_transport; + if (!getProtocolFactory(transport)) { + QPID_LOG(error, "Transport '" << transport << "' not supported"); + return Manageable::STATUS_NOT_IMPLEMENTED; + } + std::pair<Link::shared_ptr, bool> response = + links.declare (hp.i_host, hp.i_port, transport, hp.i_durable, + hp.i_authMechanism, hp.i_username, hp.i_password); + if (hp.i_durable && response.second) + store->create(*response.first); + status = Manageable::STATUS_OK; + break; + } + case _qmf::Broker::METHOD_QUEUEMOVEMESSAGES : { + _qmf::ArgsBrokerQueueMoveMessages& moveArgs= + dynamic_cast<_qmf::ArgsBrokerQueueMoveMessages&>(args); + if (queueMoveMessages(moveArgs.i_srcQueue, moveArgs.i_destQueue, moveArgs.i_qty)) + status = Manageable::STATUS_OK; + else + return Manageable::STATUS_INVALID_PARAMETER; + break; + } + default: + status = Manageable::STATUS_NOT_IMPLEMENTED; + break; + } + + return status; +} + +boost::shared_ptr<ProtocolFactory> Broker::getProtocolFactory(const std::string& name) const { + ProtocolFactoryMap::const_iterator i + = name.empty() ? protocolFactories.begin() : protocolFactories.find(name); + if (i == protocolFactories.end()) return boost::shared_ptr<ProtocolFactory>(); + else return i->second; +} + +uint16_t Broker::getPort(const std::string& name) const { + boost::shared_ptr<ProtocolFactory> factory = getProtocolFactory(name); + if (factory) { + return factory->getPort(); + } else { + throw NoSuchTransportException(QPID_MSG("No such transport: '" << name << "'")); + } +} + +void Broker::registerProtocolFactory(const std::string& name, ProtocolFactory::shared_ptr protocolFactory) { + protocolFactories[name] = protocolFactory; +} + +void Broker::accept() { + for (ProtocolFactoryMap::const_iterator i = protocolFactories.begin(); i != protocolFactories.end(); i++) { + i->second->accept(poller, factory.get()); + } +} + +void Broker::connect( + const std::string& host, uint16_t port, const std::string& transport, + boost::function2<void, int, std::string> failed, + sys::ConnectionCodec::Factory* f) +{ + boost::shared_ptr<ProtocolFactory> pf = getProtocolFactory(transport); + if (pf) pf->connect(poller, host, port, f ? f : factory.get(), failed); + else throw NoSuchTransportException(QPID_MSG("Unsupported transport type: " << transport)); +} + +void Broker::connect( + const Url& url, + boost::function2<void, int, std::string> failed, + sys::ConnectionCodec::Factory* f) +{ + url.throwIfEmpty(); + const TcpAddress* addr=url[0].get<TcpAddress>(); + connect(addr->host, addr->port, TCP_TRANSPORT, failed, f); +} + +uint32_t Broker::queueMoveMessages( + const std::string& srcQueue, + const std::string& destQueue, + uint32_t qty) +{ + Queue::shared_ptr src_queue = queues.find(srcQueue); + if (!src_queue) + return 0; + Queue::shared_ptr dest_queue = queues.find(destQueue); + if (!dest_queue) + return 0; + + return src_queue->move(dest_queue, qty); +} + + +boost::shared_ptr<sys::Poller> Broker::getPoller() { return poller; } + +std::vector<Url> +Broker::getKnownBrokersImpl() +{ + return knownBrokers; +} + +const std::string Broker::TCP_TRANSPORT("tcp"); + +}} // namespace qpid::broker + diff --git a/RC9/qpid/cpp/src/qpid/broker/Broker.h b/RC9/qpid/cpp/src/qpid/broker/Broker.h new file mode 100644 index 0000000000..c64bfa8a9f --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Broker.h @@ -0,0 +1,229 @@ +#ifndef _Broker_ +#define _Broker_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ConnectionFactory.h" +#include "ConnectionToken.h" +#include "DirectExchange.h" +#include "DtxManager.h" +#include "ExchangeRegistry.h" +#include "MessageStore.h" +#include "QueueRegistry.h" +#include "LinkRegistry.h" +#include "SessionManager.h" +#include "QueueCleaner.h" +#include "Vhost.h" +#include "System.h" +#include "Timer.h" +#include "qpid/management/Manageable.h" +#include "qpid/management/ManagementBroker.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/RefCounted.h" +#include "AclModule.h" + +#include <boost/intrusive_ptr.hpp> +#include <string> +#include <vector> + +namespace qpid { + +namespace sys { + class ProtocolFactory; + class Poller; +} + +struct Url; + +namespace broker { + +static const uint16_t DEFAULT_PORT=5672; + +struct NoSuchTransportException : qpid::Exception +{ + NoSuchTransportException(const std::string& s) : Exception(s) {} + virtual ~NoSuchTransportException() throw() {} +}; + +/** + * A broker instance. + */ +class Broker : public sys::Runnable, public Plugin::Target, + public management::Manageable, public RefCounted +{ + public: + + struct Options : public qpid::Options { + static const std::string DEFAULT_DATA_DIR_LOCATION; + static const std::string DEFAULT_DATA_DIR_NAME; + + Options(const std::string& name="Broker Options"); + + bool noDataDir; + std::string dataDir; + uint16_t port; + int workerThreads; + int maxConnections; + int connectionBacklog; + uint64_t stagingThreshold; + bool enableMgmt; + uint16_t mgmtPubInterval; + uint16_t queueCleanInterval; + bool auth; + std::string realm; + size_t replayFlushLimit; + size_t replayHardLimit; + uint queueLimit; + bool tcpNoDelay; + bool requireEncrypted; + }; + + private: + typedef std::map<std::string, boost::shared_ptr<sys::ProtocolFactory> > ProtocolFactoryMap; + + boost::shared_ptr<sys::Poller> poller; + Options config; + management::ManagementAgent::Singleton managementAgentSingleton; + ProtocolFactoryMap protocolFactories; + std::auto_ptr<MessageStore> store; + AclModule* acl; + DataDir dataDir; + + QueueRegistry queues; + ExchangeRegistry exchanges; + LinkRegistry links; + boost::shared_ptr<sys::ConnectionCodec::Factory> factory; + Timer timer; + DtxManager dtxManager; + SessionManager sessionManager; + management::ManagementAgent* managementAgent; + qmf::org::apache::qpid::broker::Broker* mgmtObject; + Vhost::shared_ptr vhostObject; + System::shared_ptr systemObject; + QueueCleaner queueCleaner; + + void declareStandardExchange(const std::string& name, const std::string& type); + + std::vector<Url> knownBrokers; + std::vector<Url> getKnownBrokersImpl(); + std::string federationTag; + + public: + + + virtual ~Broker(); + + Broker(const Options& configuration); + static boost::intrusive_ptr<Broker> create(const Options& configuration); + static boost::intrusive_ptr<Broker> create(int16_t port = DEFAULT_PORT); + + /** + * Return listening port. If called before bind this is + * the configured port. If called after it is the actual + * port, which will be different if the configured port is + * 0. + */ + virtual uint16_t getPort(const std::string& name) const; + + /** + * Run the broker. Implements Runnable::run() so the broker + * can be run in a separate thread. + */ + virtual void run(); + + /** Shut down the broker */ + virtual void shutdown(); + + void setStore (MessageStore*); + MessageStore& getStore() { return *store; } + void setAcl (AclModule* _acl) {acl = _acl;} + AclModule* getAcl() { return acl; } + QueueRegistry& getQueues() { return queues; } + ExchangeRegistry& getExchanges() { return exchanges; } + LinkRegistry& getLinks() { return links; } + uint64_t getStagingThreshold() { return config.stagingThreshold; } + DtxManager& getDtxManager() { return dtxManager; } + DataDir& getDataDir() { return dataDir; } + Options& getOptions() { return config; } + + SessionManager& getSessionManager() { return sessionManager; } + const std::string& getFederationTag() const { return federationTag; } + + management::ManagementObject* GetManagementObject (void) const; + management::Manageable* GetVhostObject (void) const; + management::Manageable::status_t ManagementMethod (uint32_t methodId, + management::Args& args, + std::string& text); + + /** Add to the broker's protocolFactorys */ + void registerProtocolFactory(const std::string& name, boost::shared_ptr<sys::ProtocolFactory>); + + /** Accept connections */ + void accept(); + + /** Create a connection to another broker. */ + void connect(const std::string& host, uint16_t port, + const std::string& transport, + boost::function2<void, int, std::string> failed, + sys::ConnectionCodec::Factory* =0); + /** Create a connection to another broker. */ + void connect(const Url& url, + boost::function2<void, int, std::string> failed, + sys::ConnectionCodec::Factory* =0); + + /** Move messages from one queue to another. + A zero quantity means to move all messages + */ + uint32_t queueMoveMessages( const std::string& srcQueue, + const std::string& destQueue, + uint32_t qty); + + // TODO: There isn't a single ProtocolFactory so the use of the following needs to be fixed + // For the present just return the first ProtocolFactory registered. + boost::shared_ptr<sys::ProtocolFactory> getProtocolFactory(const std::string& name = TCP_TRANSPORT) const; + + /** Expose poller so plugins can register their descriptors. */ + boost::shared_ptr<sys::Poller> getPoller(); + + boost::shared_ptr<sys::ConnectionCodec::Factory> getConnectionFactory() { return factory; } + void setConnectionFactory(boost::shared_ptr<sys::ConnectionCodec::Factory> f) { factory = f; } + + Timer& getTimer() { return timer; } + + boost::function<std::vector<Url> ()> getKnownBrokers; + + static const std::string TCP_TRANSPORT; +}; + +}} + + + +#endif /*!_Broker_*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/BrokerSingleton.cpp b/RC9/qpid/cpp/src/qpid/broker/BrokerSingleton.cpp new file mode 100644 index 0000000000..5ba8c9d1e1 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/BrokerSingleton.cpp @@ -0,0 +1,36 @@ +/* + * + * 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 "BrokerSingleton.h" + +namespace qpid { +namespace broker { + +BrokerSingleton::BrokerSingleton() { + if (broker.get() == 0) + broker = Broker::create(); + boost::intrusive_ptr<Broker>::operator=(broker); +} + +BrokerSingleton::~BrokerSingleton() { + broker->shutdown(); +} + +boost::intrusive_ptr<Broker> BrokerSingleton::broker; + +}} // namespace qpid::broker diff --git a/RC9/qpid/cpp/src/qpid/broker/BrokerSingleton.h b/RC9/qpid/cpp/src/qpid/broker/BrokerSingleton.h new file mode 100644 index 0000000000..22b707506b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/BrokerSingleton.h @@ -0,0 +1,52 @@ +#ifndef _broker_BrokerSingleton_h +#define _broker_BrokerSingleton_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "Broker.h" + +namespace qpid { +namespace broker { + +/** + * BrokerSingleton is a smart pointer to a process-wide singleton broker + * started on an os-chosen port. The broker starts the first time + * an instance of BrokerSingleton is created and runs untill the process exits. + * + * Useful for unit tests that want to share a broker between multiple + * tests to reduce overhead of starting/stopping a broker for every test. + * + * Tests that need a new broker can create it directly. + * + * THREAD UNSAFE. + */ +class BrokerSingleton : public boost::intrusive_ptr<Broker> +{ + public: + BrokerSingleton(); + ~BrokerSingleton(); + private: + static boost::intrusive_ptr<Broker> broker; +}; + +}} // namespace qpid::broker + + + +#endif /*!_broker_BrokerSingleton_h*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/Connection.cpp b/RC9/qpid/cpp/src/qpid/broker/Connection.cpp new file mode 100644 index 0000000000..f0b9980861 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Connection.cpp @@ -0,0 +1,271 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Connection.h" +#include "SessionState.h" +#include "Bridge.h" + +#include "qpid/log/Statement.h" +#include "qpid/ptr_map.h" +#include "qpid/framing/AMQP_ClientProxy.h" +#include "qpid/framing/enum.h" +#include "qmf/org/apache/qpid/broker/EventClientConnect.h" +#include "qmf/org/apache/qpid/broker/EventClientDisconnect.h" + +#include <boost/bind.hpp> +#include <boost/ptr_container/ptr_vector.hpp> + +#include <algorithm> +#include <iostream> +#include <assert.h> + +using namespace qpid::sys; +using namespace qpid::framing; +using namespace qpid::sys; +using qpid::ptr_map_ptr; +using qpid::management::ManagementAgent; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace qpid { +namespace broker { + +Connection::Connection(ConnectionOutputHandler* out_, Broker& broker_, const std::string& mgmtId_, bool isLink_) : + ConnectionState(out_, broker_), + adapter(*this, isLink_), + isLink(isLink_), + mgmtClosing(false), + mgmtId(mgmtId_), + mgmtObject(0), + links(broker_.getLinks()), + agent(0) +{ + Manageable* parent = broker.GetVhostObject(); + + if (isLink) + links.notifyConnection(mgmtId, this); + + if (parent != 0) + { + agent = ManagementAgent::Singleton::getInstance(); + + + // TODO set last bool true if system connection + if (agent != 0) + mgmtObject = new _qmf::Connection(agent, this, parent, mgmtId, !isLink, false); + agent->addObject(mgmtObject); + ConnectionState::setUrl(mgmtId); + } +} + +void Connection::requestIOProcessing(boost::function0<void> callback) +{ + ioCallback = callback; + out.activateOutput(); +} + +Connection::~Connection() +{ + if (mgmtObject != 0) { + mgmtObject->resourceDestroy(); + if (!isLink) + agent->raiseEvent(_qmf::EventClientDisconnect(mgmtId, ConnectionState::getUserId())); + } + if (isLink) + links.notifyClosed(mgmtId); +} + +void Connection::received(framing::AMQFrame& frame) { + if (frame.getChannel() == 0 && frame.getMethod()) { + adapter.handle(frame); + } else { + getChannel(frame.getChannel()).in(frame); + } + + if (isLink) + recordFromServer(frame); + else + recordFromClient(frame); +} + +void Connection::recordFromServer(framing::AMQFrame& frame) +{ + if (mgmtObject != 0) + { + mgmtObject->inc_framesToClient(); + mgmtObject->inc_bytesToClient(frame.encodedSize()); + } +} + +void Connection::recordFromClient(framing::AMQFrame& frame) +{ + if (mgmtObject != 0) + { + mgmtObject->inc_framesFromClient(); + mgmtObject->inc_bytesFromClient(frame.encodedSize()); + } +} + +string Connection::getAuthMechanism() +{ + if (!isLink) + return string("ANONYMOUS"); + + return links.getAuthMechanism(mgmtId); +} + +string Connection::getAuthCredentials() +{ + if (!isLink) + return string(); + + if (mgmtObject != 0) + { + if (links.getAuthMechanism(mgmtId) == "ANONYMOUS") + mgmtObject->set_authIdentity("anonymous"); + else + mgmtObject->set_authIdentity(links.getAuthIdentity(mgmtId)); + } + + return links.getAuthCredentials(mgmtId); +} + +void Connection::notifyConnectionForced(const string& text) +{ + if (isLink) + links.notifyConnectionForced(mgmtId, text); +} + +void Connection::setUserId(const string& userId) +{ + ConnectionState::setUserId(userId); + if (mgmtObject != 0) { + mgmtObject->set_authIdentity(userId); + agent->raiseEvent(_qmf::EventClientConnect(mgmtId, userId)); + } +} + +void Connection::setFederationLink(bool b) +{ + ConnectionState::setFederationLink(b); + if (mgmtObject != 0) + mgmtObject->set_federationLink(b); +} + +void Connection::close(connection::CloseCode code, const string& text) +{ + QPID_LOG_IF(error, code != connection::CLOSE_CODE_NORMAL, "Connection " << mgmtId << " closed by error: " << text << "(" << code << ")"); + adapter.close(code, text); + //make sure we delete dangling pointers from outputTasks before deleting sessions + outputTasks.removeAll(); + channels.clear(); + getOutput().close(); +} + +// Send a close to the client but keep the channels. Used by cluster. +void Connection::sendClose() { + adapter.close(connection::CLOSE_CODE_NORMAL, "OK"); + getOutput().close(); +} + +void Connection::idleOut(){} + +void Connection::idleIn(){} + +void Connection::closed(){ // Physically closed, suspend open sessions. + try { + while (!channels.empty()) + ptr_map_ptr(channels.begin())->handleDetach(); + while (!exclusiveQueues.empty()) { + Queue::shared_ptr q(exclusiveQueues.front()); + q->releaseExclusiveOwnership(); + if (q->canAutoDelete()) { + Queue::tryAutoDelete(broker, q); + } + exclusiveQueues.erase(exclusiveQueues.begin()); + } + } catch(std::exception& e) { + QPID_LOG(error, QPID_MSG("While closing connection: " << e.what())); + assert(0); + } +} + +bool Connection::hasOutput() { return outputTasks.hasOutput(); } + +bool Connection::doOutput() { + try{ + if (ioCallback) + ioCallback(); // Lend the IO thread for management processing + ioCallback = 0; + + if (mgmtClosing) + close(connection::CLOSE_CODE_CONNECTION_FORCED, "Closed by Management Request"); + else + //then do other output as needed: + return outputTasks.doOutput(); + }catch(ConnectionException& e){ + close(e.code, e.getMessage()); + }catch(std::exception& e){ + close(connection::CLOSE_CODE_CONNECTION_FORCED, e.what()); + } + return false; +} + +void Connection::closeChannel(uint16_t id) { + ChannelMap::iterator i = channels.find(id); + if (i != channels.end()) channels.erase(i); +} + +SessionHandler& Connection::getChannel(ChannelId id) { + ChannelMap::iterator i=channels.find(id); + if (i == channels.end()) { + i = channels.insert(id, new SessionHandler(*this, id)).first; + } + return *ptr_map_ptr(i); +} + +ManagementObject* Connection::GetManagementObject(void) const +{ + return (ManagementObject*) mgmtObject; +} + +Manageable::status_t Connection::ManagementMethod(uint32_t methodId, Args&, string&) +{ + Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; + + QPID_LOG(debug, "Connection::ManagementMethod [id=" << methodId << "]"); + + switch (methodId) + { + case _qmf::Connection::METHOD_CLOSE : + mgmtClosing = true; + if (mgmtObject != 0) mgmtObject->set_closing(1); + out.activateOutput(); + status = Manageable::STATUS_OK; + break; + } + + return status; +} + +}} + diff --git a/RC9/qpid/cpp/src/qpid/broker/Connection.h b/RC9/qpid/cpp/src/qpid/broker/Connection.h new file mode 100644 index 0000000000..350ed2c07f --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Connection.h @@ -0,0 +1,127 @@ +#ifndef QPID_BROKER_CONNECTION_H +#define QPID_BROKER_CONNECTION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <memory> +#include <sstream> +#include <vector> + +#include <boost/ptr_container/ptr_map.hpp> + +#include "Broker.h" +#include "ConnectionHandler.h" +#include "ConnectionState.h" +#include "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/agent/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/Socket.h" +#include "qpid/sys/TimeoutHandler.h" + +#include <boost/ptr_container/ptr_map.hpp> +#include <boost/bind.hpp> + +#include <algorithm> + +namespace qpid { +namespace broker { + +class LinkRegistry; + +class Connection : public sys::ConnectionInputHandler, + public ConnectionState, + public RefCounted +{ + public: + Connection(sys::ConnectionOutputHandler* out, Broker& broker, const std::string& mgmtId, bool isLink = false); + ~Connection (); + + /** Get the SessionHandler for channel. Create if it does not already exist */ + SessionHandler& getChannel(framing::ChannelId channel); + + /** Close the connection */ + void close(framing::connection::CloseCode code, const string& text); + + // ConnectionInputHandler methods + void received(framing::AMQFrame& frame); + void idleOut(); + void idleIn(); + bool hasOutput(); + bool doOutput(); + void closed(); + + void closeChannel(framing::ChannelId channel); + + // Manageable entry points + management::ManagementObject* GetManagementObject (void) const; + management::Manageable::status_t + ManagementMethod (uint32_t methodId, management::Args& args, std::string&); + + void requestIOProcessing (boost::function0<void>); + void recordFromServer (framing::AMQFrame& frame); + void recordFromClient (framing::AMQFrame& frame); + std::string getAuthMechanism(); + std::string getAuthCredentials(); + void notifyConnectionForced(const std::string& text); + void setUserId(const string& uid); + const std::string& getUserId() const { return ConnectionState::getUserId(); } + const std::string& getMgmtId() const { return mgmtId; } + management::ManagementAgent* getAgent() const { return agent; } + void setFederationLink(bool b); + + template <class F> void eachSessionHandler(F f) { + for (ChannelMap::iterator i = channels.begin(); i != channels.end(); ++i) + f(*ptr_map_ptr(i)); + } + + void sendClose(); + + private: + typedef boost::ptr_map<framing::ChannelId, SessionHandler> ChannelMap; + typedef std::vector<Queue::shared_ptr>::iterator queue_iterator; + + ChannelMap channels; + framing::AMQP_ClientProxy::Connection* client; + ConnectionHandler adapter; + bool isLink; + bool mgmtClosing; + const std::string mgmtId; + boost::function0<void> ioCallback; + qmf::org::apache::qpid::broker::Connection* mgmtObject; + LinkRegistry& links; + management::ManagementAgent* agent; +}; + +}} + +#endif /*!QPID_BROKER_CONNECTION_H*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp b/RC9/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp new file mode 100644 index 0000000000..e6d8c49055 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/ConnectionFactory.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 "ConnectionFactory.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/amqp_0_10/Connection.h" +#include "qpid/broker/Connection.h" + +namespace qpid { +namespace broker { + +using framing::ProtocolVersion; +typedef std::auto_ptr<amqp_0_10::Connection> ConnectionPtr; +typedef std::auto_ptr<sys::ConnectionInputHandler> InputPtr; + +ConnectionFactory::ConnectionFactory(Broker& b) : broker(b) {} + +ConnectionFactory::~ConnectionFactory() {} + +sys::ConnectionCodec* +ConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id) { + if (v == ProtocolVersion(0, 10)) { + ConnectionPtr c(new amqp_0_10::Connection(out, id, false)); + c->setInputHandler(InputPtr(new broker::Connection(c.get(), broker, id, false))); + return c.release(); + } + return 0; +} + +sys::ConnectionCodec* +ConnectionFactory::create(sys::OutputControl& out, const std::string& id) { + // 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, true))); + return c.release(); +} + + +}} // namespace qpid::broker diff --git a/RC9/qpid/cpp/src/qpid/broker/ConnectionFactory.h b/RC9/qpid/cpp/src/qpid/broker/ConnectionFactory.h new file mode 100644 index 0000000000..c61da81024 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/ConnectionFactory.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 _ConnectionFactory_ +#define _ConnectionFactory_ + +#include "qpid/sys/ConnectionCodec.h" + +namespace qpid { +namespace broker { +class Broker; + +class ConnectionFactory : public sys::ConnectionCodec::Factory +{ + public: + ConnectionFactory(Broker& b); + + virtual ~ConnectionFactory(); + + sys::ConnectionCodec* + create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id); + + sys::ConnectionCodec* + create(sys::OutputControl&, const std::string& id); + + private: + Broker& broker; +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/ConnectionHandler.cpp b/RC9/qpid/cpp/src/qpid/broker/ConnectionHandler.cpp new file mode 100644 index 0000000000..7386ce7229 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/ConnectionHandler.cpp @@ -0,0 +1,222 @@ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ConnectionHandler.h" +#include "Connection.h" +#include "qpid/framing/ClientInvoker.h" +#include "qpid/framing/ServerInvoker.h" +#include "qpid/framing/enum.h" +#include "qpid/log/Statement.h" +#include "qpid/Url.h" +#include "AclModule.h" +#include "qmf/org/apache/qpid/broker/EventClientConnectFail.h" + +using namespace qpid; +using namespace qpid::broker; +using namespace qpid::framing; +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace +{ +const std::string ANONYMOUS = "ANONYMOUS"; +const std::string PLAIN = "PLAIN"; +const std::string en_US = "en_US"; +const std::string QPID_FED_LINK = "qpid.fed_link"; +const std::string QPID_FED_TAG = "qpid.federation_tag"; +} + +void ConnectionHandler::close(connection::CloseCode code, const string& text) +{ + handler->client.close(code, text); +} + +void ConnectionHandler::handle(framing::AMQFrame& frame) +{ + AMQMethodBody* method=frame.getBody()->getMethod(); + try{ + bool handled = false; + if (handler->serverMode) { + handled = invoke(static_cast<AMQP_ServerOperations::ConnectionHandler&>(*handler.get()), *method); + } else { + handled = invoke(static_cast<AMQP_ClientOperations::ConnectionHandler&>(*handler.get()), *method); + } + if (!handled) { + handler->connection.getChannel(frame.getChannel()).in(frame); + } + + }catch(ConnectionException& e){ + handler->client.close(e.code, e.what()); + }catch(std::exception& e){ + handler->client.close(541/*internal error*/, e.what()); + } +} + +ConnectionHandler::ConnectionHandler(Connection& connection, bool isClient) : handler(new Handler(connection, isClient)) {} + +ConnectionHandler::Handler::Handler(Connection& c, bool isClient) : + client(c.getOutput()), server(c.getOutput()), + connection(c), serverMode(!isClient), acl(0) +{ + if (serverMode) { + + acl = connection.getBroker().getAcl(); + + FieldTable properties; + Array mechanisms(0x95); + + properties.setString(QPID_FED_TAG, connection.getBroker().getFederationTag()); + + authenticator = SaslAuthenticator::createAuthenticator(c); + authenticator->getMechanisms(mechanisms); + + Array locales(0x95); + boost::shared_ptr<FieldValue> l(new Str16Value(en_US)); + locales.add(l); + client.start(properties, mechanisms, locales); + } +} + + +ConnectionHandler::Handler::~Handler() {} + + +void ConnectionHandler::Handler::startOk(const framing::FieldTable& clientProperties, + const string& mechanism, + const string& response, + const string& /*locale*/) +{ + try { + authenticator->start(mechanism, response); + } catch (std::exception& /*e*/) { + management::ManagementAgent* agent = connection.getAgent(); + if (agent) { + string error; + string uid; + authenticator->getError(error); + authenticator->getUid(uid); + agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error)); + } + throw; + } + connection.setFederationLink(clientProperties.get(QPID_FED_LINK)); + connection.setFederationPeerTag(clientProperties.getAsString(QPID_FED_TAG)); + if (connection.isFederationLink()) { + if (acl && !acl->authorise(connection.getUserId(),acl::ACT_CREATE,acl::OBJ_LINK,"")){ + client.close(framing::connection::CLOSE_CODE_CONNECTION_FORCED,"ACL denied creating a federation link"); + return; + } + QPID_LOG(info, "Connection is a federation link"); + } +} + +void ConnectionHandler::Handler::secureOk(const string& response) +{ + try { + authenticator->step(response); + } catch (std::exception& /*e*/) { + management::ManagementAgent* agent = connection.getAgent(); + if (agent) { + string error; + string uid; + authenticator->getError(error); + authenticator->getUid(uid); + agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error)); + } + throw; + } +} + +void ConnectionHandler::Handler::tuneOk(uint16_t /*channelmax*/, + uint16_t framemax, uint16_t heartbeat) +{ + connection.setFrameMax(framemax); + connection.setHeartbeat(heartbeat); +} + +void ConnectionHandler::Handler::open(const string& /*virtualHost*/, + const framing::Array& /*capabilities*/, bool /*insist*/) +{ + std::vector<Url> urls = connection.broker.getKnownBrokers(); + framing::Array array(0x95); // str16 array + for (std::vector<Url>::iterator i = urls.begin(); i < urls.end(); ++i) + array.add(boost::shared_ptr<Str16Value>(new Str16Value(i->str()))); + client.openOk(array); +} + + +void ConnectionHandler::Handler::close(uint16_t replyCode, const string& replyText) +{ + if (replyCode != 200) { + QPID_LOG(warning, "Client closed connection with " << replyCode << ": " << replyText); + } + + if (replyCode == framing::connection::CLOSE_CODE_CONNECTION_FORCED) + connection.notifyConnectionForced(replyText); + + client.closeOk(); + connection.getOutput().close(); +} + +void ConnectionHandler::Handler::closeOk(){ + connection.getOutput().close(); +} + + +void ConnectionHandler::Handler::start(const FieldTable& serverProperties, + const framing::Array& /*mechanisms*/, + const framing::Array& /*locales*/) +{ + string mechanism = connection.getAuthMechanism(); + string response = connection.getAuthCredentials(); + + connection.setFederationPeerTag(serverProperties.getAsString(QPID_FED_TAG)); + + FieldTable ft; + ft.setInt(QPID_FED_LINK,1); + ft.setString(QPID_FED_TAG, connection.getBroker().getFederationTag()); + server.startOk(ft, mechanism, response, en_US); +} + +void ConnectionHandler::Handler::secure(const string& /*challenge*/) +{ + server.secureOk(""); +} + +void ConnectionHandler::Handler::tune(uint16_t channelMax, + uint16_t frameMax, + uint16_t /*heartbeatMin*/, + uint16_t heartbeatMax) +{ + connection.setFrameMax(frameMax); + connection.setHeartbeat(heartbeatMax); + server.tuneOk(channelMax, frameMax, heartbeatMax); + server.open("/", Array(), true); +} + +void ConnectionHandler::Handler::openOk(const framing::Array& /*knownHosts*/) +{ +} + +void ConnectionHandler::Handler::redirect(const string& /*host*/, const framing::Array& /*knownHosts*/) +{ + +} diff --git a/RC9/qpid/cpp/src/qpid/broker/ConnectionHandler.h b/RC9/qpid/cpp/src/qpid/broker/ConnectionHandler.h new file mode 100644 index 0000000000..d3d5965dfc --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/ConnectionHandler.h @@ -0,0 +1,95 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ConnectionAdapter_ +#define _ConnectionAdapter_ + +#include <memory> +#include "SaslAuthenticator.h" +#include "qpid/framing/amqp_types.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/AMQP_ClientOperations.h" +#include "qpid/framing/AMQP_ClientProxy.h" +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/AMQP_ServerProxy.h" +#include "qpid/framing/enum.h" +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/Exception.h" +#include "AclModule.h" + +namespace qpid { +namespace broker { + +class Connection; + +class ConnectionHandler : public framing::FrameHandler +{ + struct Handler : public framing::AMQP_ServerOperations::ConnectionHandler, + public framing::AMQP_ClientOperations::ConnectionHandler + { + framing::AMQP_ClientProxy::Connection client; + framing::AMQP_ServerProxy::Connection server; + Connection& connection; + bool serverMode; + std::auto_ptr<SaslAuthenticator> authenticator; + AclModule* acl; + + Handler(Connection& connection, bool isClient); + ~Handler(); + void startOk(const qpid::framing::FieldTable& clientProperties, + const std::string& mechanism, const std::string& response, + const std::string& locale); + void secureOk(const std::string& response); + void tuneOk(uint16_t channelMax, uint16_t frameMax, uint16_t heartbeat); + void heartbeat() {} + void open(const std::string& virtualHost, + const framing::Array& capabilities, bool insist); + void close(uint16_t replyCode, const std::string& replyText); + void closeOk(); + + + void start(const qpid::framing::FieldTable& serverProperties, + const framing::Array& mechanisms, + const framing::Array& locales); + + void secure(const std::string& challenge); + + void tune(uint16_t channelMax, + uint16_t frameMax, + uint16_t heartbeatMin, + uint16_t heartbeatMax); + + void openOk(const framing::Array& knownHosts); + + void redirect(const std::string& host, const framing::Array& knownHosts); + }; + std::auto_ptr<Handler> handler; + public: + ConnectionHandler(Connection& connection, bool isClient); + void close(framing::connection::CloseCode code, const std::string& text); + void handle(framing::AMQFrame& frame); +}; + + +}} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/ConnectionState.h b/RC9/qpid/cpp/src/qpid/broker/ConnectionState.h new file mode 100644 index 0000000000..fd69157dbd --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/ConnectionState.h @@ -0,0 +1,100 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ConnectionState_ +#define _ConnectionState_ + +#include <vector> + +#include "qpid/sys/AggregateOutput.h" +#include "qpid/sys/ConnectionOutputHandlerPtr.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/management/Manageable.h" +#include "Broker.h" + +namespace qpid { +namespace broker { + +class ConnectionState : public ConnectionToken, public management::Manageable +{ + protected: + sys::ConnectionOutputHandlerPtr out; + + public: + ConnectionState(qpid::sys::ConnectionOutputHandler* o, Broker& b) : + out(o), + broker(b), + outputTasks(out), + framemax(65535), + heartbeat(0), + stagingThreshold(broker.getStagingThreshold()), + federationLink(true) + {} + + + + virtual ~ConnectionState () {} + + uint32_t getFrameMax() const { return framemax; } + uint16_t getHeartbeat() const { return heartbeat; } + uint64_t getStagingThreshold() const { return stagingThreshold; } + + void setFrameMax(uint32_t fm) { framemax = fm; } + void setHeartbeat(uint16_t hb) { heartbeat = hb; } + void setStagingThreshold(uint64_t st) { stagingThreshold = st; } + + virtual void setUserId(const string& uid) { userId = uid; } + const string& getUserId() const { return userId; } + + void setUrl(const string& _url) { url = _url; } + const string& getUrl() const { return url; } + + void setFederationLink(bool b) { federationLink = b; } + bool isFederationLink() const { return federationLink; } + void setFederationPeerTag(const string& tag) { federationPeerTag = string(tag); } + const string& getFederationPeerTag() const { return federationPeerTag; } + + Broker& getBroker() { return broker; } + + Broker& broker; + std::vector<Queue::shared_ptr> exclusiveQueues; + + //contained output tasks + sys::AggregateOutput outputTasks; + + sys::ConnectionOutputHandlerPtr& getOutput() { return out; } + framing::ProtocolVersion getVersion() const { return version; } + + void setOutputHandler(qpid::sys::ConnectionOutputHandler* o) { out.set(o); } + + protected: + framing::ProtocolVersion version; + uint32_t framemax; + uint16_t heartbeat; + uint64_t stagingThreshold; + string userId; + string url; + bool federationLink; + string federationPeerTag; +}; + +}} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/ConnectionToken.h b/RC9/qpid/cpp/src/qpid/broker/ConnectionToken.h new file mode 100644 index 0000000000..0e3b301897 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/ConnectionToken.h @@ -0,0 +1,40 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ConnectionToken_ +#define _ConnectionToken_ + +#include "OwnershipToken.h" +namespace qpid { + namespace broker { + /** + * An empty interface allowing opaque implementations of some + * form of token to identify a connection. + */ + class ConnectionToken : public OwnershipToken { + public: + virtual bool isLocal(const ConnectionToken* t) const { return this == t; } + virtual ~ConnectionToken(){} + }; + } +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/Consumer.h b/RC9/qpid/cpp/src/qpid/broker/Consumer.h new file mode 100644 index 0000000000..5de00668b3 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Consumer.h @@ -0,0 +1,53 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _Consumer_ +#define _Consumer_ + +#include "Message.h" +#include "QueuedMessage.h" +#include "OwnershipToken.h" + +namespace qpid { +namespace broker { + +class Queue; + +class Consumer { + const bool acquires; + public: + typedef boost::shared_ptr<Consumer> shared_ptr; + + framing::SequenceNumber position; + + Consumer(bool preAcquires = true) : acquires(preAcquires) {} + bool preAcquires() const { return acquires; } + virtual bool deliver(QueuedMessage& msg) = 0; + virtual void notify() = 0; + virtual bool filter(boost::intrusive_ptr<Message>) { return true; } + virtual bool accept(boost::intrusive_ptr<Message>) { return true; } + virtual OwnershipToken* getSession() = 0; + virtual ~Consumer(){} +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/Daemon.cpp b/RC9/qpid/cpp/src/qpid/broker/Daemon.cpp new file mode 100644 index 0000000000..88eb8fc1cd --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Daemon.cpp @@ -0,0 +1,212 @@ +/* + * + * 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 "Daemon.h" +#include "qpid/log/Statement.h" +#include "qpid/Exception.h" +#include "qpid/sys/LockFile.h" + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +namespace qpid { +namespace broker { + +using namespace std; +using qpid::sys::LockFile; + +Daemon::Daemon(std::string _pidDir) : pidDir(_pidDir) { + struct stat s; + pid = -1; + pipeFds[0] = pipeFds[1] = -1; + + if (::stat(pidDir.c_str(), &s)) { + if (errno == ENOENT) { + if (::mkdir(pidDir.c_str(), 0755)) + throw Exception ("Can't create PID directory: " + pidDir); + } + else + throw Exception ("PID directory not found: " + pidDir); + } +} + +string Daemon::pidFile(string pidDir, uint16_t port) { + ostringstream path; + path << pidDir << "/qpidd." << port << ".pid"; + return path.str(); +} + +/* + * Rewritten using low-level IO, for compatibility + * with earlier Boost versions, i.e. 103200. + */ +void Daemon::fork() +{ + if(::pipe(pipeFds) < 0) throw ErrnoException("Can't create pipe"); + if ((pid = ::fork()) < 0) throw ErrnoException("Daemon fork failed"); + if (pid == 0) { // Child + try { + QPID_LOG(debug, "Forked daemon child process"); + + // File descriptors + if(::close(pipeFds[0])<0) throw ErrnoException("Cannot close read pipe"); + if(::close(0)<0) throw ErrnoException("Cannot close stdin"); + if(::close(1)<0) throw ErrnoException("Cannot close stdout"); + if(::close(2)<0) throw ErrnoException("Cannot close stderr"); + int fd=::open("/dev/null",O_RDWR); // stdin + if(fd != 0) throw ErrnoException("Cannot re-open stdin"); + if(::dup(fd)<0) throw ErrnoException("Cannot re-open stdout"); + if(::dup(fd)<0) throw ErrnoException("Cannot re-open stderror"); + + // Misc + if(setsid()<0) throw ErrnoException("Cannot set session ID"); + if(chdir(pidDir.c_str()) < 0) throw ErrnoException("Cannot change directory to "+pidDir); + umask(027); + + // Child behavior + child(); + } + catch (const exception& e) { + QPID_LOG(critical, "Daemon startup failed: " << e.what()); + uint16_t port = 0; + write(pipeFds[1], &port, sizeof(uint16_t)); + + std::string pipeFailureMessage = e.what(); + write ( pipeFds[1], + pipeFailureMessage.c_str(), + strlen(pipeFailureMessage.c_str()) + ); + } + } + else { // Parent + close(pipeFds[1]); // Write side. + parent(); + } +} + +Daemon::~Daemon() { + if (!lockFile.empty()) + unlink(lockFile.c_str()); +} + +uint16_t Daemon::wait(int timeout) { // parent waits for child. + try { + errno = 0; + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + + /* + * Rewritten using low-level IO, for compatibility + * with earlier Boost versions, i.e. 103200. + */ + fd_set fds; + FD_ZERO(&fds); + FD_SET(pipeFds[0], &fds); + int n=select(FD_SETSIZE, &fds, 0, 0, &tv); + if(n==0) throw Exception("Timed out waiting for daemon"); + if(n<0) throw ErrnoException("Error waiting for daemon"); + uint16_t port = 0; + /* + * Read the child's port number from the pipe. + */ + int desired_read = sizeof(uint16_t); + if ( desired_read > ::read(pipeFds[0], & port, desired_read) ) + throw Exception("Cannot read from child process."); + + /* + * If the port number is 0, the child has put an error message + * on the pipe. Get it and throw it. + */ + if ( 0 == port ) { + // Skip whitespace + char c = ' '; + while ( isspace(c) ) { + if ( 1 > ::read(pipeFds[0], &c, 1) ) + throw Exception("Child port == 0, and no error message on pipe."); + } + + // Get Message + string errmsg; + do { + errmsg += c; + } while (::read(pipeFds[0], &c, 1)); + throw Exception("Daemon startup failed"+ + (errmsg.empty() ? string(".") : ": " + errmsg)); + } + return port; + } + catch (const std::exception& e) { + // Print directly to cerr. The caller will catch and log the + // exception, but in the case of a daemon parent process we + // also need to be sure the error goes to stderr. A + // dameon's logging configuration normally does not log to + // stderr. + std::cerr << e.what() << endl; + throw; + } +} + + +/* + * When the child is ready, it writes its pid to the + * lockfile and its port number on the pipe back to + * its parent process. This indicates that the + * child has successfully daemonized. When the parent + * hears the good news, it ill exit. + */ +void Daemon::ready(uint16_t port) { // child + lockFile = pidFile(pidDir, port); + LockFile lf(lockFile, true); + + /* + * Write the PID to the lockfile. + */ + lf.writePid(); + + /* + * Write the port number to the parent. + */ + int desired_write = sizeof(uint16_t); + if ( desired_write > ::write(pipeFds[1], & port, desired_write) ) { + throw Exception("Error writing to parent." ); + } + + QPID_LOG(debug, "Daemon ready on port: " << port); +} + +/* + * The parent process reads the child's pid + * from the lockfile. + */ +pid_t Daemon::getPid(string _pidDir, uint16_t port) { + string name = pidFile(_pidDir, port); + LockFile lf(name, false); + pid_t pid = lf.readPid(); + if (kill(pid, 0) < 0 && errno != EPERM) { + unlink(name.c_str()); + throw Exception("Removing stale lock file "+name); + } + return pid; +} + + +}} // namespace qpid::broker diff --git a/RC9/qpid/cpp/src/qpid/broker/Daemon.h b/RC9/qpid/cpp/src/qpid/broker/Daemon.h new file mode 100644 index 0000000000..98468debb7 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Daemon.h @@ -0,0 +1,82 @@ +#ifndef _broker_Daemon_h +#define _broker_Daemon_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <string> +#include <boost/scoped_ptr.hpp> +#include <boost/function.hpp> +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace broker { + +/** + * Tools for forking and managing a daemon process. + * NB: Only one Daemon instance is allowed in a process. + */ +class Daemon : private boost::noncopyable +{ + public: + /** Check daemon is running on port, throw exception if not */ + static pid_t getPid(std::string pidDir, uint16_t port); + + Daemon(std::string pidDir); + + virtual ~Daemon(); + + /** + * Fork a daemon process. + * Call parent() in the parent process, child() in the child. + */ + void fork(); + + protected: + + /** Called in parent process */ + virtual void parent() = 0; + + /** Called in child process */ + virtual void child() = 0; + + /** Call from parent(): wait for child to indicate it is ready. + * @timeout in seconds to wait for response. + * @return port passed by child to ready(). + */ + uint16_t wait(int timeout); + + /** Call from child(): Notify the parent we are ready and write the + * PID file. + *@param port returned by parent call to wait(). + */ + void ready(uint16_t port); + + private: + static std::string pidFile(std::string pidDir, uint16_t port); + + pid_t pid; + int pipeFds[2]; + int lockFileFd; + std::string lockFile; + std::string pidDir; +}; + +}} // namespace qpid::broker + +#endif /*!_broker_Daemon_h*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/Deliverable.h b/RC9/qpid/cpp/src/qpid/broker/Deliverable.h new file mode 100644 index 0000000000..e0ceeb2408 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Deliverable.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 _Deliverable_ +#define _Deliverable_ + +#include "Queue.h" +#include "Message.h" + +namespace qpid { + namespace broker { + class Deliverable{ + public: + bool delivered; + Deliverable() : delivered(false) {} + + virtual Message& getMessage() = 0; + + virtual void deliverTo(const boost::shared_ptr<Queue>& queue) = 0; + virtual uint64_t contentSize() { return 0; } + virtual ~Deliverable(){} + }; + } +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/DeliverableMessage.cpp b/RC9/qpid/cpp/src/qpid/broker/DeliverableMessage.cpp new file mode 100644 index 0000000000..5fff54b329 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DeliverableMessage.cpp @@ -0,0 +1,43 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "DeliverableMessage.h" + +using namespace qpid::broker; + +DeliverableMessage::DeliverableMessage(const boost::intrusive_ptr<Message>& _msg) : msg(_msg) +{ +} + +void DeliverableMessage::deliverTo(const boost::shared_ptr<Queue>& queue) +{ + queue->deliver(msg); + delivered = true; +} + +Message& DeliverableMessage::getMessage() +{ + return *msg; +} + +uint64_t DeliverableMessage::contentSize () +{ + return msg->contentSize (); +} diff --git a/RC9/qpid/cpp/src/qpid/broker/DeliverableMessage.h b/RC9/qpid/cpp/src/qpid/broker/DeliverableMessage.h new file mode 100644 index 0000000000..f5db473c22 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DeliverableMessage.h @@ -0,0 +1,45 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _DeliverableMessage_ +#define _DeliverableMessage_ + +#include "Deliverable.h" +#include "Queue.h" +#include "Message.h" + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { + namespace broker { + class DeliverableMessage : public Deliverable{ + boost::intrusive_ptr<Message> msg; + public: + DeliverableMessage(const boost::intrusive_ptr<Message>& msg); + virtual void deliverTo(const boost::shared_ptr<Queue>& queue); + Message& getMessage(); + uint64_t contentSize(); + virtual ~DeliverableMessage(){} + }; + } +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/DeliveryAdapter.h b/RC9/qpid/cpp/src/qpid/broker/DeliveryAdapter.h new file mode 100644 index 0000000000..0e9d7d3929 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DeliveryAdapter.h @@ -0,0 +1,53 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _DeliveryAdapter_ +#define _DeliveryAdapter_ + +#include "DeliveryId.h" +#include "Message.h" +#include "qpid/framing/amqp_types.h" + +namespace qpid { +namespace broker { + +class DeliveryRecord; + +/** + * The intention behind this interface is to separate the generic + * handling of some form of message delivery to clients that is + * contained in the version independent Channel class from the + * details required for a particular situation or + * version. i.e. where the existing adapters allow (through + * supporting the generated interface for a version of the + * protocol) inputs of a channel to be adapted to the version + * independent part, this does the same for the outputs. + */ +class DeliveryAdapter +{ + public: + virtual void deliver(DeliveryRecord&) = 0; + virtual ~DeliveryAdapter(){} +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/DeliveryId.h b/RC9/qpid/cpp/src/qpid/broker/DeliveryId.h new file mode 100644 index 0000000000..05b19f032e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DeliveryId.h @@ -0,0 +1,35 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _DeliveryId_ +#define _DeliveryId_ + +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/SequenceNumberSet.h" + +namespace qpid { +namespace broker { + + typedef framing::SequenceNumber DeliveryId; + typedef framing::SequenceNumberSet DeliveryIds; +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/DeliveryRecord.cpp b/RC9/qpid/cpp/src/qpid/broker/DeliveryRecord.cpp new file mode 100644 index 0000000000..b0c060aea5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DeliveryRecord.cpp @@ -0,0 +1,214 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "DeliveryRecord.h" +#include "DeliverableMessage.h" +#include "SemanticState.h" +#include "Exchange.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/MessageTransferBody.h" + +using namespace qpid; +using namespace qpid::broker; +using std::string; + +DeliveryRecord::DeliveryRecord(const QueuedMessage& _msg, + const Queue::shared_ptr& _queue, + const std::string& _tag, + bool _acquired, + bool accepted, + bool _windowing, + uint32_t _credit) : msg(_msg), + queue(_queue), + tag(_tag), + acquired(_acquired), + acceptExpected(!accepted), + cancelled(false), + completed(false), + ended(accepted), + windowing(_windowing), + credit(msg.payload ? msg.payload->getRequiredCredit() : _credit) +{} + +void DeliveryRecord::setEnded() +{ + ended = true; + //reset msg pointer, don't need to hold on to it anymore + msg.payload = boost::intrusive_ptr<Message>(); + + QPID_LOG(debug, "DeliveryRecord::setEnded() id=" << id); +} + +bool DeliveryRecord::matches(DeliveryId tag) const{ + return id == tag; +} + +bool DeliveryRecord::matchOrAfter(DeliveryId tag) const{ + return matches(tag) || after(tag); +} + +bool DeliveryRecord::after(DeliveryId tag) const{ + return id > tag; +} + +bool DeliveryRecord::coveredBy(const framing::SequenceSet* const range) const{ + return range->contains(id); +} + +void DeliveryRecord::redeliver(SemanticState* const session) { + if (!ended) { + if(cancelled){ + //if subscription was cancelled, requeue it (waiting for + //final confirmation for AMQP WG on this case) + requeue(); + }else{ + msg.payload->redeliver();//mark as redelivered + session->deliver(*this); + } + } +} + +void DeliveryRecord::deliver(framing::FrameHandler& h, DeliveryId deliveryId, uint16_t framesize) +{ + id = deliveryId; + if (msg.payload->getRedelivered()){ + msg.payload->getProperties<framing::DeliveryProperties>()->setRedelivered(true); + } + + framing::AMQFrame method(framing::in_place<framing::MessageTransferBody>(framing::ProtocolVersion(), tag, acceptExpected ? 0 : 1, acquired ? 0 : 1)); + method.setEof(false); + h.handle(method); + msg.payload->sendHeader(h, framesize); + msg.payload->sendContent(*queue, h, framesize); +} + +void DeliveryRecord::requeue() const +{ + if (acquired && !ended) { + msg.payload->redeliver(); + queue->requeue(msg); + } +} + +void DeliveryRecord::release(bool setRedelivered) +{ + if (acquired && !ended) { + if (setRedelivered) msg.payload->redeliver(); + queue->requeue(msg); + acquired = false; + setEnded(); + } else { + QPID_LOG(debug, "Ignoring release for " << id << " acquired=" << acquired << ", ended =" << ended); + } +} + +void DeliveryRecord::complete() +{ + completed = true; +} + +void DeliveryRecord::accept(TransactionContext* ctxt) { + if (acquired && !ended) { + queue->dequeue(ctxt, msg); + setEnded(); + QPID_LOG(debug, "Accepted " << id); + } +} + +void DeliveryRecord::dequeue(TransactionContext* ctxt) const{ + if (acquired && !ended) { + queue->dequeue(ctxt, msg); + } +} + +void DeliveryRecord::committed() const{ + queue->dequeueCommitted(msg); +} + +void DeliveryRecord::reject() +{ + Exchange::shared_ptr alternate = queue->getAlternateExchange(); + if (alternate) { + DeliverableMessage delivery(msg.payload); + alternate->route(delivery, msg.payload->getRoutingKey(), msg.payload->getApplicationHeaders()); + QPID_LOG(info, "Routed rejected message from " << queue->getName() << " to " + << alternate->getName()); + } else { + //just drop it + QPID_LOG(info, "Dropping rejected message from " << queue->getName()); + } +} + +uint32_t DeliveryRecord::getCredit() const +{ + return credit; +} + +void DeliveryRecord::acquire(DeliveryIds& results) { + if (queue->acquire(msg)) { + acquired = true; + results.push_back(id); + } else { + QPID_LOG(info, "Message already acquired " << id.getValue()); + } +} + +void DeliveryRecord::cancel(const std::string& cancelledTag) +{ + if (tag == cancelledTag) + cancelled = true; +} + +AckRange DeliveryRecord::findRange(DeliveryRecords& records, DeliveryId first, DeliveryId last) +{ + ack_iterator start = find_if(records.begin(), records.end(), boost::bind(&DeliveryRecord::matchOrAfter, _1, first)); + ack_iterator end = start; + + if (start != records.end()) { + if (first == last) { + //just acked single element (move end past it) + ++end; + } else { + //need to find end (position it just after the last record in range) + end = find_if(start, records.end(), boost::bind(&DeliveryRecord::after, _1, last)); + } + } + return AckRange(start, end); +} + + +namespace qpid { +namespace broker { + +std::ostream& operator<<(std::ostream& out, const DeliveryRecord& r) +{ + out << "{" << "id=" << r.id.getValue(); + out << ", tag=" << r.tag << "}"; + out << ", queue=" << r.queue->getName() << "}"; + return out; +} + +bool operator<(const DeliveryRecord& a, const DeliveryRecord& b) +{ + return a.id < b.id; +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/broker/DeliveryRecord.h b/RC9/qpid/cpp/src/qpid/broker/DeliveryRecord.h new file mode 100644 index 0000000000..d7ccab0726 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DeliveryRecord.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. + * + */ +#ifndef _DeliveryRecord_ +#define _DeliveryRecord_ + +#include <algorithm> +#include <list> +#include <vector> +#include <ostream> +#include "qpid/framing/SequenceSet.h" +#include "Queue.h" +#include "QueuedMessage.h" +#include "DeliveryId.h" +#include "Message.h" + +namespace qpid { +namespace broker { +class SemanticState; +class DeliveryRecord; + +typedef std::list<DeliveryRecord> DeliveryRecords; +typedef std::list<DeliveryRecord>::iterator ack_iterator; + +struct AckRange +{ + ack_iterator start; + ack_iterator end; + AckRange(ack_iterator _start, ack_iterator _end) : start(_start), end(_end) {} +}; + + +/** + * Record of a delivery for which an ack is outstanding. + */ +class DeliveryRecord +{ + QueuedMessage msg; + mutable Queue::shared_ptr queue; + const std::string tag; + DeliveryId id; + bool acquired; + bool acceptExpected; + bool cancelled; + + bool completed; + bool ended; + const bool windowing; + + /** + * Record required credit on construction as the pointer to the + * message may be reset once we no longer need to deliver it + * (e.g. when it is accepted), but we will still need to be able + * to reallocate credit when it is completed (which could happen + * after that). + */ + const uint32_t credit; + + public: + DeliveryRecord( + const QueuedMessage& msg, + const Queue::shared_ptr& queue, + const std::string& tag, + bool acquired, + bool accepted, + bool windowing, + uint32_t credit=0 // Only used if msg is empty. + ); + + bool matches(DeliveryId tag) const; + bool matchOrAfter(DeliveryId tag) const; + bool after(DeliveryId tag) const; + bool coveredBy(const framing::SequenceSet* const range) const; + + void dequeue(TransactionContext* ctxt = 0) const; + void requeue() const; + void release(bool setRedelivered); + void reject(); + void cancel(const std::string& tag); + void redeliver(SemanticState* const); + void acquire(DeliveryIds& results); + void complete(); + void accept(TransactionContext* ctxt); + void setEnded(); + void committed() const; + + bool isAcquired() const { return acquired; } + bool isComplete() const { return completed; } + bool isRedundant() const { return ended && (!windowing || completed); } + bool isCancelled() const { return cancelled; } + bool isAccepted() const { return !acceptExpected; } + bool isEnded() const { return ended; } + bool isWindowing() const { return windowing; } + + uint32_t getCredit() const; + const std::string& getTag() const { return tag; } + + void deliver(framing::FrameHandler& h, DeliveryId deliveryId, uint16_t framesize); + void setId(DeliveryId _id) { id = _id; } + + static AckRange findRange(DeliveryRecords& records, DeliveryId first, DeliveryId last); + const QueuedMessage& getMessage() const { return msg; } + framing::SequenceNumber getId() const { return id; } + Queue::shared_ptr getQueue() const { return queue; } + friend bool operator<(const DeliveryRecord&, const DeliveryRecord&); + friend std::ostream& operator<<(std::ostream&, const DeliveryRecord&); +}; + +struct AcquireFunctor +{ + DeliveryIds& results; + + AcquireFunctor(DeliveryIds& _results) : results(_results) {} + + void operator()(DeliveryRecord& record) + { + record.acquire(results); + } +}; +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/DirectExchange.cpp b/RC9/qpid/cpp/src/qpid/broker/DirectExchange.cpp new file mode 100644 index 0000000000..2eb488de67 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DirectExchange.cpp @@ -0,0 +1,190 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/log/Statement.h" +#include "DirectExchange.h" +#include <iostream> + +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; +using qpid::management::Manageable; +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace +{ +const std::string qpidFedOp("qpid.fed.op"); +const std::string qpidFedTags("qpid.fed.tags"); +const std::string qpidFedOrigin("qpid.fed.origin"); + +const std::string fedOpBind("B"); +const std::string fedOpUnbind("U"); +const std::string fedOpReorigin("R"); +const std::string fedOpHello("H"); +} + +DirectExchange::DirectExchange(const string& _name, Manageable* _parent) : Exchange(_name, _parent) +{ + if (mgmtExchange != 0) + mgmtExchange->set_type(typeName); +} + +DirectExchange::DirectExchange(const string& _name, bool _durable, + const FieldTable& _args, Manageable* _parent) : + Exchange(_name, _durable, _args, _parent) +{ + if (mgmtExchange != 0) + mgmtExchange->set_type(typeName); +} + +bool DirectExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* args) +{ + string fedOp(args ? args->getAsString(qpidFedOp) : fedOpBind); + string fedTags(args ? args->getAsString(qpidFedTags) : ""); + string fedOrigin(args ? args->getAsString(qpidFedOrigin) : ""); + bool propagate = false; + + if (args == 0 || fedOp.empty() || fedOp == fedOpBind) { + Mutex::ScopedLock l(lock); + Binding::shared_ptr b(new Binding(routingKey, queue, this, FieldTable(), fedOrigin)); + BoundKey& bk = bindings[routingKey]; + if (bk.queues.add_unless(b, MatchQueue(queue))) { + propagate = bk.fedBinding.addOrigin(fedOrigin); + if (mgmtExchange != 0) { + mgmtExchange->inc_bindingCount(); + ((_qmf::Queue*) queue->GetManagementObject())->inc_bindingCount(); + } + } else { + return false; + } + } else if (fedOp == fedOpUnbind) { + Mutex::ScopedLock l(lock); + BoundKey& bk = bindings[routingKey]; + propagate = bk.fedBinding.delOrigin(fedOrigin); + if (bk.fedBinding.count() == 0) + unbind(queue, routingKey, 0); + } else if (fedOp == fedOpReorigin) { + for (std::map<string, BoundKey>::iterator iter = bindings.begin(); + iter != bindings.end(); iter++) { + const BoundKey& bk = iter->second; + if (bk.fedBinding.hasLocal()) { + propagateFedOp(iter->first, string(), fedOpBind, string()); + } + } + } + + routeIVE(); + if (propagate) + propagateFedOp(routingKey, fedTags, fedOp, fedOrigin); + return true; +} + +bool DirectExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/) +{ + bool propagate = false; + + { + Mutex::ScopedLock l(lock); + BoundKey& bk = bindings[routingKey]; + if (bk.queues.remove_if(MatchQueue(queue))) { + propagate = bk.fedBinding.delOrigin(); + if (mgmtExchange != 0) { + mgmtExchange->dec_bindingCount(); + ((_qmf::Queue*) queue->GetManagementObject())->dec_bindingCount(); + } + } else { + return false; + } + } + + if (propagate) + propagateFedOp(routingKey, string(), fedOpUnbind, string()); + return true; +} + +void DirectExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/) +{ + PreRoute pr(msg, this); + Queues::ConstPtr p; + { + Mutex::ScopedLock l(lock); + p = bindings[routingKey].queues.snapshot(); + } + int count(0); + + if (p) { + for(std::vector<Binding::shared_ptr>::const_iterator i = p->begin(); i != p->end(); i++, count++) { + msg.deliverTo((*i)->queue); + if ((*i)->mgmtBinding != 0) + (*i)->mgmtBinding->inc_msgMatched(); + } + } + + if(!count){ + QPID_LOG(info, "DirectExchange " << getName() << " could not route message with key " << routingKey + << "; no matching binding found"); + if (mgmtExchange != 0) { + mgmtExchange->inc_msgDrops(); + mgmtExchange->inc_byteDrops(msg.contentSize()); + } + } else { + if (mgmtExchange != 0) { + mgmtExchange->inc_msgRoutes(count); + mgmtExchange->inc_byteRoutes(count * msg.contentSize()); + } + } + + if (mgmtExchange != 0) { + mgmtExchange->inc_msgReceives(); + mgmtExchange->inc_byteReceives(msg.contentSize()); + } +} + + +bool DirectExchange::isBound(Queue::shared_ptr queue, const string* const routingKey, const FieldTable* const) +{ + Mutex::ScopedLock l(lock); + if (routingKey) { + Bindings::iterator i = bindings.find(*routingKey); + + if (i == bindings.end()) + return false; + if (!queue) + return true; + + Queues::ConstPtr p = i->second.queues.snapshot(); + return p && std::find_if(p->begin(), p->end(), MatchQueue(queue)) != p->end(); + } else if (!queue) { + //if no queue or routing key is specified, just report whether any bindings exist + return bindings.size() > 0; + } else { + for (Bindings::iterator i = bindings.begin(); i != bindings.end(); i++) { + Queues::ConstPtr p = i->second.queues.snapshot(); + if (p && std::find_if(p->begin(), p->end(), MatchQueue(queue)) != p->end()) return true; + } + return false; + } + + return false; +} + +DirectExchange::~DirectExchange() {} + +const std::string DirectExchange::typeName("direct"); diff --git a/RC9/qpid/cpp/src/qpid/broker/DirectExchange.h b/RC9/qpid/cpp/src/qpid/broker/DirectExchange.h new file mode 100644 index 0000000000..ba60469df8 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DirectExchange.h @@ -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. + * + */ +#ifndef _DirectExchange_ +#define _DirectExchange_ + +#include <map> +#include <vector> +#include "Exchange.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/CopyOnWriteArray.h" +#include "qpid/sys/Mutex.h" +#include "Queue.h" + +namespace qpid { +namespace broker { +class DirectExchange : public virtual Exchange { + typedef qpid::sys::CopyOnWriteArray<Binding::shared_ptr> Queues; + struct BoundKey { + Queues queues; + FedBinding fedBinding; + }; + typedef std::map<string, BoundKey> Bindings; + Bindings bindings; + qpid::sys::Mutex lock; + +public: + static const std::string typeName; + + DirectExchange(const std::string& name, management::Manageable* parent = 0); + DirectExchange(const string& _name, bool _durable, + const qpid::framing::FieldTable& _args, management::Manageable* parent = 0); + + virtual std::string getType() const { return typeName; } + + virtual bool bind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args); + virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args); + + virtual ~DirectExchange(); + + virtual bool supportsDynamicBinding() { return true; } +}; + +}} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/DtxAck.cpp b/RC9/qpid/cpp/src/qpid/broker/DtxAck.cpp new file mode 100644 index 0000000000..bc7d662afb --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DtxAck.cpp @@ -0,0 +1,59 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "DtxAck.h" +#include "qpid/log/Statement.h" + +using std::bind1st; +using std::bind2nd; +using std::mem_fun_ref; +using namespace qpid::broker; + +DtxAck::DtxAck(const qpid::framing::SequenceSet& acked, std::list<DeliveryRecord>& unacked) +{ + remove_copy_if(unacked.begin(), unacked.end(), inserter(pending, pending.end()), + not1(bind2nd(mem_fun_ref(&DeliveryRecord::coveredBy), &acked))); +} + +bool DtxAck::prepare(TransactionContext* ctxt) throw() +{ + try{ + //record dequeue in the store + for (ack_iterator i = pending.begin(); i != pending.end(); i++) { + i->dequeue(ctxt); + } + return true; + }catch(...){ + QPID_LOG(error, "Failed to prepare"); + return false; + } +} + +void DtxAck::commit() throw() +{ + for_each(pending.begin(), pending.end(), mem_fun_ref(&DeliveryRecord::committed)); + pending.clear(); +} + +void DtxAck::rollback() throw() +{ + for_each(pending.begin(), pending.end(), mem_fun_ref(&DeliveryRecord::requeue)); + pending.clear(); +} diff --git a/RC9/qpid/cpp/src/qpid/broker/DtxAck.h b/RC9/qpid/cpp/src/qpid/broker/DtxAck.h new file mode 100644 index 0000000000..d43532906a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DtxAck.h @@ -0,0 +1,48 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _DtxAck_ +#define _DtxAck_ + +#include <algorithm> +#include <functional> +#include <list> +#include "qpid/framing/SequenceSet.h" +#include "DeliveryRecord.h" +#include "TxOp.h" + +namespace qpid { + namespace broker { + class DtxAck : public TxOp{ + std::list<DeliveryRecord> pending; + + public: + DtxAck(const framing::SequenceSet& acked, std::list<DeliveryRecord>& unacked); + virtual bool prepare(TransactionContext* ctxt) throw(); + virtual void commit() throw(); + virtual void rollback() throw(); + virtual ~DtxAck(){} + virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); } + }; + } +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/DtxBuffer.cpp b/RC9/qpid/cpp/src/qpid/broker/DtxBuffer.cpp new file mode 100644 index 0000000000..29a07ea6d9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DtxBuffer.cpp @@ -0,0 +1,83 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "DtxBuffer.h" + +using namespace qpid::broker; +using qpid::sys::Mutex; + +DtxBuffer::DtxBuffer(const std::string& _xid) + : xid(_xid), ended(false), suspended(false), failed(false), expired(false) {} + +DtxBuffer::~DtxBuffer() {} + +void DtxBuffer::markEnded() +{ + Mutex::ScopedLock locker(lock); + ended = true; +} + +bool DtxBuffer::isEnded() +{ + Mutex::ScopedLock locker(lock); + return ended; +} + +void DtxBuffer::setSuspended(bool isSuspended) +{ + suspended = isSuspended; +} + +bool DtxBuffer::isSuspended() +{ + return suspended; +} + +void DtxBuffer::fail() +{ + Mutex::ScopedLock locker(lock); + rollback(); + failed = true; + ended = true; +} + +bool DtxBuffer::isRollbackOnly() +{ + Mutex::ScopedLock locker(lock); + return failed; +} + +const std::string& DtxBuffer::getXid() +{ + return xid; +} + +void DtxBuffer::timedout() +{ + Mutex::ScopedLock locker(lock); + expired = true; + fail(); +} + +bool DtxBuffer::isExpired() +{ + Mutex::ScopedLock locker(lock); + return expired; +} diff --git a/RC9/qpid/cpp/src/qpid/broker/DtxBuffer.h b/RC9/qpid/cpp/src/qpid/broker/DtxBuffer.h new file mode 100644 index 0000000000..b302632037 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DtxBuffer.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 _DtxBuffer_ +#define _DtxBuffer_ + +#include "TxBuffer.h" +#include "qpid/sys/Mutex.h" + +namespace qpid { + namespace broker { + class DtxBuffer : public TxBuffer{ + sys::Mutex lock; + const std::string xid; + bool ended; + bool suspended; + bool failed; + bool expired; + + public: + typedef boost::shared_ptr<DtxBuffer> shared_ptr; + + DtxBuffer(const std::string& xid = ""); + ~DtxBuffer(); + void markEnded(); + bool isEnded(); + void setSuspended(bool suspended); + bool isSuspended(); + void fail(); + bool isRollbackOnly(); + void timedout(); + bool isExpired(); + const std::string& getXid(); + }; + } +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/DtxManager.cpp b/RC9/qpid/cpp/src/qpid/broker/DtxManager.cpp new file mode 100644 index 0000000000..f4494fccc6 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DtxManager.cpp @@ -0,0 +1,171 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "DtxManager.h" +#include "DtxTimeout.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Statement.h" +#include "qpid/ptr_map.h" + +#include <boost/format.hpp> +#include <iostream> + +using boost::intrusive_ptr; +using qpid::sys::Mutex; +using qpid::ptr_map_ptr; +using namespace qpid::broker; +using namespace qpid::framing; + +DtxManager::DtxManager(Timer& t) : store(0), timer(t) {} + +DtxManager::~DtxManager() {} + +void DtxManager::start(const std::string& xid, DtxBuffer::shared_ptr ops) +{ + createWork(xid)->add(ops); +} + +void DtxManager::join(const std::string& xid, DtxBuffer::shared_ptr ops) +{ + getWork(xid)->add(ops); +} + +void DtxManager::recover(const std::string& xid, std::auto_ptr<TPCTransactionContext> txn, DtxBuffer::shared_ptr ops) +{ + createWork(xid)->recover(txn, ops); +} + +bool DtxManager::prepare(const std::string& xid) +{ + QPID_LOG(debug, "preparing: " << xid); + try { + return getWork(xid)->prepare(); + } catch (DtxTimeoutException& e) { + remove(xid); + throw e; + } +} + +bool DtxManager::commit(const std::string& xid, bool onePhase) +{ + QPID_LOG(debug, "committing: " << xid); + try { + bool result = getWork(xid)->commit(onePhase); + remove(xid); + return result; + } catch (DtxTimeoutException& e) { + remove(xid); + throw e; + } +} + +void DtxManager::rollback(const std::string& xid) +{ + QPID_LOG(debug, "rolling back: " << xid); + try { + getWork(xid)->rollback(); + remove(xid); + } catch (DtxTimeoutException& e) { + remove(xid); + throw e; + } +} + +DtxWorkRecord* DtxManager::getWork(const std::string& xid) +{ + Mutex::ScopedLock locker(lock); + WorkMap::iterator i = work.find(xid); + if (i == work.end()) { + throw NotFoundException(QPID_MSG("Unrecognised xid " << xid)); + } + return ptr_map_ptr(i); +} + +void DtxManager::remove(const std::string& xid) +{ + Mutex::ScopedLock locker(lock); + WorkMap::iterator i = work.find(xid); + if (i == work.end()) { + throw NotFoundException(QPID_MSG("Unrecognised xid " << xid)); + } else { + work.erase(i); + } +} + +DtxWorkRecord* DtxManager::createWork(std::string xid) +{ + Mutex::ScopedLock locker(lock); + WorkMap::iterator i = work.find(xid); + if (i != work.end()) { + throw NotAllowedException(QPID_MSG("Xid " << xid << " is already known (use 'join' to add work to an existing xid)")); + } else { + return ptr_map_ptr(work.insert(xid, new DtxWorkRecord(xid, store)).first); + } +} + +void DtxManager::setTimeout(const std::string& xid, uint32_t secs) +{ + DtxWorkRecord* record = getWork(xid); + intrusive_ptr<DtxTimeout> timeout = record->getTimeout(); + if (timeout.get()) { + if (timeout->timeout == secs) return;//no need to do anything further if timeout hasn't changed + timeout->cancelled = true; + } + timeout = intrusive_ptr<DtxTimeout>(new DtxTimeout(secs, *this, xid)); + record->setTimeout(timeout); + timer.add(boost::static_pointer_cast<TimerTask>(timeout)); + +} + +uint32_t DtxManager::getTimeout(const std::string& xid) +{ + intrusive_ptr<DtxTimeout> timeout = getWork(xid)->getTimeout(); + return !timeout ? 0 : timeout->timeout; +} + +void DtxManager::timedout(const std::string& xid) +{ + Mutex::ScopedLock locker(lock); + WorkMap::iterator i = work.find(xid); + if (i == work.end()) { + QPID_LOG(warning, "Transaction timeout failed: no record for xid"); + } else { + ptr_map_ptr(i)->timedout(); + //TODO: do we want to have a timed task to cleanup, or can we rely on an explicit completion? + //timer.add(intrusive_ptr<TimerTask>(new DtxCleanup(60*30/*30 mins*/, *this, xid))); + } +} + +DtxManager::DtxCleanup::DtxCleanup(uint32_t _timeout, DtxManager& _mgr, const std::string& _xid) + : TimerTask(qpid::sys::Duration(_timeout * qpid::sys::TIME_SEC)), mgr(_mgr), xid(_xid) {} + +void DtxManager::DtxCleanup::fire() +{ + try { + mgr.remove(xid); + } catch (ConnectionException& /*e*/) { + //assume it was explicitly cleaned up after a call to prepare, commit or rollback + } +} + +void DtxManager::setStore (TransactionalStore* _store) +{ + store = _store; +} diff --git a/RC9/qpid/cpp/src/qpid/broker/DtxManager.h b/RC9/qpid/cpp/src/qpid/broker/DtxManager.h new file mode 100644 index 0000000000..a61e8610f0 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DtxManager.h @@ -0,0 +1,74 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _DtxManager_ +#define _DtxManager_ + +#include <boost/ptr_container/ptr_map.hpp> +#include "DtxBuffer.h" +#include "DtxWorkRecord.h" +#include "Timer.h" +#include "TransactionalStore.h" +#include "qpid/framing/amqp_types.h" +#include "qpid/sys/Mutex.h" + +namespace qpid { +namespace broker { + +class DtxManager{ + typedef boost::ptr_map<std::string, DtxWorkRecord> WorkMap; + + struct DtxCleanup : public TimerTask + { + DtxManager& mgr; + const std::string& xid; + + DtxCleanup(uint32_t timeout, DtxManager& mgr, const std::string& xid); + void fire(); + }; + + WorkMap work; + TransactionalStore* store; + qpid::sys::Mutex lock; + Timer& timer; + + void remove(const std::string& xid); + DtxWorkRecord* getWork(const std::string& xid); + DtxWorkRecord* createWork(std::string xid); + +public: + DtxManager(Timer&); + ~DtxManager(); + void start(const std::string& xid, DtxBuffer::shared_ptr work); + void join(const std::string& xid, DtxBuffer::shared_ptr work); + void recover(const std::string& xid, std::auto_ptr<TPCTransactionContext> txn, DtxBuffer::shared_ptr work); + bool prepare(const std::string& xid); + bool commit(const std::string& xid, bool onePhase); + void rollback(const std::string& xid); + void setTimeout(const std::string& xid, uint32_t secs); + uint32_t getTimeout(const std::string& xid); + void timedout(const std::string& xid); + void setStore(TransactionalStore* store); +}; + +} +} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/DtxTimeout.cpp b/RC9/qpid/cpp/src/qpid/broker/DtxTimeout.cpp new file mode 100644 index 0000000000..8e0a7741c4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DtxTimeout.cpp @@ -0,0 +1,35 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "DtxTimeout.h" +#include "DtxManager.h" +#include "qpid/sys/Time.h" + +using namespace qpid::broker; + +DtxTimeout::DtxTimeout(uint32_t _timeout, DtxManager& _mgr, const std::string& _xid) + : TimerTask(qpid::sys::Duration(_timeout * qpid::sys::TIME_SEC)), timeout(_timeout), mgr(_mgr), xid(_xid) +{ +} + +void DtxTimeout::fire() +{ + mgr.timedout(xid); +} diff --git a/RC9/qpid/cpp/src/qpid/broker/DtxTimeout.h b/RC9/qpid/cpp/src/qpid/broker/DtxTimeout.h new file mode 100644 index 0000000000..6e949eab0d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DtxTimeout.h @@ -0,0 +1,48 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _DtxTimeout_ +#define _DtxTimeout_ + +#include "qpid/Exception.h" +#include "Timer.h" + +namespace qpid { +namespace broker { + +class DtxManager; + +struct DtxTimeoutException : public Exception {}; + +struct DtxTimeout : public TimerTask +{ + const uint32_t timeout; + DtxManager& mgr; + const std::string xid; + + DtxTimeout(uint32_t timeout, DtxManager& mgr, const std::string& xid); + void fire(); +}; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/DtxWorkRecord.cpp b/RC9/qpid/cpp/src/qpid/broker/DtxWorkRecord.cpp new file mode 100644 index 0000000000..cc79813dab --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DtxWorkRecord.cpp @@ -0,0 +1,177 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "DtxWorkRecord.h" +#include "qpid/framing/reply_exceptions.h" +#include <boost/format.hpp> +#include <boost/mem_fn.hpp> +using boost::mem_fn; +using qpid::sys::Mutex; + +using namespace qpid::broker; +using namespace qpid::framing; + +DtxWorkRecord::DtxWorkRecord(const std::string& _xid, TransactionalStore* const _store) : + xid(_xid), store(_store), completed(false), rolledback(false), prepared(false), expired(false) {} + +DtxWorkRecord::~DtxWorkRecord() +{ + if (timeout.get()) { + timeout->cancelled = true; + } +} + +bool DtxWorkRecord::prepare() +{ + Mutex::ScopedLock locker(lock); + if (check()) { + txn = store->begin(xid); + if (prepare(txn.get())) { + store->prepare(*txn); + prepared = true; + } else { + abort(); + //TODO: this should probably be flagged as internal error + } + } else { + //some part of the work has been marked rollback only + abort(); + } + return prepared; +} + +bool DtxWorkRecord::prepare(TransactionContext* _txn) +{ + bool succeeded(true); + for (Work::iterator i = work.begin(); succeeded && i != work.end(); i++) { + succeeded = (*i)->prepare(_txn); + } + return succeeded; +} + +bool DtxWorkRecord::commit(bool onePhase) +{ + Mutex::ScopedLock locker(lock); + if (check()) { + if (prepared) { + //already prepared i.e. 2pc + if (onePhase) { + throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " has been prepared, one-phase option not valid!")); + } + + store->commit(*txn); + txn.reset(); + + std::for_each(work.begin(), work.end(), mem_fn(&TxBuffer::commit)); + return true; + } else { + //1pc commit optimisation, don't need a 2pc transaction context: + if (!onePhase) { + throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " has not been prepared, one-phase option required!")); + } + std::auto_ptr<TransactionContext> localtxn = store->begin(); + if (prepare(localtxn.get())) { + store->commit(*localtxn); + std::for_each(work.begin(), work.end(), mem_fn(&TxBuffer::commit)); + return true; + } else { + store->abort(*localtxn); + abort(); + //TODO: this should probably be flagged as internal error + return false; + } + } + } else { + //some part of the work has been marked rollback only + abort(); + return false; + } +} + +void DtxWorkRecord::rollback() +{ + Mutex::ScopedLock locker(lock); + check(); + abort(); +} + +void DtxWorkRecord::add(DtxBuffer::shared_ptr ops) +{ + Mutex::ScopedLock locker(lock); + if (expired) { + throw DtxTimeoutException(); + } + if (completed) { + throw CommandInvalidException(QPID_MSG("Branch with xid " << xid << " has been completed!")); + } + work.push_back(ops); +} + +bool DtxWorkRecord::check() +{ + if (expired) { + throw DtxTimeoutException(); + } + if (!completed) { + //iterate through all DtxBuffers and ensure they are all ended + for (Work::iterator i = work.begin(); i != work.end(); i++) { + if (!(*i)->isEnded()) { + throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " not completed!")); + } else if ((*i)->isRollbackOnly()) { + rolledback = true; + } + } + completed = true; + } + return !rolledback; +} + +void DtxWorkRecord::abort() +{ + if (txn.get()) { + store->abort(*txn); + txn.reset(); + } + std::for_each(work.begin(), work.end(), mem_fn(&TxBuffer::rollback)); +} + +void DtxWorkRecord::recover(std::auto_ptr<TPCTransactionContext> _txn, DtxBuffer::shared_ptr ops) +{ + add(ops); + txn = _txn; + ops->markEnded(); + completed = true; + prepared = true; +} + +void DtxWorkRecord::timedout() +{ + Mutex::ScopedLock locker(lock); + expired = true; + rolledback = true; + if (!completed) { + for (Work::iterator i = work.begin(); i != work.end(); i++) { + if (!(*i)->isEnded()) { + (*i)->timedout(); + } + } + } + abort(); +} diff --git a/RC9/qpid/cpp/src/qpid/broker/DtxWorkRecord.h b/RC9/qpid/cpp/src/qpid/broker/DtxWorkRecord.h new file mode 100644 index 0000000000..6677784c32 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/DtxWorkRecord.h @@ -0,0 +1,79 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _DtxWorkRecord_ +#define _DtxWorkRecord_ + +#include "DtxBuffer.h" +#include "DtxTimeout.h" +#include "TransactionalStore.h" + +#include "qpid/framing/amqp_types.h" +#include "qpid/sys/Mutex.h" + +#include <algorithm> +#include <functional> +#include <vector> + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { + +/** + * Represents the work done under a particular distributed transaction + * across potentially multiple channels. Identified by a xid. Allows + * that work to be prepared, committed and rolled-back. + */ +class DtxWorkRecord +{ + typedef std::vector<DtxBuffer::shared_ptr> Work; + + const std::string xid; + TransactionalStore* const store; + bool completed; + bool rolledback; + bool prepared; + bool expired; + boost::intrusive_ptr<DtxTimeout> timeout; + Work work; + std::auto_ptr<TPCTransactionContext> txn; + qpid::sys::Mutex lock; + + bool check(); + void abort(); + bool prepare(TransactionContext* txn); +public: + DtxWorkRecord(const std::string& xid, TransactionalStore* const store); + ~DtxWorkRecord(); + bool prepare(); + bool commit(bool onePhase); + void rollback(); + void add(DtxBuffer::shared_ptr ops); + void recover(std::auto_ptr<TPCTransactionContext> txn, DtxBuffer::shared_ptr ops); + void timedout(); + void setTimeout(boost::intrusive_ptr<DtxTimeout> t) { timeout = t; } + boost::intrusive_ptr<DtxTimeout> getTimeout() { return timeout; } +}; + +} +} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/Exchange.cpp b/RC9/qpid/cpp/src/qpid/broker/Exchange.cpp new file mode 100644 index 0000000000..34673bdab3 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Exchange.cpp @@ -0,0 +1,270 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Exchange.h" +#include "ExchangeRegistry.h" +#include "qpid/agent/ManagementAgent.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/MessageProperties.h" +#include "DeliverableMessage.h" + +using namespace qpid::broker; +using namespace qpid::framing; +using qpid::framing::Buffer; +using qpid::framing::FieldTable; +using qpid::sys::Mutex; +using qpid::management::ManagementAgent; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace +{ +const std::string qpidMsgSequence("qpid.msg_sequence"); +const std::string qpidSequenceCounter("qpid.sequence_counter"); +const std::string qpidIVE("qpid.ive"); +const std::string qpidFedOp("qpid.fed.op"); +const std::string qpidFedTags("qpid.fed.tags"); +const std::string qpidFedOrigin("qpid.fed.origin"); + +const std::string fedOpBind("B"); +const std::string fedOpUnbind("U"); +const std::string fedOpReorigin("R"); +const std::string fedOpHello("H"); +} + + +Exchange::PreRoute::PreRoute(Deliverable& msg, Exchange* _p):parent(_p) { + if (parent){ + if (parent->sequence || parent->ive) parent->sequenceLock.lock(); + + if (parent->sequence){ + parent->sequenceNo++; + msg.getMessage().getProperties<MessageProperties>()->getApplicationHeaders().setInt64(qpidMsgSequence,parent->sequenceNo); + } + if (parent->ive) { + parent->lastMsg = &( msg.getMessage()); + } + } +} + +Exchange::PreRoute::~PreRoute(){ + if (parent && (parent->sequence || parent->ive)){ + parent->sequenceLock.unlock(); + } +} + +void Exchange::routeIVE(){ + if (ive && lastMsg.get()){ + DeliverableMessage dmsg(lastMsg); + route(dmsg, lastMsg->getRoutingKey(), lastMsg->getApplicationHeaders()); + } +} + + +Exchange::Exchange (const string& _name, Manageable* parent) : + name(_name), durable(false), persistenceId(0), sequence(false), + sequenceNo(0), ive(false), mgmtExchange(0) +{ + if (parent != 0) + { + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + if (agent != 0) + { + mgmtExchange = new _qmf::Exchange (agent, this, parent, _name, durable); + agent->addObject (mgmtExchange); + } + } +} + +Exchange::Exchange(const string& _name, bool _durable, const qpid::framing::FieldTable& _args, + Manageable* parent) + : name(_name), durable(_durable), args(_args), alternateUsers(0), persistenceId(0), + sequence(false), sequenceNo(0), ive(false), mgmtExchange(0) +{ + if (parent != 0) + { + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + if (agent != 0) + { + mgmtExchange = new _qmf::Exchange (agent, this, parent, _name, durable); + mgmtExchange->set_arguments(args); + if (!durable) { + if (name == "") + agent->addObject (mgmtExchange, 0x1000000000000004LL); // Special default exchange ID + else if (name == "qpid.management") + agent->addObject (mgmtExchange, 0x1000000000000005LL); // Special management exchange ID + else + agent->addObject (mgmtExchange); + } + } + } + + sequence = _args.get(qpidMsgSequence); + if (sequence) { + QPID_LOG(debug, "Configured exchange "+ _name +" with Msg sequencing"); + args.setInt64(std::string(qpidSequenceCounter), sequenceNo); + } + + ive = _args.get(qpidIVE); + if (ive) QPID_LOG(debug, "Configured exchange "+ _name +" with Initial Value"); +} + +Exchange::~Exchange () +{ + if (mgmtExchange != 0) + mgmtExchange->resourceDestroy (); +} + +void Exchange::setPersistenceId(uint64_t id) const +{ + if (mgmtExchange != 0 && persistenceId == 0) + { + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + agent->addObject (mgmtExchange, 0x2000000000000000LL + id); + } + persistenceId = id; +} + +Exchange::shared_ptr Exchange::decode(ExchangeRegistry& exchanges, Buffer& buffer) +{ + string name; + string type; + FieldTable args; + + buffer.getShortString(name); + bool durable(buffer.getOctet()); + buffer.getShortString(type); + buffer.get(args); + + Exchange::shared_ptr exch = exchanges.declare(name, type, durable, args).first; + exch->sequenceNo = args.getAsInt64(qpidSequenceCounter); + return exch; +} + +void Exchange::encode(Buffer& buffer) const +{ + buffer.putShortString(name); + buffer.putOctet(durable); + buffer.putShortString(getType()); + if (args.isSet(qpidSequenceCounter)) + args.setInt64(std::string(qpidSequenceCounter),sequenceNo); + buffer.put(args); +} + +uint32_t Exchange::encodedSize() const +{ + return name.size() + 1/*short string size*/ + + 1 /*durable*/ + + getType().size() + 1/*short string size*/ + + args.encodedSize(); +} + +ManagementObject* Exchange::GetManagementObject (void) const +{ + return (ManagementObject*) mgmtExchange; +} + +void Exchange::registerDynamicBridge(DynamicBridge* db) +{ + if (!supportsDynamicBinding()) + throw Exception("Exchange type does not support dynamic binding"); + + { + Mutex::ScopedLock l(bridgeLock); + for (std::vector<DynamicBridge*>::iterator iter = bridgeVector.begin(); + iter != bridgeVector.end(); iter++) + (*iter)->sendReorigin(); + + bridgeVector.push_back(db); + } + + FieldTable args; + args.setString(qpidFedOp, fedOpReorigin); + bind(Queue::shared_ptr(), string(), &args); +} + +void Exchange::removeDynamicBridge(DynamicBridge* db) +{ + Mutex::ScopedLock l(bridgeLock); + for (std::vector<DynamicBridge*>::iterator iter = bridgeVector.begin(); + iter != bridgeVector.end(); iter++) + if (*iter == db) { + bridgeVector.erase(iter); + break; + } +} + +void Exchange::handleHelloRequest() +{ +} + +void Exchange::propagateFedOp(const string& routingKey, const string& tags, const string& op, const string& origin) +{ + Mutex::ScopedLock l(bridgeLock); + string myOp(op.empty() ? fedOpBind : op); + + for (std::vector<DynamicBridge*>::iterator iter = bridgeVector.begin(); + iter != bridgeVector.end(); iter++) + (*iter)->propagateBinding(routingKey, tags, op, origin); +} + +Exchange::Binding::Binding(const string& _key, Queue::shared_ptr _queue, Exchange* parent, + FieldTable _args, const string& origin) + : queue(_queue), key(_key), args(_args), mgmtBinding(0) +{ + if (parent != 0) + { + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + if (agent != 0) + { + ManagementObject* mo = queue->GetManagementObject(); + if (mo != 0) + { + management::ObjectId queueId = mo->getObjectId(); + mgmtBinding = new _qmf::Binding + (agent, this, (Manageable*) parent, queueId, key, args); + if (!origin.empty()) + mgmtBinding->set_origin(origin); + agent->addObject (mgmtBinding); + } + } + } +} + +Exchange::Binding::~Binding () +{ + if (mgmtBinding != 0) + mgmtBinding->resourceDestroy (); +} + +ManagementObject* Exchange::Binding::GetManagementObject () const +{ + return (ManagementObject*) mgmtBinding; +} + +Exchange::MatchQueue::MatchQueue(Queue::shared_ptr q) : queue(q) {} + +bool Exchange::MatchQueue::operator()(Exchange::Binding::shared_ptr b) +{ + return b->queue == queue; +} diff --git a/RC9/qpid/cpp/src/qpid/broker/Exchange.h b/RC9/qpid/cpp/src/qpid/broker/Exchange.h new file mode 100644 index 0000000000..5de3e98bc0 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Exchange.h @@ -0,0 +1,178 @@ +#ifndef _broker_Exchange_h +#define _broker_Exchange_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/shared_ptr.hpp> +#include "Deliverable.h" +#include "Queue.h" +#include "MessageStore.h" +#include "PersistableExchange.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/Mutex.h" +#include "qpid/management/Manageable.h" +#include "qmf/org/apache/qpid/broker/Exchange.h" +#include "qmf/org/apache/qpid/broker/Binding.h" + +namespace qpid { +namespace broker { + +class ExchangeRegistry; + +class Exchange : public PersistableExchange, public management::Manageable { +private: + const std::string name; + const bool durable; + mutable qpid::framing::FieldTable args; + boost::shared_ptr<Exchange> alternate; + uint32_t alternateUsers; + mutable uint64_t persistenceId; + +protected: + bool sequence; + mutable qpid::sys::Mutex sequenceLock; + int64_t sequenceNo; + bool ive; + boost::intrusive_ptr<Message> lastMsg; + + class PreRoute{ + public: + PreRoute(Deliverable& msg, Exchange* _p); + ~PreRoute(); + private: + Exchange* parent; + }; + + void routeIVE(); + + struct Binding : public management::Manageable { + typedef boost::shared_ptr<Binding> shared_ptr; + typedef std::vector<Binding::shared_ptr> vector; + + Queue::shared_ptr queue; + const std::string key; + const framing::FieldTable args; + qmf::org::apache::qpid::broker::Binding* mgmtBinding; + + Binding(const std::string& key, Queue::shared_ptr queue, Exchange* parent = 0, + framing::FieldTable args = framing::FieldTable(), const std::string& origin = std::string()); + ~Binding(); + management::ManagementObject* GetManagementObject() const; + }; + + struct MatchQueue { + const Queue::shared_ptr queue; + MatchQueue(Queue::shared_ptr q); + bool operator()(Exchange::Binding::shared_ptr b); + }; + + class FedBinding { + uint32_t localBindings; + std::set<std::string> originSet; + public: + FedBinding() : localBindings(0) {} + bool hasLocal() const { return localBindings != 0; } + bool addOrigin(const std::string& origin) { + if (origin.empty()) { + localBindings++; + return localBindings == 1; + } + originSet.insert(origin); + return true; + } + bool delOrigin(const std::string& origin) { + originSet.erase(origin); + return true; + } + bool delOrigin() { + if (localBindings > 0) + localBindings--; + return localBindings == 0; + } + uint32_t count() { + return localBindings + originSet.size(); + } + }; + + qmf::org::apache::qpid::broker::Exchange* mgmtExchange; + +public: + typedef boost::shared_ptr<Exchange> shared_ptr; + + explicit Exchange(const std::string& name, management::Manageable* parent = 0); + Exchange(const std::string& _name, bool _durable, const qpid::framing::FieldTable& _args, + management::Manageable* parent = 0); + virtual ~Exchange(); + + const std::string& getName() const { return name; } + bool isDurable() { return durable; } + qpid::framing::FieldTable& getArgs() { return args; } + + Exchange::shared_ptr getAlternate() { return alternate; } + void setAlternate(Exchange::shared_ptr _alternate) { alternate = _alternate; } + void incAlternateUsers() { alternateUsers++; } + void decAlternateUsers() { alternateUsers--; } + bool inUseAsAlternate() { return alternateUsers > 0; } + + virtual std::string getType() const = 0; + virtual bool bind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0; + virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0; + virtual bool isBound(Queue::shared_ptr queue, const std::string* const routingKey, const qpid::framing::FieldTable* const args) = 0; + virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0; + + //PersistableExchange: + void setPersistenceId(uint64_t id) const; + uint64_t getPersistenceId() const { return persistenceId; } + uint32_t encodedSize() const; + void encode(framing::Buffer& buffer) const; + + static Exchange::shared_ptr decode(ExchangeRegistry& exchanges, framing::Buffer& buffer); + + // Manageable entry points + management::ManagementObject* GetManagementObject(void) const; + + // Federation hooks + class DynamicBridge { + public: + virtual ~DynamicBridge() {} + virtual void propagateBinding(const std::string& key, const std::string& tagList, const std::string& op, const std::string& origin) = 0; + virtual void sendReorigin() = 0; + virtual bool containsLocalTag(const std::string& tagList) const = 0; + virtual const std::string& getLocalTag() const = 0; + }; + + void registerDynamicBridge(DynamicBridge* db); + void removeDynamicBridge(DynamicBridge* db); + virtual bool supportsDynamicBinding() { return false; } + +protected: + qpid::sys::Mutex bridgeLock; + std::vector<DynamicBridge*> bridgeVector; + + virtual void handleHelloRequest(); + void propagateFedOp(const std::string& routingKey, const std::string& tags, + const std::string& op, const std::string& origin); +}; + +}} + +#endif /*!_broker_Exchange.cpp_h*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp b/RC9/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp new file mode 100644 index 0000000000..01f8bbfee6 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp @@ -0,0 +1,109 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ExchangeRegistry.h" +#include "DirectExchange.h" +#include "FanOutExchange.h" +#include "HeadersExchange.h" +#include "TopicExchange.h" +#include "qpid/management/ManagementExchange.h" +#include "qpid/framing/reply_exceptions.h" + +using namespace qpid::broker; +using namespace qpid::sys; +using std::pair; +using qpid::framing::FieldTable; + +pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, const string& type) + throw(UnknownExchangeTypeException){ + + return declare(name, type, false, FieldTable()); +} + +pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, const string& type, + bool durable, const FieldTable& args) + throw(UnknownExchangeTypeException){ + RWlock::ScopedWlock locker(lock); + ExchangeMap::iterator i = exchanges.find(name); + if (i == exchanges.end()) { + Exchange::shared_ptr exchange; + + if(type == TopicExchange::typeName){ + exchange = Exchange::shared_ptr(new TopicExchange(name, durable, args, parent)); + }else if(type == DirectExchange::typeName){ + exchange = Exchange::shared_ptr(new DirectExchange(name, durable, args, parent)); + }else if(type == FanOutExchange::typeName){ + exchange = Exchange::shared_ptr(new FanOutExchange(name, durable, args, parent)); + }else if (type == HeadersExchange::typeName) { + exchange = Exchange::shared_ptr(new HeadersExchange(name, durable, args, parent)); + }else if (type == ManagementExchange::typeName) { + exchange = Exchange::shared_ptr(new ManagementExchange(name, durable, args, parent)); + } + else{ + FunctionMap::iterator i = factory.find(type); + if (i == factory.end()) { + throw UnknownExchangeTypeException(); + } else { + exchange = i->second(name, durable, args, parent); + } + } + exchanges[name] = exchange; + return std::pair<Exchange::shared_ptr, bool>(exchange, true); + } else { + return std::pair<Exchange::shared_ptr, bool>(i->second, false); + } +} + +void ExchangeRegistry::destroy(const string& name){ + RWlock::ScopedWlock locker(lock); + ExchangeMap::iterator i = exchanges.find(name); + if (i != exchanges.end()) { + exchanges.erase(i); + } +} + +Exchange::shared_ptr ExchangeRegistry::get(const string& name){ + RWlock::ScopedRlock locker(lock); + ExchangeMap::iterator i = exchanges.find(name); + if (i == exchanges.end()) + throw framing::NotFoundException(QPID_MSG("Exchange not found: " << name)); + return i->second; +} + +bool ExchangeRegistry::registerExchange(const Exchange::shared_ptr& ex) { + return exchanges.insert(ExchangeMap::value_type(ex->getName(), ex)).second; +} + +void ExchangeRegistry::registerType(const std::string& type, FactoryFunction f) +{ + factory[type] = f; +} + + +namespace +{ +const std::string empty; +} + +Exchange::shared_ptr ExchangeRegistry::getDefault() +{ + return get(empty); +} diff --git a/RC9/qpid/cpp/src/qpid/broker/ExchangeRegistry.h b/RC9/qpid/cpp/src/qpid/broker/ExchangeRegistry.h new file mode 100644 index 0000000000..787b7896f0 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/ExchangeRegistry.h @@ -0,0 +1,90 @@ +#ifndef _broker_ExchangeRegistry_h +#define _broker_ExchangeRegistry_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Exchange.h" +#include "MessageStore.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/Monitor.h" +#include "qpid/management/Manageable.h" + +#include <boost/function.hpp> +#include <boost/bind.hpp> + +#include <algorithm> +#include <map> + +namespace qpid { +namespace broker { + +struct UnknownExchangeTypeException{}; + +class ExchangeRegistry{ + public: + typedef boost::function4<Exchange::shared_ptr, const std::string&, + bool, const qpid::framing::FieldTable&, qpid::management::Manageable*> FactoryFunction; + + ExchangeRegistry () : parent(0) {} + std::pair<Exchange::shared_ptr, bool> declare(const std::string& name, const std::string& type) + throw(UnknownExchangeTypeException); + std::pair<Exchange::shared_ptr, bool> declare(const std::string& name, const std::string& type, + bool durable, const qpid::framing::FieldTable& args = framing::FieldTable()) + throw(UnknownExchangeTypeException); + void destroy(const std::string& name); + Exchange::shared_ptr get(const std::string& name); + Exchange::shared_ptr getDefault(); + + /** + * Register the manageable parent for declared exchanges + */ + void setParent (management::Manageable* _parent) { parent = _parent; } + + /** Register an exchange instance. + *@return true if registered, false if exchange with same name is already registered. + */ + bool registerExchange(const Exchange::shared_ptr&); + + void registerType(const std::string& type, FactoryFunction); + + /** Call f for each exchange in the registry. */ + template <class F> void eachExchange(F f) const { + qpid::sys::RWlock::ScopedWlock l(lock); + for (ExchangeMap::const_iterator i = exchanges.begin(); i != exchanges.end(); ++i) + f(i->second); + } + + private: + typedef std::map<std::string, Exchange::shared_ptr> ExchangeMap; + typedef std::map<std::string, FactoryFunction > FunctionMap; + + ExchangeMap exchanges; + FunctionMap factory; + mutable qpid::sys::RWlock lock; + management::Manageable* parent; + +}; + +}} // namespace qpid::broker + + +#endif /*!_broker_ExchangeRegistry_h*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/FanOutExchange.cpp b/RC9/qpid/cpp/src/qpid/broker/FanOutExchange.cpp new file mode 100644 index 0000000000..aa1f7ff30a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/FanOutExchange.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 "FanOutExchange.h" +#include <algorithm> + +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace +{ +const std::string qpidFedOp("qpid.fed.op"); +const std::string qpidFedTags("qpid.fed.tags"); +const std::string qpidFedOrigin("qpid.fed.origin"); + +const std::string fedOpBind("B"); +const std::string fedOpUnbind("U"); +const std::string fedOpReorigin("R"); +const std::string fedOpHello("H"); +} + +FanOutExchange::FanOutExchange(const std::string& _name, Manageable* _parent) : + Exchange(_name, _parent) +{ + if (mgmtExchange != 0) + mgmtExchange->set_type (typeName); +} + +FanOutExchange::FanOutExchange(const std::string& _name, bool _durable, + const FieldTable& _args, Manageable* _parent) : + Exchange(_name, _durable, _args, _parent) +{ + if (mgmtExchange != 0) + mgmtExchange->set_type (typeName); +} + +bool FanOutExchange::bind(Queue::shared_ptr queue, const string& /*key*/, const FieldTable* args) +{ + string fedOp(args ? args->getAsString(qpidFedOp) : fedOpBind); + string fedTags(args ? args->getAsString(qpidFedTags) : ""); + string fedOrigin(args ? args->getAsString(qpidFedOrigin) : ""); + bool propagate = false; + + if (args == 0 || fedOp.empty() || fedOp == fedOpBind) { + Binding::shared_ptr binding (new Binding ("", queue, this, FieldTable(), fedOrigin)); + if (bindings.add_unless(binding, MatchQueue(queue))) { + propagate = fedBinding.addOrigin(fedOrigin); + if (mgmtExchange != 0) { + mgmtExchange->inc_bindingCount(); + ((_qmf::Queue*) queue->GetManagementObject())->inc_bindingCount(); + } + } else { + return false; + } + } else if (fedOp == fedOpUnbind) { + propagate = fedBinding.delOrigin(fedOrigin); + if (fedBinding.count() == 0) + unbind(queue, "", 0); + } else if (fedOp == fedOpReorigin) { + if (fedBinding.hasLocal()) { + propagateFedOp(string(), string(), fedOpBind, string()); + } + } + + routeIVE(); + if (propagate) + propagateFedOp(string(), fedTags, fedOp, fedOrigin); + return true; +} + +bool FanOutExchange::unbind(Queue::shared_ptr queue, const string& /*key*/, const FieldTable* /*args*/) +{ + bool propagate = false; + + if (bindings.remove_if(MatchQueue(queue))) { + propagate = fedBinding.delOrigin(); + if (mgmtExchange != 0) { + mgmtExchange->dec_bindingCount(); + ((_qmf::Queue*) queue->GetManagementObject())->dec_bindingCount(); + } + } else { + return false; + } + + if (propagate) + propagateFedOp(string(), string(), fedOpUnbind, string()); + return true; +} + +void FanOutExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* /*args*/){ + PreRoute pr(msg, this); + uint32_t count(0); + + BindingsArray::ConstPtr p = bindings.snapshot(); + if (p.get()){ + for(std::vector<Binding::shared_ptr>::const_iterator i = p->begin(); i != p->end(); ++i, count++){ + msg.deliverTo((*i)->queue); + if ((*i)->mgmtBinding != 0) + (*i)->mgmtBinding->inc_msgMatched (); + } + } + + if (mgmtExchange != 0) + { + mgmtExchange->inc_msgReceives (); + mgmtExchange->inc_byteReceives (msg.contentSize ()); + if (count == 0) + { + mgmtExchange->inc_msgDrops (); + mgmtExchange->inc_byteDrops (msg.contentSize ()); + } + else + { + mgmtExchange->inc_msgRoutes (count); + mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + } + } +} + +bool FanOutExchange::isBound(Queue::shared_ptr queue, const string* const, const FieldTable* const) +{ + BindingsArray::ConstPtr ptr = bindings.snapshot(); + return ptr && std::find_if(ptr->begin(), ptr->end(), MatchQueue(queue)) != ptr->end(); +} + + +FanOutExchange::~FanOutExchange() {} + +const std::string FanOutExchange::typeName("fanout"); diff --git a/RC9/qpid/cpp/src/qpid/broker/FanOutExchange.h b/RC9/qpid/cpp/src/qpid/broker/FanOutExchange.h new file mode 100644 index 0000000000..5884a19732 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/FanOutExchange.h @@ -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. + * + */ +#ifndef _FanOutExchange_ +#define _FanOutExchange_ + +#include <map> +#include <vector> +#include "Exchange.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/CopyOnWriteArray.h" +#include "Queue.h" + +namespace qpid { +namespace broker { + +class FanOutExchange : public virtual Exchange { + typedef qpid::sys::CopyOnWriteArray<Binding::shared_ptr> BindingsArray; + BindingsArray bindings; + FedBinding fedBinding; + public: + static const std::string typeName; + + FanOutExchange(const std::string& name, management::Manageable* parent = 0); + FanOutExchange(const string& _name, bool _durable, + const qpid::framing::FieldTable& _args, + management::Manageable* parent = 0); + + virtual std::string getType() const { return typeName; } + + virtual bool bind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args); + + virtual ~FanOutExchange(); + virtual bool supportsDynamicBinding() { return true; } +}; + +} +} + + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/HandlerImpl.h b/RC9/qpid/cpp/src/qpid/broker/HandlerImpl.h new file mode 100644 index 0000000000..4c51e2a826 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/HandlerImpl.h @@ -0,0 +1,53 @@ +#ifndef _broker_HandlerImpl_h +#define _broker_HandlerImpl_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "SemanticState.h" +#include "SessionContext.h" +#include "ConnectionState.h" + +namespace qpid { +namespace broker { + +class Broker; + +/** + * Base template for protocol handler implementations. + * Provides convenience methods for getting common session objects. + */ +class HandlerImpl { + protected: + SemanticState& state; + SessionContext& session; + + HandlerImpl(SemanticState& s) : state(s), session(s.getSession()) {} + + framing::AMQP_ClientProxy& getProxy() { return session.getProxy(); } + ConnectionState& getConnection() { return session.getConnection(); } + Broker& getBroker() { return session.getConnection().getBroker(); } +}; + +}} // namespace qpid::broker + + + +#endif /*!_broker_HandlerImpl_h*/ + + diff --git a/RC9/qpid/cpp/src/qpid/broker/HeadersExchange.cpp b/RC9/qpid/cpp/src/qpid/broker/HeadersExchange.cpp new file mode 100644 index 0000000000..104b34da8b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/HeadersExchange.cpp @@ -0,0 +1,226 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "HeadersExchange.h" +#include "qpid/framing/FieldValue.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Statement.h" +#include <algorithm> + + +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; +namespace _qmf = qmf::org::apache::qpid::broker; + +// TODO aconway 2006-09-20: More efficient matching algorithm. +// The current search algorithm really sucks. +// Fieldtables are heavy, maybe use shared_ptr to do handle-body. + +using namespace qpid::broker; + +namespace { + const std::string all("all"); + const std::string any("any"); + const std::string x_match("x-match"); + const std::string empty; +} + +HeadersExchange::HeadersExchange(const string& _name, Manageable* _parent) : + Exchange(_name, _parent) +{ + if (mgmtExchange != 0) + mgmtExchange->set_type (typeName); +} + +HeadersExchange::HeadersExchange(const std::string& _name, bool _durable, + const FieldTable& _args, Manageable* _parent) : + Exchange(_name, _durable, _args, _parent) +{ + if (mgmtExchange != 0) + mgmtExchange->set_type (typeName); +} + +std::string HeadersExchange::getMatch(const FieldTable* args) +{ + if (!args) { + throw InternalErrorException(QPID_MSG("No arguments given.")); + } + FieldTable::ValuePtr what = args->get(x_match); + if (!what) { + return empty; + } + if (!what->convertsTo<std::string>()) { + throw InternalErrorException(QPID_MSG("Invalid x-match value binding to headers exchange.")); + } + return what->get<std::string>(); +} + +bool HeadersExchange::bind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable* args){ + std::string what = getMatch(args); + if (what != all && what != any) + throw InternalErrorException(QPID_MSG("Invalid x-match value binding to headers exchange.")); + + Binding::shared_ptr binding (new Binding (bindingKey, queue, this, *args)); + if (bindings.add_unless(binding, MatchArgs(queue, args))) { + if (mgmtExchange != 0) { + mgmtExchange->inc_bindingCount(); + ((_qmf::Queue*) queue->GetManagementObject())->inc_bindingCount(); + } + routeIVE(); + return true; + } else { + return false; + } +} + +bool HeadersExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable*){ + if (bindings.remove_if(MatchKey(queue, bindingKey))) { + if (mgmtExchange != 0) { + mgmtExchange->dec_bindingCount(); + ((_qmf::Queue*) queue->GetManagementObject())->dec_bindingCount(); + } + return true; + } else { + return false; + } +} + + +void HeadersExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* args){ + if (!args) return;//can't match if there were no headers passed in + PreRoute pr(msg, this); + + uint32_t count(0); + + Bindings::ConstPtr p = bindings.snapshot(); + if (p.get()){ + for (std::vector<Binding::shared_ptr>::const_iterator i = p->begin(); i != p->end(); ++i, count++) { + if (match((*i)->args, *args)) msg.deliverTo((*i)->queue); + if ((*i)->mgmtBinding != 0) + (*i)->mgmtBinding->inc_msgMatched (); + } + } + + if (mgmtExchange != 0) + { + mgmtExchange->inc_msgReceives (); + mgmtExchange->inc_byteReceives (msg.contentSize ()); + if (count == 0) + { + mgmtExchange->inc_msgDrops (); + mgmtExchange->inc_byteDrops (msg.contentSize ()); + } + else + { + mgmtExchange->inc_msgRoutes (count); + mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + } + } +} + + +bool HeadersExchange::isBound(Queue::shared_ptr queue, const string* const, const FieldTable* const args) +{ + Bindings::ConstPtr p = bindings.snapshot(); + if (p.get()){ + for (std::vector<Binding::shared_ptr>::const_iterator i = p->begin(); i != p->end(); ++i) { + if ( (!args || equal((*i)->args, *args)) && (!queue || (*i)->queue == queue)) { + return true; + } + } + } + return false; +} + +HeadersExchange::~HeadersExchange() {} + +const std::string HeadersExchange::typeName("headers"); + +namespace +{ + + bool match_values(const FieldValue& bind, const FieldValue& msg) { + return bind.empty() || bind == msg; + } + +} + + +bool HeadersExchange::match(const FieldTable& bind, const FieldTable& msg) { + typedef FieldTable::ValueMap Map; + std::string what = getMatch(&bind); + if (what == all) { + for (Map::const_iterator i = bind.begin(); + i != bind.end(); + ++i) + { + if (i->first != x_match) + { + Map::const_iterator j = msg.find(i->first); + if (j == msg.end()) return false; + if (!match_values(*(i->second), *(j->second))) return false; + } + } + return true; + } else if (what == any) { + for (Map::const_iterator i = bind.begin(); + i != bind.end(); + ++i) + { + if (i->first != x_match) + { + Map::const_iterator j = msg.find(i->first); + if (j != msg.end()) { + if (match_values(*(i->second), *(j->second))) return true; + } + } + } + return false; + } else { + return false; + } +} + +bool HeadersExchange::equal(const FieldTable& a, const FieldTable& b) { + typedef FieldTable::ValueMap Map; + for (Map::const_iterator i = a.begin(); + i != a.end(); + ++i) + { + Map::const_iterator j = b.find(i->first); + if (j == b.end()) return false; + if (!match_values(*(i->second), *(j->second))) return false; + } + return true; +} + +HeadersExchange::MatchArgs::MatchArgs(Queue::shared_ptr q, const qpid::framing::FieldTable* a) : queue(q), args(a) {} +bool HeadersExchange::MatchArgs::operator()(Exchange::Binding::shared_ptr b) +{ + return b->queue == queue && b->args == *args; +} + +HeadersExchange::MatchKey::MatchKey(Queue::shared_ptr q, const std::string& k) : queue(q), key(k) {} + +bool HeadersExchange::MatchKey::operator()(Exchange::Binding::shared_ptr b) +{ + return b->queue == queue && b->key == key; +} diff --git a/RC9/qpid/cpp/src/qpid/broker/HeadersExchange.h b/RC9/qpid/cpp/src/qpid/broker/HeadersExchange.h new file mode 100644 index 0000000000..e10fab2250 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/HeadersExchange.h @@ -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. + * + */ +#ifndef _HeadersExchange_ +#define _HeadersExchange_ + +#include <vector> +#include "Exchange.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/CopyOnWriteArray.h" +#include "qpid/sys/Mutex.h" +#include "Queue.h" + +namespace qpid { +namespace broker { + + +class HeadersExchange : public virtual Exchange { + typedef std::pair<qpid::framing::FieldTable, Binding::shared_ptr> HeaderMap; + typedef qpid::sys::CopyOnWriteArray<Binding::shared_ptr> Bindings; + + struct MatchArgs + { + const Queue::shared_ptr queue; + const qpid::framing::FieldTable* args; + MatchArgs(Queue::shared_ptr q, const qpid::framing::FieldTable* a); + bool operator()(Exchange::Binding::shared_ptr b); + }; + struct MatchKey + { + const Queue::shared_ptr queue; + const std::string& key; + MatchKey(Queue::shared_ptr q, const std::string& k); + bool operator()(Exchange::Binding::shared_ptr b); + }; + + Bindings bindings; + qpid::sys::Mutex lock; + + static std::string getMatch(const framing::FieldTable* args); + + public: + static const std::string typeName; + + HeadersExchange(const string& name, management::Manageable* parent = 0); + HeadersExchange(const string& _name, bool _durable, + const qpid::framing::FieldTable& _args, + management::Manageable* parent = 0); + + virtual std::string getType() const { return typeName; } + + virtual bool bind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool unbind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual void route(Deliverable& msg, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args); + + virtual ~HeadersExchange(); + + static bool match(const qpid::framing::FieldTable& bindArgs, const qpid::framing::FieldTable& msgArgs); + static bool equal(const qpid::framing::FieldTable& bindArgs, const qpid::framing::FieldTable& msgArgs); +}; + + + +} +} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/IncompleteMessageList.cpp b/RC9/qpid/cpp/src/qpid/broker/IncompleteMessageList.cpp new file mode 100644 index 0000000000..2077e633ec --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/IncompleteMessageList.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 "IncompleteMessageList.h" + +namespace qpid { +namespace broker { + +IncompleteMessageList::IncompleteMessageList() : + callback(boost::bind(&IncompleteMessageList::enqueueComplete, this, _1)) +{} + +IncompleteMessageList::~IncompleteMessageList() +{ + sys::Mutex::ScopedLock l(lock); + std::for_each(incomplete.begin(), incomplete.end(), boost::bind(&Message::resetEnqueueCompleteCallback, _1)); +} + +void IncompleteMessageList::add(boost::intrusive_ptr<Message> msg) +{ + sys::Mutex::ScopedLock l(lock); + msg->setEnqueueCompleteCallback(callback); + incomplete.push_back(msg); +} + +void IncompleteMessageList::enqueueComplete(const boost::intrusive_ptr<Message>& ) { + sys::Mutex::ScopedLock l(lock); + lock.notify(); +} + +void IncompleteMessageList::process(const CompletionListener& listen, bool sync) +{ + sys::Mutex::ScopedLock l(lock); + while (!incomplete.empty()) { + boost::intrusive_ptr<Message>& msg = incomplete.front(); + if (!msg->isEnqueueComplete()) { + if (sync){ + { + sys::Mutex::ScopedUnlock u(lock); + msg->flush(); // Can re-enter IncompleteMessageList::enqueueComplete + } + while (!msg->isEnqueueComplete()) + lock.wait(); + } else { + //leave the message as incomplete for now + return; + } + } + listen(msg); + incomplete.pop_front(); + } +} + +void IncompleteMessageList::each(const CompletionListener& listen) { + Messages snapshot; + { + sys::Mutex::ScopedLock l(lock); + snapshot = incomplete; + } + std::for_each(incomplete.begin(), incomplete.end(), listen); // FIXME aconway 2008-11-07: passed by ref or value? +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/broker/IncompleteMessageList.h b/RC9/qpid/cpp/src/qpid/broker/IncompleteMessageList.h new file mode 100644 index 0000000000..f89c0023b0 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/IncompleteMessageList.h @@ -0,0 +1,57 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _IncompleteMessageList_ +#define _IncompleteMessageList_ + +#include "qpid/sys/Monitor.h" +#include "qpid/broker/Message.h" +#include <boost/intrusive_ptr.hpp> +#include <boost/function.hpp> +#include <list> + +namespace qpid { +namespace broker { + +class IncompleteMessageList +{ + typedef std::list< boost::intrusive_ptr<Message> > Messages; + + void enqueueComplete(const boost::intrusive_ptr<Message>&); + + sys::Monitor lock; + Messages incomplete; + Message::MessageCallback callback; + +public: + typedef Message::MessageCallback CompletionListener; + + IncompleteMessageList(); + ~IncompleteMessageList(); + + void add(boost::intrusive_ptr<Message> msg); + void process(const CompletionListener& l, bool sync); + void each(const CompletionListener& l); +}; + + +}} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/Link.cpp b/RC9/qpid/cpp/src/qpid/broker/Link.cpp new file mode 100644 index 0000000000..bda9c80f0b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Link.cpp @@ -0,0 +1,398 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Link.h" +#include "LinkRegistry.h" +#include "Broker.h" +#include "Connection.h" +#include "qmf/org/apache/qpid/broker/EventBrokerLinkUp.h" +#include "qmf/org/apache/qpid/broker/EventBrokerLinkDown.h" +#include "boost/bind.hpp" +#include "qpid/log/Statement.h" +#include "qpid/framing/enum.h" +#include "qpid/framing/reply_exceptions.h" +#include "AclModule.h" + +using namespace qpid::broker; +using qpid::framing::Buffer; +using qpid::framing::FieldTable; +using qpid::framing::NotAllowedException; +using qpid::framing::connection::CLOSE_CODE_CONNECTION_FORCED; +using qpid::management::ManagementAgent; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; +using qpid::sys::Mutex; +using std::stringstream; +namespace _qmf = qmf::org::apache::qpid::broker; + +Link::Link(LinkRegistry* _links, + MessageStore* _store, + string& _host, + uint16_t _port, + string& _transport, + bool _durable, + string& _authMechanism, + string& _username, + string& _password, + Broker* _broker, + Manageable* parent) + : links(_links), store(_store), host(_host), port(_port), + transport(_transport), + durable(_durable), + authMechanism(_authMechanism), username(_username), password(_password), + persistenceId(0), mgmtObject(0), broker(_broker), state(0), + visitCount(0), + currentInterval(1), + closing(false), + channelCounter(1), + connection(0), + agent(0) +{ + if (parent != 0) + { + agent = ManagementAgent::Singleton::getInstance(); + if (agent != 0) + { + mgmtObject = new _qmf::Link(agent, this, parent, _host, _port, _transport, _durable); + if (!durable) + agent->addObject(mgmtObject); + } + } + setStateLH(STATE_WAITING); +} + +Link::~Link () +{ + if (state == STATE_OPERATIONAL && connection != 0) + connection->close(CLOSE_CODE_CONNECTION_FORCED, "closed by management"); + + if (mgmtObject != 0) + mgmtObject->resourceDestroy (); +} + +void Link::setStateLH (int newState) +{ + if (newState == state) + return; + + state = newState; + if (mgmtObject == 0) + return; + + switch (state) + { + case STATE_WAITING : mgmtObject->set_state("Waiting"); break; + case STATE_CONNECTING : mgmtObject->set_state("Connecting"); break; + case STATE_OPERATIONAL : mgmtObject->set_state("Operational"); break; + case STATE_FAILED : mgmtObject->set_state("Failed"); break; + case STATE_CLOSED : mgmtObject->set_state("Closed"); break; + } +} + +void Link::startConnectionLH () +{ + try { + // Set the state before calling connect. It is possible that connect + // will fail synchronously and call Link::closed before returning. + setStateLH(STATE_CONNECTING); + broker->connect (host, port, transport, + boost::bind (&Link::closed, this, _1, _2)); + } catch(std::exception& e) { + setStateLH(STATE_WAITING); + if (mgmtObject != 0) + mgmtObject->set_lastError (e.what()); + } +} + +void Link::established () +{ + Mutex::ScopedLock mutex(lock); + stringstream addr; + addr << host << ":" << port; + + QPID_LOG (info, "Inter-broker link established to " << addr.str()); + agent->raiseEvent(_qmf::EventBrokerLinkUp(addr.str())); + setStateLH(STATE_OPERATIONAL); + currentInterval = 1; + visitCount = 0; + if (closing) + destroy(); +} + +void Link::closed (int, std::string text) +{ + Mutex::ScopedLock mutex(lock); + + connection = 0; + + if (state == STATE_OPERATIONAL) { + stringstream addr; + addr << host << ":" << port; + QPID_LOG (warning, "Inter-broker link disconnected from " << addr.str()); + agent->raiseEvent(_qmf::EventBrokerLinkDown(addr.str())); + } + + for (Bridges::iterator i = active.begin(); i != active.end(); i++) { + (*i)->cancel(); + created.push_back(*i); + } + active.clear(); + + if (state != STATE_FAILED) + { + setStateLH(STATE_WAITING); + if (mgmtObject != 0) + mgmtObject->set_lastError (text); + } + + if (closing) + destroy(); +} + +void Link::destroy () +{ + { + Mutex::ScopedLock mutex(lock); + Bridges toDelete; + + AclModule* acl = getBroker()->getAcl(); + std::string userID = getUsername() + "@" + getBroker()->getOptions().realm; + if (acl && !acl->authorise(userID,acl::ACT_DELETE,acl::OBJ_LINK,"")){ + throw NotAllowedException("ACL denied delete link request"); + } + + QPID_LOG (info, "Inter-broker link to " << host << ":" << port << " removed by management"); + if (connection) + connection->close(CLOSE_CODE_CONNECTION_FORCED, "closed by management"); + + setStateLH(STATE_CLOSED); + + // Move the bridges to be deleted into a local vector so there is no + // corruption of the iterator caused by bridge deletion. + for (Bridges::iterator i = active.begin(); i != active.end(); i++) + toDelete.push_back(*i); + active.clear(); + + for (Bridges::iterator i = created.begin(); i != created.end(); i++) + toDelete.push_back(*i); + created.clear(); + + // Now delete all bridges on this link. + for (Bridges::iterator i = toDelete.begin(); i != toDelete.end(); i++) + (*i)->destroy(); + toDelete.clear(); + } + links->destroy (host, port); +} + +void Link::add(Bridge::shared_ptr bridge) +{ + Mutex::ScopedLock mutex(lock); + created.push_back (bridge); +} + +void Link::cancel(Bridge::shared_ptr bridge) +{ + Mutex::ScopedLock mutex(lock); + + for (Bridges::iterator i = created.begin(); i != created.end(); i++) { + if ((*i).get() == bridge.get()) { + created.erase(i); + break; + } + } + for (Bridges::iterator i = active.begin(); i != active.end(); i++) { + if ((*i).get() == bridge.get()) { + bridge->cancel(); + active.erase(i); + break; + } + } +} + +void Link::ioThreadProcessing() +{ + Mutex::ScopedLock mutex(lock); + + if (state != STATE_OPERATIONAL) + return; + + //process any pending creates + if (!created.empty()) { + for (Bridges::iterator i = created.begin(); i != created.end(); ++i) { + active.push_back(*i); + (*i)->create(*connection); + } + created.clear(); + } +} + +void Link::setConnection(Connection* c) +{ + Mutex::ScopedLock mutex(lock); + connection = c; +} + +void Link::maintenanceVisit () +{ + Mutex::ScopedLock mutex(lock); + + if (state == STATE_WAITING) + { + visitCount++; + if (visitCount >= currentInterval) + { + visitCount = 0; + currentInterval *= 2; + if (currentInterval > MAX_INTERVAL) + currentInterval = MAX_INTERVAL; + startConnectionLH(); + } + } + else if (state == STATE_OPERATIONAL && !created.empty() && connection != 0) + connection->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this)); +} + +uint Link::nextChannel() +{ + Mutex::ScopedLock mutex(lock); + + return channelCounter++; +} + +void Link::notifyConnectionForced(const string text) +{ + Mutex::ScopedLock mutex(lock); + + setStateLH(STATE_FAILED); + if (mgmtObject != 0) + mgmtObject->set_lastError(text); +} + +void Link::setPersistenceId(uint64_t id) const +{ + if (mgmtObject != 0 && persistenceId == 0) { + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + agent->addObject(mgmtObject, id); + } + persistenceId = id; +} + +const string& Link::getName() const +{ + return host; +} + +Link::shared_ptr Link::decode(LinkRegistry& links, Buffer& buffer) +{ + string host; + uint16_t port; + string transport; + string authMechanism; + string username; + string password; + + buffer.getShortString(host); + port = buffer.getShort(); + buffer.getShortString(transport); + bool durable(buffer.getOctet()); + buffer.getShortString(authMechanism); + buffer.getShortString(username); + buffer.getShortString(password); + + return links.declare(host, port, transport, durable, authMechanism, username, password).first; +} + +void Link::encode(Buffer& buffer) const +{ + buffer.putShortString(string("link")); + buffer.putShortString(host); + buffer.putShort(port); + buffer.putShortString(transport); + buffer.putOctet(durable ? 1 : 0); + buffer.putShortString(authMechanism); + buffer.putShortString(username); + buffer.putShortString(password); +} + +uint32_t Link::encodedSize() const +{ + return host.size() + 1 // short-string (host) + + 5 // short-string ("link") + + 2 // port + + transport.size() + 1 // short-string(transport) + + 1 // durable + + authMechanism.size() + 1 + + username.size() + 1 + + password.size() + 1; +} + +ManagementObject* Link::GetManagementObject (void) const +{ + return (ManagementObject*) mgmtObject; +} + +Manageable::status_t Link::ManagementMethod (uint32_t op, Args& args, string& text) +{ + switch (op) + { + case _qmf::Link::METHOD_CLOSE : + closing = true; + if (state != STATE_CONNECTING) + destroy(); + return Manageable::STATUS_OK; + + case _qmf::Link::METHOD_BRIDGE : + _qmf::ArgsLinkBridge& iargs = (_qmf::ArgsLinkBridge&) args; + + // Durable bridges are only valid on durable links + if (iargs.i_durable && !durable) { + text = "Can't create a durable route on a non-durable link"; + return Manageable::STATUS_USER; + } + + if (iargs.i_dynamic) { + Exchange::shared_ptr exchange = getBroker()->getExchanges().get(iargs.i_src); + if (exchange.get() == 0) { + text = "Exchange not found"; + return Manageable::STATUS_USER; + } + if (!exchange->supportsDynamicBinding()) { + text = "Exchange type does not support dynamic routing"; + return Manageable::STATUS_USER; + } + } + + std::pair<Bridge::shared_ptr, bool> result = + links->declare (host, port, iargs.i_durable, iargs.i_src, + iargs.i_dest, iargs.i_key, iargs.i_srcIsQueue, + iargs.i_srcIsLocal, iargs.i_tag, iargs.i_excludes, + iargs.i_dynamic); + + if (result.second && iargs.i_durable) + store->create(*result.first); + + return Manageable::STATUS_OK; + } + + return Manageable::STATUS_UNKNOWN_METHOD; +} diff --git a/RC9/qpid/cpp/src/qpid/broker/Link.h b/RC9/qpid/cpp/src/qpid/broker/Link.h new file mode 100644 index 0000000000..a8dd528071 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Link.h @@ -0,0 +1,136 @@ +#ifndef _broker_Link_h +#define _broker_Link_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/shared_ptr.hpp> +#include "MessageStore.h" +#include "PersistableConfig.h" +#include "Bridge.h" +#include "qpid/sys/Mutex.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/management/Manageable.h" +#include "qpid/agent/ManagementAgent.h" +#include "qmf/org/apache/qpid/broker/Link.h" +#include <boost/ptr_container/ptr_vector.hpp> + +namespace qpid { + namespace broker { + + using std::string; + class LinkRegistry; + class Broker; + class Connection; + + class Link : public PersistableConfig, public management::Manageable { + private: + sys::Mutex lock; + LinkRegistry* links; + MessageStore* store; + string host; + uint16_t port; + string transport; + bool durable; + string authMechanism; + string username; + string password; + mutable uint64_t persistenceId; + qmf::org::apache::qpid::broker::Link* mgmtObject; + Broker* broker; + int state; + uint32_t visitCount; + uint32_t currentInterval; + bool closing; + + typedef std::vector<Bridge::shared_ptr> Bridges; + Bridges created; // Bridges pending creation + Bridges active; // Bridges active + uint channelCounter; + Connection* connection; + management::ManagementAgent* agent; + + static const int STATE_WAITING = 1; + static const int STATE_CONNECTING = 2; + static const int STATE_OPERATIONAL = 3; + static const int STATE_FAILED = 4; + static const int STATE_CLOSED = 5; + + static const uint32_t MAX_INTERVAL = 32; + + void setStateLH (int newState); + void startConnectionLH(); // Start the IO Connection + void destroy(); // Called when mgmt deletes this link + void ioThreadProcessing(); // Called on connection's IO thread by request + + public: + typedef boost::shared_ptr<Link> shared_ptr; + + Link(LinkRegistry* links, + MessageStore* store, + string& host, + uint16_t port, + string& transport, + bool durable, + string& authMechanism, + string& username, + string& password, + Broker* broker, + management::Manageable* parent = 0); + virtual ~Link(); + + std::string getHost() { return host; } + uint16_t getPort() { return port; } + bool isDurable() { return durable; } + void maintenanceVisit (); + uint nextChannel(); + void add(Bridge::shared_ptr); + void cancel(Bridge::shared_ptr); + + void established(); // Called when connection is created + void closed(int, std::string); // Called when connection goes away + void setConnection(Connection*); // Set pointer to the AMQP Connection + + string getAuthMechanism() { return authMechanism; } + string getUsername() { return username; } + string getPassword() { return password; } + Broker* getBroker() { return broker; } + + void notifyConnectionForced(const std::string text); + + // PersistableConfig: + void setPersistenceId(uint64_t id) const; + uint64_t getPersistenceId() const { return persistenceId; } + uint32_t encodedSize() const; + void encode(framing::Buffer& buffer) const; + const string& getName() const; + + static Link::shared_ptr decode(LinkRegistry& links, framing::Buffer& buffer); + + // Manageable entry points + management::ManagementObject* GetManagementObject(void) const; + management::Manageable::status_t ManagementMethod(uint32_t, management::Args&, std::string&); + }; + } +} + + +#endif /*!_broker_Link.cpp_h*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/LinkRegistry.cpp b/RC9/qpid/cpp/src/qpid/broker/LinkRegistry.cpp new file mode 100644 index 0000000000..960e9f21ba --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/LinkRegistry.cpp @@ -0,0 +1,252 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "LinkRegistry.h" +#include <iostream> + +using namespace qpid::broker; +using namespace qpid::sys; +using std::pair; +using std::stringstream; +using boost::intrusive_ptr; +namespace _qmf = qmf::org::apache::qpid::broker; + +#define LINK_MAINT_INTERVAL 2 + +LinkRegistry::LinkRegistry (Broker* _broker) : broker(_broker), parent(0), store(0) +{ + timer.add (intrusive_ptr<TimerTask> (new Periodic(*this))); +} + +LinkRegistry::Periodic::Periodic (LinkRegistry& _links) : + TimerTask (Duration (LINK_MAINT_INTERVAL * TIME_SEC)), links(_links) {} + +void LinkRegistry::Periodic::fire () +{ + links.periodicMaintenance (); + links.timer.add (intrusive_ptr<TimerTask> (new Periodic(links))); +} + +void LinkRegistry::periodicMaintenance () +{ + Mutex::ScopedLock locker(lock); + + linksToDestroy.clear(); + bridgesToDestroy.clear(); + for (LinkMap::iterator i = links.begin(); i != links.end(); i++) + i->second->maintenanceVisit(); +} + +pair<Link::shared_ptr, bool> LinkRegistry::declare(string& host, + uint16_t port, + string& transport, + bool durable, + string& authMechanism, + string& username, + string& password) + +{ + Mutex::ScopedLock locker(lock); + stringstream keystream; + keystream << host << ":" << port; + string key = string(keystream.str()); + + LinkMap::iterator i = links.find(key); + if (i == links.end()) + { + Link::shared_ptr link; + + link = Link::shared_ptr (new Link (this, store, host, port, transport, durable, + authMechanism, username, password, + broker, parent)); + links[key] = link; + return std::pair<Link::shared_ptr, bool>(link, true); + } + return std::pair<Link::shared_ptr, bool>(i->second, false); +} + +pair<Bridge::shared_ptr, bool> LinkRegistry::declare(std::string& host, + uint16_t port, + bool durable, + std::string& src, + std::string& dest, + std::string& key, + bool isQueue, + bool isLocal, + std::string& tag, + std::string& excludes, + bool dynamic) +{ + Mutex::ScopedLock locker(lock); + stringstream keystream; + keystream << host << ":" << port; + string linkKey = string(keystream.str()); + + keystream << "!" << src << "!" << dest << "!" << key; + string bridgeKey = string(keystream.str()); + + LinkMap::iterator l = links.find(linkKey); + if (l == links.end()) + return pair<Bridge::shared_ptr, bool>(Bridge::shared_ptr(), false); + + BridgeMap::iterator b = bridges.find(bridgeKey); + if (b == bridges.end()) + { + _qmf::ArgsLinkBridge args; + Bridge::shared_ptr bridge; + + args.i_durable = durable; + args.i_src = src; + args.i_dest = dest; + args.i_key = key; + args.i_srcIsQueue = isQueue; + args.i_srcIsLocal = isLocal; + args.i_tag = tag; + args.i_excludes = excludes; + args.i_dynamic = dynamic; + + bridge = Bridge::shared_ptr + (new Bridge (l->second.get(), l->second->nextChannel(), + boost::bind(&LinkRegistry::destroy, this, + host, port, src, dest, key), args)); + bridges[bridgeKey] = bridge; + l->second->add(bridge); + return std::pair<Bridge::shared_ptr, bool>(bridge, true); + } + return std::pair<Bridge::shared_ptr, bool>(b->second, false); +} + +void LinkRegistry::destroy(const string& host, const uint16_t port) +{ + Mutex::ScopedLock locker(lock); + stringstream keystream; + keystream << host << ":" << port; + string key = string(keystream.str()); + + LinkMap::iterator i = links.find(key); + if (i != links.end()) + { + if (i->second->isDurable() && store) + store->destroy(*(i->second)); + linksToDestroy[key] = i->second; + links.erase(i); + } +} + +void LinkRegistry::destroy(const std::string& host, + const uint16_t port, + const std::string& src, + const std::string& dest, + const std::string& key) +{ + Mutex::ScopedLock locker(lock); + stringstream keystream; + keystream << host << ":" << port; + string linkKey = string(keystream.str()); + + LinkMap::iterator l = links.find(linkKey); + if (l == links.end()) + return; + + keystream << "!" << src << "!" << dest << "!" << key; + string bridgeKey = string(keystream.str()); + BridgeMap::iterator b = bridges.find(bridgeKey); + if (b == bridges.end()) + return; + + l->second->cancel(b->second); + if (b->second->isDurable()) + store->destroy(*(b->second)); + bridgesToDestroy[bridgeKey] = b->second; + bridges.erase(b); +} + +void LinkRegistry::setStore (MessageStore* _store) +{ + store = _store; +} + +MessageStore* LinkRegistry::getStore() const { + return store; +} + +void LinkRegistry::notifyConnection(const std::string& key, Connection* c) +{ + Mutex::ScopedLock locker(lock); + LinkMap::iterator l = links.find(key); + if (l != links.end()) + { + l->second->established(); + l->second->setConnection(c); + } +} + +void LinkRegistry::notifyClosed(const std::string& key) +{ + Mutex::ScopedLock locker(lock); + LinkMap::iterator l = links.find(key); + if (l != links.end()) + l->second->closed(0, "Closed by peer"); +} + +void LinkRegistry::notifyConnectionForced(const std::string& key, const std::string& text) +{ + Mutex::ScopedLock locker(lock); + LinkMap::iterator l = links.find(key); + if (l != links.end()) + l->second->notifyConnectionForced(text); +} + +std::string LinkRegistry::getAuthMechanism(const std::string& key) +{ + Mutex::ScopedLock locker(lock); + LinkMap::iterator l = links.find(key); + if (l != links.end()) + return l->second->getAuthMechanism(); + return string("ANONYMOUS"); +} + +std::string LinkRegistry::getAuthCredentials(const std::string& key) +{ + Mutex::ScopedLock locker(lock); + LinkMap::iterator l = links.find(key); + if (l == links.end()) + return string(); + + string result; + result += '\0'; + result += l->second->getUsername(); + result += '\0'; + result += l->second->getPassword(); + + return result; +} + +std::string LinkRegistry::getAuthIdentity(const std::string& key) +{ + Mutex::ScopedLock locker(lock); + LinkMap::iterator l = links.find(key); + if (l == links.end()) + return string(); + + return l->second->getUsername(); +} + + diff --git a/RC9/qpid/cpp/src/qpid/broker/LinkRegistry.h b/RC9/qpid/cpp/src/qpid/broker/LinkRegistry.h new file mode 100644 index 0000000000..d563412cc1 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/LinkRegistry.h @@ -0,0 +1,123 @@ +#ifndef _broker_LinkRegistry_h +#define _broker_LinkRegistry_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <map> +#include "Link.h" +#include "Bridge.h" +#include "MessageStore.h" +#include "Timer.h" +#include "qpid/sys/Mutex.h" +#include "qpid/management/Manageable.h" + +namespace qpid { +namespace broker { + + class Broker; + class Connection; + class LinkRegistry { + + // Declare a timer task to manage the establishment of link connections and the + // re-establishment of lost link connections. + struct Periodic : public TimerTask + { + LinkRegistry& links; + + Periodic(LinkRegistry& links); + virtual ~Periodic() {}; + void fire(); + }; + + typedef std::map<std::string, Link::shared_ptr> LinkMap; + typedef std::map<std::string, Bridge::shared_ptr> BridgeMap; + + LinkMap links; + LinkMap linksToDestroy; + BridgeMap bridges; + BridgeMap bridgesToDestroy; + + qpid::sys::Mutex lock; + Broker* broker; + Timer timer; + management::Manageable* parent; + MessageStore* store; + + void periodicMaintenance (); + + public: + LinkRegistry (Broker* _broker); + std::pair<Link::shared_ptr, bool> + declare(std::string& host, + uint16_t port, + std::string& transport, + bool durable, + std::string& authMechanism, + std::string& username, + std::string& password); + std::pair<Bridge::shared_ptr, bool> + declare(std::string& host, + uint16_t port, + bool durable, + std::string& src, + std::string& dest, + std::string& key, + bool isQueue, + bool isLocal, + std::string& id, + std::string& excludes, + bool dynamic); + + void destroy(const std::string& host, const uint16_t port); + void destroy(const std::string& host, + const uint16_t port, + const std::string& src, + const std::string& dest, + const std::string& key); + + /** + * Register the manageable parent for declared queues + */ + void setParent (management::Manageable* _parent) { parent = _parent; } + + /** + * Set the store to use. May only be called once. + */ + void setStore (MessageStore*); + + /** + * Return the message store used. + */ + MessageStore* getStore() const; + + void notifyConnection (const std::string& key, Connection* c); + void notifyClosed (const std::string& key); + void notifyConnectionForced (const std::string& key, const std::string& text); + std::string getAuthMechanism (const std::string& key); + std::string getAuthCredentials (const std::string& key); + std::string getAuthIdentity (const std::string& key); + }; +} +} + + +#endif /*!_broker_LinkRegistry_h*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/Message.cpp b/RC9/qpid/cpp/src/qpid/broker/Message.cpp new file mode 100644 index 0000000000..89f2653a21 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Message.cpp @@ -0,0 +1,369 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Message.h" +#include "ExchangeRegistry.h" +#include "qpid/StringUtils.h" +#include "qpid/framing/frame_functors.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/SendContent.h" +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/TypeFilter.h" +#include "qpid/log/Statement.h" + +#include <time.h> + +using boost::intrusive_ptr; +using qpid::sys::AbsTime; +using qpid::sys::Duration; +using qpid::sys::TIME_MSEC; +using qpid::sys::FAR_FUTURE; +using std::string; +using namespace qpid::framing; + +namespace qpid { +namespace broker { + +TransferAdapter Message::TRANSFER; + +Message::Message(const framing::SequenceNumber& id) : + frames(id), persistenceId(0), redelivered(false), loaded(false), + staged(false), forcePersistentPolicy(false), publisher(0), adapter(0), + expiration(FAR_FUTURE), enqueueCallback(0), dequeueCallback(0) {} + +Message::~Message() +{ +} + +void Message::forcePersistent() +{ + forcePersistentPolicy = true; +} + +std::string Message::getRoutingKey() const +{ + return getAdapter().getRoutingKey(frames); +} + +std::string Message::getExchangeName() const +{ + return getAdapter().getExchange(frames); +} + +const boost::shared_ptr<Exchange> Message::getExchange(ExchangeRegistry& registry) const +{ + if (!exchange) { + exchange = registry.get(getExchangeName()); + } + return exchange; +} + +bool Message::isImmediate() const +{ + return getAdapter().isImmediate(frames); +} + +const FieldTable* Message::getApplicationHeaders() const +{ + return getAdapter().getApplicationHeaders(frames); +} + +bool Message::isPersistent() +{ + return (getAdapter().isPersistent(frames) || forcePersistentPolicy); +} + +bool Message::requiresAccept() +{ + return getAdapter().requiresAccept(frames); +} + +uint32_t Message::getRequiredCredit() const +{ + //add up payload for all header and content frames in the frameset + SumBodySize sum; + frames.map_if(sum, TypeFilter2<HEADER_BODY, CONTENT_BODY>()); + return sum.getSize(); +} + +void Message::encode(framing::Buffer& buffer) const +{ + //encode method and header frames + EncodeFrame f1(buffer); + frames.map_if(f1, TypeFilter2<METHOD_BODY, HEADER_BODY>()); + + //then encode the payload of each content frame + framing::EncodeBody f2(buffer); + frames.map_if(f2, TypeFilter<CONTENT_BODY>()); +} + +void Message::encodeContent(framing::Buffer& buffer) const +{ + //encode the payload of each content frame + EncodeBody f2(buffer); + frames.map_if(f2, TypeFilter<CONTENT_BODY>()); +} + +uint32_t Message::encodedSize() const +{ + return encodedHeaderSize() + encodedContentSize(); +} + +uint32_t Message::encodedContentSize() const +{ + return frames.getContentSize(); +} + +uint32_t Message::encodedHeaderSize() const +{ + //add up the size for all method and header frames in the frameset + SumFrameSize sum; + frames.map_if(sum, TypeFilter2<METHOD_BODY, HEADER_BODY>()); + return sum.getSize(); +} + +void Message::decodeHeader(framing::Buffer& buffer) +{ + AMQFrame method; + method.decode(buffer); + frames.append(method); + + AMQFrame header; + header.decode(buffer); + frames.append(header); +} + +void Message::decodeContent(framing::Buffer& buffer) +{ + if (buffer.available()) { + //get the data as a string and set that as the content + //body on a frame then add that frame to the frameset + AMQFrame frame; + frame.setBody(AMQContentBody()); + frame.castBody<AMQContentBody>()->decode(buffer, buffer.available()); + frames.append(frame); + } else { + //adjust header flags + MarkLastSegment f; + frames.map_if(f, TypeFilter<HEADER_BODY>()); + } + //mark content loaded + loaded = true; +} + +void Message::releaseContent(MessageStore* _store) +{ + if (!store) { + store = _store; + } + if (store) { + if (!getPersistenceId()) { + intrusive_ptr<PersistableMessage> pmsg(this); + store->stage(pmsg); + staged = true; + } + //remove any content frames from the frameset + frames.remove(TypeFilter<CONTENT_BODY>()); + setContentReleased(); + } +} + +void Message::destroy() +{ + if (staged) { + if (store) { + store->destroy(*this); + } else { + QPID_LOG(error, "Message content was staged but no store is set so it can't be destroyed"); + } + } +} + +void Message::sendContent(Queue& queue, framing::FrameHandler& out, uint16_t maxFrameSize) const +{ + if (isContentReleased()) { + //load content from store in chunks of maxContentSize + uint16_t maxContentSize = maxFrameSize - AMQFrame::frameOverhead(); + intrusive_ptr<const PersistableMessage> pmsg(this); + + bool done = false; + for (uint64_t offset = 0; !done; offset += maxContentSize) + { + AMQFrame frame(in_place<AMQContentBody>()); + string& data = frame.castBody<AMQContentBody>()->getData(); + + store->loadContent(queue, pmsg, data, offset, maxContentSize); + done = data.size() < maxContentSize; + frame.setBof(false); + frame.setEof(true); + if (offset > 0) { + frame.setBos(false); + } + if (!done) { + frame.setEos(false); + } + QPID_LOG(debug, "loaded frame for delivery: " << frame); + out.handle(frame); + } + } else { + Count c; + frames.map_if(c, TypeFilter<CONTENT_BODY>()); + + SendContent f(out, maxFrameSize, c.getCount()); + frames.map_if(f, TypeFilter<CONTENT_BODY>()); + } +} + +void Message::sendHeader(framing::FrameHandler& out, uint16_t /*maxFrameSize*/) const +{ + sys::Mutex::ScopedLock l(lock); + Relay f(out); + frames.map_if(f, TypeFilter<HEADER_BODY>()); +} + +// TODO aconway 2007-11-09: Obsolete, remove. Was used to cover over +// 0-8/0-9 message differences. +MessageAdapter& Message::getAdapter() const +{ + if (!adapter) { + if(frames.isA<MessageTransferBody>()) { + adapter = &TRANSFER; + } else { + const AMQMethodBody* method = frames.getMethod(); + if (!method) throw Exception("Can't adapt message with no method"); + else throw Exception(QPID_MSG("Can't adapt message based on " << *method)); + } + } + return *adapter; +} + +uint64_t Message::contentSize() const +{ + return frames.getContentSize(); +} + +bool Message::isContentLoaded() const +{ + return loaded; +} + + +namespace +{ +const std::string X_QPID_TRACE("x-qpid.trace"); +} + +bool Message::isExcluded(const std::vector<std::string>& excludes) const +{ + const FieldTable* headers = getApplicationHeaders(); + if (headers) { + std::string traceStr = headers->getAsString(X_QPID_TRACE); + if (traceStr.size()) { + std::vector<std::string> trace = split(traceStr, ", "); + + for (std::vector<std::string>::const_iterator i = excludes.begin(); i != excludes.end(); i++) { + for (std::vector<std::string>::const_iterator j = trace.begin(); j != trace.end(); j++) { + if (*i == *j) { + return true; + } + } + } + } + } + return false; +} + +void Message::addTraceId(const std::string& id) +{ + sys::Mutex::ScopedLock l(lock); + if (isA<MessageTransferBody>()) { + FieldTable& headers = getProperties<MessageProperties>()->getApplicationHeaders(); + std::string trace = headers.getAsString(X_QPID_TRACE); + if (trace.empty()) { + headers.setString(X_QPID_TRACE, id); + } else if (trace.find(id) == std::string::npos) { + trace += ","; + trace += id; + headers.setString(X_QPID_TRACE, trace); + } + } +} + +void Message::setTimestamp() +{ + DeliveryProperties* props = getProperties<DeliveryProperties>(); + //Spec states that timestamp should be set, evaluate the + //performance impact before re-enabling this: + //time_t now = ::time(0); + //props->setTimestamp(now); + if (props->getTtl()) { + //set expiration (nb: ttl is in millisecs, time_t is in secs) + time_t now = ::time(0); + props->setExpiration(now + (props->getTtl()/1000)); + expiration = AbsTime(AbsTime::now(), Duration(props->getTtl() * TIME_MSEC)); + } +} + +bool Message::hasExpired() const +{ + return expiration < FAR_FUTURE && expiration < AbsTime::now(); +} + +boost::intrusive_ptr<Message>& Message::getReplacementMessage(const Queue* qfor) const +{ + Replacement::iterator i = replacement.find(qfor); + if (i != replacement.end()){ + return i->second; + } + return empty; +} + +void Message::setReplacementMessage(boost::intrusive_ptr<Message> msg, const Queue* qfor) +{ + replacement[qfor] = msg; +} + +void Message::allEnqueuesComplete() { + MessageCallback* cb = 0; + { + sys::Mutex::ScopedLock l(lock); + std::swap(cb, enqueueCallback); + } + if (cb && *cb) (*cb)(intrusive_ptr<Message>(this)); +} + +void Message::allDequeuesComplete() { + MessageCallback* cb = 0; + { + sys::Mutex::ScopedLock l(lock); + std::swap(cb, dequeueCallback); + } + if (cb && *cb) (*cb)(intrusive_ptr<Message>(this)); +} + +void Message::setEnqueueCompleteCallback(MessageCallback& cb) { enqueueCallback = &cb; } +void Message::resetEnqueueCompleteCallback() { enqueueCallback = 0; } + +void Message::setDequeueCompleteCallback(MessageCallback& cb) { dequeueCallback = &cb; } +void Message::resetDequeueCompleteCallback() { dequeueCallback = 0; } + +}} // namespace qpid::broker diff --git a/RC9/qpid/cpp/src/qpid/broker/Message.h b/RC9/qpid/cpp/src/qpid/broker/Message.h new file mode 100644 index 0000000000..bed191fb8d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Message.h @@ -0,0 +1,185 @@ +#ifndef _broker_Message_h +#define _broker_Message_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "PersistableMessage.h" +#include "MessageAdapter.h" +#include "qpid/framing/amqp_types.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Time.h" +#include "qpid/shared_ptr.h" +#include <boost/function.hpp> +#include <string> +#include <vector> + +namespace qpid { + +namespace framing { +class FieldTable; +class SequenceNumber; +} + +namespace broker { +class ConnectionToken; +class Exchange; +class ExchangeRegistry; +class MessageStore; +class Queue; + +class Message : public PersistableMessage { +public: + typedef boost::function<void (const boost::intrusive_ptr<Message>&)> MessageCallback; + + Message(const framing::SequenceNumber& id = framing::SequenceNumber()); + ~Message(); + + uint64_t getPersistenceId() const { return persistenceId; } + void setPersistenceId(uint64_t _persistenceId) const { persistenceId = _persistenceId; } + + bool getRedelivered() const { return redelivered; } + void redeliver() { redelivered = true; } + + const ConnectionToken* getPublisher() const { return publisher; } + void setPublisher(ConnectionToken* p) { publisher = p; } + + const framing::SequenceNumber& getCommandId() { return frames.getId(); } + + uint64_t contentSize() const; + + std::string getRoutingKey() const; + const boost::shared_ptr<Exchange> getExchange(ExchangeRegistry&) const; + std::string getExchangeName() const; + bool isImmediate() const; + const framing::FieldTable* getApplicationHeaders() const; + bool isPersistent(); + bool requiresAccept(); + void setTimestamp(); + bool hasExpired() const; + + framing::FrameSet& getFrames() { return frames; } + const framing::FrameSet& getFrames() const { return frames; } + + template <class T> T* getProperties() { + qpid::framing::AMQHeaderBody* p = frames.getHeaders(); + return p->get<T>(true); + } + + template <class T> const T* getProperties() const { + qpid::framing::AMQHeaderBody* p = frames.getHeaders(); + return p->get<T>(true); + } + + template <class T> const T* hasProperties() const { + const qpid::framing::AMQHeaderBody* p = frames.getHeaders(); + return p->get<T>(); + } + + template <class T> const T* getMethod() const { + return frames.as<T>(); + } + + template <class T> bool isA() const { + return frames.isA<T>(); + } + + uint32_t getRequiredCredit() const; + + void encode(framing::Buffer& buffer) const; + void encodeContent(framing::Buffer& buffer) const; + + /** + * @returns the size of the buffer needed to encode this + * message in its entirety + */ + uint32_t encodedSize() const; + /** + * @returns the size of the buffer needed to encode the + * 'header' of this message (not just the header frame, + * but other meta data e.g.routing key and exchange) + */ + uint32_t encodedHeaderSize() const; + uint32_t encodedContentSize() const; + + void decodeHeader(framing::Buffer& buffer); + void decodeContent(framing::Buffer& buffer); + + /** + * Releases the in-memory content data held by this + * message. Must pass in a store from which the data can + * be reloaded. + */ + void releaseContent(MessageStore* store); + void destroy(); + + void sendContent(Queue& queue, framing::FrameHandler& out, uint16_t maxFrameSize) const; + void sendHeader(framing::FrameHandler& out, uint16_t maxFrameSize) const; + + bool isContentLoaded() const; + + bool isExcluded(const std::vector<std::string>& excludes) const; + void addTraceId(const std::string& id); + + void forcePersistent(); + + boost::intrusive_ptr<Message>& getReplacementMessage(const Queue* qfor) const; + void setReplacementMessage(boost::intrusive_ptr<Message> msg, const Queue* qfor); + + /** Call cb when enqueue is complete, may call immediately. Holds cb by reference. */ + void setEnqueueCompleteCallback(MessageCallback& cb); + void resetEnqueueCompleteCallback(); + + /** Call cb when dequeue is complete, may call immediately. Holds cb by reference. */ + void setDequeueCompleteCallback(MessageCallback& cb); + void resetDequeueCompleteCallback(); + + private: + typedef std::map<const Queue*,boost::intrusive_ptr<Message> > Replacement; + + MessageAdapter& getAdapter() const; + void allEnqueuesComplete(); + void allDequeuesComplete(); + + mutable sys::Mutex lock; + framing::FrameSet frames; + mutable boost::shared_ptr<Exchange> exchange; + mutable uint64_t persistenceId; + bool redelivered; + bool loaded; + bool staged; + bool forcePersistentPolicy; // used to force message as durable, via a broker policy + ConnectionToken* publisher; + mutable MessageAdapter* adapter; + qpid::sys::AbsTime expiration; + + static TransferAdapter TRANSFER; + + mutable Replacement replacement; + mutable boost::intrusive_ptr<Message> empty; + MessageCallback* enqueueCallback; + MessageCallback* dequeueCallback; +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/MessageAdapter.cpp b/RC9/qpid/cpp/src/qpid/broker/MessageAdapter.cpp new file mode 100644 index 0000000000..12f01494de --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/MessageAdapter.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 "MessageAdapter.h" + +#include "qpid/framing/DeliveryProperties.h" +#include "qpid/framing/MessageProperties.h" +#include "qpid/framing/MessageTransferBody.h" + +namespace { + const std::string empty; +} + +namespace qpid { +namespace broker{ + + std::string TransferAdapter::getRoutingKey(const framing::FrameSet& f) + { + const framing::DeliveryProperties* p = f.getHeaders()->get<framing::DeliveryProperties>(); + return p ? p->getRoutingKey() : empty; + } + + std::string TransferAdapter::getExchange(const framing::FrameSet& f) + { + return f.as<framing::MessageTransferBody>()->getDestination(); + } + + bool TransferAdapter::isImmediate(const framing::FrameSet&) + { + //TODO: delete this, immediate is no longer part of the spec + return false; + } + + const framing::FieldTable* TransferAdapter::getApplicationHeaders(const framing::FrameSet& f) + { + const framing::MessageProperties* p = f.getHeaders()->get<framing::MessageProperties>(); + return p ? &(p->getApplicationHeaders()) : 0; + } + + bool TransferAdapter::isPersistent(const framing::FrameSet& f) + { + const framing::DeliveryProperties* p = f.getHeaders()->get<framing::DeliveryProperties>(); + return p && p->getDeliveryMode() == 2; + } + + bool TransferAdapter::requiresAccept(const framing::FrameSet& f) + { + const framing::MessageTransferBody* b = f.as<framing::MessageTransferBody>(); + return b && b->getAcceptMode() == 0/*EXPLICIT == 0*/; + } + +}} diff --git a/RC9/qpid/cpp/src/qpid/broker/MessageAdapter.h b/RC9/qpid/cpp/src/qpid/broker/MessageAdapter.h new file mode 100644 index 0000000000..61a1bc4794 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/MessageAdapter.h @@ -0,0 +1,58 @@ +#ifndef _broker_MessageAdapter_h +#define _broker_MessageAdapter_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <string> +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/FrameSet.h" + +namespace qpid { +namespace broker { + +// TODO aconway 2007-11-09: No longer needed, we only have one type of message. +struct MessageAdapter +{ + virtual ~MessageAdapter() {} + + virtual std::string getRoutingKey(const framing::FrameSet& f) = 0; + virtual std::string getExchange(const framing::FrameSet& f) = 0; + virtual bool isImmediate(const framing::FrameSet& f) = 0; + virtual const framing::FieldTable* getApplicationHeaders(const framing::FrameSet& f) = 0; + virtual bool isPersistent(const framing::FrameSet& f) = 0; + virtual bool requiresAccept(const framing::FrameSet& f) = 0; +}; + +struct TransferAdapter : MessageAdapter +{ + virtual std::string getRoutingKey(const framing::FrameSet& f); + virtual std::string getExchange(const framing::FrameSet& f); + virtual const framing::FieldTable* getApplicationHeaders(const framing::FrameSet& f); + virtual bool isPersistent(const framing::FrameSet& f); + bool isImmediate(const framing::FrameSet&); + bool requiresAccept(const framing::FrameSet& f); +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/MessageBuilder.cpp b/RC9/qpid/cpp/src/qpid/broker/MessageBuilder.cpp new file mode 100644 index 0000000000..8f0e3344d5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/MessageBuilder.cpp @@ -0,0 +1,128 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "MessageBuilder.h" + +#include "Message.h" +#include "MessageStore.h" +#include "NullMessageStore.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/reply_exceptions.h" + +using boost::intrusive_ptr; +using namespace qpid::broker; +using namespace qpid::framing; + +namespace +{ + std::string type_str(uint8_t type); +} +MessageBuilder::MessageBuilder(MessageStore* const _store, uint64_t _stagingThreshold) : + state(DORMANT), store(_store), stagingThreshold(_stagingThreshold), staging(false) {} + +void MessageBuilder::handle(AMQFrame& frame) +{ + uint8_t type = frame.getBody()->type(); + switch(state) { + case METHOD: + checkType(METHOD_BODY, type); + state = HEADER; + break; + case HEADER: + if (type == CONTENT_BODY) { + //TODO: rethink how to handle non-existent headers(?)... + //didn't get a header: add in a dummy + AMQFrame header; + header.setBody(AMQHeaderBody()); + header.setBof(false); + header.setEof(false); + message->getFrames().append(header); + } else if (type != HEADER_BODY) { + throw CommandInvalidException( + QPID_MSG("Invalid frame sequence for message, expected header or content got " + << type_str(type) << ")")); + } + state = CONTENT; + break; + case CONTENT: + checkType(CONTENT_BODY, type); + break; + default: + throw CommandInvalidException(QPID_MSG("Invalid frame sequence for message (state=" << state << ")")); + } + if (staging) { + intrusive_ptr<const PersistableMessage> cpmsg = boost::static_pointer_cast<const PersistableMessage>(message); + store->appendContent(cpmsg, frame.castBody<AMQContentBody>()->getData()); + } else { + message->getFrames().append(frame); + //have we reached the staging limit? if so stage message and release content + if (state == CONTENT + && stagingThreshold + && message->getFrames().getContentSize() >= stagingThreshold + && !NullMessageStore::isNullStore(store)) + { + message->releaseContent(store); + staging = true; + } + } +} + +void MessageBuilder::end() +{ + message = 0; + state = DORMANT; + staging = false; +} + +void MessageBuilder::start(const SequenceNumber& id) +{ + message = intrusive_ptr<Message>(new Message(id)); + state = METHOD; + staging = false; +} + +namespace { + +const std::string HEADER_BODY_S = "HEADER"; +const std::string METHOD_BODY_S = "METHOD"; +const std::string CONTENT_BODY_S = "CONTENT"; +const std::string HEARTBEAT_BODY_S = "HEARTBEAT"; +const std::string UNKNOWN = "unknown"; + +std::string type_str(uint8_t type) +{ + switch(type) { + case METHOD_BODY: return METHOD_BODY_S; + case HEADER_BODY: return HEADER_BODY_S; + case CONTENT_BODY: return CONTENT_BODY_S; + case HEARTBEAT_BODY: return HEARTBEAT_BODY_S; + } + return UNKNOWN; +} + +} + +void MessageBuilder::checkType(uint8_t expected, uint8_t actual) +{ + if (expected != actual) { + throw CommandInvalidException(QPID_MSG("Invalid frame sequence for message (expected " + << type_str(expected) << " got " << type_str(actual) << ")")); + } +} diff --git a/RC9/qpid/cpp/src/qpid/broker/MessageBuilder.h b/RC9/qpid/cpp/src/qpid/broker/MessageBuilder.h new file mode 100644 index 0000000000..395de024ab --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/MessageBuilder.h @@ -0,0 +1,57 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _MessageBuilder_ +#define _MessageBuilder_ + +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/SequenceNumber.h" +#include "qpid/RefCounted.h" + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { + namespace broker { + class Message; + class MessageStore; + + class MessageBuilder : public framing::FrameHandler{ + public: + MessageBuilder(MessageStore* const store, uint64_t stagingThreshold); + void handle(framing::AMQFrame& frame); + boost::intrusive_ptr<Message> getMessage() { return message; } + void start(const framing::SequenceNumber& id); + void end(); + private: + enum State {DORMANT, METHOD, HEADER, CONTENT}; + State state; + boost::intrusive_ptr<Message> message; + MessageStore* const store; + const uint64_t stagingThreshold; + bool staging; + + void checkType(uint8_t expected, uint8_t actual); + }; + } +} + + +#endif + diff --git a/RC9/qpid/cpp/src/qpid/broker/MessageStore.h b/RC9/qpid/cpp/src/qpid/broker/MessageStore.h new file mode 100644 index 0000000000..4c4c21dfba --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/MessageStore.h @@ -0,0 +1,199 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _MessageStore_ +#define _MessageStore_ + +#include "PersistableExchange.h" +#include "PersistableMessage.h" +#include "PersistableQueue.h" +#include "PersistableConfig.h" +#include "RecoveryManager.h" +#include "TransactionalStore.h" +#include "qpid/framing/FieldTable.h" + +#include <qpid/Options.h> + +#include <boost/shared_ptr.hpp> +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { + +/** + * An abstraction of the persistent storage for messages. (In + * all methods, any pointers/references to queues or messages + * are valid only for the duration of the call). + */ +class MessageStore : public TransactionalStore, public Recoverable { + public: + + /** + * init the store, call before any other call. If not called, store + * is free to pick any defaults + * + * @param options Options object provided by concrete store plug in. + */ + virtual bool init(const Options* options) = 0; + + /** + * Record the existence of a durable queue + */ + virtual void create(PersistableQueue& queue, + const framing::FieldTable& args) = 0; + /** + * Destroy a durable queue + */ + virtual void destroy(PersistableQueue& queue) = 0; + + /** + * Record the existence of a durable exchange + */ + virtual void create(const PersistableExchange& exchange, + const framing::FieldTable& args) = 0; + /** + * Destroy a durable exchange + */ + virtual void destroy(const PersistableExchange& exchange) = 0; + + /** + * Record a binding + */ + virtual void bind(const PersistableExchange& exchange, const PersistableQueue& queue, + const std::string& key, const framing::FieldTable& args) = 0; + + /** + * Forget a binding + */ + virtual void unbind(const PersistableExchange& exchange, const PersistableQueue& queue, + const std::string& key, const framing::FieldTable& args) = 0; + + /** + * Record generic durable configuration + */ + virtual void create(const PersistableConfig& config) = 0; + + /** + * Destroy generic durable configuration + */ + virtual void destroy(const PersistableConfig& config) = 0; + + /** + * Stores a messages before it has been enqueued + * (enqueueing automatically stores the message so this is + * only required if storage is required prior to that + * point). If the message has not yet been stored it will + * store the headers as well as any content passed in. A + * persistence id will be set on the message which can be + * used to load the content or to append to it. + */ + virtual void stage(const boost::intrusive_ptr<PersistableMessage>& msg) = 0; + + /** + * Destroys a previously staged message. This only needs + * to be called if the message is never enqueued. (Once + * enqueued, deletion will be automatic when the message + * is dequeued from all queues it was enqueued onto). + */ + virtual void destroy(PersistableMessage& msg) = 0; + + /** + * Appends content to a previously staged message + */ + virtual void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg, + const std::string& data) = 0; + + /** + * Loads (a section) of content data for the specified + * message (previously stored through a call to stage or + * enqueue) into data. The offset refers to the content + * only (i.e. an offset of 0 implies that the start of the + * content should be loaded, not the headers or related + * meta-data). + */ + virtual void loadContent(const qpid::broker::PersistableQueue& queue, + const boost::intrusive_ptr<const PersistableMessage>& msg, + std::string& data, uint64_t offset, uint32_t length) = 0; + + /** + * Enqueues a message, storing the message if it has not + * been previously stored and recording that the given + * message is on the given queue. + * + * Note: that this is async so the return of the function does + * not mean the opperation is complete. + * + * @param msg the message to enqueue + * @param queue the name of the queue onto which it is to be enqueued + * @param xid (a pointer to) an identifier of the + * distributed transaction in which the operation takes + * place or null for 'local' transactions + */ + virtual void enqueue(TransactionContext* ctxt, + const boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue) = 0; + + /** + * Dequeues a message, recording that the given message is + * no longer on the given queue and deleting the message + * if it is no longer on any other queue. + * + * Note: that this is async so the return of the function does + * not mean the opperation is complete. + * + * @param msg the message to dequeue + * @param queue the name of the queue from which it is to be dequeued + * @param xid (a pointer to) an identifier of the + * distributed transaction in which the operation takes + * place or null for 'local' transactions + */ + virtual void dequeue(TransactionContext* ctxt, + const boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue) = 0; + + /** + * Flushes all async messages to disk for the specified queue + * + * Note: that this is async so the return of the function does + * not mean the opperation is complete. + * + * @param queue the name of the queue from which it is to be dequeued + */ + virtual void flush(const qpid::broker::PersistableQueue& queue)=0; + + /** + * Returns the number of outstanding AIO's for a given queue + * + * If 0, than all the enqueue / dequeues have been stored + * to disk + * + * @param queue the name of the queue to check for outstanding AIO + */ + virtual uint32_t outstandingQueueAIO(const PersistableQueue& queue) = 0; + + + virtual ~MessageStore(){} +}; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp b/RC9/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp new file mode 100644 index 0000000000..96186d508b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp @@ -0,0 +1,174 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "MessageStoreModule.h" +#include "NullMessageStore.h" +#include <iostream> + +// This transfer protects against the unloading of the store lib prior to the handling of the exception +#define TRANSFER_EXCEPTION(fn) try { fn; } catch (std::exception& e) { throw Exception(e.what()); } + +using boost::intrusive_ptr; +using qpid::framing::FieldTable; + +namespace qpid { +namespace broker { + +MessageStoreModule::MessageStoreModule(MessageStore* _store) : store(_store) {} + +MessageStoreModule::~MessageStoreModule() +{ + delete store; +} + +bool MessageStoreModule::init(const Options*) { return true; } + +void MessageStoreModule::create(PersistableQueue& queue, const FieldTable& args) +{ + TRANSFER_EXCEPTION(store->create(queue, args)); +} + +void MessageStoreModule::destroy(PersistableQueue& queue) +{ + TRANSFER_EXCEPTION(store->destroy(queue)); +} + +void MessageStoreModule::create(const PersistableExchange& exchange, const FieldTable& args) +{ + TRANSFER_EXCEPTION(store->create(exchange, args)); +} + +void MessageStoreModule::destroy(const PersistableExchange& exchange) +{ + TRANSFER_EXCEPTION(store->destroy(exchange)); +} + +void MessageStoreModule::bind(const PersistableExchange& e, const PersistableQueue& q, + const std::string& k, const framing::FieldTable& a) +{ + TRANSFER_EXCEPTION(store->bind(e, q, k, a)); +} + +void MessageStoreModule::unbind(const PersistableExchange& e, const PersistableQueue& q, + const std::string& k, const framing::FieldTable& a) +{ + TRANSFER_EXCEPTION(store->unbind(e, q, k, a)); +} + +void MessageStoreModule::create(const PersistableConfig& config) +{ + TRANSFER_EXCEPTION(store->create(config)); +} + +void MessageStoreModule::destroy(const PersistableConfig& config) +{ + TRANSFER_EXCEPTION(store->destroy(config)); +} + +void MessageStoreModule::recover(RecoveryManager& registry) +{ + TRANSFER_EXCEPTION(store->recover(registry)); +} + +void MessageStoreModule::stage(const intrusive_ptr<PersistableMessage>& msg) +{ + TRANSFER_EXCEPTION(store->stage(msg)); +} + +void MessageStoreModule::destroy(PersistableMessage& msg) +{ + TRANSFER_EXCEPTION(store->destroy(msg)); +} + +void MessageStoreModule::appendContent(const intrusive_ptr<const PersistableMessage>& msg, + const std::string& data) +{ + TRANSFER_EXCEPTION(store->appendContent(msg, data)); +} + +void MessageStoreModule::loadContent( + const qpid::broker::PersistableQueue& queue, + const intrusive_ptr<const PersistableMessage>& msg, + string& data, uint64_t offset, uint32_t length) +{ + TRANSFER_EXCEPTION(store->loadContent(queue, msg, data, offset, length)); +} + +void MessageStoreModule::enqueue(TransactionContext* ctxt, + const intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue) +{ + TRANSFER_EXCEPTION(store->enqueue(ctxt, msg, queue)); +} + +void MessageStoreModule::dequeue(TransactionContext* ctxt, + const intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue) +{ + TRANSFER_EXCEPTION(store->dequeue(ctxt, msg, queue)); +} + +void MessageStoreModule::flush(const qpid::broker::PersistableQueue& queue) +{ + TRANSFER_EXCEPTION(store->flush(queue)); +} + +uint32_t MessageStoreModule::outstandingQueueAIO(const PersistableQueue& queue) +{ + TRANSFER_EXCEPTION(return store->outstandingQueueAIO(queue)); +} + +std::auto_ptr<TransactionContext> MessageStoreModule::begin() +{ + TRANSFER_EXCEPTION(return store->begin()); +} + +std::auto_ptr<TPCTransactionContext> MessageStoreModule::begin(const std::string& xid) +{ + TRANSFER_EXCEPTION(return store->begin(xid)); +} + +void MessageStoreModule::prepare(TPCTransactionContext& txn) +{ + TRANSFER_EXCEPTION(store->prepare(txn)); +} + +void MessageStoreModule::commit(TransactionContext& ctxt) +{ + TRANSFER_EXCEPTION(store->commit(ctxt)); +} + +void MessageStoreModule::abort(TransactionContext& ctxt) +{ + TRANSFER_EXCEPTION(store->abort(ctxt)); +} + +void MessageStoreModule::collectPreparedXids(std::set<std::string>& xids) +{ + TRANSFER_EXCEPTION(store->collectPreparedXids(xids)); +} + +bool MessageStoreModule::isNull() const +{ + return NullMessageStore::isNullStore(store); +} + +}} // namespace qpid::broker diff --git a/RC9/qpid/cpp/src/qpid/broker/MessageStoreModule.h b/RC9/qpid/cpp/src/qpid/broker/MessageStoreModule.h new file mode 100644 index 0000000000..0b51610a46 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/MessageStoreModule.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. + * + */ +#ifndef _MessageStoreModule_ +#define _MessageStoreModule_ + +#include "MessageStore.h" +#include "Queue.h" +#include "RecoveryManager.h" + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { + +/** + * A null implementation of the MessageStore interface + */ +class MessageStoreModule : public MessageStore +{ + MessageStore* store; + public: + MessageStoreModule(MessageStore* store); + + bool init(const Options* options); + std::auto_ptr<TransactionContext> begin(); + std::auto_ptr<TPCTransactionContext> begin(const std::string& xid); + void prepare(TPCTransactionContext& txn); + void commit(TransactionContext& txn); + void abort(TransactionContext& txn); + void collectPreparedXids(std::set<std::string>& xids); + + void create(PersistableQueue& queue, const framing::FieldTable& args); + void destroy(PersistableQueue& queue); + void create(const PersistableExchange& exchange, const framing::FieldTable& args); + void destroy(const PersistableExchange& exchange); + void bind(const PersistableExchange& exchange, const PersistableQueue& queue, + const std::string& key, const framing::FieldTable& args); + void unbind(const PersistableExchange& exchange, const PersistableQueue& queue, + const std::string& key, const framing::FieldTable& args); + void create(const PersistableConfig& config); + void destroy(const PersistableConfig& config); + void recover(RecoveryManager& queues); + void stage(const boost::intrusive_ptr<PersistableMessage>& msg); + void destroy(PersistableMessage& msg); + void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg, const std::string& data); + void loadContent(const qpid::broker::PersistableQueue& queue, + const boost::intrusive_ptr<const PersistableMessage>& msg, std::string& data, + uint64_t offset, uint32_t length); + + void enqueue(TransactionContext* ctxt, + const boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue); + void dequeue(TransactionContext* ctxt, + const boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue); + uint32_t outstandingQueueAIO(const PersistableQueue& queue); + void flush(const qpid::broker::PersistableQueue& queue); + bool isNull() const; + + ~MessageStoreModule(); +}; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/NameGenerator.cpp b/RC9/qpid/cpp/src/qpid/broker/NameGenerator.cpp new file mode 100644 index 0000000000..8484f921e9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/NameGenerator.cpp @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "NameGenerator.h" +#include <sstream> + +using namespace qpid::broker; + +NameGenerator::NameGenerator(const std::string& _base) : base(_base), counter(1) {} + +std::string NameGenerator::generate(){ + std::stringstream ss; + ss << base << counter++; + return ss.str(); +} diff --git a/RC9/qpid/cpp/src/qpid/broker/NameGenerator.h b/RC9/qpid/cpp/src/qpid/broker/NameGenerator.h new file mode 100644 index 0000000000..6ea25c9797 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/NameGenerator.h @@ -0,0 +1,39 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _NameGenerator_ +#define _NameGenerator_ + +#include <string> + +namespace qpid { + namespace broker { + class NameGenerator{ + const std::string base; + unsigned int counter; + public: + NameGenerator(const std::string& base); + std::string generate(); + }; + } +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/NullMessageStore.cpp b/RC9/qpid/cpp/src/qpid/broker/NullMessageStore.cpp new file mode 100644 index 0000000000..ad0143ce43 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/NullMessageStore.cpp @@ -0,0 +1,166 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "NullMessageStore.h" +#include "MessageStoreModule.h" +#include "RecoveryManager.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/reply_exceptions.h" + +#include <iostream> + +using boost::intrusive_ptr; + +namespace qpid{ +namespace broker{ + +const std::string nullxid = ""; + +class SimpleDummyCtxt : public TransactionContext {}; + +class DummyCtxt : public TPCTransactionContext +{ + const std::string xid; +public: + DummyCtxt(const std::string& _xid) : xid(_xid) {} + static std::string getXid(TransactionContext& ctxt) + { + DummyCtxt* c(dynamic_cast<DummyCtxt*>(&ctxt)); + return c ? c->xid : nullxid; + } +}; + +NullMessageStore::NullMessageStore() : nextPersistenceId(1) { + QPID_LOG(info, "No message store configured, persistence is disabled.") +} + +bool NullMessageStore::init(const Options* /*options*/) {return true;} + +void NullMessageStore::create(PersistableQueue& queue, const framing::FieldTable& /*args*/) +{ + queue.setPersistenceId(nextPersistenceId++); +} + +void NullMessageStore::destroy(PersistableQueue&) +{ +} + +void NullMessageStore::create(const PersistableExchange& exchange, const framing::FieldTable& /*args*/) +{ + exchange.setPersistenceId(nextPersistenceId++); +} + +void NullMessageStore::destroy(const PersistableExchange& ) +{} + +void NullMessageStore::bind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&){} + +void NullMessageStore::unbind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&){} + +void NullMessageStore::create(const PersistableConfig& config) +{ + config.setPersistenceId(nextPersistenceId++); +} + +void NullMessageStore::destroy(const PersistableConfig&) {} + +void NullMessageStore::recover(RecoveryManager&) {} + +void NullMessageStore::stage(const intrusive_ptr<PersistableMessage>&) {} + +void NullMessageStore::destroy(PersistableMessage&) {} + +void NullMessageStore::appendContent(const intrusive_ptr<const PersistableMessage>&, const string&) {} + +void NullMessageStore::loadContent(const qpid::broker::PersistableQueue&, + const intrusive_ptr<const PersistableMessage>&, + string&, uint64_t, uint32_t) +{ + throw qpid::framing::InternalErrorException("Can't load content; persistence not enabled"); +} + +void NullMessageStore::enqueue(TransactionContext*, + const intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue&) +{ + msg->enqueueComplete(); +} + +void NullMessageStore::dequeue(TransactionContext*, + const intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue&) +{ + msg->dequeueComplete(); +} + +void NullMessageStore::flush(const qpid::broker::PersistableQueue&) {} + +uint32_t NullMessageStore::outstandingQueueAIO(const PersistableQueue& ) { + return 0; +} + +std::auto_ptr<TransactionContext> NullMessageStore::begin() +{ + return std::auto_ptr<TransactionContext>(new SimpleDummyCtxt()); +} + +std::auto_ptr<TPCTransactionContext> NullMessageStore::begin(const std::string& xid) +{ + return std::auto_ptr<TPCTransactionContext>(new DummyCtxt(xid)); +} + +void NullMessageStore::prepare(TPCTransactionContext& ctxt) +{ + prepared.insert(DummyCtxt::getXid(ctxt)); +} + +void NullMessageStore::commit(TransactionContext& ctxt) +{ + prepared.erase(DummyCtxt::getXid(ctxt)); +} + +void NullMessageStore::abort(TransactionContext& ctxt) +{ + prepared.erase(DummyCtxt::getXid(ctxt)); +} + +void NullMessageStore::collectPreparedXids(std::set<string>& out) +{ + out.insert(prepared.begin(), prepared.end()); +} + +bool NullMessageStore::isNull() const +{ + return true; +} + +bool NullMessageStore::isNullStore(const MessageStore* store) +{ + const MessageStoreModule* wrapper = dynamic_cast<const MessageStoreModule*>(store); + if (wrapper) { + return wrapper->isNull(); + } else { + const NullMessageStore* test = dynamic_cast<const NullMessageStore*>(store); + return test && test->isNull(); + } +} + +}} // namespace qpid::broker diff --git a/RC9/qpid/cpp/src/qpid/broker/NullMessageStore.h b/RC9/qpid/cpp/src/qpid/broker/NullMessageStore.h new file mode 100644 index 0000000000..d99c751d26 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/NullMessageStore.h @@ -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. + * + */ +#ifndef _NullMessageStore_ +#define _NullMessageStore_ + +#include <set> +#include "MessageStore.h" +#include "Queue.h" + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { + +/** + * A null implementation of the MessageStore interface + */ +class NullMessageStore : public MessageStore +{ + std::set<std::string> prepared; + uint64_t nextPersistenceId; + public: + NullMessageStore(); + + virtual bool init(const Options* options); + virtual std::auto_ptr<TransactionContext> begin(); + virtual std::auto_ptr<TPCTransactionContext> begin(const std::string& xid); + virtual void prepare(TPCTransactionContext& txn); + virtual void commit(TransactionContext& txn); + virtual void abort(TransactionContext& txn); + virtual void collectPreparedXids(std::set<std::string>& xids); + + virtual void create(PersistableQueue& queue, const framing::FieldTable& args); + virtual void destroy(PersistableQueue& queue); + virtual void create(const PersistableExchange& exchange, const framing::FieldTable& args); + virtual void destroy(const PersistableExchange& exchange); + + virtual void bind(const PersistableExchange& exchange, const PersistableQueue& queue, + const std::string& key, const framing::FieldTable& args); + virtual void unbind(const PersistableExchange& exchange, const PersistableQueue& queue, + const std::string& key, const framing::FieldTable& args); + virtual void create(const PersistableConfig& config); + virtual void destroy(const PersistableConfig& config); + virtual void recover(RecoveryManager& queues); + virtual void stage(const boost::intrusive_ptr<PersistableMessage>& msg); + virtual void destroy(PersistableMessage& msg); + virtual void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg, + const std::string& data); + virtual void loadContent(const qpid::broker::PersistableQueue& queue, + const boost::intrusive_ptr<const PersistableMessage>& msg, std::string& data, + uint64_t offset, uint32_t length); + virtual void enqueue(TransactionContext* ctxt, + const boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue); + virtual void dequeue(TransactionContext* ctxt, + const boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue); + virtual uint32_t outstandingQueueAIO(const PersistableQueue& queue); + virtual void flush(const qpid::broker::PersistableQueue& queue); + ~NullMessageStore(){} + + virtual bool isNull() const; + static bool isNullStore(const MessageStore*); +}; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/OwnershipToken.h b/RC9/qpid/cpp/src/qpid/broker/OwnershipToken.h new file mode 100644 index 0000000000..effd2f5b3c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/OwnershipToken.h @@ -0,0 +1,38 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _OwnershipToken_ +#define _OwnershipToken_ + +namespace qpid { +namespace broker { + +class ConnectionToken; + +class OwnershipToken{ +public: + virtual bool isLocal(const ConnectionToken* t) const = 0; + virtual ~OwnershipToken(){} +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/Persistable.h b/RC9/qpid/cpp/src/qpid/broker/Persistable.h new file mode 100644 index 0000000000..36499c7a1a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Persistable.h @@ -0,0 +1,63 @@ +#ifndef _broker_Persistable_h +#define _broker_Persistable_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/framing/amqp_types.h" +#include "qpid/framing/Buffer.h" +#include "qpid/RefCounted.h" + +namespace qpid { +namespace broker { + +/** + * Base class for all persistable objects + */ +class Persistable : public RefCounted +{ +public: + /** + * Allows the store to attach its own identifier to this object + */ + virtual void setPersistenceId(uint64_t id) const = 0; + /** + * Returns any identifier the store may have attached to this + * object + */ + virtual uint64_t getPersistenceId() const = 0; + /** + * Encodes the persistable state of this object into the supplied + * buffer + */ + virtual void encode(framing::Buffer& buffer) const = 0; + /** + * @returns the size of the buffer needed to encode this object + */ + virtual uint32_t encodedSize() const = 0; + + virtual ~Persistable() {}; +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/PersistableConfig.h b/RC9/qpid/cpp/src/qpid/broker/PersistableConfig.h new file mode 100644 index 0000000000..914e91ea80 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/PersistableConfig.h @@ -0,0 +1,45 @@ +#ifndef _broker_PersistableConfig_h +#define _broker_PersistableConfig_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <string> +#include "Persistable.h" + +namespace qpid { +namespace broker { + +/** + * The interface used by general-purpose persistable configuration for + * the message store. + */ +class PersistableConfig : public Persistable +{ +public: + virtual const std::string& getName() const = 0; + virtual ~PersistableConfig() {}; +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/PersistableExchange.h b/RC9/qpid/cpp/src/qpid/broker/PersistableExchange.h new file mode 100644 index 0000000000..683b740ddc --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/PersistableExchange.h @@ -0,0 +1,45 @@ +#ifndef _broker_PersistableExchange_h +#define _broker_PersistableExchange_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <string> +#include "Persistable.h" + +namespace qpid { +namespace broker { + +/** + * The interface exchanges must expose to the MessageStore in order to be + * persistable. + */ +class PersistableExchange : public Persistable +{ +public: + virtual const std::string& getName() const = 0; + virtual ~PersistableExchange() {}; +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/PersistableMessage.cpp b/RC9/qpid/cpp/src/qpid/broker/PersistableMessage.cpp new file mode 100644 index 0000000000..4d272c3780 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/PersistableMessage.cpp @@ -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. + * + */ + + +#include "PersistableMessage.h" +#include "MessageStore.h" +#include <iostream> + +using namespace qpid::broker; + +namespace qpid { +namespace broker { + +class MessageStore; + +PersistableMessage::~PersistableMessage() {} + +PersistableMessage::PersistableMessage() : + asyncEnqueueCounter(0), + asyncDequeueCounter(0), + contentReleased(false), + store(0) +{} + +void PersistableMessage::flush() +{ + syncList copy; + { + sys::ScopedLock<sys::Mutex> l(storeLock); + if (store) { + copy = synclist; + } else { + return;//early exit as nothing to do + } + } + for (syncList::iterator i = copy.begin(); i != copy.end(); ++i) { + PersistableQueue::shared_ptr q(i->lock()); + if (q) { + store->flush(*q); + } + } +} + +void PersistableMessage::setContentReleased() {contentReleased = true; } + +bool PersistableMessage::isContentReleased()const { return contentReleased; } + +bool PersistableMessage::isEnqueueComplete() { + sys::ScopedLock<sys::Mutex> l(asyncEnqueueLock); + return asyncEnqueueCounter == 0; +} + +void PersistableMessage::enqueueComplete() { + bool notify = false; + { + sys::ScopedLock<sys::Mutex> l(asyncEnqueueLock); + if (asyncEnqueueCounter > 0) { + if (--asyncEnqueueCounter == 0) { + notify = true; + } + } + } + if (notify) { + allEnqueuesComplete(); + sys::ScopedLock<sys::Mutex> l(storeLock); + if (store) { + for (syncList::iterator i = synclist.begin(); i != synclist.end(); ++i) { + PersistableQueue::shared_ptr q(i->lock()); + if (q) q->notifyDurableIOComplete(); + } + } + } +} + +void PersistableMessage::enqueueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store) { + if (_store){ + sys::ScopedLock<sys::Mutex> l(storeLock); + store = _store; + boost::weak_ptr<PersistableQueue> q(queue); + synclist.push_back(q); + } + enqueueAsync(); +} + +void PersistableMessage::enqueueAsync() { + sys::ScopedLock<sys::Mutex> l(asyncEnqueueLock); + asyncEnqueueCounter++; +} + +bool PersistableMessage::isDequeueComplete() { + sys::ScopedLock<sys::Mutex> l(asyncDequeueLock); + return asyncDequeueCounter == 0; +} + +void PersistableMessage::dequeueComplete() { + bool notify = false; + { + sys::ScopedLock<sys::Mutex> l(asyncDequeueLock); + if (asyncDequeueCounter > 0) { + if (--asyncDequeueCounter == 0) { + notify = true; + } + } + } + if (notify) allDequeuesComplete(); +} + +void PersistableMessage::dequeueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store) { + if (_store){ + sys::ScopedLock<sys::Mutex> l(storeLock); + store = _store; + boost::weak_ptr<PersistableQueue> q(queue); + synclist.push_back(q); + } + dequeueAsync(); +} + +void PersistableMessage::dequeueAsync() { + sys::ScopedLock<sys::Mutex> l(asyncDequeueLock); + asyncDequeueCounter++; +} + +}} + + diff --git a/RC9/qpid/cpp/src/qpid/broker/PersistableMessage.h b/RC9/qpid/cpp/src/qpid/broker/PersistableMessage.h new file mode 100644 index 0000000000..4f2e3abafa --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/PersistableMessage.h @@ -0,0 +1,116 @@ +#ifndef _broker_PersistableMessage_h +#define _broker_PersistableMessage_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <string> +#include <list> +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include "Persistable.h" +#include "qpid/framing/amqp_types.h" +#include "qpid/sys/Mutex.h" +#include "PersistableQueue.h" + +namespace qpid { +namespace broker { + +class MessageStore; + +/** + * Base class for persistable messages. + */ +class PersistableMessage : public Persistable +{ + typedef std::list< boost::weak_ptr<PersistableQueue> > syncList; + sys::Mutex asyncEnqueueLock; + sys::Mutex asyncDequeueLock; + sys::Mutex storeLock; + + /** + * Tracks the number of outstanding asynchronous enqueue + * operations. When the message is enqueued asynchronously the + * count is incremented; when that enqueue completes it is + * decremented. Thus when it is 0, there are no outstanding + * enqueues. + */ + int asyncEnqueueCounter; + + /** + * Tracks the number of outstanding asynchronous dequeue + * operations. When the message is dequeued asynchronously the + * count is incremented; when that dequeue completes it is + * decremented. Thus when it is 0, there are no outstanding + * dequeues. + */ + int asyncDequeueCounter; + + bool contentReleased; + syncList synclist; + + protected: + /** Called when all enqueues are complete for this message. */ + virtual void allEnqueuesComplete() = 0; + /** Called when all dequeues are complete for this message. */ + virtual void allDequeuesComplete() = 0; + + void setContentReleased(); + + MessageStore* store; + + public: + typedef boost::shared_ptr<PersistableMessage> shared_ptr; + + /** + * @returns the size of the headers when encoded + */ + virtual uint32_t encodedHeaderSize() const = 0; + + virtual ~PersistableMessage(); + + PersistableMessage(); + + void flush(); + + bool isContentReleased() const; + + bool isEnqueueComplete(); + + void enqueueComplete(); + + void enqueueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store); + + void enqueueAsync(); + + bool isDequeueComplete(); + + void dequeueComplete(); + + void dequeueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store); + + void dequeueAsync(); +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/PersistableQueue.h b/RC9/qpid/cpp/src/qpid/broker/PersistableQueue.h new file mode 100644 index 0000000000..9236814ae3 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/PersistableQueue.h @@ -0,0 +1,87 @@ +#ifndef _broker_PersistableQueue_h +#define _broker_PersistableQueue_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <string> +#include "Persistable.h" +#include "qpid/management/Manageable.h" +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace broker { + + +/** +* Empty class to be used by any module that wanted to set an external per queue store into +* persistableQueue +*/ + +class ExternalQueueStore : public management::Manageable +{ +public: + virtual ~ExternalQueueStore() {}; + +}; + + +/** + * The interface queues must expose to the MessageStore in order to be + * persistable. + */ +class PersistableQueue : public Persistable +{ +public: + typedef boost::shared_ptr<PersistableQueue> shared_ptr; + + virtual const std::string& getName() const = 0; + virtual ~PersistableQueue() { + if (externalQueueStore) + delete externalQueueStore; + }; + + virtual void setExternalQueueStore(ExternalQueueStore* inst) = 0; + + inline ExternalQueueStore* getExternalQueueStore() const {return externalQueueStore;}; + + PersistableQueue():externalQueueStore(NULL){ + }; + + + /** + * call back to signal async AIO writes have + * completed (enqueue/dequeue etc) + * + * Note: DO NOT do work on this callback, if you block + * this callback you will block the store. + */ + virtual void notifyDurableIOComplete() = 0; +protected: + + ExternalQueueStore* externalQueueStore; + +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/Queue.cpp b/RC9/qpid/cpp/src/qpid/broker/Queue.cpp new file mode 100644 index 0000000000..9089ba0c54 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Queue.cpp @@ -0,0 +1,900 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Broker.h" +#include "Queue.h" +#include "Exchange.h" +#include "DeliverableMessage.h" +#include "MessageStore.h" +#include "NullMessageStore.h" +#include "QueueRegistry.h" + +#include "qpid/StringUtils.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/sys/Monitor.h" +#include "qpid/sys/Time.h" +#include "qmf/org/apache/qpid/broker/ArgsQueuePurge.h" + +#include <iostream> +#include <algorithm> +#include <functional> + +#include <boost/bind.hpp> +#include <boost/intrusive_ptr.hpp> + +using namespace qpid::broker; +using namespace qpid::sys; +using namespace qpid::framing; +using qpid::management::ManagementAgent; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; +using std::for_each; +using std::mem_fun; +namespace _qmf = qmf::org::apache::qpid::broker; + + +namespace +{ +const std::string qpidMaxSize("qpid.max_size"); +const std::string qpidMaxCount("qpid.max_count"); +const std::string qpidNoLocal("no-local"); +const std::string qpidTraceIdentity("qpid.trace.id"); +const std::string qpidTraceExclude("qpid.trace.exclude"); +const std::string qpidLastValueQueue("qpid.last_value_queue"); +const std::string qpidLastValueQueueNoBrowse("qpid.last_value_queue_no_browse"); +const std::string qpidPersistLastNode("qpid.persist_last_node"); +const std::string qpidVQMatchProperty("qpid.LVQ_key"); +} + + +Queue::Queue(const string& _name, bool _autodelete, + MessageStore* const _store, + const OwnershipToken* const _owner, + Manageable* parent) : + + name(_name), + autodelete(_autodelete), + store(_store), + owner(_owner), + consumerCount(0), + exclusive(0), + noLocal(false), + lastValueQueue(false), + lastValueQueueNoBrowse(false), + persistLastNode(false), + inLastNodeFailure(false), + persistenceId(0), + policyExceeded(false), + mgmtObject(0) +{ + if (parent != 0) + { + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + + if (agent != 0) + { + mgmtObject = new _qmf::Queue(agent, this, parent, _name, _store != 0, _autodelete, _owner != 0); + + // Add the object to the management agent only if this queue is not durable. + // If it's durable, we will add it later when the queue is assigned a persistenceId. + if (store == 0) + agent->addObject (mgmtObject); + } + } +} + +Queue::~Queue() +{ + if (mgmtObject != 0) + mgmtObject->resourceDestroy (); +} + +void Queue::notifyDurableIOComplete() +{ + QueueListeners::NotificationSet copy; + { + Mutex::ScopedLock locker(messageLock); + listeners.populate(copy); + } + copy.notify(); +} + +bool isLocalTo(const OwnershipToken* token, boost::intrusive_ptr<Message>& msg) +{ + return token && token->isLocal(msg->getPublisher()); +} + +bool Queue::isLocal(boost::intrusive_ptr<Message>& msg) +{ + //message is considered local if it was published on the same + //connection as that of the session which declared this queue + //exclusive (owner) or which has an exclusive subscription + //(exclusive) + return noLocal && (isLocalTo(owner, msg) || isLocalTo(exclusive, msg)); +} + +bool Queue::isExcluded(boost::intrusive_ptr<Message>& msg) +{ + return traceExclude.size() && msg->isExcluded(traceExclude); +} + +void Queue::deliver(boost::intrusive_ptr<Message>& msg){ + + if (msg->isImmediate() && getConsumerCount() == 0) { + if (alternateExchange) { + DeliverableMessage deliverable(msg); + alternateExchange->route(deliverable, msg->getRoutingKey(), msg->getApplicationHeaders()); + } + } else if (isLocal(msg)) { + //drop message + QPID_LOG(info, "Dropping 'local' message from " << getName()); + } else if (isExcluded(msg)) { + //drop message + QPID_LOG(info, "Dropping excluded message from " << getName()); + } else { + // if no store then mark as enqueued + if (!enqueue(0, msg)){ + push(msg); + msg->enqueueComplete(); + }else { + push(msg); + } + mgntEnqStats(msg); + QPID_LOG(debug, "Message " << msg << " enqueued on " << name << "[" << this << "]"); + } +} + + +void Queue::recover(boost::intrusive_ptr<Message>& msg){ + push(msg); + msg->enqueueComplete(); // mark the message as enqueued + mgntEnqStats(msg); + + if (store && !msg->isContentLoaded()) { + //content has not been loaded, need to ensure that lazy loading mode is set: + //TODO: find a nicer way to do this + msg->releaseContent(store); + } +} + +void Queue::process(boost::intrusive_ptr<Message>& msg){ + push(msg); + mgntEnqStats(msg); + if (mgmtObject != 0){ + mgmtObject->inc_msgTxnEnqueues (); + mgmtObject->inc_byteTxnEnqueues (msg->contentSize ()); + } +} + +void Queue::requeue(const QueuedMessage& msg){ + if (policy.get() && !policy->isEnqueued(msg)) return; + + QueueListeners::NotificationSet copy; + { + Mutex::ScopedLock locker(messageLock); + msg.payload->enqueueComplete(); // mark the message as enqueued + messages.push_front(msg); + listeners.populate(copy); + } + copy.notify(); +} + +void Queue::clearLVQIndex(const QueuedMessage& msg){ + if (lastValueQueue){ + const framing::FieldTable* ft = msg.payload->getApplicationHeaders(); + string key = ft->getAsString(qpidVQMatchProperty); + lvq.erase(key); + } +} + +bool Queue::acquire(const QueuedMessage& msg) { + Mutex::ScopedLock locker(messageLock); + QPID_LOG(debug, "attempting to acquire " << msg.position); + for (Messages::iterator i = messages.begin(); i != messages.end(); i++) { + if ((i->position == msg.position && !lastValueQueue) // note that in some cases payload not be set + || (lastValueQueue && (i->position == msg.position) && + msg.payload.get() == checkLvqReplace(*i).payload.get()) ) { + + clearLVQIndex(msg); + messages.erase(i); + QPID_LOG(debug, "Match found, acquire succeeded: " << i->position << " == " << msg.position); + return true; + } else { + QPID_LOG(debug, "No match: " << i->position << " != " << msg.position); + } + } + QPID_LOG(debug, "Acquire failed for " << msg.position); + return false; +} + +bool Queue::getNextMessage(QueuedMessage& m, Consumer::shared_ptr c) +{ + if (c->preAcquires()) { + return consumeNextMessage(m, c); + } else { + return browseNextMessage(m, c); + } +} + +bool Queue::checkForMessages(Consumer::shared_ptr c) +{ + Mutex::ScopedLock locker(messageLock); + if (messages.empty()) { + //no message available, register consumer for notification + //when this changes + listeners.addListener(c); + return false; + } else { + QueuedMessage msg = getFront(); + if (store && !msg.payload->isEnqueueComplete()) { + //though a message is on the queue, it has not yet been + //enqueued and so is not available for consumption yet, + //register consumer for notification when this changes + listeners.addListener(c); + return false; + } else { + //check that consumer has sufficient credit for the + //message (if it does not, no need to register it for + //notification as the consumer itself will handle the + //credit allocation required to change this condition). + return c->accept(msg.payload); + } + } +} + +bool Queue::consumeNextMessage(QueuedMessage& m, Consumer::shared_ptr c) +{ + while (true) { + Mutex::ScopedLock locker(messageLock); + if (messages.empty()) { + QPID_LOG(debug, "No messages to dispatch on queue '" << name << "'"); + listeners.addListener(c); + return false; + } else { + QueuedMessage msg = getFront(); + if (msg.payload->hasExpired()) { + QPID_LOG(debug, "Message expired from queue '" << name << "'"); + popAndDequeue(); + continue; + } + + if (c->filter(msg.payload)) { + if (c->accept(msg.payload)) { + m = msg; + popMsg(msg); + return true; + } else { + //message(s) are available but consumer hasn't got enough credit + QPID_LOG(debug, "Consumer can't currently accept message from '" << name << "'"); + return false; + } + } else { + //consumer will never want this message + QPID_LOG(debug, "Consumer doesn't want message from '" << name << "'"); + return false; + } + } + } +} + + +bool Queue::browseNextMessage(QueuedMessage& m, Consumer::shared_ptr c) +{ + QueuedMessage msg(this); + while (seek(msg, c)) { + if (c->filter(msg.payload) && !msg.payload->hasExpired()) { + if (c->accept(msg.payload)) { + //consumer wants the message + c->position = msg.position; + m = msg; + if (!lastValueQueueNoBrowse) clearLVQIndex(msg); + if (lastValueQueue) { + boost::intrusive_ptr<Message> replacement = msg.payload->getReplacementMessage(this); + if (replacement.get()) m.payload = replacement; + } + return true; + } else { + //browser hasn't got enough credit for the message + QPID_LOG(debug, "Browser can't currently accept message from '" << name << "'"); + return false; + } + } else { + //consumer will never want this message, continue seeking + c->position = msg.position; + QPID_LOG(debug, "Browser skipping message from '" << name << "'"); + } + } + return false; +} + +void Queue::removeListener(Consumer::shared_ptr c) +{ + QueueListeners::NotificationSet set; + { + Mutex::ScopedLock locker(messageLock); + listeners.removeListener(c); + if (messages.size()) { + listeners.populate(set); + } + } + set.notify(); +} + +bool Queue::dispatch(Consumer::shared_ptr c) +{ + QueuedMessage msg(this); + if (getNextMessage(msg, c)) { + c->deliver(msg); + return true; + } else { + return false; + } +} + +bool Queue::seek(QueuedMessage& msg, Consumer::shared_ptr c) { + Mutex::ScopedLock locker(messageLock); + if (!messages.empty() && messages.back().position > c->position) { + if (c->position < getFront().position) { + msg = getFront(); + return true; + } else { + //TODO: can improve performance of this search, for now just searching linearly from end + Messages::reverse_iterator pos; + for (Messages::reverse_iterator i = messages.rbegin(); i != messages.rend() && i->position > c->position; i++) { + pos = i; + } + msg = *pos; + return true; + } + } + listeners.addListener(c); + return false; +} + +namespace { +struct PositionEquals { + SequenceNumber pos; + PositionEquals(SequenceNumber p) : pos(p) {} + bool operator()(const QueuedMessage& msg) const { return msg.position == pos; } +}; +}// namespace + +QueuedMessage Queue::find(SequenceNumber pos) const { + Mutex::ScopedLock locker(messageLock); + Messages::const_iterator i = std::find_if(messages.begin(), messages.end(), PositionEquals(pos)); + if (i != messages.end()) + return *i; + return QueuedMessage(); +} + +void Queue::consume(Consumer::shared_ptr c, bool requestExclusive){ + Mutex::ScopedLock locker(consumerLock); + if(exclusive) { + throw ResourceLockedException( + QPID_MSG("Queue " << getName() << " has an exclusive consumer. No more consumers allowed.")); + } else if(requestExclusive) { + if(consumerCount) { + throw ResourceLockedException( + QPID_MSG("Queue " << getName() << " already has consumers. Exclusive access denied.")); + } else { + exclusive = c->getSession(); + } + } + consumerCount++; + if (mgmtObject != 0) + mgmtObject->inc_consumerCount (); +} + +void Queue::cancel(Consumer::shared_ptr c){ + removeListener(c); + Mutex::ScopedLock locker(consumerLock); + consumerCount--; + if(exclusive) exclusive = 0; + if (mgmtObject != 0) + mgmtObject->dec_consumerCount (); +} + +QueuedMessage Queue::get(){ + Mutex::ScopedLock locker(messageLock); + QueuedMessage msg(this); + + if(!messages.empty()){ + msg = getFront(); + popMsg(msg); + } + return msg; +} + +void Queue::purgeExpired() +{ + //As expired messages are discarded during dequeue also, only + //bother explicitly expiring if the rate of dequeues since last + //attempt is less than one per second. + if (dequeueTracker.sampleRatePerSecond() < 1) { + Messages expired; + { + Mutex::ScopedLock locker(messageLock); + for (Messages::iterator i = messages.begin(); i != messages.end();) { + if (lastValueQueue) checkLvqReplace(*i); + if (i->payload->hasExpired()) { + expired.push_back(*i); + i = messages.erase(i); + } else { + ++i; + } + } + } + for_each(expired.begin(), expired.end(), bind(&Queue::dequeue, this, (TransactionContext*) 0, _1)); + } +} + +/** + * purge - for purging all or some messages on a queue + * depending on the purge_request + * + * purge_request == 0 then purge all messages + * == N then purge N messages from queue + * Sometimes purge_request == 1 to unblock the top of queue + */ +uint32_t Queue::purge(const uint32_t purge_request){ + Mutex::ScopedLock locker(messageLock); + uint32_t purge_count = purge_request; // only comes into play if >0 + + uint32_t count = 0; + // Either purge them all or just the some (purge_count) while the queue isn't empty. + while((!purge_request || purge_count--) && !messages.empty()) { + popAndDequeue(); + count++; + } + return count; +} + +uint32_t Queue::move(const Queue::shared_ptr destq, uint32_t qty) { + Mutex::ScopedLock locker(messageLock); + uint32_t move_count = qty; // only comes into play if qty >0 + uint32_t count = 0; // count how many were moved for returning + + while((!qty || move_count--) && !messages.empty()) { + QueuedMessage qmsg = getFront(); + boost::intrusive_ptr<Message> msg = qmsg.payload; + destq->deliver(msg); // deliver message to the destination queue + popMsg(qmsg); + dequeue(0, qmsg); + count++; + } + return count; +} + +void Queue::popMsg(QueuedMessage& qmsg) +{ + if (lastValueQueue){ + const framing::FieldTable* ft = qmsg.payload->getApplicationHeaders(); + string key = ft->getAsString(qpidVQMatchProperty); + lvq.erase(key); + } + messages.pop_front(); + ++dequeueTracker; +} + +void Queue::push(boost::intrusive_ptr<Message>& msg){ + QueueListeners::NotificationSet copy; + { + Mutex::ScopedLock locker(messageLock); + QueuedMessage qm(this, msg, ++sequence); + if (policy.get()) policy->tryEnqueue(qm); + + LVQ::iterator i; + if (lastValueQueue){ + const framing::FieldTable* ft = msg->getApplicationHeaders(); + string key = ft->getAsString(qpidVQMatchProperty); + + i = lvq.find(key); + if (i == lvq.end()){ + messages.push_back(qm); + listeners.populate(copy); + lvq[key] = msg; + }else { + i->second->setReplacementMessage(msg,this); + qm.payload = i->second; + dequeued(qm); + } + }else { + messages.push_back(qm); + listeners.populate(copy); + } + } + copy.notify(); +} + +QueuedMessage Queue::getFront() +{ + QueuedMessage msg = messages.front(); + if (lastValueQueue) { + boost::intrusive_ptr<Message> replacement = msg.payload->getReplacementMessage(this); + if (replacement.get()) msg.payload = replacement; + } + return msg; +} + +QueuedMessage& Queue::checkLvqReplace(QueuedMessage& msg) const +{ + boost::intrusive_ptr<Message> replacement = msg.payload->getReplacementMessage(this); + if (replacement.get()) msg.payload = replacement; + return msg; +} + +/** function only provided for unit tests, or code not in critical message path */ +uint32_t Queue::getMessageCount() const +{ + Mutex::ScopedLock locker(messageLock); + + uint32_t count = 0; + for ( Messages::const_iterator i = messages.begin(); i != messages.end(); ++i ) { + //NOTE: don't need to use checkLvqReplace() here as it + //is only relevant for LVQ which does not support persistence + //so the enqueueComplete check has no effect + if ( i->payload->isEnqueueComplete() ) count ++; + } + + return count; +} + +uint32_t Queue::getConsumerCount() const +{ + Mutex::ScopedLock locker(consumerLock); + return consumerCount; +} + +bool Queue::canAutoDelete() const +{ + Mutex::ScopedLock locker(consumerLock); + return autodelete && !consumerCount; +} + +void Queue::clearLastNodeFailure() +{ + inLastNodeFailure = false; +} + +void Queue::setLastNodeFailure() +{ + if (persistLastNode){ + Mutex::ScopedLock locker(messageLock); + for ( Messages::iterator i = messages.begin(); i != messages.end(); ++i ) { + if (lastValueQueue) checkLvqReplace(*i); + i->payload->forcePersistent(); + if (i->payload->getPersistenceId() == 0){ + enqueue(0, i->payload); + } + } + inLastNodeFailure = true; + } +} + +// return true if store exists, +bool Queue::enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message> msg) +{ + if (inLastNodeFailure && persistLastNode){ + msg->forcePersistent(); + } + + if (traceId.size()) { + msg->addTraceId(traceId); + } + + if (msg->isPersistent() && store && !lastValueQueue) { + msg->enqueueAsync(shared_from_this(), store); //increment to async counter -- for message sent to more than one queue + boost::intrusive_ptr<PersistableMessage> pmsg = boost::static_pointer_cast<PersistableMessage>(msg); + store->enqueue(ctxt, pmsg, *this); + return true; + } + return false; +} + +// return true if store exists, +bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg) +{ + if (policy.get() && !policy->isEnqueued(msg)) return false; + { + Mutex::ScopedLock locker(messageLock); + if (!ctxt) { + dequeued(msg); + } + } + if (msg.payload->isPersistent() && store && !lastValueQueue) { + msg.payload->dequeueAsync(shared_from_this(), store); //increment to async counter -- for message sent to more than one queue + boost::intrusive_ptr<PersistableMessage> pmsg = boost::static_pointer_cast<PersistableMessage>(msg.payload); + store->dequeue(ctxt, pmsg, *this); + return true; + } + return false; +} + +void Queue::dequeueCommitted(const QueuedMessage& msg) +{ + Mutex::ScopedLock locker(messageLock); + dequeued(msg); + if (mgmtObject != 0) { + mgmtObject->inc_msgTxnDequeues(); + mgmtObject->inc_byteTxnDequeues(msg.payload->contentSize()); + } +} + +/** + * Removes a message from the in-memory delivery queue as well + * dequeing it from the logical (and persistent if applicable) queue + */ +void Queue::popAndDequeue() +{ + QueuedMessage msg = getFront(); + popMsg(msg); + dequeue(0, msg); +} + +/** + * Updates policy and management when a message has been dequeued, + * expects messageLock to be held + */ +void Queue::dequeued(const QueuedMessage& msg) +{ + if (policy.get()) policy->dequeued(msg); + mgntDeqStats(msg.payload); +} + + +void Queue::create(const FieldTable& _settings) +{ + settings = _settings; + if (store) { + store->create(*this, _settings); + } + configure(_settings); +} + +void Queue::configure(const FieldTable& _settings) +{ + setPolicy(QueuePolicy::createQueuePolicy(_settings)); + //set this regardless of owner to allow use of no-local with exclusive consumers also + noLocal = _settings.get(qpidNoLocal); + QPID_LOG(debug, "Configured queue with no-local=" << noLocal); + + lastValueQueue= _settings.get(qpidLastValueQueue); + if (lastValueQueue) QPID_LOG(debug, "Configured queue as Last Value Queue"); + + lastValueQueueNoBrowse = _settings.get(qpidLastValueQueueNoBrowse); + if (lastValueQueueNoBrowse){ + QPID_LOG(debug, "Configured queue as Last Value Queue No Browse"); + lastValueQueue = lastValueQueueNoBrowse; + } + + persistLastNode= _settings.get(qpidPersistLastNode); + if (persistLastNode) QPID_LOG(debug, "Configured queue to Persist data if cluster fails to one node"); + + traceId = _settings.getAsString(qpidTraceIdentity); + std::string excludeList = _settings.getAsString(qpidTraceExclude); + if (excludeList.size()) { + split(traceExclude, excludeList, ", "); + } + QPID_LOG(debug, "Configured queue " << getName() << " with qpid.trace.id='" << traceId + << "' and qpid.trace.exclude='"<< excludeList << "' i.e. " << traceExclude.size() << " elements"); + + if (mgmtObject != 0) + mgmtObject->set_arguments (_settings); +} + +void Queue::destroy() +{ + if (alternateExchange.get()) { + Mutex::ScopedLock locker(messageLock); + while(!messages.empty()){ + DeliverableMessage msg(getFront().payload); + alternateExchange->route(msg, msg.getMessage().getRoutingKey(), + msg.getMessage().getApplicationHeaders()); + popAndDequeue(); + } + alternateExchange->decAlternateUsers(); + } + + if (store) { + store->flush(*this); + store->destroy(*this); + store = 0;//ensure we make no more calls to the store for this queue + } +} + +void Queue::bound(const string& exchange, const string& key, + const FieldTable& args) +{ + bindings.add(exchange, key, args); +} + +void Queue::unbind(ExchangeRegistry& exchanges, Queue::shared_ptr shared_ref) +{ + bindings.unbind(exchanges, shared_ref); +} + +void Queue::setPolicy(std::auto_ptr<QueuePolicy> _policy) +{ + policy = _policy; +} + +const QueuePolicy* Queue::getPolicy() +{ + return policy.get(); +} + +uint64_t Queue::getPersistenceId() const +{ + return persistenceId; +} + +void Queue::setPersistenceId(uint64_t _persistenceId) const +{ + if (mgmtObject != 0 && persistenceId == 0) + { + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + agent->addObject (mgmtObject, 0x3000000000000000LL + _persistenceId); + + if (externalQueueStore) { + ManagementObject* childObj = externalQueueStore->GetManagementObject(); + if (childObj != 0) + childObj->setReference(mgmtObject->getObjectId()); + } + } + persistenceId = _persistenceId; +} + +void Queue::encode(Buffer& buffer) const +{ + buffer.putShortString(name); + buffer.put(settings); + if (policy.get()) { + buffer.put(*policy); + } +} + +uint32_t Queue::encodedSize() const +{ + return name.size() + 1/*short string size octet*/ + settings.encodedSize() + + (policy.get() ? (*policy).encodedSize() : 0); +} + +Queue::shared_ptr Queue::decode(QueueRegistry& queues, Buffer& buffer) +{ + string name; + buffer.getShortString(name); + std::pair<Queue::shared_ptr, bool> result = queues.declare(name, true); + buffer.get(result.first->settings); + result.first->configure(result.first->settings); + if (result.first->policy.get() && buffer.available() >= result.first->policy->encodedSize()) { + buffer.get ( *(result.first->policy) ); + } + return result.first; +} + + +void Queue::setAlternateExchange(boost::shared_ptr<Exchange> exchange) +{ + alternateExchange = exchange; +} + +boost::shared_ptr<Exchange> Queue::getAlternateExchange() +{ + return alternateExchange; +} + +void Queue::tryAutoDelete(Broker& broker, Queue::shared_ptr queue) +{ + if (broker.getQueues().destroyIf(queue->getName(), + boost::bind(boost::mem_fn(&Queue::canAutoDelete), queue))) { + queue->unbind(broker.getExchanges(), queue); + queue->destroy(); + } +} + +bool Queue::isExclusiveOwner(const OwnershipToken* const o) const +{ + Mutex::ScopedLock locker(ownershipLock); + return o == owner; +} + +void Queue::releaseExclusiveOwnership() +{ + Mutex::ScopedLock locker(ownershipLock); + owner = 0; +} + +bool Queue::setExclusiveOwner(const OwnershipToken* const o) +{ + Mutex::ScopedLock locker(ownershipLock); + if (owner) { + return false; + } else { + owner = o; + return true; + } +} + +bool Queue::hasExclusiveOwner() const +{ + Mutex::ScopedLock locker(ownershipLock); + return owner != 0; +} + +bool Queue::hasExclusiveConsumer() const +{ + return exclusive; +} + +void Queue::setExternalQueueStore(ExternalQueueStore* inst) { + if (externalQueueStore!=inst && externalQueueStore) + delete externalQueueStore; + externalQueueStore = inst; + + if (inst) { + ManagementObject* childObj = inst->GetManagementObject(); + if (childObj != 0 && mgmtObject != 0) + childObj->setReference(mgmtObject->getObjectId()); + } +} + +bool Queue::releaseMessageContent(const QueuedMessage& m) +{ + if (store && !NullMessageStore::isNullStore(store)) { + QPID_LOG(debug, "Message " << m.position << " on " << name << " released from memory"); + m.payload->releaseContent(store); + return true; + } else { + QPID_LOG(warning, "Message " << m.position << " on " << name + << " cannot be released from memory as the queue is not durable"); + return false; + } +} + +ManagementObject* Queue::GetManagementObject (void) const +{ + return (ManagementObject*) mgmtObject; +} + +Manageable::status_t Queue::ManagementMethod (uint32_t methodId, Args& args, string&) +{ + Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; + + QPID_LOG (debug, "Queue::ManagementMethod [id=" << methodId << "]"); + + switch (methodId) + { + case _qmf::Queue::METHOD_PURGE : + _qmf::ArgsQueuePurge& iargs = (_qmf::ArgsQueuePurge&) args; + purge (iargs.i_request); + status = Manageable::STATUS_OK; + break; + } + + return status; +} + +void Queue::setPosition(SequenceNumber n) { + Mutex::ScopedLock locker(messageLock); + sequence = n; +} diff --git a/RC9/qpid/cpp/src/qpid/broker/Queue.h b/RC9/qpid/cpp/src/qpid/broker/Queue.h new file mode 100644 index 0000000000..e0bcc25fa3 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Queue.h @@ -0,0 +1,287 @@ +#ifndef _broker_Queue_h +#define _broker_Queue_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "OwnershipToken.h" +#include "Consumer.h" +#include "Message.h" +#include "PersistableQueue.h" +#include "QueuePolicy.h" +#include "QueueBindings.h" +#include "QueueListeners.h" +#include "RateTracker.h" + +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/Monitor.h" +#include "qpid/management/Manageable.h" +#include "qmf/org/apache/qpid/broker/Queue.h" +#include "qpid/framing/amqp_types.h" + +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/intrusive_ptr.hpp> + +#include <list> +#include <vector> +#include <memory> +#include <deque> +#include <algorithm> + +namespace qpid { + namespace broker { + class Broker; + class MessageStore; + class QueueRegistry; + class TransactionContext; + class Exchange; + + using std::string; + + /** + * The brokers representation of an amqp queue. Messages are + * delivered to a queue from where they can be dispatched to + * registered consumers or be stored until dequeued or until one + * or more consumers registers. + */ + class Queue : public boost::enable_shared_from_this<Queue>, + public PersistableQueue, public management::Manageable { + + typedef std::deque<QueuedMessage> Messages; + typedef std::map<string,boost::intrusive_ptr<Message> > LVQ; + + const string name; + const bool autodelete; + MessageStore* store; + const OwnershipToken* owner; + uint32_t consumerCount; + OwnershipToken* exclusive; + bool noLocal; + bool lastValueQueue; + bool lastValueQueueNoBrowse; + bool persistLastNode; + bool inLastNodeFailure; + std::string traceId; + std::vector<std::string> traceExclude; + QueueListeners listeners; + Messages messages; + LVQ lvq; + mutable qpid::sys::Mutex consumerLock; + mutable qpid::sys::Mutex messageLock; + mutable qpid::sys::Mutex ownershipLock; + mutable uint64_t persistenceId; + framing::FieldTable settings; + std::auto_ptr<QueuePolicy> policy; + bool policyExceeded; + QueueBindings bindings; + boost::shared_ptr<Exchange> alternateExchange; + framing::SequenceNumber sequence; + qmf::org::apache::qpid::broker::Queue* mgmtObject; + RateTracker dequeueTracker; + + void push(boost::intrusive_ptr<Message>& msg); + void setPolicy(std::auto_ptr<QueuePolicy> policy); + bool seek(QueuedMessage& msg, Consumer::shared_ptr position); + bool getNextMessage(QueuedMessage& msg, Consumer::shared_ptr c); + bool consumeNextMessage(QueuedMessage& msg, Consumer::shared_ptr c); + bool browseNextMessage(QueuedMessage& msg, Consumer::shared_ptr c); + + void removeListener(Consumer::shared_ptr); + + bool isExcluded(boost::intrusive_ptr<Message>& msg); + + void dequeued(const QueuedMessage& msg); + void popAndDequeue(); + QueuedMessage getFront(); + QueuedMessage& checkLvqReplace(QueuedMessage& msg) const; + void clearLVQIndex(const QueuedMessage& msg); + + inline void mgntEnqStats(const boost::intrusive_ptr<Message>& msg) + { + if (mgmtObject != 0) { + mgmtObject->inc_msgTotalEnqueues (); + mgmtObject->inc_byteTotalEnqueues (msg->contentSize ()); + if (msg->isPersistent ()) { + mgmtObject->inc_msgPersistEnqueues (); + mgmtObject->inc_bytePersistEnqueues (msg->contentSize ()); + } + } + } + inline void mgntDeqStats(const boost::intrusive_ptr<Message>& msg) + { + if (mgmtObject != 0){ + mgmtObject->inc_msgTotalDequeues (); + mgmtObject->inc_byteTotalDequeues (msg->contentSize()); + if (msg->isPersistent ()){ + mgmtObject->inc_msgPersistDequeues (); + mgmtObject->inc_bytePersistDequeues (msg->contentSize()); + } + } + } + + public: + + virtual void notifyDurableIOComplete(); + typedef boost::shared_ptr<Queue> shared_ptr; + + typedef std::vector<shared_ptr> vector; + + Queue(const string& name, bool autodelete = false, + MessageStore* const store = 0, + const OwnershipToken* const owner = 0, + management::Manageable* parent = 0); + ~Queue(); + + bool dispatch(Consumer::shared_ptr); + /** + * Check whether there would be a message available for + * dispatch to this consumer. If not, the consumer will be + * notified of events that may have changed this + * situation. + */ + bool checkForMessages(Consumer::shared_ptr); + + void create(const qpid::framing::FieldTable& settings); + void configure(const qpid::framing::FieldTable& settings); + void destroy(); + void bound(const string& exchange, const string& key, const qpid::framing::FieldTable& args); + void unbind(ExchangeRegistry& exchanges, Queue::shared_ptr shared_ref); + + bool acquire(const QueuedMessage& msg); + + /** + * Delivers a message to the queue. Will record it as + * enqueued if persistent then process it. + */ + void deliver(boost::intrusive_ptr<Message>& msg); + /** + * Dispatches the messages immediately to a consumer if + * one is available or stores it for later if not. + */ + void process(boost::intrusive_ptr<Message>& msg); + /** + * Returns a message to the in-memory queue (due to lack + * of acknowledegement from a receiver). If a consumer is + * available it will be dispatched immediately, else it + * will be returned to the front of the queue. + */ + void requeue(const QueuedMessage& msg); + /** + * Used during recovery to add stored messages back to the queue + */ + void recover(boost::intrusive_ptr<Message>& msg); + + void consume(Consumer::shared_ptr c, bool exclusive = false); + void cancel(Consumer::shared_ptr c); + + uint32_t purge(const uint32_t purge_request = 0); //defaults to all messages + void purgeExpired(); + + //move qty # of messages to destination Queue destq + uint32_t move(const Queue::shared_ptr destq, uint32_t qty); + + uint32_t getMessageCount() const; + uint32_t getConsumerCount() const; + inline const string& getName() const { return name; } + bool isExclusiveOwner(const OwnershipToken* const o) const; + void releaseExclusiveOwnership(); + bool setExclusiveOwner(const OwnershipToken* const o); + bool hasExclusiveConsumer() const; + bool hasExclusiveOwner() const; + inline bool isDurable() const { return store != 0; } + inline const framing::FieldTable& getSettings() const { return settings; } + inline bool isAutoDelete() const { return autodelete; } + bool canAutoDelete() const; + const QueueBindings& getBindings() const { return bindings; } + + /** + * used to take messages from in memory and flush down to disk. + */ + void setLastNodeFailure(); + void clearLastNodeFailure(); + + bool enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message> msg); + /** + * dequeue from store (only done once messages is acknowledged) + */ + bool dequeue(TransactionContext* ctxt, const QueuedMessage &msg); + /** + * Inform the queue that a previous transactional dequeue + * committed. + */ + void dequeueCommitted(const QueuedMessage& msg); + + /** + * Gets the next available message + */ + QueuedMessage get(); + + /** Get the message at position pos */ + QueuedMessage find(framing::SequenceNumber pos) const; + + const QueuePolicy* getPolicy(); + + void setAlternateExchange(boost::shared_ptr<Exchange> exchange); + boost::shared_ptr<Exchange> getAlternateExchange(); + bool isLocal(boost::intrusive_ptr<Message>& msg); + + //PersistableQueue support: + uint64_t getPersistenceId() const; + void setPersistenceId(uint64_t persistenceId) const; + void encode(framing::Buffer& buffer) const; + uint32_t encodedSize() const; + + static Queue::shared_ptr decode(QueueRegistry& queues, framing::Buffer& buffer); + static void tryAutoDelete(Broker& broker, Queue::shared_ptr); + + virtual void setExternalQueueStore(ExternalQueueStore* inst); + + // Manageable entry points + management::ManagementObject* GetManagementObject (void) const; + management::Manageable::status_t + ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); + + /** Apply f to each Message on the queue. */ + template <class F> void eachMessage(F f) const { + sys::Mutex::ScopedLock l(messageLock); + std::for_each(messages.begin(), messages.end(), f); + } + + /** Apply f to each QueueBinding on the queue */ + template <class F> void eachBinding(F f) { + bindings.eachBinding(f); + } + + bool releaseMessageContent(const QueuedMessage&); + + void popMsg(QueuedMessage& qmsg); + + /** Set the position sequence number for the next message on the queue. + * Must be >= the current sequence number. + * Used by cluster to replicate queues. + */ + void setPosition(framing::SequenceNumber pos); + }; + } +} + + +#endif /*!_broker_Queue_h*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/QueueBindings.cpp b/RC9/qpid/cpp/src/qpid/broker/QueueBindings.cpp new file mode 100644 index 0000000000..6a1fa6aca3 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/QueueBindings.cpp @@ -0,0 +1,46 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "QueueBindings.h" +#include "ExchangeRegistry.h" +#include "qpid/framing/reply_exceptions.h" + +using qpid::framing::FieldTable; +using qpid::framing::NotFoundException; +using std::string; +using namespace qpid::broker; + +void QueueBindings::add(const string& exchange, const string& key, const FieldTable& args) +{ + bindings.push_back(QueueBinding(exchange, key, args)); +} + +void QueueBindings::unbind(ExchangeRegistry& exchanges, Queue::shared_ptr queue) +{ + for (Bindings::iterator i = bindings.begin(); i != bindings.end(); i++) { + try { + exchanges.get(i->exchange)->unbind(queue, i->key, &(i->args)); + } catch (const NotFoundException&) {} + } +} + +QueueBinding::QueueBinding(const string& _exchange, const string& _key, const FieldTable& _args) + : exchange(_exchange), key(_key), args(_args) +{} diff --git a/RC9/qpid/cpp/src/qpid/broker/QueueBindings.h b/RC9/qpid/cpp/src/qpid/broker/QueueBindings.h new file mode 100644 index 0000000000..1b90ba5540 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/QueueBindings.h @@ -0,0 +1,61 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _QueueBindings_ +#define _QueueBindings_ + +#include "qpid/framing/FieldTable.h" +#include <boost/ptr_container/ptr_list.hpp> +#include <boost/shared_ptr.hpp> +#include <algorithm> + +namespace qpid { +namespace broker { + +class ExchangeRegistry; +class Queue; + +struct QueueBinding{ + std::string exchange; + std::string key; + qpid::framing::FieldTable args; + QueueBinding(const std::string& exchange, const std::string& key, const qpid::framing::FieldTable& args); +}; + +class QueueBindings +{ + public: + + /** Apply f to each QueueBinding. */ + template <class F> void eachBinding(F f) const { std::for_each(bindings.begin(), bindings.end(), f); } + + void add(const std::string& exchange, const std::string& key, const qpid::framing::FieldTable& args); + void unbind(ExchangeRegistry& exchanges, boost::shared_ptr<Queue> queue); + + private: + typedef std::vector<QueueBinding> Bindings; + Bindings bindings; +}; + + +}} // namespace qpid::broker + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/QueueCleaner.cpp b/RC9/qpid/cpp/src/qpid/broker/QueueCleaner.cpp new file mode 100644 index 0000000000..0774dce2b7 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/QueueCleaner.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 "QueueCleaner.h" + +#include "Broker.h" +#include <boost/bind.hpp> + +namespace qpid { +namespace broker { + +QueueCleaner::QueueCleaner(QueueRegistry& q, Timer& t) : queues(q), timer(t) {} + +void QueueCleaner::start(qpid::sys::Duration p) +{ + task = boost::intrusive_ptr<TimerTask>(new Task(*this, p)); + timer.add(task); +} + +QueueCleaner::Task::Task(QueueCleaner& p, qpid::sys::Duration d) : TimerTask(d), parent(p) {} + +void QueueCleaner::Task::fire() +{ + parent.fired(); +} + +void QueueCleaner::fired() +{ + queues.eachQueue(boost::bind(&Queue::purgeExpired, _1)); + task->reset(); + timer.add(task); +} + + +}} // namespace qpid::broker diff --git a/RC9/qpid/cpp/src/qpid/broker/QueueCleaner.h b/RC9/qpid/cpp/src/qpid/broker/QueueCleaner.h new file mode 100644 index 0000000000..7903266f5f --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/QueueCleaner.h @@ -0,0 +1,57 @@ +#ifndef QPID_BROKER_QUEUECLEANER_H +#define QPID_BROKER_QUEUECLEANER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Timer.h" + +namespace qpid { +namespace broker { + +class QueueRegistry; +/** + * TimerTask to purge expired messages from queues + */ +class QueueCleaner +{ + public: + QueueCleaner(QueueRegistry& queues, Timer& timer); + void start(qpid::sys::Duration period); + private: + class Task : public TimerTask + { + public: + Task(QueueCleaner& parent, qpid::sys::Duration duration); + void fire(); + private: + QueueCleaner& parent; + }; + + boost::intrusive_ptr<TimerTask> task; + QueueRegistry& queues; + Timer& timer; + + void fired(); +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_QUEUECLEANER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/QueueListeners.cpp b/RC9/qpid/cpp/src/qpid/broker/QueueListeners.cpp new file mode 100644 index 0000000000..7baca7d0f4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/QueueListeners.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 "QueueListeners.h" +#include <boost/bind.hpp> + +namespace qpid { +namespace broker { + +void QueueListeners::addListener(Consumer::shared_ptr c) +{ + if (c->preAcquires()) { + add(consumers, c); + } else { + add(browsers, c); + } +} + +void QueueListeners::removeListener(Consumer::shared_ptr c) +{ + if (c->preAcquires()) { + remove(consumers, c); + } else { + remove(browsers, c); + } +} + +void QueueListeners::populate(NotificationSet& set) +{ + if (consumers.size()) { + set.consumer = consumers.front(); + consumers.pop_front(); + } else { + browsers.swap(set.browsers); + } +} + +void QueueListeners::add(Listeners& listeners, Consumer::shared_ptr c) +{ + Listeners::iterator i = std::find(listeners.begin(), listeners.end(), c); + if (i == listeners.end()) listeners.push_back(c); +} + +void QueueListeners::remove(Listeners& listeners, Consumer::shared_ptr c) +{ + Listeners::iterator i = std::find(listeners.begin(), listeners.end(), c); + if (i != listeners.end()) listeners.erase(i); +} + +void QueueListeners::NotificationSet::notify() +{ + if (consumer) consumer->notify(); + else for_each(browsers.begin(), browsers.end(), boost::mem_fn(&Consumer::notify)); +} + +}} // namespace qpid::broker diff --git a/RC9/qpid/cpp/src/qpid/broker/QueueListeners.h b/RC9/qpid/cpp/src/qpid/broker/QueueListeners.h new file mode 100644 index 0000000000..53ed6a17e4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/QueueListeners.h @@ -0,0 +1,68 @@ +#ifndef QPID_BROKER_QUEUELISTENERS_H +#define QPID_BROKER_QUEUELISTENERS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Consumer.h" +#include <list> + +namespace qpid { +namespace broker { + +/** + * Track and notify components that wish to be notified of messages + * that become available on a queue. + * + * None of the methods defined here are protected by locking. However + * the populate method allows a 'snapshot' to be taken of the + * listeners to be notified. NotificationSet::notify() may then be + * called outside of any lock that protects the QueueListeners + * instance from concurrent access. + */ +class QueueListeners +{ + public: + typedef std::list<Consumer::shared_ptr> Listeners; + + class NotificationSet + { + public: + void notify(); + private: + Listeners browsers; + Consumer::shared_ptr consumer; + friend class QueueListeners; + }; + + void addListener(Consumer::shared_ptr); + void removeListener(Consumer::shared_ptr); + void populate(NotificationSet&); + private: + Listeners consumers; + Listeners browsers; + + void add(Listeners&, Consumer::shared_ptr); + void remove(Listeners&, Consumer::shared_ptr); + +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_QUEUELISTENERS_H*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/QueuePolicy.cpp b/RC9/qpid/cpp/src/qpid/broker/QueuePolicy.cpp new file mode 100644 index 0000000000..41a6709d27 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/QueuePolicy.cpp @@ -0,0 +1,297 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "QueuePolicy.h" +#include "Queue.h" +#include "qpid/Exception.h" +#include "qpid/framing/FieldValue.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Statement.h" + +using namespace qpid::broker; +using namespace qpid::framing; + +QueuePolicy::QueuePolicy(uint32_t _maxCount, uint64_t _maxSize, const std::string& _type) : + maxCount(_maxCount), maxSize(_maxSize), type(_type), count(0), size(0), policyExceeded(false) {} + +void QueuePolicy::enqueued(uint64_t _size) +{ + if (maxCount) ++count; + if (maxSize) size += _size; +} + +void QueuePolicy::dequeued(uint64_t _size) +{ + //Note: underflow detection is not reliable in the face of + //concurrent updates (at present locking in Queue.cpp prevents + //these anyway); updates are atomic and are safe regardless. + if (maxCount) { + if (count.get() > 0) { + --count; + } else { + throw Exception(QPID_MSG("Attempted count underflow on dequeue(" << _size << "): " << *this)); + } + } + if (maxSize) { + if (_size > size.get()) { + throw Exception(QPID_MSG("Attempted size underflow on dequeue(" << _size << "): " << *this)); + } else { + size -= _size; + } + } +} + +bool QueuePolicy::checkLimit(const QueuedMessage& m) +{ + bool exceeded = (maxSize && (size.get() + m.payload->contentSize()) > maxSize) || (maxCount && (count.get() + 1) > maxCount); + if (exceeded) { + if (!policyExceeded) { + policyExceeded = true; + if (m.queue) { + QPID_LOG(info, "Queue size exceeded policy for " << m.queue->getName()); + } + } + } else { + if (policyExceeded) { + policyExceeded = false; + if (m.queue) { + QPID_LOG(info, "Queue size within policy for " << m.queue->getName()); + } + } + } + return !exceeded; +} + +void QueuePolicy::tryEnqueue(const QueuedMessage& m) +{ + if (checkLimit(m)) { + enqueued(m); + } else { + std::string queue = m.queue ? m.queue->getName() : std::string("unknown queue"); + throw ResourceLimitExceededException( + QPID_MSG("Policy exceeded on " << queue << " by message " << m.position + << " of size " << m.payload->contentSize() << " , policy: " << *this)); + } +} + +void QueuePolicy::enqueued(const QueuedMessage& m) +{ + enqueued(m.payload->contentSize()); +} + +void QueuePolicy::dequeued(const QueuedMessage& m) +{ + dequeued(m.payload->contentSize()); +} + +bool QueuePolicy::isEnqueued(const QueuedMessage&) +{ + return true; +} + +void QueuePolicy::update(FieldTable& settings) +{ + if (maxCount) settings.setInt(maxCountKey, maxCount); + if (maxSize) settings.setInt(maxSizeKey, maxSize); + settings.setString(typeKey, type); +} + + +int QueuePolicy::getInt(const FieldTable& settings, const std::string& key, int defaultValue) +{ + FieldTable::ValuePtr v = settings.get(key); + if (v && v->convertsTo<int>()) return v->get<int>(); + else return defaultValue; +} + +std::string QueuePolicy::getType(const FieldTable& settings) +{ + FieldTable::ValuePtr v = settings.get(typeKey); + if (v && v->convertsTo<std::string>()) { + std::string t = v->get<std::string>(); + transform(t.begin(), t.end(), t.begin(), tolower); + if (t == REJECT || t == FLOW_TO_DISK || t == RING || t == RING_STRICT) return t; + } + return FLOW_TO_DISK; +} + +void QueuePolicy::setDefaultMaxSize(uint64_t s) +{ + defaultMaxSize = s; +} + + + + + +void QueuePolicy::encode(Buffer& buffer) const +{ + buffer.putLong(maxCount); + buffer.putLongLong(maxSize); + buffer.putLong(count.get()); + buffer.putLongLong(size.get()); +} + +void QueuePolicy::decode ( Buffer& buffer ) +{ + maxCount = buffer.getLong(); + maxSize = buffer.getLongLong(); + count = buffer.getLong(); + size = buffer.getLongLong(); +} + + +uint32_t QueuePolicy::encodedSize() const { + return sizeof(uint32_t) + // maxCount + sizeof(uint64_t) + // maxSize + sizeof(uint32_t) + // count + sizeof(uint64_t); // size +} + + + +const std::string QueuePolicy::maxCountKey("qpid.max_count"); +const std::string QueuePolicy::maxSizeKey("qpid.max_size"); +const std::string QueuePolicy::typeKey("qpid.policy_type"); +const std::string QueuePolicy::REJECT("reject"); +const std::string QueuePolicy::FLOW_TO_DISK("flow_to_disk"); +const std::string QueuePolicy::RING("ring"); +const std::string QueuePolicy::RING_STRICT("ring_strict"); +uint64_t QueuePolicy::defaultMaxSize(0); + +FlowToDiskPolicy::FlowToDiskPolicy(uint32_t _maxCount, uint64_t _maxSize) : + QueuePolicy(_maxCount, _maxSize, FLOW_TO_DISK) {} + +bool FlowToDiskPolicy::checkLimit(const QueuedMessage& m) +{ + return QueuePolicy::checkLimit(m) || m.queue->releaseMessageContent(m); +} + +RingQueuePolicy::RingQueuePolicy(uint32_t _maxCount, uint64_t _maxSize, const std::string& _type) : + QueuePolicy(_maxCount, _maxSize, _type), strict(_type == RING_STRICT) {} + +void RingQueuePolicy::enqueued(const QueuedMessage& m) +{ + QueuePolicy::enqueued(m); + qpid::sys::Mutex::ScopedLock l(lock); + queue.push_back(m); +} + +void RingQueuePolicy::dequeued(const QueuedMessage& m) +{ + qpid::sys::Mutex::ScopedLock l(lock); + QueuePolicy::dequeued(m); + //find and remove m from queue + for (Messages::iterator i = queue.begin(); i != queue.end() && m.position <= i->position; i++) { + if (i->position == m.position) { + queue.erase(i); + break; + } + } +} + +bool RingQueuePolicy::isEnqueued(const QueuedMessage& m) +{ + qpid::sys::Mutex::ScopedLock l(lock); + //for non-strict ring policy, a message can be dequeued before acked; need to detect this + for (Messages::iterator i = queue.begin(); i != queue.end() && m.position <= i->position; i++) { + if (i->position == m.position) { + return true; + } + } + return false; +} + +bool RingQueuePolicy::checkLimit(const QueuedMessage& m) +{ + if (QueuePolicy::checkLimit(m)) return true;//if haven't hit limit, ok to accept + + QueuedMessage oldest; + { + qpid::sys::Mutex::ScopedLock l(lock); + if (queue.empty()) { + QPID_LOG(debug, "Message too large for ring queue " + << (m.queue ? m.queue->getName() : std::string("unknown queue")) + << " [" << *this << "] " + << ": message size = " << m.payload->contentSize() << " bytes"); + return false; + } + oldest = queue.front(); + } + if (oldest.queue->acquire(oldest) || !strict) { + qpid::sys::Mutex::ScopedLock l(lock); + if (oldest.position == queue.front().position) { + queue.pop_front(); + QPID_LOG(debug, "Ring policy triggered in queue " + << (m.queue ? m.queue->getName() : std::string("unknown queue")) + << ": removed message " << oldest.position << " to make way for " << m.position); + } + return true; + } else { + QPID_LOG(debug, "Ring policy could not be triggered in queue " + << (m.queue ? m.queue->getName() : std::string("unknown queue")) + << ": oldest message (seq-no=" << oldest.position << ") has been delivered but not yet acknowledged or requeued"); + //in strict mode, if oldest message has been delivered (hence + //cannot be acquired) but not yet acked, it should not be + //removed and the attempted enqueue should fail + return false; + } +} + +std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(const qpid::framing::FieldTable& settings) +{ + uint32_t maxCount = getInt(settings, maxCountKey, 0); + uint32_t maxSize = getInt(settings, maxSizeKey, defaultMaxSize); + if (maxCount || maxSize) { + return createQueuePolicy(maxCount, maxSize, getType(settings)); + } else { + return std::auto_ptr<QueuePolicy>(); + } +} + +std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(uint32_t maxCount, uint64_t maxSize, const std::string& type) +{ + if (type == RING || type == RING_STRICT) { + return std::auto_ptr<QueuePolicy>(new RingQueuePolicy(maxCount, maxSize, type)); + } else if (type == FLOW_TO_DISK) { + return std::auto_ptr<QueuePolicy>(new FlowToDiskPolicy(maxCount, maxSize)); + } else { + return std::auto_ptr<QueuePolicy>(new QueuePolicy(maxCount, maxSize, type)); + } + +} + +namespace qpid { + namespace broker { + +std::ostream& operator<<(std::ostream& out, const QueuePolicy& p) +{ + if (p.maxSize) out << "size: max=" << p.maxSize << ", current=" << p.size.get(); + else out << "size: unlimited"; + out << "; "; + if (p.maxCount) out << "count: max=" << p.maxCount << ", current=" << p.count.get(); + else out << "count: unlimited"; + out << "; type=" << p.type; + return out; +} + + } +} + diff --git a/RC9/qpid/cpp/src/qpid/broker/QueuePolicy.h b/RC9/qpid/cpp/src/qpid/broker/QueuePolicy.h new file mode 100644 index 0000000000..0e8c15aa0e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/QueuePolicy.h @@ -0,0 +1,109 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _QueuePolicy_ +#define _QueuePolicy_ + +#include <deque> +#include <iostream> +#include <memory> +#include "QueuedMessage.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/AtomicValue.h" +#include "qpid/sys/Mutex.h" + +namespace qpid { +namespace broker { + +class QueuePolicy +{ + static uint64_t defaultMaxSize; + + uint32_t maxCount; + uint64_t maxSize; + const std::string type; + qpid::sys::AtomicValue<uint32_t> count; + qpid::sys::AtomicValue<uint64_t> size; + bool policyExceeded; + + static int getInt(const qpid::framing::FieldTable& settings, const std::string& key, int defaultValue); + static std::string getType(const qpid::framing::FieldTable& settings); + + public: + static const std::string maxCountKey; + static const std::string maxSizeKey; + static const std::string typeKey; + static const std::string REJECT; + static const std::string FLOW_TO_DISK; + static const std::string RING; + static const std::string RING_STRICT; + + virtual ~QueuePolicy() {} + void tryEnqueue(const QueuedMessage&); + virtual void dequeued(const QueuedMessage&); + virtual bool isEnqueued(const QueuedMessage&); + virtual bool checkLimit(const QueuedMessage&); + void update(qpid::framing::FieldTable& settings); + uint32_t getMaxCount() const { return maxCount; } + uint64_t getMaxSize() const { return maxSize; } + void encode(framing::Buffer& buffer) const; + void decode ( framing::Buffer& buffer ); + uint32_t encodedSize() const; + + + static std::auto_ptr<QueuePolicy> createQueuePolicy(const qpid::framing::FieldTable& settings); + static std::auto_ptr<QueuePolicy> createQueuePolicy(uint32_t maxCount, uint64_t maxSize, const std::string& type = REJECT); + static void setDefaultMaxSize(uint64_t); + friend std::ostream& operator<<(std::ostream&, const QueuePolicy&); + protected: + QueuePolicy(uint32_t maxCount, uint64_t maxSize, const std::string& type = REJECT); + + virtual void enqueued(const QueuedMessage&); + void enqueued(uint64_t size); + void dequeued(uint64_t size); +}; + + +class FlowToDiskPolicy : public QueuePolicy +{ + public: + FlowToDiskPolicy(uint32_t maxCount, uint64_t maxSize); + bool checkLimit(const QueuedMessage&); +}; + +class RingQueuePolicy : public QueuePolicy +{ + public: + RingQueuePolicy(uint32_t maxCount, uint64_t maxSize, const std::string& type = RING); + void enqueued(const QueuedMessage&); + void dequeued(const QueuedMessage&); + bool isEnqueued(const QueuedMessage&); + bool checkLimit(const QueuedMessage&); + private: + typedef std::deque<QueuedMessage> Messages; + qpid::sys::Mutex lock; + Messages queue; + const bool strict; +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/QueueRegistry.cpp b/RC9/qpid/cpp/src/qpid/broker/QueueRegistry.cpp new file mode 100644 index 0000000000..2447ce5402 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/QueueRegistry.cpp @@ -0,0 +1,107 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "QueueRegistry.h" +#include "qpid/log/Statement.h" +#include <sstream> +#include <assert.h> + +using namespace qpid::broker; +using namespace qpid::sys; + +QueueRegistry::QueueRegistry() : + counter(1), store(0), parent(0), lastNode(false) {} + +QueueRegistry::~QueueRegistry(){} + +std::pair<Queue::shared_ptr, bool> +QueueRegistry::declare(const string& declareName, bool durable, + bool autoDelete, const OwnershipToken* owner) +{ + RWlock::ScopedWlock locker(lock); + string name = declareName.empty() ? generateName() : declareName; + assert(!name.empty()); + QueueMap::iterator i = queues.find(name); + + if (i == queues.end()) { + Queue::shared_ptr queue(new Queue(name, autoDelete, durable ? store : 0, owner, parent)); + queues[name] = queue; + if (lastNode) queue->setLastNodeFailure(); + + return std::pair<Queue::shared_ptr, bool>(queue, true); + } else { + return std::pair<Queue::shared_ptr, bool>(i->second, false); + } +} + +void QueueRegistry::destroyLH (const string& name){ + queues.erase(name); +} + +void QueueRegistry::destroy (const string& name){ + RWlock::ScopedWlock locker(lock); + destroyLH (name); +} + +Queue::shared_ptr QueueRegistry::find(const string& name){ + RWlock::ScopedRlock locker(lock); + QueueMap::iterator i = queues.find(name); + + if (i == queues.end()) { + return Queue::shared_ptr(); + } else { + return i->second; + } +} + +string QueueRegistry::generateName(){ + string name; + do { + std::stringstream ss; + ss << "tmp_" << counter++; + name = ss.str(); + // Thread safety: Private function, only called with lock held + // so this is OK. + } while(queues.find(name) != queues.end()); + return name; +} + +void QueueRegistry::setStore (MessageStore* _store) +{ + store = _store; +} + +MessageStore* QueueRegistry::getStore() const { + return store; +} + +void QueueRegistry::updateQueueClusterState(bool _lastNode) +{ + RWlock::ScopedRlock locker(lock); + for (QueueMap::iterator i = queues.begin(); i != queues.end(); i++) { + if (_lastNode){ + i->second->setLastNodeFailure(); + } else { + i->second->clearLastNodeFailure(); + } + } + lastNode = _lastNode; +} + diff --git a/RC9/qpid/cpp/src/qpid/broker/QueueRegistry.h b/RC9/qpid/cpp/src/qpid/broker/QueueRegistry.h new file mode 100644 index 0000000000..ca2cd132c4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/QueueRegistry.h @@ -0,0 +1,134 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _QueueRegistry_ +#define _QueueRegistry_ + +#include "Queue.h" +#include "qpid/sys/Mutex.h" +#include "qpid/management/Manageable.h" +#include <boost/bind.hpp> +#include <algorithm> +#include <map> + +namespace qpid { +namespace broker { + +/** + * A registry of queues indexed by queue name. + * + * Queues are reference counted using shared_ptr to ensure that they + * are deleted when and only when they are no longer in use. + * + */ +class QueueRegistry{ + public: + QueueRegistry(); + ~QueueRegistry(); + + /** + * Declare a queue. + * + * @return The queue and a boolean flag which is true if the queue + * was created by this declare call false if it already existed. + */ + std::pair<Queue::shared_ptr, bool> declare(const string& name, bool durable = false, bool autodelete = false, + const OwnershipToken* owner = 0); + + /** + * Destroy the named queue. + * + * Note: if the queue is in use it is not actually destroyed until + * all shared_ptrs to it are destroyed. During that time it is + * possible that a new queue with the same name may be + * created. This should not create any problems as the new and + * old queues exist independently. The registry has + * forgotten the old queue so there can be no confusion for + * subsequent calls to find or declare with the same name. + * + */ + void destroy (const string& name); + template <class Test> bool destroyIf(const string& name, Test test) + { + qpid::sys::RWlock::ScopedWlock locker(lock); + if (test()) { + destroyLH (name); + return true; + } else { + return false; + } + } + + /** + * Find the named queue. Return 0 if not found. + */ + Queue::shared_ptr find(const string& name); + + /** + * Generate unique queue name. + */ + string generateName(); + + /** + * Set the store to use. May only be called once. + */ + void setStore (MessageStore*); + + /** + * Return the message store used. + */ + MessageStore* getStore() const; + + /** + * Register the manageable parent for declared queues + */ + void setParent (management::Manageable* _parent) { parent = _parent; } + + /** Call f for each queue in the registry. */ + template <class F> void eachQueue(F f) const { + qpid::sys::RWlock::ScopedWlock l(lock); + for (QueueMap::const_iterator i = queues.begin(); i != queues.end(); ++i) + f(i->second); + } + + /** + * Change queue mode when cluster size drops to 1 node, expands again + * in practice allows flow queue to disk when last name to be exectuted + */ + void updateQueueClusterState(bool lastNode); + +private: + typedef std::map<string, Queue::shared_ptr> QueueMap; + QueueMap queues; + mutable qpid::sys::RWlock lock; + int counter; + MessageStore* store; + management::Manageable* parent; + bool lastNode; //used to set mode on queue declare + + //destroy impl that assumes lock is already held: + void destroyLH (const string& name); +}; + + +}} // namespace qpid::broker + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/QueuedMessage.h b/RC9/qpid/cpp/src/qpid/broker/QueuedMessage.h new file mode 100644 index 0000000000..82f5073d87 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/QueuedMessage.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 _QueuedMessage_ +#define _QueuedMessage_ + +#include "Message.h" + +namespace qpid { +namespace broker { + +class Queue; + +struct QueuedMessage +{ + boost::intrusive_ptr<Message> payload; + framing::SequenceNumber position; + Queue* queue; + + QueuedMessage() : queue(0) {} + QueuedMessage(Queue* q, boost::intrusive_ptr<Message> msg, framing::SequenceNumber sn) : + payload(msg), position(sn), queue(q) {} + QueuedMessage(Queue* q) : queue(q) {} +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/RateTracker.cpp b/RC9/qpid/cpp/src/qpid/broker/RateTracker.cpp new file mode 100644 index 0000000000..fc88d99cfa --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/RateTracker.cpp @@ -0,0 +1,51 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "RateTracker.h" + +using qpid::sys::AbsTime; +using qpid::sys::Duration; +using qpid::sys::TIME_SEC; + +namespace qpid { +namespace broker { + +RateTracker::RateTracker() : currentCount(0), lastCount(0), lastTime(AbsTime::now()) {} + +RateTracker& RateTracker::operator++() +{ + ++currentCount; + return *this; +} + +double RateTracker::sampleRatePerSecond() +{ + int32_t increment = currentCount - lastCount; + AbsTime now = AbsTime::now(); + Duration interval(lastTime, now); + lastCount = currentCount; + lastTime = now; + //if sampling at higher frequency than supported, will just return the number of increments + if (interval < TIME_SEC) return increment; + else if (increment == 0) return 0; + else return increment / (interval / TIME_SEC); +} + +}} // namespace qpid::broker diff --git a/RC9/qpid/cpp/src/qpid/broker/RateTracker.h b/RC9/qpid/cpp/src/qpid/broker/RateTracker.h new file mode 100644 index 0000000000..0c20b37312 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/RateTracker.h @@ -0,0 +1,57 @@ +#ifndef QPID_BROKER_RATETRACKER_H +#define QPID_BROKER_RATETRACKER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Time.h" + +namespace qpid { +namespace broker { + +/** + * Simple rate tracker: represents some value that can be incremented, + * then can periodcially sample the rate of increments. + */ +class RateTracker +{ + public: + RateTracker(); + /** + * Increments the count being tracked. Can be called concurrently + * with other calls to this operator as well as with calls to + * sampleRatePerSecond(). + */ + RateTracker& operator++(); + /** + * Returns the rate of increments per second since last + * called. Calls to this method should be serialised, but can be + * called concurrently with the increment operator + */ + double sampleRatePerSecond(); + private: + volatile int32_t currentCount; + int32_t lastCount; + qpid::sys::AbsTime lastTime; +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_RATETRACKER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/RecoverableConfig.h b/RC9/qpid/cpp/src/qpid/broker/RecoverableConfig.h new file mode 100644 index 0000000000..838a8582dc --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/RecoverableConfig.h @@ -0,0 +1,45 @@ +#ifndef _broker_RecoverableConfig_h +#define _broker_RecoverableConfig_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace broker { + +/** + * The interface through which configurations are recovered. + */ +class RecoverableConfig +{ +public: + typedef boost::shared_ptr<RecoverableConfig> shared_ptr; + + virtual void setPersistenceId(uint64_t id) = 0; + virtual ~RecoverableConfig() {}; +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/RecoverableExchange.h b/RC9/qpid/cpp/src/qpid/broker/RecoverableExchange.h new file mode 100644 index 0000000000..76d0d2ecdf --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/RecoverableExchange.h @@ -0,0 +1,50 @@ +#ifndef _broker_RecoverableExchange_h +#define _broker_RecoverableExchange_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/shared_ptr.hpp> +#include "qpid/framing/FieldTable.h" + +namespace qpid { +namespace broker { + +/** + * The interface through which bindings are recovered. + */ +class RecoverableExchange +{ +public: + typedef boost::shared_ptr<RecoverableExchange> shared_ptr; + + virtual void setPersistenceId(uint64_t id) = 0; + /** + * Recover binding. Nb: queue must have been recovered earlier. + */ + virtual void bind(std::string& queue, std::string& routingKey, qpid::framing::FieldTable& args) = 0; + virtual ~RecoverableExchange() {}; +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/RecoverableMessage.h b/RC9/qpid/cpp/src/qpid/broker/RecoverableMessage.h new file mode 100644 index 0000000000..f755fdf727 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/RecoverableMessage.h @@ -0,0 +1,58 @@ +#ifndef _broker_RecoverableMessage_h +#define _broker_RecoverableMessage_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/shared_ptr.hpp> +#include "qpid/framing/amqp_types.h" +#include "qpid/framing/Buffer.h" + +namespace qpid { +namespace broker { + +/** + * The interface through which messages are reloaded on recovery. + */ +class RecoverableMessage +{ +public: + typedef boost::shared_ptr<RecoverableMessage> shared_ptr; + virtual void setPersistenceId(uint64_t id) = 0; + /** + * Used by store to determine whether to load content on recovery + * or let message load its own content as and when it requires it. + * + * @returns true if the content of the message should be loaded + */ + virtual bool loadContent(uint64_t available) = 0; + /** + * Loads the content held in the supplied buffer (may do checking + * of length as necessary) + */ + virtual void decodeContent(framing::Buffer& buffer) = 0; + virtual ~RecoverableMessage() {}; +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/RecoverableQueue.h b/RC9/qpid/cpp/src/qpid/broker/RecoverableQueue.h new file mode 100644 index 0000000000..b32bae7f07 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/RecoverableQueue.h @@ -0,0 +1,59 @@ +#ifndef _broker_RecoverableQueue_h +#define _broker_RecoverableQueue_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "RecoverableMessage.h" +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace broker { + +class ExternalQueueStore; + +/** + * The interface through which messages are added back to queues on + * recovery. + */ +class RecoverableQueue +{ +public: + typedef boost::shared_ptr<RecoverableQueue> shared_ptr; + + virtual void setPersistenceId(uint64_t id) = 0; + virtual uint64_t getPersistenceId() const = 0; + /** + * Used during recovery to add stored messages back to the queue + */ + virtual void recover(RecoverableMessage::shared_ptr msg) = 0; + virtual ~RecoverableQueue() {}; + + virtual const std::string& getName() const = 0; + virtual void setExternalQueueStore(ExternalQueueStore* inst) = 0; + virtual ExternalQueueStore* getExternalQueueStore() const = 0; + +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/RecoverableTransaction.h b/RC9/qpid/cpp/src/qpid/broker/RecoverableTransaction.h new file mode 100644 index 0000000000..7fe34b6756 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/RecoverableTransaction.h @@ -0,0 +1,49 @@ +#ifndef _broker_RecoverableTransaction_h +#define _broker_RecoverableTransaction_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/shared_ptr.hpp> + +#include "RecoverableMessage.h" +#include "RecoverableQueue.h" + +namespace qpid { +namespace broker { + +/** + * The interface through which prepared 2pc transactions are + * recovered. + */ +class RecoverableTransaction +{ +public: + typedef boost::shared_ptr<RecoverableTransaction> shared_ptr; + virtual void enqueue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message) = 0; + virtual void dequeue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message) = 0; + virtual ~RecoverableTransaction() {}; +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/RecoveredDequeue.cpp b/RC9/qpid/cpp/src/qpid/broker/RecoveredDequeue.cpp new file mode 100644 index 0000000000..e2d70964fb --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/RecoveredDequeue.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 "RecoveredDequeue.h" + +using boost::intrusive_ptr; +using namespace qpid::broker; + +RecoveredDequeue::RecoveredDequeue(Queue::shared_ptr _queue, intrusive_ptr<Message> _msg) : queue(_queue), msg(_msg) {} + +bool RecoveredDequeue::prepare(TransactionContext*) throw(){ + //should never be called; transaction has already prepared if an enqueue is recovered + return false; +} + +void RecoveredDequeue::commit() throw(){ +} + +void RecoveredDequeue::rollback() throw(){ + msg->enqueueComplete(); + queue->process(msg); +} + diff --git a/RC9/qpid/cpp/src/qpid/broker/RecoveredDequeue.h b/RC9/qpid/cpp/src/qpid/broker/RecoveredDequeue.h new file mode 100644 index 0000000000..ef6b8f1f74 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/RecoveredDequeue.h @@ -0,0 +1,57 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _RecoveredDequeue_ +#define _RecoveredDequeue_ + +#include "Deliverable.h" +#include "Message.h" +#include "MessageStore.h" +#include "Queue.h" +#include "TxOp.h" + +#include <boost/intrusive_ptr.hpp> + +#include <algorithm> +#include <functional> +#include <list> + +namespace qpid { + namespace broker { + class RecoveredDequeue : public TxOp{ + Queue::shared_ptr queue; + boost::intrusive_ptr<Message> msg; + + public: + RecoveredDequeue(Queue::shared_ptr queue, boost::intrusive_ptr<Message> msg); + virtual bool prepare(TransactionContext* ctxt) throw(); + virtual void commit() throw(); + virtual void rollback() throw(); + virtual ~RecoveredDequeue(){} + virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); } + + Queue::shared_ptr getQueue() const { return queue; } + boost::intrusive_ptr<Message> getMessage() const { return msg; } + }; + } +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/RecoveredEnqueue.cpp b/RC9/qpid/cpp/src/qpid/broker/RecoveredEnqueue.cpp new file mode 100644 index 0000000000..1984a5d4a8 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/RecoveredEnqueue.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 "RecoveredEnqueue.h" + +using boost::intrusive_ptr; +using namespace qpid::broker; + +RecoveredEnqueue::RecoveredEnqueue(Queue::shared_ptr _queue, intrusive_ptr<Message> _msg) : queue(_queue), msg(_msg) {} + +bool RecoveredEnqueue::prepare(TransactionContext*) throw(){ + //should never be called; transaction has already prepared if an enqueue is recovered + return false; +} + +void RecoveredEnqueue::commit() throw(){ + msg->enqueueComplete(); + queue->process(msg); +} + +void RecoveredEnqueue::rollback() throw(){ +} + diff --git a/RC9/qpid/cpp/src/qpid/broker/RecoveredEnqueue.h b/RC9/qpid/cpp/src/qpid/broker/RecoveredEnqueue.h new file mode 100644 index 0000000000..2d97768e65 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/RecoveredEnqueue.h @@ -0,0 +1,58 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _RecoveredEnqueue_ +#define _RecoveredEnqueue_ + +#include "Deliverable.h" +#include "Message.h" +#include "MessageStore.h" +#include "Queue.h" +#include "TxOp.h" + +#include <boost/intrusive_ptr.hpp> + +#include <algorithm> +#include <functional> +#include <list> + +namespace qpid { + namespace broker { + class RecoveredEnqueue : public TxOp{ + Queue::shared_ptr queue; + boost::intrusive_ptr<Message> msg; + + public: + RecoveredEnqueue(Queue::shared_ptr queue, boost::intrusive_ptr<Message> msg); + virtual bool prepare(TransactionContext* ctxt) throw(); + virtual void commit() throw(); + virtual void rollback() throw(); + virtual ~RecoveredEnqueue(){} + virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); } + + Queue::shared_ptr getQueue() const { return queue; } + boost::intrusive_ptr<Message> getMessage() const { return msg; } + + }; + } +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/RecoveryManager.h b/RC9/qpid/cpp/src/qpid/broker/RecoveryManager.h new file mode 100644 index 0000000000..7dcbe3a2b0 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/RecoveryManager.h @@ -0,0 +1,61 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _RecoveryManager_ +#define _RecoveryManager_ + +#include "RecoverableExchange.h" +#include "RecoverableQueue.h" +#include "RecoverableMessage.h" +#include "RecoverableTransaction.h" +#include "RecoverableConfig.h" +#include "TransactionalStore.h" +#include "qpid/framing/Buffer.h" + +namespace qpid { +namespace broker { + +class RecoveryManager{ + public: + virtual ~RecoveryManager(){} + virtual RecoverableExchange::shared_ptr recoverExchange(framing::Buffer& buffer) = 0; + virtual RecoverableQueue::shared_ptr recoverQueue(framing::Buffer& buffer) = 0; + virtual RecoverableMessage::shared_ptr recoverMessage(framing::Buffer& buffer) = 0; + virtual RecoverableTransaction::shared_ptr recoverTransaction(const std::string& xid, + std::auto_ptr<TPCTransactionContext> txn) = 0; + virtual RecoverableConfig::shared_ptr recoverConfig(framing::Buffer& buffer) = 0; + + virtual void recoveryComplete() = 0; +}; + +class Recoverable { + public: + virtual ~Recoverable() {} + + /** + * Request recovery of queue and message state. + */ + virtual void recover(RecoveryManager& recoverer) = 0; +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp b/RC9/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp new file mode 100644 index 0000000000..d3eacefc3c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.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. + * + */ +#include "RecoveryManagerImpl.h" + +#include "Message.h" +#include "Queue.h" +#include "Link.h" +#include "Bridge.h" +#include "RecoveredEnqueue.h" +#include "RecoveredDequeue.h" +#include "qpid/framing/reply_exceptions.h" + +using namespace qpid; +using namespace qpid::broker; +using boost::dynamic_pointer_cast; +using boost::intrusive_ptr; + +RecoveryManagerImpl::RecoveryManagerImpl(QueueRegistry& _queues, ExchangeRegistry& _exchanges, LinkRegistry& _links, + DtxManager& _dtxMgr, uint64_t _stagingThreshold) + : queues(_queues), exchanges(_exchanges), links(_links), dtxMgr(_dtxMgr), stagingThreshold(_stagingThreshold) {} + +RecoveryManagerImpl::~RecoveryManagerImpl() {} + +class RecoverableMessageImpl : public RecoverableMessage +{ + intrusive_ptr<Message> msg; + const uint64_t stagingThreshold; +public: + RecoverableMessageImpl(const intrusive_ptr<Message>& _msg, uint64_t _stagingThreshold) + : msg(_msg), stagingThreshold(_stagingThreshold) {} + ~RecoverableMessageImpl() {}; + void setPersistenceId(uint64_t id); + bool loadContent(uint64_t available); + void decodeContent(framing::Buffer& buffer); + void recover(Queue::shared_ptr queue); + void enqueue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue); + void dequeue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue); +}; + +class RecoverableQueueImpl : public RecoverableQueue +{ + Queue::shared_ptr queue; +public: + RecoverableQueueImpl(const boost::shared_ptr<Queue>& _queue) : queue(_queue) {} + ~RecoverableQueueImpl() {}; + void setPersistenceId(uint64_t id); + uint64_t getPersistenceId() const; + const std::string& getName() const; + void setExternalQueueStore(ExternalQueueStore* inst); + ExternalQueueStore* getExternalQueueStore() const; + void recover(RecoverableMessage::shared_ptr msg); + void enqueue(DtxBuffer::shared_ptr buffer, RecoverableMessage::shared_ptr msg); + void dequeue(DtxBuffer::shared_ptr buffer, RecoverableMessage::shared_ptr msg); +}; + +class RecoverableExchangeImpl : public RecoverableExchange +{ + Exchange::shared_ptr exchange; + QueueRegistry& queues; +public: + RecoverableExchangeImpl(Exchange::shared_ptr _exchange, QueueRegistry& _queues) : exchange(_exchange), queues(_queues) {} + void setPersistenceId(uint64_t id); + void bind(std::string& queue, std::string& routingKey, qpid::framing::FieldTable& args); +}; + +class RecoverableConfigImpl : public RecoverableConfig +{ + Link::shared_ptr link; + Bridge::shared_ptr bridge; +public: + RecoverableConfigImpl(Link::shared_ptr _link) : link(_link) {} + RecoverableConfigImpl(Bridge::shared_ptr _bridge) : bridge(_bridge) {} + void setPersistenceId(uint64_t id); +}; + +class RecoverableTransactionImpl : public RecoverableTransaction +{ + DtxBuffer::shared_ptr buffer; +public: + RecoverableTransactionImpl(DtxBuffer::shared_ptr _buffer) : buffer(_buffer) {} + void enqueue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message); + void dequeue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message); +}; + +RecoverableExchange::shared_ptr RecoveryManagerImpl::recoverExchange(framing::Buffer& buffer) +{ + return RecoverableExchange::shared_ptr(new RecoverableExchangeImpl(Exchange::decode(exchanges, buffer), queues)); +} + +RecoverableQueue::shared_ptr RecoveryManagerImpl::recoverQueue(framing::Buffer& buffer) +{ + Queue::shared_ptr queue = Queue::decode(queues, buffer); + try { + Exchange::shared_ptr exchange = exchanges.getDefault(); + if (exchange) { + exchange->bind(queue, queue->getName(), 0); + } + } catch (const framing::NotFoundException& /*e*/) { + //assume no default exchange has been declared + } + return RecoverableQueue::shared_ptr(new RecoverableQueueImpl(queue)); +} + +RecoverableMessage::shared_ptr RecoveryManagerImpl::recoverMessage(framing::Buffer& buffer) +{ + boost::intrusive_ptr<Message> message(new Message()); + message->decodeHeader(buffer); + return RecoverableMessage::shared_ptr(new RecoverableMessageImpl(message, stagingThreshold)); +} + +RecoverableTransaction::shared_ptr RecoveryManagerImpl::recoverTransaction(const std::string& xid, + std::auto_ptr<TPCTransactionContext> txn) +{ + DtxBuffer::shared_ptr buffer(new DtxBuffer()); + dtxMgr.recover(xid, txn, buffer); + return RecoverableTransaction::shared_ptr(new RecoverableTransactionImpl(buffer)); +} + +RecoverableConfig::shared_ptr RecoveryManagerImpl::recoverConfig(framing::Buffer& buffer) +{ + string kind; + + buffer.getShortString (kind); + if (kind == "link") + return RecoverableConfig::shared_ptr(new RecoverableConfigImpl(Link::decode (links, buffer))); + else if (kind == "bridge") + return RecoverableConfig::shared_ptr(new RecoverableConfigImpl(Bridge::decode (links, buffer))); + + return RecoverableConfig::shared_ptr(); // TODO: raise an exception instead +} + +void RecoveryManagerImpl::recoveryComplete() +{ + //TODO (finalise binding setup etc) +} + +bool RecoverableMessageImpl::loadContent(uint64_t available) +{ + return !stagingThreshold || available < stagingThreshold; +} + +void RecoverableMessageImpl::decodeContent(framing::Buffer& buffer) +{ + msg->decodeContent(buffer); +} + +void RecoverableMessageImpl::recover(Queue::shared_ptr queue) +{ + queue->recover(msg); +} + +void RecoverableMessageImpl::setPersistenceId(uint64_t id) +{ + msg->setPersistenceId(id); +} + +void RecoverableQueueImpl::recover(RecoverableMessage::shared_ptr msg) +{ + dynamic_pointer_cast<RecoverableMessageImpl>(msg)->recover(queue); +} + +void RecoverableQueueImpl::setPersistenceId(uint64_t id) +{ + queue->setPersistenceId(id); +} + +uint64_t RecoverableQueueImpl::getPersistenceId() const +{ + return queue->getPersistenceId(); +} + +const std::string& RecoverableQueueImpl::getName() const +{ + return queue->getName(); +} + +void RecoverableQueueImpl::setExternalQueueStore(ExternalQueueStore* inst) +{ + queue->setExternalQueueStore(inst); +} + +ExternalQueueStore* RecoverableQueueImpl::getExternalQueueStore() const +{ + return queue->getExternalQueueStore(); +} + +void RecoverableExchangeImpl::setPersistenceId(uint64_t id) +{ + exchange->setPersistenceId(id); +} + +void RecoverableConfigImpl::setPersistenceId(uint64_t id) +{ + if (link.get()) + link->setPersistenceId(id); + else if (bridge.get()) + bridge->setPersistenceId(id); +} + +void RecoverableExchangeImpl::bind(string& queueName, string& key, framing::FieldTable& args) +{ + Queue::shared_ptr queue = queues.find(queueName); + exchange->bind(queue, key, &args); +} + +void RecoverableMessageImpl::dequeue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue) +{ + buffer->enlist(TxOp::shared_ptr(new RecoveredDequeue(queue, msg))); +} + +void RecoverableMessageImpl::enqueue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue) +{ + msg->enqueueComplete(); // recoved nmessage to enqueued in store already + buffer->enlist(TxOp::shared_ptr(new RecoveredEnqueue(queue, msg))); +} + +void RecoverableQueueImpl::dequeue(DtxBuffer::shared_ptr buffer, RecoverableMessage::shared_ptr message) +{ + dynamic_pointer_cast<RecoverableMessageImpl>(message)->dequeue(buffer, queue); +} + +void RecoverableQueueImpl::enqueue(DtxBuffer::shared_ptr buffer, RecoverableMessage::shared_ptr message) +{ + dynamic_pointer_cast<RecoverableMessageImpl>(message)->enqueue(buffer, queue); +} + +void RecoverableTransactionImpl::dequeue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message) +{ + dynamic_pointer_cast<RecoverableQueueImpl>(queue)->dequeue(buffer, message); +} + +void RecoverableTransactionImpl::enqueue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message) +{ + dynamic_pointer_cast<RecoverableQueueImpl>(queue)->enqueue(buffer, message); +} diff --git a/RC9/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.h b/RC9/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.h new file mode 100644 index 0000000000..cd34d464f5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.h @@ -0,0 +1,59 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _RecoveryManagerImpl_ +#define _RecoveryManagerImpl_ + +#include <list> +#include "DtxManager.h" +#include "ExchangeRegistry.h" +#include "QueueRegistry.h" +#include "LinkRegistry.h" +#include "RecoveryManager.h" + +namespace qpid { +namespace broker { + + class RecoveryManagerImpl : public RecoveryManager{ + QueueRegistry& queues; + ExchangeRegistry& exchanges; + LinkRegistry& links; + DtxManager& dtxMgr; + const uint64_t stagingThreshold; + public: + RecoveryManagerImpl(QueueRegistry& queues, ExchangeRegistry& exchanges, LinkRegistry& links, + DtxManager& dtxMgr, uint64_t stagingThreshold); + ~RecoveryManagerImpl(); + + RecoverableExchange::shared_ptr recoverExchange(framing::Buffer& buffer); + RecoverableQueue::shared_ptr recoverQueue(framing::Buffer& buffer); + RecoverableMessage::shared_ptr recoverMessage(framing::Buffer& buffer); + RecoverableTransaction::shared_ptr recoverTransaction(const std::string& xid, + std::auto_ptr<TPCTransactionContext> txn); + RecoverableConfig::shared_ptr recoverConfig(framing::Buffer& buffer); + void recoveryComplete(); + }; + + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp b/RC9/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp new file mode 100644 index 0000000000..370de8a1d1 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp @@ -0,0 +1,337 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "Connection.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/reply_exceptions.h" +#include <boost/format.hpp> + +#if HAVE_SASL +#include <sasl/sasl.h> +#endif + +using namespace qpid::framing; +using boost::format; +using boost::str; + +namespace qpid { +namespace broker { + + +class NullAuthenticator : public SaslAuthenticator +{ + Connection& connection; + framing::AMQP_ClientProxy::Connection client; + std::string realm; +public: + NullAuthenticator(Connection& connection); + ~NullAuthenticator(); + void getMechanisms(framing::Array& mechanisms); + void start(const std::string& mechanism, const std::string& response); + void step(const std::string&) {} +}; + +#if HAVE_SASL + +class CyrusAuthenticator : public SaslAuthenticator +{ + sasl_conn_t *sasl_conn; + Connection& connection; + framing::AMQP_ClientProxy::Connection client; + + void processAuthenticationStep(int code, const char *challenge, unsigned int challenge_len); + +public: + CyrusAuthenticator(Connection& connection); + ~CyrusAuthenticator(); + void init(); + void getMechanisms(framing::Array& mechanisms); + void start(const std::string& mechanism, const std::string& response); + void step(const std::string& response); + void getUid(std::string& uid); + void getError(std::string& error); +}; + +bool SaslAuthenticator::available(void) +{ + return true; +} + +// Initialize the SASL mechanism; throw if it fails. +void SaslAuthenticator::init(const std::string& saslName) +{ + int code = sasl_server_init(NULL, saslName.c_str()); + if (code != SASL_OK) { + // TODO: Figure out who owns the char* returned by + // sasl_errstring, though it probably does not matter much + throw Exception(sasl_errstring(code, NULL, NULL)); + } +} + +void SaslAuthenticator::fini(void) +{ + sasl_done(); +} + +#else + +typedef NullAuthenticator CyrusAuthenticator; + +bool SaslAuthenticator::available(void) +{ + return false; +} + +void SaslAuthenticator::init(const std::string& /*saslName*/) +{ + throw Exception("Requested authentication but SASL unavailable"); +} + +void SaslAuthenticator::fini(void) +{ + return; +} + +#endif + +std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connection& c) +{ + static bool needWarning = true; + if (c.getBroker().getOptions().auth) { + return std::auto_ptr<SaslAuthenticator>(new CyrusAuthenticator(c)); + } else { + QPID_LOG(warning, "SASL: No Authentication Performed"); + needWarning = false; + return std::auto_ptr<SaslAuthenticator>(new NullAuthenticator(c)); + } +} + +NullAuthenticator::NullAuthenticator(Connection& c) : connection(c), client(c.getOutput()), + realm(c.getBroker().getOptions().realm) {} +NullAuthenticator::~NullAuthenticator() {} + +void NullAuthenticator::getMechanisms(Array& mechanisms) +{ + mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value("ANONYMOUS"))); +} + +void NullAuthenticator::start(const string& mechanism, const string& response) +{ + if (mechanism == "PLAIN") { // Old behavior + if (response.size() > 0 && response[0] == (char) 0) { + string temp = response.substr(1); + string::size_type i = temp.find((char)0); + string uid = temp.substr(0, i); + string pwd = temp.substr(i + 1); + i = uid.find_last_of(realm); + if (i == string::npos || i != (uid.size() - 1)) { + uid = str(format("%1%@%2%") % uid % realm); + } + connection.setUserId(uid); + } + } else { + connection.setUserId("anonymous"); + } + client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0); +} + + +#if HAVE_SASL + +CyrusAuthenticator::CyrusAuthenticator(Connection& c) : sasl_conn(0), connection(c), client(c.getOutput()) +{ + init(); +} + +void CyrusAuthenticator::init() +{ + /* Next to the service name, which specifies the + * /etc/sasl2/<service name>.conf file to read, the realm is + * currently the most important argument below. When + * performing authentication the user that is authenticating + * will be looked up in a specific realm. If none is given + * then the realm defaults to the hostname, which can cause + * confusion when the daemon is run on different hosts that + * may be logically sharing a realm (aka a user domain). This + * is especially important for SASL PLAIN authentication, + * which cannot specify a realm for the user that is + * authenticating. + */ + const char *realm = connection.getBroker().getOptions().realm.c_str(); + int code = sasl_server_new(BROKER_SASL_NAME, /* Service name */ + NULL, /* Server FQDN, gethostname() */ + realm, /* Authentication realm */ + NULL, /* Local IP, needed for some mechanism */ + NULL, /* Remote IP, needed for some mechanism */ + NULL, /* Callbacks */ + 0, /* Connection flags */ + &sasl_conn); + + if (SASL_OK != code) { + QPID_LOG(info, "SASL: Connection creation failed: [" << code << "] " << sasl_errdetail(sasl_conn)); + + // TODO: Change this to an exception signaling + // server error, when one is available + throw ConnectionForcedException("Unable to perform authentication"); + } +} + +CyrusAuthenticator::~CyrusAuthenticator() +{ + if (sasl_conn) { + sasl_dispose(&sasl_conn); + sasl_conn = 0; + } +} + +void CyrusAuthenticator::getError(string& error) +{ + error = string(sasl_errdetail(sasl_conn)); +} + +void CyrusAuthenticator::getUid(string& uid) +{ + int code; + const void* ptr; + + code = sasl_getprop(sasl_conn, SASL_USERNAME, &ptr); + if (SASL_OK != code) + return; + + uid = string(const_cast<char*>(static_cast<const char*>(ptr))); +} + +void CyrusAuthenticator::getMechanisms(Array& mechanisms) +{ + const char *separator = " "; + const char *list; + unsigned int list_len; + int count; + + int code = sasl_listmech(sasl_conn, NULL, + "", separator, "", + &list, &list_len, + &count); + + if (SASL_OK != code) { + QPID_LOG(info, "SASL: Mechanism listing failed: " << sasl_errdetail(sasl_conn)); + + // TODO: Change this to an exception signaling + // server error, when one is available + throw ConnectionForcedException("Mechanism listing failed"); + } else { + string mechanism; + unsigned int start; + unsigned int end; + + QPID_LOG(info, "SASL: Mechanism list: " << list); + + end = 0; + do { + start = end; + + // Seek to end of next mechanism + while (end < list_len && separator[0] != list[end]) + end++; + + // Record the mechanism + mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value(string(list, start, end - start)))); + end++; + } while (end < list_len); + } +} + +void CyrusAuthenticator::start(const string& mechanism, const string& response) +{ + const char *challenge; + unsigned int challenge_len; + + QPID_LOG(info, "SASL: Starting authentication with mechanism: " << mechanism); + int code = sasl_server_start(sasl_conn, + mechanism.c_str(), + response.c_str(), response.length(), + &challenge, &challenge_len); + + processAuthenticationStep(code, challenge, challenge_len); +} + +void CyrusAuthenticator::step(const string& response) +{ + const char *challenge; + unsigned int challenge_len; + + int code = sasl_server_step(sasl_conn, + response.c_str(), response.length(), + &challenge, &challenge_len); + + processAuthenticationStep(code, challenge, challenge_len); +} + +void CyrusAuthenticator::processAuthenticationStep(int code, const char *challenge, unsigned int challenge_len) +{ + if (SASL_OK == code) { + const void *uid; + + code = sasl_getprop(sasl_conn, SASL_USERNAME, &uid); + if (SASL_OK != code) { + QPID_LOG(info, "SASL: Authentication succeeded, username unavailable"); + // TODO: Change this to an exception signaling + // authentication failure, when one is available + throw ConnectionForcedException("Authenticated username unavailable"); + } + + QPID_LOG(info, "SASL: Authentication succeeded for: " + << const_cast<char*>(static_cast<const char*>(uid))); + + connection.setUserId(const_cast<char*>(static_cast<const char*>(uid))); + + client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0); + } else if (SASL_CONTINUE == code) { + string challenge_str(challenge, challenge_len); + + QPID_LOG(debug, "SASL: sending challenge to client"); + + client.secure(challenge_str); + } else { + QPID_LOG(info, "SASL: Authentication failed: " << sasl_errdetail(sasl_conn)); + + // TODO: Change to more specific exceptions, when they are + // available + switch (code) { + case SASL_NOMECH: + throw ConnectionForcedException("Unsupported mechanism"); + break; + case SASL_TRYAGAIN: + throw ConnectionForcedException("Transient failure, try again"); + break; + default: + throw ConnectionForcedException("Authentication failed"); + break; + } + } +} +#endif + +}} diff --git a/RC9/qpid/cpp/src/qpid/broker/SaslAuthenticator.h b/RC9/qpid/cpp/src/qpid/broker/SaslAuthenticator.h new file mode 100644 index 0000000000..2598b6d177 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/SaslAuthenticator.h @@ -0,0 +1,55 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _SaslAuthenticator_ +#define _SaslAuthenticator_ + +#include "qpid/framing/amqp_types.h" +#include "qpid/framing/AMQP_ClientProxy.h" +#include "qpid/Exception.h" +#include <memory> + +namespace qpid { +namespace broker { + +class Connection; + +class SaslAuthenticator +{ +public: + virtual ~SaslAuthenticator() {} + virtual void getMechanisms(framing::Array& mechanisms) = 0; + virtual void start(const std::string& mechanism, const std::string& response) = 0; + virtual void step(const std::string& response) = 0; + virtual void getUid(std::string&) {} + virtual void getError(std::string&) {} + + static bool available(void); + + // Initialize the SASL mechanism; throw if it fails. + static void init(const std::string& saslName); + static void fini(void); + + static std::auto_ptr<SaslAuthenticator> createAuthenticator(Connection& connection); +}; + +}} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/SemanticState.cpp b/RC9/qpid/cpp/src/qpid/broker/SemanticState.cpp new file mode 100644 index 0000000000..d9896b388b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/SemanticState.cpp @@ -0,0 +1,669 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SessionState.h" +#include "Connection.h" +#include "DeliverableMessage.h" +#include "DtxAck.h" +#include "DtxTimeout.h" +#include "Message.h" +#include "Queue.h" +#include "SessionContext.h" +#include "TxAccept.h" +#include "TxPublish.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/log/Statement.h" +#include "qpid/ptr_map.h" +#include "AclModule.h" + +#include <boost/bind.hpp> +#include <boost/format.hpp> + +#include <iostream> +#include <sstream> +#include <algorithm> +#include <functional> + +#include <assert.h> + + +namespace qpid { +namespace broker { + +using std::mem_fun_ref; +using boost::intrusive_ptr; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; +using qpid::ptr_map_ptr; + +SemanticState::SemanticState(DeliveryAdapter& da, SessionContext& ss) + : session(ss), + deliveryAdapter(da), + tagGenerator("sgen"), + dtxSelected(false), + outputTasks(ss), + authMsg(getSession().getBroker().getOptions().auth && !getSession().getConnection().isFederationLink()), + userID(getSession().getConnection().getUserId().substr(0,getSession().getConnection().getUserId().find('@'))) +{ + acl = getSession().getBroker().getAcl(); +} + +SemanticState::~SemanticState() { + //cancel all consumers + for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) { + cancel(i->second); + } + + if (dtxBuffer.get()) { + dtxBuffer->fail(); + } + recover(true); +} + +bool SemanticState::exists(const string& consumerTag){ + return consumers.find(consumerTag) != consumers.end(); +} + +void SemanticState::consume(const string& tag, + Queue::shared_ptr queue, bool ackRequired, bool acquire, + bool exclusive, const string& resumeId, uint64_t resumeTtl, const FieldTable& arguments) +{ + ConsumerImpl::shared_ptr c(new ConsumerImpl(this, tag, queue, ackRequired, acquire, exclusive, resumeId, resumeTtl, arguments)); + queue->consume(c, exclusive);//may throw exception + outputTasks.addOutputTask(c.get()); + consumers[tag] = c; +} + +void SemanticState::cancel(const string& tag){ + ConsumerImplMap::iterator i = consumers.find(tag); + if (i != consumers.end()) { + cancel(i->second); + consumers.erase(i); + //should cancel all unacked messages for this consumer so that + //they are not redelivered on recovery + for_each(unacked.begin(), unacked.end(), boost::bind(&DeliveryRecord::cancel, _1, tag)); + + } +} + + +void SemanticState::startTx() +{ + txBuffer = TxBuffer::shared_ptr(new TxBuffer()); +} + +void SemanticState::commit(MessageStore* const store) +{ + if (!txBuffer) throw + CommandInvalidException(QPID_MSG("Session has not been selected for use with transactions")); + + TxOp::shared_ptr txAck(static_cast<TxOp*>(new TxAccept(accumulatedAck, unacked))); + txBuffer->enlist(txAck); + if (txBuffer->commitLocal(store)) { + accumulatedAck.clear(); + } else { + throw InternalErrorException(QPID_MSG("Commit failed")); + } +} + +void SemanticState::rollback() +{ + if (!txBuffer) + throw CommandInvalidException(QPID_MSG("Session has not been selected for use with transactions")); + + txBuffer->rollback(); + accumulatedAck.clear(); +} + +void SemanticState::selectDtx() +{ + dtxSelected = true; +} + +void SemanticState::startDtx(const std::string& xid, DtxManager& mgr, bool join) +{ + if (!dtxSelected) { + throw CommandInvalidException(QPID_MSG("Session has not been selected for use with dtx")); + } + dtxBuffer = DtxBuffer::shared_ptr(new DtxBuffer(xid)); + txBuffer = static_pointer_cast<TxBuffer>(dtxBuffer); + if (join) { + mgr.join(xid, dtxBuffer); + } else { + mgr.start(xid, dtxBuffer); + } +} + +void SemanticState::endDtx(const std::string& xid, bool fail) +{ + if (!dtxBuffer) { + throw IllegalStateException(QPID_MSG("xid " << xid << " not associated with this session")); + } + if (dtxBuffer->getXid() != xid) { + throw CommandInvalidException( + QPID_MSG("xid specified on start was " << dtxBuffer->getXid() << ", but " << xid << " specified on end")); + + } + + txBuffer.reset();//ops on this session no longer transactional + + checkDtxTimeout(); + if (fail) { + dtxBuffer->fail(); + } else { + dtxBuffer->markEnded(); + } + dtxBuffer.reset(); +} + +void SemanticState::suspendDtx(const std::string& xid) +{ + if (dtxBuffer->getXid() != xid) { + throw CommandInvalidException( + QPID_MSG("xid specified on start was " << dtxBuffer->getXid() << ", but " << xid << " specified on suspend")); + } + txBuffer.reset();//ops on this session no longer transactional + + checkDtxTimeout(); + dtxBuffer->setSuspended(true); + suspendedXids[xid] = dtxBuffer; + dtxBuffer.reset(); +} + +void SemanticState::resumeDtx(const std::string& xid) +{ + if (!dtxSelected) { + throw CommandInvalidException(QPID_MSG("Session has not been selected for use with dtx")); + } + + dtxBuffer = suspendedXids[xid]; + if (!dtxBuffer) { + throw CommandInvalidException(QPID_MSG("xid " << xid << " not attached")); + } else { + suspendedXids.erase(xid); + } + + if (dtxBuffer->getXid() != xid) { + throw CommandInvalidException( + QPID_MSG("xid specified on start was " << dtxBuffer->getXid() << ", but " << xid << " specified on resume")); + + } + if (!dtxBuffer->isSuspended()) { + throw CommandInvalidException(QPID_MSG("xid " << xid << " not suspended")); + } + + checkDtxTimeout(); + dtxBuffer->setSuspended(false); + txBuffer = static_pointer_cast<TxBuffer>(dtxBuffer); +} + +void SemanticState::checkDtxTimeout() +{ + if (dtxBuffer->isExpired()) { + dtxBuffer.reset(); + throw DtxTimeoutException(); + } +} + +void SemanticState::record(const DeliveryRecord& delivery) +{ + unacked.push_back(delivery); +} + +SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent, + const string& _name, + Queue::shared_ptr _queue, + bool ack, + bool _acquire, + bool _exclusive, + const string& _resumeId, + uint64_t _resumeTtl, + const framing::FieldTable& _arguments + + +) : + Consumer(_acquire), + parent(_parent), + name(_name), + queue(_queue), + ackExpected(ack), + acquire(_acquire), + blocked(true), + windowing(true), + exclusive(_exclusive), + resumeId(_resumeId), + resumeTtl(_resumeTtl), + arguments(_arguments), + msgCredit(0), + byteCredit(0), + notifyEnabled(true) {} + +OwnershipToken* SemanticState::ConsumerImpl::getSession() +{ + return &(parent->session); +} + +bool SemanticState::ConsumerImpl::deliver(QueuedMessage& msg) +{ + allocateCredit(msg.payload); + DeliveryRecord record(msg, queue, name, acquire, !ackExpected, windowing); + parent->deliver(record); + if (!ackExpected) record.setEnded();//allows message to be released now its been delivered + if (windowing || ackExpected || !acquire) { + parent->record(record); + } + if (acquire && !ackExpected) { + queue->dequeue(0, msg); + } + return true; +} + +bool SemanticState::ConsumerImpl::filter(intrusive_ptr<Message>) +{ + return true; +} + +bool SemanticState::ConsumerImpl::accept(intrusive_ptr<Message> msg) +{ + blocked = !(filter(msg) && checkCredit(msg)); + return !blocked; +} + +void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr<Message>& msg) +{ + uint32_t originalMsgCredit = msgCredit; + uint32_t originalByteCredit = byteCredit; + if (msgCredit != 0xFFFFFFFF) { + msgCredit--; + } + if (byteCredit != 0xFFFFFFFF) { + byteCredit -= msg->getRequiredCredit(); + } + QPID_LOG(debug, "Credit allocated for '" << name << "' on " << parent + << ", was " << " bytes: " << originalByteCredit << " msgs: " << originalMsgCredit + << " now bytes: " << byteCredit << " msgs: " << msgCredit); + +} + +bool SemanticState::ConsumerImpl::checkCredit(intrusive_ptr<Message>& msg) +{ + if (msgCredit == 0 || (byteCredit != 0xFFFFFFFF && byteCredit < msg->getRequiredCredit())) { + QPID_LOG(debug, "Not enough credit for '" << name << "' on " << parent + << ", bytes: " << byteCredit << " msgs: " << msgCredit); + return false; + } else { + QPID_LOG(debug, "Credit available for '" << name << "' on " << parent + << " bytes: " << byteCredit << " msgs: " << msgCredit); + return true; + } +} + +SemanticState::ConsumerImpl::~ConsumerImpl() {} + +void SemanticState::cancel(ConsumerImpl::shared_ptr c) +{ + c->disableNotify(); + outputTasks.removeOutputTask(c.get()); + Queue::shared_ptr queue = c->getQueue(); + if(queue) { + queue->cancel(c); + if (queue->canAutoDelete() && !queue->hasExclusiveOwner()) { + Queue::tryAutoDelete(session.getBroker(), queue); + } + } +} + +void SemanticState::handle(intrusive_ptr<Message> msg) { + if (txBuffer.get()) { + TxPublish* deliverable(new TxPublish(msg)); + TxOp::shared_ptr op(deliverable); + route(msg, *deliverable); + txBuffer->enlist(op); + } else { + DeliverableMessage deliverable(msg); + route(msg, deliverable); + } +} + +namespace +{ +const std::string nullstring; +} + +void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) { + std::string exchangeName = msg->getExchangeName(); + //TODO: the following should be hidden behind message (using MessageAdapter or similar) + + // Do not replace the delivery-properties.exchange if it is is already set. + // This is used internally (by the cluster) to force the exchange name on a message. + // The client library ensures this is always empty for messages from normal clients. + if (msg->isA<MessageTransferBody>()) { + if (!msg->hasProperties<DeliveryProperties>() || + msg->getProperties<DeliveryProperties>()->getExchange().empty()) + msg->getProperties<DeliveryProperties>()->setExchange(exchangeName); + msg->setTimestamp(); + } + if (!cacheExchange || cacheExchange->getName() != exchangeName){ + cacheExchange = session.getBroker().getExchanges().get(exchangeName); + } + + /* verify the userid if specified: */ + std::string id = + msg->hasProperties<MessageProperties>() ? msg->getProperties<MessageProperties>()->getUserId() : nullstring; + + if (authMsg && !id.empty() && id != userID ) + { + QPID_LOG(debug, "authorised user id : " << userID << " but user id in message declared as " << id); + throw UnauthorizedAccessException(QPID_MSG("authorised user id : " << userID << " but user id in message declared as " << id)); + } + + if (acl && acl->doTransferAcl()) + { + if (!acl->authorise(getSession().getConnection().getUserId(),acl::ACT_PUBLISH,acl::OBJ_EXCHANGE,exchangeName, msg->getRoutingKey() )) + throw NotAllowedException(QPID_MSG(getSession().getConnection().getUserId() << " cannot publish to " << + exchangeName << " with routing-key " << msg->getRoutingKey())); + } + + cacheExchange->route(strategy, msg->getRoutingKey(), msg->getApplicationHeaders()); + + if (!strategy.delivered) { + //TODO:if discard-unroutable, just drop it + //TODO:else if accept-mode is explicit, reject it + //else route it to alternate exchange + if (cacheExchange->getAlternate()) { + cacheExchange->getAlternate()->route(strategy, msg->getRoutingKey(), msg->getApplicationHeaders()); + } + if (!strategy.delivered) { + msg->destroy(); + } + } + +} + +void SemanticState::requestDispatch() +{ + for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) { + requestDispatch(*(i->second)); + } +} + +void SemanticState::requestDispatch(ConsumerImpl& c) +{ + if(c.isBlocked()) + outputTasks.activateOutput(); +} + +void SemanticState::complete(DeliveryRecord& delivery) +{ + ConsumerImplMap::iterator i = consumers.find(delivery.getTag()); + if (i != consumers.end()) { + i->second->complete(delivery); + } +} + +void SemanticState::ConsumerImpl::complete(DeliveryRecord& delivery) +{ + if (!delivery.isComplete()) { + delivery.complete(); + if (windowing) { + if (msgCredit != 0xFFFFFFFF) msgCredit++; + if (byteCredit != 0xFFFFFFFF) byteCredit += delivery.getCredit(); + } + } +} + +void SemanticState::recover(bool requeue) +{ + if(requeue){ + //take copy and clear unacked as requeue may result in redelivery to this session + //which will in turn result in additions to unacked + DeliveryRecords copy = unacked; + unacked.clear(); + for_each(copy.rbegin(), copy.rend(), mem_fun_ref(&DeliveryRecord::requeue)); + }else{ + for_each(unacked.begin(), unacked.end(), boost::bind(&DeliveryRecord::redeliver, _1, this)); + //unconfirmed messages re redelivered and therefore have their + //id adjusted, confirmed messages are not and so the ordering + //w.r.t id is lost + unacked.sort(); + } +} + +void SemanticState::deliver(DeliveryRecord& msg) +{ + return deliveryAdapter.deliver(msg); +} + +SemanticState::ConsumerImpl& SemanticState::find(const std::string& destination) +{ + ConsumerImplMap::iterator i = consumers.find(destination); + if (i == consumers.end()) { + throw NotFoundException(QPID_MSG("Unknown destination " << destination)); + } else { + return *(i->second); + } +} + +void SemanticState::setWindowMode(const std::string& destination) +{ + find(destination).setWindowMode(); +} + +void SemanticState::setCreditMode(const std::string& destination) +{ + find(destination).setCreditMode(); +} + +void SemanticState::addByteCredit(const std::string& destination, uint32_t value) +{ + ConsumerImpl& c = find(destination); + c.addByteCredit(value); + requestDispatch(c); +} + + +void SemanticState::addMessageCredit(const std::string& destination, uint32_t value) +{ + ConsumerImpl& c = find(destination); + c.addMessageCredit(value); + requestDispatch(c); +} + +void SemanticState::flush(const std::string& destination) +{ + find(destination).flush(); +} + + +void SemanticState::stop(const std::string& destination) +{ + find(destination).stop(); +} + +void SemanticState::ConsumerImpl::setWindowMode() +{ + windowing = true; +} + +void SemanticState::ConsumerImpl::setCreditMode() +{ + windowing = false; +} + +void SemanticState::ConsumerImpl::addByteCredit(uint32_t value) +{ + if (byteCredit != 0xFFFFFFFF) { + byteCredit += value; + } +} + +void SemanticState::ConsumerImpl::addMessageCredit(uint32_t value) +{ + if (msgCredit != 0xFFFFFFFF) { + msgCredit += value; + } +} + +void SemanticState::ConsumerImpl::flush() +{ + while(queue->dispatch(shared_from_this())) + ; + stop(); +} + +void SemanticState::ConsumerImpl::stop() +{ + msgCredit = 0; + byteCredit = 0; +} + +Queue::shared_ptr SemanticState::getQueue(const string& name) const { + Queue::shared_ptr queue; + if (name.empty()) { + throw NotAllowedException(QPID_MSG("No queue name specified.")); + } else { + queue = session.getBroker().getQueues().find(name); + if (!queue) + throw NotFoundException(QPID_MSG("Queue not found: "<<name)); + } + return queue; +} + +AckRange SemanticState::findRange(DeliveryId first, DeliveryId last) +{ + return DeliveryRecord::findRange(unacked, first, last); +} + +void SemanticState::acquire(DeliveryId first, DeliveryId last, DeliveryIds& acquired) +{ + AckRange range = findRange(first, last); + for_each(range.start, range.end, AcquireFunctor(acquired)); +} + +void SemanticState::release(DeliveryId first, DeliveryId last, bool setRedelivered) +{ + AckRange range = findRange(first, last); + //release results in the message being added to the head so want + //to release in reverse order to keep the original transfer order + DeliveryRecords::reverse_iterator start(range.end); + DeliveryRecords::reverse_iterator end(range.start); + for_each(start, end, boost::bind(&DeliveryRecord::release, _1, setRedelivered)); +} + +void SemanticState::reject(DeliveryId first, DeliveryId last) +{ + AckRange range = findRange(first, last); + for_each(range.start, range.end, mem_fun_ref(&DeliveryRecord::reject)); + //need to remove the delivery records as well + unacked.erase(range.start, range.end); +} + +bool SemanticState::ConsumerImpl::hasOutput() { + return queue->checkForMessages(shared_from_this()); +} + +bool SemanticState::ConsumerImpl::doOutput() +{ + return queue->dispatch(shared_from_this()); +} + +void SemanticState::ConsumerImpl::enableNotify() +{ + Mutex::ScopedLock l(lock); + notifyEnabled = true; +} + +void SemanticState::ConsumerImpl::disableNotify() +{ + Mutex::ScopedLock l(lock); + notifyEnabled = false; +} + +bool SemanticState::ConsumerImpl::isNotifyEnabled() const { + Mutex::ScopedLock l(lock); + return notifyEnabled; +} + +void SemanticState::ConsumerImpl::notify() +{ + //TODO: alter this, don't want to hold locks across external + //calls; for now its is required to protect the notify() from + //having part of the object chain of the invocation being + //concurrently deleted + Mutex::ScopedLock l(lock); + if (notifyEnabled) parent->outputTasks.activateOutput(); +} + + +void SemanticState::accepted(DeliveryId first, DeliveryId last) +{ + AckRange range = findRange(first, last); + if (txBuffer.get()) { + //in transactional mode, don't dequeue or remove, just + //maintain set of acknowledged messages: + accumulatedAck.add(first, last); + + if (dtxBuffer.get()) { + //if enlisted in a dtx, copy the relevant slice from + //unacked and record it against that transaction + TxOp::shared_ptr txAck(new DtxAck(accumulatedAck, unacked)); + accumulatedAck.clear(); + dtxBuffer->enlist(txAck); + + //mark the relevant messages as 'ended' in unacked + for_each(range.start, range.end, mem_fun_ref(&DeliveryRecord::setEnded)); + + //if the messages are already completed, they can be + //removed from the record + unacked.remove_if(mem_fun_ref(&DeliveryRecord::isRedundant)); + + } + } else { + for_each(range.start, range.end, boost::bind(&DeliveryRecord::accept, _1, (TransactionContext*) 0)); + unacked.remove_if(mem_fun_ref(&DeliveryRecord::isRedundant)); + } +} + +void SemanticState::completed(DeliveryId first, DeliveryId last) +{ + AckRange range = findRange(first, last); + for_each(range.start, range.end, boost::bind(&SemanticState::complete, this, _1)); + unacked.remove_if(mem_fun_ref(&DeliveryRecord::isRedundant)); + requestDispatch(); +} + +void SemanticState::attached() +{ + for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) { + i->second->enableNotify(); + } +} + +void SemanticState::detached() +{ + for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) { + i->second->disableNotify(); + } +} + +}} // namespace qpid::broker diff --git a/RC9/qpid/cpp/src/qpid/broker/SemanticState.h b/RC9/qpid/cpp/src/qpid/broker/SemanticState.h new file mode 100644 index 0000000000..340017ddf0 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/SemanticState.h @@ -0,0 +1,232 @@ +#ifndef QPID_BROKER_SEMANTICSTATE_H +#define QPID_BROKER_SEMANTICSTATE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Consumer.h" +#include "Deliverable.h" +#include "DeliveryAdapter.h" +#include "DeliveryRecord.h" +#include "DtxBuffer.h" +#include "DtxManager.h" +#include "NameGenerator.h" +#include "TxBuffer.h" + +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/SequenceSet.h" +#include "qpid/framing/Uuid.h" +#include "qpid/sys/AggregateOutput.h" +#include "qpid/sys/Mutex.h" +#include "qpid/shared_ptr.h" +#include "AclModule.h" + +#include <list> +#include <map> +#include <vector> + +#include <boost/intrusive_ptr.hpp> +#include <boost/cast.hpp> + +namespace qpid { +namespace broker { + +class SessionContext; + +/** + * SemanticState holds the L3 and L4 state of an open session, whether + * attached to a channel or suspended. + */ +class SemanticState : public sys::OutputTask, + private boost::noncopyable +{ + public: + class ConsumerImpl : public Consumer, public sys::OutputTask, + public boost::enable_shared_from_this<ConsumerImpl> + { + mutable qpid::sys::Mutex lock; + SemanticState* const parent; + const string name; + const Queue::shared_ptr queue; + const bool ackExpected; + const bool acquire; + bool blocked; + bool windowing; + bool exclusive; + string resumeId; + uint64_t resumeTtl; + framing::FieldTable arguments; + uint32_t msgCredit; + uint32_t byteCredit; + bool notifyEnabled; + + bool checkCredit(boost::intrusive_ptr<Message>& msg); + void allocateCredit(boost::intrusive_ptr<Message>& msg); + + public: + typedef boost::shared_ptr<ConsumerImpl> shared_ptr; + + ConsumerImpl(SemanticState* parent, + const string& name, Queue::shared_ptr queue, + bool ack, bool acquire, bool exclusive, + const std::string& resumeId, uint64_t resumeTtl, const framing::FieldTable& arguments); + ~ConsumerImpl(); + OwnershipToken* getSession(); + bool deliver(QueuedMessage& msg); + bool filter(boost::intrusive_ptr<Message> msg); + bool accept(boost::intrusive_ptr<Message> msg); + + void disableNotify(); + void enableNotify(); + void notify(); + bool isNotifyEnabled() const; + + void setWindowMode(); + void setCreditMode(); + void addByteCredit(uint32_t value); + void addMessageCredit(uint32_t value); + void flush(); + void stop(); + void complete(DeliveryRecord&); + Queue::shared_ptr getQueue() const { return queue; } + bool isBlocked() const { return blocked; } + bool setBlocked(bool set) { std::swap(set, blocked); return set; } + + bool hasOutput(); + bool doOutput(); + + std::string getName() const { return name; } + + bool isAckExpected() const { return ackExpected; } + bool isAcquire() const { return acquire; } + bool isWindowing() const { return windowing; } + bool isExclusive() const { return exclusive; } + uint32_t getMsgCredit() const { return msgCredit; } + uint32_t getByteCredit() const { return byteCredit; } + std::string getResumeId() const { return resumeId; }; + uint64_t getResumeTtl() const { return resumeTtl; } + const framing::FieldTable& getArguments() const { return arguments; } + }; + + private: + typedef std::map<std::string, ConsumerImpl::shared_ptr> ConsumerImplMap; + typedef std::map<std::string, DtxBuffer::shared_ptr> DtxBufferMap; + + SessionContext& session; + DeliveryAdapter& deliveryAdapter; + ConsumerImplMap consumers; + NameGenerator tagGenerator; + DeliveryRecords unacked; + TxBuffer::shared_ptr txBuffer; + DtxBuffer::shared_ptr dtxBuffer; + bool dtxSelected; + DtxBufferMap suspendedXids; + framing::SequenceSet accumulatedAck; + boost::shared_ptr<Exchange> cacheExchange; + sys::AggregateOutput outputTasks; + AclModule* acl; + const bool authMsg; + const string userID; + + void route(boost::intrusive_ptr<Message> msg, Deliverable& strategy); + void checkDtxTimeout(); + + void complete(DeliveryRecord&); + AckRange findRange(DeliveryId first, DeliveryId last); + void requestDispatch(); + void requestDispatch(ConsumerImpl&); + void cancel(ConsumerImpl::shared_ptr); + + public: + SemanticState(DeliveryAdapter&, SessionContext&); + ~SemanticState(); + + SessionContext& getSession() { return session; } + + ConsumerImpl& find(const std::string& destination); + + /** + * Get named queue, never returns 0. + * @return: named queue + * @exception: ChannelException if no queue of that name is found. + * @exception: ConnectionException if name="" and session has no default. + */ + Queue::shared_ptr getQueue(const std::string& name) const; + + bool exists(const string& consumerTag); + + void consume(const string& destination, + Queue::shared_ptr queue, + bool ackRequired, bool acquire, bool exclusive, + const string& resumeId=string(), uint64_t resumeTtl=0, + const framing::FieldTable& = framing::FieldTable()); + + void cancel(const string& tag); + + void setWindowMode(const std::string& destination); + void setCreditMode(const std::string& destination); + void addByteCredit(const std::string& destination, uint32_t value); + void addMessageCredit(const std::string& destination, uint32_t value); + void flush(const std::string& destination); + void stop(const std::string& destination); + + void startTx(); + void commit(MessageStore* const store); + void rollback(); + void selectDtx(); + void startDtx(const std::string& xid, DtxManager& mgr, bool join); + void endDtx(const std::string& xid, bool fail); + void suspendDtx(const std::string& xid); + void resumeDtx(const std::string& xid); + void recover(bool requeue); + void deliver(DeliveryRecord& message); + void acquire(DeliveryId first, DeliveryId last, DeliveryIds& acquired); + void release(DeliveryId first, DeliveryId last, bool setRedelivered); + void reject(DeliveryId first, DeliveryId last); + void handle(boost::intrusive_ptr<Message> msg); + bool hasOutput() { return outputTasks.hasOutput(); } + bool doOutput() { return outputTasks.doOutput(); } + + //final 0-10 spec (completed and accepted are distinct): + void completed(DeliveryId deliveryTag, DeliveryId endTag); + void accepted(DeliveryId deliveryTag, DeliveryId endTag); + + void attached(); + void detached(); + + // Used by cluster to re-create replica sessions + static ConsumerImpl* castToConsumerImpl(OutputTask* p) { return boost::polymorphic_downcast<ConsumerImpl*>(p); } + + template <class F> void eachConsumer(F f) { outputTasks.eachOutput(boost::bind(f, boost::bind(castToConsumerImpl, _1))); } + DeliveryRecords& getUnacked() { return unacked; } + framing::SequenceSet getAccumulatedAck() const { return accumulatedAck; } + TxBuffer::shared_ptr getTxBuffer() const { return txBuffer; } + void setTxBuffer(const TxBuffer::shared_ptr& txb) { txBuffer = txb; } + void setAccumulatedAck(const framing::SequenceSet& s) { accumulatedAck = s; } + void record(const DeliveryRecord& delivery); +}; + +}} // namespace qpid::broker + + + + +#endif /*!QPID_BROKER_SEMANTICSTATE_H*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/SessionAdapter.cpp b/RC9/qpid/cpp/src/qpid/broker/SessionAdapter.cpp new file mode 100644 index 0000000000..4450d56efb --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/SessionAdapter.cpp @@ -0,0 +1,749 @@ +/* + * + * 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 "SessionAdapter.h" +#include "Connection.h" +#include "Queue.h" +#include "qpid/Exception.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/enum.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/SequenceSet.h" +#include "qpid/agent/ManagementAgent.h" +#include "qmf/org/apache/qpid/broker/EventExchangeDeclare.h" +#include "qmf/org/apache/qpid/broker/EventExchangeDelete.h" +#include "qmf/org/apache/qpid/broker/EventQueueDeclare.h" +#include "qmf/org/apache/qpid/broker/EventQueueDelete.h" +#include "qmf/org/apache/qpid/broker/EventBind.h" +#include "qmf/org/apache/qpid/broker/EventUnbind.h" +#include "qmf/org/apache/qpid/broker/EventSubscribe.h" +#include "qmf/org/apache/qpid/broker/EventUnsubscribe.h" +#include <boost/format.hpp> +#include <boost/cast.hpp> +#include <boost/bind.hpp> + +namespace qpid { +namespace broker { + +using namespace qpid; +using namespace qpid::framing; +using namespace qpid::framing::dtx; +using namespace qpid::management; +namespace _qmf = qmf::org::apache::qpid::broker; + +typedef std::vector<Queue::shared_ptr> QueueVector; + +SessionAdapter::SessionAdapter(SemanticState& s) : + HandlerImpl(s), + exchangeImpl(s), + queueImpl(s), + messageImpl(s), + executionImpl(s), + txImpl(s), + dtxImpl(s) +{} + + +void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const string& type, + const string& alternateExchange, + bool passive, bool durable, bool /*autoDelete*/, const FieldTable& args){ + + AclModule* acl = getBroker().getAcl(); + if (acl) { + std::map<acl::Property, std::string> params; + params.insert(make_pair(acl::PROP_TYPE, type)); + params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange)); + params.insert(make_pair(acl::PROP_PASSIVE, std::string(passive ? "true" : "false") )); + params.insert(make_pair(acl::PROP_DURABLE, std::string(durable ? "true" : "false"))); + if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_EXCHANGE,exchange,¶ms) ) + throw NotAllowedException("ACL denied exhange declare request"); + } + + //TODO: implement autoDelete + Exchange::shared_ptr alternate; + if (!alternateExchange.empty()) { + alternate = getBroker().getExchanges().get(alternateExchange); + } + if(passive){ + Exchange::shared_ptr actual(getBroker().getExchanges().get(exchange)); + checkType(actual, type); + checkAlternate(actual, alternate); + }else{ + try{ + std::pair<Exchange::shared_ptr, bool> response = getBroker().getExchanges().declare(exchange, type, durable, args); + if (response.second) { + if (durable) { + getBroker().getStore().create(*response.first, args); + } + if (alternate) { + response.first->setAlternate(alternate); + alternate->incAlternateUsers(); + } + } else { + checkType(response.first, type); + checkAlternate(response.first, alternate); + } + + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + if (agent) + agent->raiseEvent(_qmf::EventExchangeDeclare(getConnection().getUrl(), getConnection().getUserId(), exchange, type, + alternateExchange, durable, false, args, + response.second ? "created" : "existing")); + + }catch(UnknownExchangeTypeException& /*e*/){ + throw CommandInvalidException(QPID_MSG("Exchange type not implemented: " << type)); + } + } +} + +void SessionAdapter::ExchangeHandlerImpl::checkType(Exchange::shared_ptr exchange, const std::string& type) +{ + if (!type.empty() && exchange->getType() != type) { + throw NotAllowedException(QPID_MSG("Exchange declared to be of type " << exchange->getType() << ", requested " << type)); + } +} + +void SessionAdapter::ExchangeHandlerImpl::checkAlternate(Exchange::shared_ptr exchange, Exchange::shared_ptr alternate) +{ + if (alternate && alternate != exchange->getAlternate()) + throw NotAllowedException(QPID_MSG("Exchange declared with alternate-exchange " + << exchange->getAlternate()->getName() << ", requested " + << alternate->getName())); +} + +void SessionAdapter::ExchangeHandlerImpl::delete_(const string& name, bool /*ifUnused*/) +{ + AclModule* acl = getBroker().getAcl(); + if (acl) { + if (!acl->authorise(getConnection().getUserId(),acl::ACT_DELETE,acl::OBJ_EXCHANGE,name,NULL) ) + throw NotAllowedException("ACL denied exhange delete request"); + } + + //TODO: implement unused + Exchange::shared_ptr exchange(getBroker().getExchanges().get(name)); + if (exchange->inUseAsAlternate()) throw NotAllowedException(QPID_MSG("Exchange in use as alternate-exchange.")); + if (exchange->isDurable()) getBroker().getStore().destroy(*exchange); + if (exchange->getAlternate()) exchange->getAlternate()->decAlternateUsers(); + getBroker().getExchanges().destroy(name); + + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + if (agent) + agent->raiseEvent(_qmf::EventExchangeDelete(getConnection().getUrl(), getConnection().getUserId(), name)); +} + +ExchangeQueryResult SessionAdapter::ExchangeHandlerImpl::query(const string& name) +{ + AclModule* acl = getBroker().getAcl(); + if (acl) { + if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_EXCHANGE,name,NULL) ) + throw NotAllowedException("ACL denied exhange query request"); + } + + try { + Exchange::shared_ptr exchange(getBroker().getExchanges().get(name)); + return ExchangeQueryResult(exchange->getType(), exchange->isDurable(), false, exchange->getArgs()); + } catch (const NotFoundException& /*e*/) { + return ExchangeQueryResult("", false, true, FieldTable()); + } +} + +void SessionAdapter::ExchangeHandlerImpl::bind(const string& queueName, + const string& exchangeName, const string& routingKey, + const FieldTable& arguments) +{ + AclModule* acl = getBroker().getAcl(); + if (acl) { + if (!acl->authorise(getConnection().getUserId(),acl::ACT_BIND,acl::OBJ_EXCHANGE,exchangeName,routingKey) ) + throw NotAllowedException("ACL denied exhange bind request"); + } + + Queue::shared_ptr queue = getQueue(queueName); + Exchange::shared_ptr exchange = getBroker().getExchanges().get(exchangeName); + if(exchange){ + string exchangeRoutingKey = routingKey.empty() && queueName.empty() ? queue->getName() : routingKey; + if (exchange->bind(queue, exchangeRoutingKey, &arguments)) { + queue->bound(exchangeName, routingKey, arguments); + if (exchange->isDurable() && queue->isDurable()) { + getBroker().getStore().bind(*exchange, *queue, routingKey, arguments); + } + + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + if (agent) + agent->raiseEvent(_qmf::EventBind(getConnection().getUrl(), getConnection().getUserId(), exchangeName, queueName, exchangeRoutingKey, arguments)); + } + }else{ + throw NotFoundException("Bind failed. No such exchange: " + exchangeName); + } +} + +void SessionAdapter::ExchangeHandlerImpl::unbind(const string& queueName, + const string& exchangeName, + const string& routingKey) +{ + AclModule* acl = getBroker().getAcl(); + if (acl) { + std::map<acl::Property, std::string> params; + params.insert(make_pair(acl::PROP_QUEUENAME, queueName)); + params.insert(make_pair(acl::PROP_ROUTINGKEY, routingKey)); + if (!acl->authorise(getConnection().getUserId(),acl::ACT_UNBIND,acl::OBJ_EXCHANGE,exchangeName,¶ms) ) + throw NotAllowedException("ACL denied exchange unbind request"); + } + + Queue::shared_ptr queue = getQueue(queueName); + if (!queue.get()) throw NotFoundException("Unbind failed. No such exchange: " + exchangeName); + + Exchange::shared_ptr exchange = getBroker().getExchanges().get(exchangeName); + if (!exchange.get()) throw NotFoundException("Unbind failed. No such exchange: " + exchangeName); + + //TODO: revise unbind to rely solely on binding key (not args) + if (exchange->unbind(queue, routingKey, 0)) { + if (exchange->isDurable() && queue->isDurable()) + getBroker().getStore().unbind(*exchange, *queue, routingKey, FieldTable()); + + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + if (agent) + agent->raiseEvent(_qmf::EventUnbind(getConnection().getUrl(), getConnection().getUserId(), exchangeName, queueName, routingKey)); + } +} + +ExchangeBoundResult SessionAdapter::ExchangeHandlerImpl::bound(const std::string& exchangeName, + const std::string& queueName, + const std::string& key, + const framing::FieldTable& args) +{ + AclModule* acl = getBroker().getAcl(); + if (acl) { + std::map<acl::Property, std::string> params; + params.insert(make_pair(acl::PROP_QUEUENAME, queueName)); + params.insert(make_pair(acl::PROP_ROUTINGKEY, key)); + if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_EXCHANGE,exchangeName,¶ms) ) + throw NotAllowedException("ACL denied exhange bound request"); + } + + Exchange::shared_ptr exchange; + try { + exchange = getBroker().getExchanges().get(exchangeName); + } catch (const NotFoundException&) {} + + Queue::shared_ptr queue; + if (!queueName.empty()) { + queue = getBroker().getQueues().find(queueName); + } + + if (!exchange) { + return ExchangeBoundResult(true, false, false, false, false); + } else if (!queueName.empty() && !queue) { + return ExchangeBoundResult(false, true, false, false, false); + } else if (exchange->isBound(queue, key.empty() ? 0 : &key, args.count() > 0 ? &args : &args)) { + return ExchangeBoundResult(false, false, false, false, false); + } else { + //need to test each specified option individually + bool queueMatched = queueName.empty() || exchange->isBound(queue, 0, 0); + bool keyMatched = key.empty() || exchange->isBound(Queue::shared_ptr(), &key, 0); + bool argsMatched = args.count() == 0 || exchange->isBound(Queue::shared_ptr(), 0, &args); + + return ExchangeBoundResult(false, false, !queueMatched, !keyMatched, !argsMatched); + } +} + +SessionAdapter::QueueHandlerImpl::QueueHandlerImpl(SemanticState& session) : HandlerHelper(session), broker(getBroker()) +{} + + +SessionAdapter::QueueHandlerImpl::~QueueHandlerImpl() +{ + try { + destroyExclusiveQueues(); + } catch (std::exception& e) { + QPID_LOG(error, e.what()); + } +} + +void SessionAdapter::QueueHandlerImpl::destroyExclusiveQueues() +{ + while (!exclusiveQueues.empty()) { + Queue::shared_ptr q(exclusiveQueues.front()); + q->releaseExclusiveOwnership(); + if (q->canAutoDelete()) { + Queue::tryAutoDelete(broker, q); + } + exclusiveQueues.erase(exclusiveQueues.begin()); + } +} + + +bool SessionAdapter::QueueHandlerImpl::isLocal(const ConnectionToken* t) const +{ + return session.isLocal(t); +} + + +QueueQueryResult SessionAdapter::QueueHandlerImpl::query(const string& name) +{ + AclModule* acl = getBroker().getAcl(); + if (acl) { + if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_QUEUE,name,NULL) ) + throw NotAllowedException("ACL denied queue query request"); + } + + Queue::shared_ptr queue = session.getBroker().getQueues().find(name); + if (queue) { + + Exchange::shared_ptr alternateExchange = queue->getAlternateExchange(); + + return QueueQueryResult(queue->getName(), + alternateExchange ? alternateExchange->getName() : "", + queue->isDurable(), + queue->hasExclusiveOwner(), + queue->isAutoDelete(), + queue->getSettings(), + queue->getMessageCount(), + queue->getConsumerCount()); + } else { + return QueueQueryResult(); + } +} + +void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string& alternateExchange, + bool passive, bool durable, bool exclusive, + bool autoDelete, const qpid::framing::FieldTable& arguments) +{ + AclModule* acl = getBroker().getAcl(); + if (acl) { + std::map<acl::Property, std::string> params; + params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange)); + params.insert(make_pair(acl::PROP_PASSIVE, std::string(passive ? "true" : "false") )); + params.insert(make_pair(acl::PROP_DURABLE, std::string(durable ? "true" : "false"))); + params.insert(make_pair(acl::PROP_EXCLUSIVE, std::string(exclusive ? "true" : "false"))); + params.insert(make_pair(acl::PROP_AUTODELETE, std::string(autoDelete ? "true" : "false"))); + if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_QUEUE,name,¶ms) ) + throw NotAllowedException("ACL denied queue create request"); + } + + Exchange::shared_ptr alternate; + if (!alternateExchange.empty()) { + alternate = getBroker().getExchanges().get(alternateExchange); + } + Queue::shared_ptr queue; + if (passive && !name.empty()) { + queue = getQueue(name); + //TODO: check alternate-exchange is as expected + } else { + std::pair<Queue::shared_ptr, bool> queue_created = + getBroker().getQueues().declare(name, durable, + autoDelete, + exclusive ? &session : 0); + queue = queue_created.first; + assert(queue); + if (queue_created.second) { // This is a new queue + if (alternate) { + queue->setAlternateExchange(alternate); + alternate->incAlternateUsers(); + } + + //apply settings & create persistent record if required + queue_created.first->create(arguments); + + //add default binding: + getBroker().getExchanges().getDefault()->bind(queue, name, 0); + queue->bound(getBroker().getExchanges().getDefault()->getName(), name, arguments); + + //handle automatic cleanup: + if (exclusive) { + exclusiveQueues.push_back(queue); + } + } else { + if (exclusive && queue->setExclusiveOwner(&session)) { + exclusiveQueues.push_back(queue); + } + } + + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + if (agent) + agent->raiseEvent(_qmf::EventQueueDeclare(getConnection().getUrl(), getConnection().getUserId(), + name, durable, exclusive, autoDelete, arguments, + queue_created.second ? "created" : "existing")); + } + + if (exclusive && !queue->isExclusiveOwner(&session)) + throw ResourceLockedException(QPID_MSG("Cannot grant exclusive access to queue " + << queue->getName())); +} + + +void SessionAdapter::QueueHandlerImpl::purge(const string& queue){ + AclModule* acl = getBroker().getAcl(); + if (acl) + { + if (!acl->authorise(getConnection().getUserId(),acl::ACT_PURGE,acl::OBJ_QUEUE,queue,NULL) ) + throw NotAllowedException("ACL denied queue purge request"); + } + getQueue(queue)->purge(); +} + +void SessionAdapter::QueueHandlerImpl::delete_(const string& queue, bool ifUnused, bool ifEmpty){ + + AclModule* acl = getBroker().getAcl(); + if (acl) + { + if (!acl->authorise(getConnection().getUserId(),acl::ACT_DELETE,acl::OBJ_QUEUE,queue,NULL) ) + throw NotAllowedException("ACL denied queue delete request"); + } + + Queue::shared_ptr q = getQueue(queue); + if (q->hasExclusiveOwner() && !q->isExclusiveOwner(&session)) + throw ResourceLockedException(QPID_MSG("Cannot delete queue " + << queue << "; it is exclusive to another session")); + if(ifEmpty && q->getMessageCount() > 0){ + throw PreconditionFailedException("Queue not empty."); + }else if(ifUnused && q->getConsumerCount() > 0){ + throw PreconditionFailedException("Queue in use."); + }else{ + //remove the queue from the list of exclusive queues if necessary + if(q->isExclusiveOwner(&getConnection())){ + QueueVector::iterator i = std::find(getConnection().exclusiveQueues.begin(), getConnection().exclusiveQueues.end(), q); + if(i < getConnection().exclusiveQueues.end()) getConnection().exclusiveQueues.erase(i); + } + q->destroy(); + getBroker().getQueues().destroy(queue); + q->unbind(getBroker().getExchanges(), q); + + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + if (agent) + agent->raiseEvent(_qmf::EventQueueDelete(getConnection().getUrl(), getConnection().getUserId(), queue)); + } +} + + +SessionAdapter::MessageHandlerImpl::MessageHandlerImpl(SemanticState& s) : + HandlerHelper(s), + releaseRedeliveredOp(boost::bind(&SemanticState::release, &state, _1, _2, true)), + releaseOp(boost::bind(&SemanticState::release, &state, _1, _2, false)), + rejectOp(boost::bind(&SemanticState::reject, &state, _1, _2)), + acceptOp(boost::bind(&SemanticState::accepted, &state, _1, _2)) + {} + +// +// Message class method handlers +// + +void SessionAdapter::MessageHandlerImpl::transfer(const string& /*destination*/, + uint8_t /*acceptMode*/, + uint8_t /*acquireMode*/) +{ + //not yet used (content containing assemblies treated differently at present + std::cout << "SessionAdapter::MessageHandlerImpl::transfer() called" << std::endl; +} + +void SessionAdapter::MessageHandlerImpl::release(const SequenceSet& transfers, bool setRedelivered) +{ + transfers.for_each(setRedelivered ? releaseRedeliveredOp : releaseOp); +} + +void +SessionAdapter::MessageHandlerImpl::subscribe(const string& queueName, + const string& destination, + uint8_t acceptMode, + uint8_t acquireMode, + bool exclusive, + const string& resumeId, + uint64_t resumeTtl, + const FieldTable& arguments) +{ + + AclModule* acl = getBroker().getAcl(); + if (acl) + { + // add flags as needed + if (!acl->authorise(getConnection().getUserId(),acl::ACT_CONSUME,acl::OBJ_QUEUE,queueName,NULL) ) + throw NotAllowedException("ACL denied Queue subscribe request"); + } + + Queue::shared_ptr queue = getQueue(queueName); + if(!destination.empty() && state.exists(destination)) + throw NotAllowedException(QPID_MSG("Consumer tags must be unique")); + if (queue->hasExclusiveOwner() && !queue->isExclusiveOwner(&session)) + throw ResourceLockedException(QPID_MSG("Cannot subscribe to exclusive queue " + << queue->getName())); + + state.consume(destination, queue, + acceptMode == 0, acquireMode == 0, exclusive, + resumeId, resumeTtl, arguments); + + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + if (agent) + agent->raiseEvent(_qmf::EventSubscribe(getConnection().getUrl(), getConnection().getUserId(), + queueName, destination, exclusive, arguments)); +} + +void +SessionAdapter::MessageHandlerImpl::cancel(const string& destination ) +{ + state.cancel(destination); + + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + if (agent) + agent->raiseEvent(_qmf::EventUnsubscribe(getConnection().getUrl(), getConnection().getUserId(), destination)); +} + +void +SessionAdapter::MessageHandlerImpl::reject(const SequenceSet& transfers, uint16_t /*code*/, const string& /*text*/ ) +{ + transfers.for_each(rejectOp); +} + +void SessionAdapter::MessageHandlerImpl::flow(const std::string& destination, uint8_t unit, uint32_t value) +{ + if (unit == 0) { + //message + state.addMessageCredit(destination, value); + } else if (unit == 1) { + //bytes + state.addByteCredit(destination, value); + } else { + //unknown + throw InvalidArgumentException(QPID_MSG("Invalid value for unit " << unit)); + } + +} + +void SessionAdapter::MessageHandlerImpl::setFlowMode(const std::string& destination, uint8_t mode) +{ + if (mode == 0) { + //credit + state.setCreditMode(destination); + } else if (mode == 1) { + //window + state.setWindowMode(destination); + } else{ + throw InvalidArgumentException(QPID_MSG("Invalid value for mode " << mode)); + } +} + +void SessionAdapter::MessageHandlerImpl::flush(const std::string& destination) +{ + state.flush(destination); +} + +void SessionAdapter::MessageHandlerImpl::stop(const std::string& destination) +{ + state.stop(destination); +} + +void SessionAdapter::MessageHandlerImpl::accept(const framing::SequenceSet& commands) +{ + + commands.for_each(acceptOp); +} + +framing::MessageAcquireResult SessionAdapter::MessageHandlerImpl::acquire(const framing::SequenceSet& transfers) +{ + // FIXME aconway 2008-05-12: create SequenceSet directly, no need for intermediate results vector. + SequenceNumberSet results; + RangedOperation f = boost::bind(&SemanticState::acquire, &state, _1, _2, boost::ref(results)); + transfers.for_each(f); + + results = results.condense(); + SequenceSet acquisitions; + RangedOperation g = boost::bind(&SequenceSet::add, &acquisitions, _1, _2); + results.processRanges(g); + + return MessageAcquireResult(acquisitions); +} + +framing::MessageResumeResult SessionAdapter::MessageHandlerImpl::resume(const std::string& /*destination*/, + const std::string& /*resumeId*/) +{ + throw NotImplementedException("resuming transfers not yet supported"); +} + + + +void SessionAdapter::ExecutionHandlerImpl::sync() {} //essentially a no-op + +void SessionAdapter::ExecutionHandlerImpl::result(const SequenceNumber& /*commandId*/, const string& /*value*/) +{ + //TODO: but currently never used client->server +} + +void SessionAdapter::ExecutionHandlerImpl::exception(uint16_t /*errorCode*/, + const SequenceNumber& /*commandId*/, + uint8_t /*classCode*/, + uint8_t /*commandCode*/, + uint8_t /*fieldIndex*/, + const std::string& /*description*/, + const framing::FieldTable& /*errorInfo*/) +{ + //TODO: again, not really used client->server but may be important + //for inter-broker links +} + + + +void SessionAdapter::TxHandlerImpl::select() +{ + state.startTx(); +} + +void SessionAdapter::TxHandlerImpl::commit() +{ + state.commit(&getBroker().getStore()); +} + +void SessionAdapter::TxHandlerImpl::rollback() +{ + state.rollback(); +} + +std::string SessionAdapter::DtxHandlerImpl::convert(const framing::Xid& xid) +{ + std::string encoded; + encode(xid, encoded); + return encoded; +} + +void SessionAdapter::DtxHandlerImpl::select() +{ + state.selectDtx(); +} + +XaResult SessionAdapter::DtxHandlerImpl::end(const Xid& xid, + bool fail, + bool suspend) +{ + try { + if (fail) { + state.endDtx(convert(xid), true); + if (suspend) { + throw CommandInvalidException(QPID_MSG("End and suspend cannot both be set.")); + } else { + return XaResult(XA_STATUS_XA_RBROLLBACK); + } + } else { + if (suspend) { + state.suspendDtx(convert(xid)); + } else { + state.endDtx(convert(xid), false); + } + return XaResult(XA_STATUS_XA_OK); + } + } catch (const DtxTimeoutException& /*e*/) { + return XaResult(XA_STATUS_XA_RBTIMEOUT); + } +} + +XaResult SessionAdapter::DtxHandlerImpl::start(const Xid& xid, + bool join, + bool resume) +{ + if (join && resume) { + throw CommandInvalidException(QPID_MSG("Join and resume cannot both be set.")); + } + try { + if (resume) { + state.resumeDtx(convert(xid)); + } else { + state.startDtx(convert(xid), getBroker().getDtxManager(), join); + } + return XaResult(XA_STATUS_XA_OK); + } catch (const DtxTimeoutException& /*e*/) { + return XaResult(XA_STATUS_XA_RBTIMEOUT); + } +} + +XaResult SessionAdapter::DtxHandlerImpl::prepare(const Xid& xid) +{ + try { + bool ok = getBroker().getDtxManager().prepare(convert(xid)); + return XaResult(ok ? XA_STATUS_XA_OK : XA_STATUS_XA_RBROLLBACK); + } catch (const DtxTimeoutException& /*e*/) { + return XaResult(XA_STATUS_XA_RBTIMEOUT); + } +} + +XaResult SessionAdapter::DtxHandlerImpl::commit(const Xid& xid, + bool onePhase) +{ + try { + bool ok = getBroker().getDtxManager().commit(convert(xid), onePhase); + return XaResult(ok ? XA_STATUS_XA_OK : XA_STATUS_XA_RBROLLBACK); + } catch (const DtxTimeoutException& /*e*/) { + return XaResult(XA_STATUS_XA_RBTIMEOUT); + } +} + + +XaResult SessionAdapter::DtxHandlerImpl::rollback(const Xid& xid) +{ + try { + getBroker().getDtxManager().rollback(convert(xid)); + return XaResult(XA_STATUS_XA_OK); + } catch (const DtxTimeoutException& /*e*/) { + return XaResult(XA_STATUS_XA_RBTIMEOUT); + } +} + +DtxRecoverResult SessionAdapter::DtxHandlerImpl::recover() +{ + std::set<std::string> xids; + getBroker().getStore().collectPreparedXids(xids); + /* + * create array of long structs + */ + Array indoubt(0xAB); + for (std::set<std::string>::iterator i = xids.begin(); i != xids.end(); i++) { + boost::shared_ptr<FieldValue> xid(new Struct32Value(*i)); + indoubt.add(xid); + } + return DtxRecoverResult(indoubt); +} + +void SessionAdapter::DtxHandlerImpl::forget(const Xid& xid) +{ + //Currently no heuristic completion is supported, so this should never be used. + throw NotImplementedException(QPID_MSG("Forget not implemented. Branch with xid " << xid << " not heuristically completed!")); +} + +DtxGetTimeoutResult SessionAdapter::DtxHandlerImpl::getTimeout(const Xid& xid) +{ + uint32_t timeout = getBroker().getDtxManager().getTimeout(convert(xid)); + return DtxGetTimeoutResult(timeout); +} + + +void SessionAdapter::DtxHandlerImpl::setTimeout(const Xid& xid, + uint32_t timeout) +{ + getBroker().getDtxManager().setTimeout(convert(xid), timeout); +} + + +Queue::shared_ptr SessionAdapter::HandlerHelper::getQueue(const string& name) const { + Queue::shared_ptr queue; + if (name.empty()) { + throw framing::IllegalArgumentException(QPID_MSG("No queue name specified.")); + } else { + queue = session.getBroker().getQueues().find(name); + if (!queue) + throw framing::NotFoundException(QPID_MSG("Queue not found: "<<name)); + } + return queue; +} + +}} // namespace qpid::broker + + diff --git a/RC9/qpid/cpp/src/qpid/broker/SessionAdapter.h b/RC9/qpid/cpp/src/qpid/broker/SessionAdapter.h new file mode 100644 index 0000000000..60a5a0f285 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/SessionAdapter.h @@ -0,0 +1,260 @@ +#ifndef _broker_SessionAdapter_h +#define _broker_SessionAdapter_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "HandlerImpl.h" + +#include "ConnectionToken.h" +#include "OwnershipToken.h" +#include "qpid/Exception.h" +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/StructHelper.h" + +#include <vector> +#include <boost/function.hpp> +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace broker { + +class Channel; +class Connection; +class Broker; +class Queue; + +/** + * Per-channel protocol adapter. + * + * A container for a collection of AMQP-class adapters that translate + * AMQP method bodies into calls on the core Broker objects. Each + * adapter class also provides a client proxy to send methods to the + * peer. + * + */ + class SessionAdapter : public HandlerImpl, public framing::AMQP_ServerOperations +{ + public: + SessionAdapter(SemanticState& session); + + framing::ProtocolVersion getVersion() const { return session.getConnection().getVersion();} + + MessageHandler* getMessageHandler(){ return &messageImpl; } + ExchangeHandler* getExchangeHandler(){ return &exchangeImpl; } + QueueHandler* getQueueHandler(){ return &queueImpl; } + ExecutionHandler* getExecutionHandler(){ return &executionImpl; } + TxHandler* getTxHandler(){ return &txImpl; } + DtxHandler* getDtxHandler(){ return &dtxImpl; } + + ConnectionHandler* getConnectionHandler() { throw framing::NotImplementedException("Class not implemented"); } + SessionHandler* getSessionHandler() { throw framing::NotImplementedException("Class not implemented"); } + FileHandler* getFileHandler() { throw framing::NotImplementedException("Class not implemented"); } + StreamHandler* getStreamHandler() { throw framing::NotImplementedException("Class not implemented"); } + + private: + //common base for utility methods etc that are specific to this adapter + struct HandlerHelper : public HandlerImpl + { + HandlerHelper(SemanticState& s) : HandlerImpl(s) {} + + Queue::shared_ptr getQueue(const string& name) const; + }; + + + class ExchangeHandlerImpl : + public ExchangeHandler, + public HandlerHelper + { + public: + ExchangeHandlerImpl(SemanticState& session) : HandlerHelper(session) {} + + void declare(const std::string& exchange, const std::string& type, + const std::string& alternateExchange, + bool passive, bool durable, bool autoDelete, + const qpid::framing::FieldTable& arguments); + void delete_(const std::string& exchange, bool ifUnused); + framing::ExchangeQueryResult query(const std::string& name); + void bind(const std::string& queue, + const std::string& exchange, const std::string& routingKey, + const qpid::framing::FieldTable& arguments); + void unbind(const std::string& queue, + const std::string& exchange, + const std::string& routingKey); + framing::ExchangeBoundResult bound(const std::string& exchange, + const std::string& queue, + const std::string& routingKey, + const framing::FieldTable& arguments); + private: + void checkType(shared_ptr<Exchange> exchange, const std::string& type); + + void checkAlternate(shared_ptr<Exchange> exchange, + shared_ptr<Exchange> alternate); + }; + + class QueueHandlerImpl : public QueueHandler, + public HandlerHelper + { + Broker& broker; + std::vector< boost::shared_ptr<Queue> > exclusiveQueues; + + public: + QueueHandlerImpl(SemanticState& session); + ~QueueHandlerImpl(); + + void declare(const std::string& queue, + const std::string& alternateExchange, + bool passive, bool durable, bool exclusive, + bool autoDelete, + const qpid::framing::FieldTable& arguments); + void delete_(const std::string& queue, + bool ifUnused, bool ifEmpty); + void purge(const std::string& queue); + framing::QueueQueryResult query(const std::string& queue); + bool isLocal(const ConnectionToken* t) const; + + void destroyExclusiveQueues(); + }; + + class MessageHandlerImpl : + public MessageHandler, + public HandlerHelper + { + typedef boost::function<void(DeliveryId, DeliveryId)> RangedOperation; + RangedOperation releaseRedeliveredOp; + RangedOperation releaseOp; + RangedOperation rejectOp; + RangedOperation acceptOp; + + public: + MessageHandlerImpl(SemanticState& session); + void transfer(const string& destination, + uint8_t acceptMode, + uint8_t acquireMode); + + void accept(const framing::SequenceSet& commands); + + void reject(const framing::SequenceSet& commands, + uint16_t code, + const string& text); + + void release(const framing::SequenceSet& commands, + bool setRedelivered); + + framing::MessageAcquireResult acquire(const framing::SequenceSet&); + + void subscribe(const string& queue, + const string& destination, + uint8_t acceptMode, + uint8_t acquireMode, + bool exclusive, + const string& resumeId, + uint64_t resumeTtl, + const framing::FieldTable& arguments); + + void cancel(const string& destination); + + void setFlowMode(const string& destination, + uint8_t flowMode); + + void flow(const string& destination, + uint8_t unit, + uint32_t value); + + void flush(const string& destination); + + void stop(const string& destination); + + framing::MessageResumeResult resume(const std::string& destination, + const std::string& resumeId); + + }; + + class ExecutionHandlerImpl : public ExecutionHandler, public HandlerHelper + { + public: + ExecutionHandlerImpl(SemanticState& session) : HandlerHelper(session) {} + + void sync(); + void result(const framing::SequenceNumber& commandId, const string& value); + void exception(uint16_t errorCode, + const framing::SequenceNumber& commandId, + uint8_t classCode, + uint8_t commandCode, + uint8_t fieldIndex, + const std::string& description, + const framing::FieldTable& errorInfo); + + }; + + class TxHandlerImpl : public TxHandler, public HandlerHelper + { + public: + TxHandlerImpl(SemanticState& session) : HandlerHelper(session) {} + + void select(); + void commit(); + void rollback(); + }; + + class DtxHandlerImpl : public DtxHandler, public HandlerHelper, private framing::StructHelper + { + std::string convert(const framing::Xid& xid); + + public: + DtxHandlerImpl(SemanticState& session) : HandlerHelper(session) {} + + void select(); + + framing::XaResult start(const framing::Xid& xid, + bool join, + bool resume); + + framing::XaResult end(const framing::Xid& xid, + bool fail, + bool suspend); + + framing::XaResult commit(const framing::Xid& xid, + bool onePhase); + + void forget(const framing::Xid& xid); + + framing::DtxGetTimeoutResult getTimeout(const framing::Xid& xid); + + framing::XaResult prepare(const framing::Xid& xid); + + framing::DtxRecoverResult recover(); + + framing::XaResult rollback(const framing::Xid& xid); + + void setTimeout(const framing::Xid& xid, uint32_t timeout); + }; + + ExchangeHandlerImpl exchangeImpl; + QueueHandlerImpl queueImpl; + MessageHandlerImpl messageImpl; + ExecutionHandlerImpl executionImpl; + TxHandlerImpl txImpl; + DtxHandlerImpl dtxImpl; +}; +}} // namespace qpid::broker + + + +#endif /*!_broker_SessionAdapter_h*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/SessionContext.h b/RC9/qpid/cpp/src/qpid/broker/SessionContext.h new file mode 100644 index 0000000000..7a277964ab --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/SessionContext.h @@ -0,0 +1,52 @@ +#ifndef QPID_BROKER_SESSIONCONTEXT_H +#define QPID_BROKER_SESSIONCONTEXT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/AMQP_ClientProxy.h" +#include "qpid/framing/amqp_types.h" +#include "qpid/sys/OutputControl.h" +#include "ConnectionState.h" +#include "OwnershipToken.h" + + +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace broker { + +class SessionContext : public OwnershipToken, public sys::OutputControl +{ + public: + virtual ~SessionContext(){} + virtual bool isLocal(const ConnectionToken* t) const = 0; + virtual ConnectionState& getConnection() = 0; + virtual framing::AMQP_ClientProxy& getProxy() = 0; + virtual Broker& getBroker() = 0; +}; + +}} // namespace qpid::broker + + + +#endif /*!QPID_BROKER_SESSIONCONTEXT_H*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/SessionHandler.cpp b/RC9/qpid/cpp/src/qpid/broker/SessionHandler.cpp new file mode 100644 index 0000000000..163102d008 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/SessionHandler.cpp @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SessionHandler.h" +#include "SessionState.h" +#include "Connection.h" +#include "qpid/log/Statement.h" + +#include <boost/bind.hpp> + +namespace qpid { +namespace broker { +using namespace framing; +using namespace std; +using namespace qpid::sys; + +SessionHandler::SessionHandler(Connection& c, ChannelId ch) + : amqp_0_10::SessionHandler(&c.getOutput(), ch), + connection(c), + proxy(out) +{} + +SessionHandler::~SessionHandler() {} + +namespace { +ClassId classId(AMQMethodBody* m) { return m ? m->amqpMethodId() : 0; } +MethodId methodId(AMQMethodBody* m) { return m ? m->amqpClassId() : 0; } +} // namespace + +void SessionHandler::channelException(framing::session::DetachCode, const std::string&) { + handleDetach(); +} + +void SessionHandler::connectionException(framing::connection::CloseCode code, const std::string& msg) { + connection.close(code, msg); +} + +ConnectionState& SessionHandler::getConnection() { return connection; } + +const ConnectionState& SessionHandler::getConnection() const { return connection; } + +void SessionHandler::handleDetach() { + amqp_0_10::SessionHandler::handleDetach(); + assert(&connection.getChannel(channel.get()) == this); + if (session.get()) + connection.getBroker().getSessionManager().detach(session); + assert(!session.get()); + connection.closeChannel(channel.get()); +} + +void SessionHandler::setState(const std::string& name, bool force) { + assert(!session.get()); + SessionId id(connection.getUserId(), name); + session = connection.broker.getSessionManager().attach(*this, id, force); +} + +void SessionHandler::detaching() +{ + assert(session.get()); + session->disableOutput(); +} + +FrameHandler* SessionHandler::getInHandler() { return session.get() ? &session->in : 0; } +qpid::SessionState* SessionHandler::getState() { return session.get(); } + +void SessionHandler::readyToSend() { + if (session.get()) session->readyToSend(); +} + +// TODO aconway 2008-05-12: hacky - handle attached for bridge clients. +// We need to integrate the client code so we can run a real client +// in the bridge. +// +void SessionHandler::attached(const std::string& name) { + if (session.get()) + checkName(name); + else { + SessionId id(connection.getUserId(), name); + SessionState::Configuration config = connection.broker.getSessionManager().getSessionConfig(); + session.reset(new SessionState(connection.getBroker(), *this, id, config)); +} +} + +}} // namespace qpid::broker diff --git a/RC9/qpid/cpp/src/qpid/broker/SessionHandler.h b/RC9/qpid/cpp/src/qpid/broker/SessionHandler.h new file mode 100644 index 0000000000..7449db1560 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/SessionHandler.h @@ -0,0 +1,81 @@ +#ifndef QPID_BROKER_SESSIONHANDLER_H +#define QPID_BROKER_SESSIONHANDLER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/amqp_0_10/SessionHandler.h" +#include "qpid/framing/AMQP_ClientProxy.h" + +namespace qpid { +class SessionState; + +namespace broker { + +class Connection; +class ConnectionState; +class SessionState; + +/** + * A SessionHandler is associated with each active channel. It + * receives incoming frames, handles session controls and manages the + * association between the channel and a session. + */ +class SessionHandler : public amqp_0_10::SessionHandler { + public: + SessionHandler(Connection&, framing::ChannelId); + ~SessionHandler(); + + /** Get broker::SessionState */ + SessionState* getSession() { return session.get(); } + const SessionState* getSession() const { return session.get(); } + + ConnectionState& getConnection(); + const ConnectionState& getConnection() const; + + framing::AMQP_ClientProxy& getProxy() { return proxy; } + const framing::AMQP_ClientProxy& getProxy() const { return proxy; } + + virtual void handleDetach(); + + // Overrides + void attached(const std::string& name); + + protected: + virtual void setState(const std::string& sessionName, bool force); + virtual qpid::SessionState* getState(); + virtual framing::FrameHandler* getInHandler(); + virtual void channelException(framing::session::DetachCode code, const std::string& msg); + virtual void connectionException(framing::connection::CloseCode code, const std::string& msg); + virtual void detaching(); + virtual void readyToSend(); + + private: + Connection& connection; + framing::AMQP_ClientProxy proxy; + std::auto_ptr<SessionState> session; +}; + +}} // namespace qpid::broker + + + +#endif /*!QPID_BROKER_SESSIONHANDLER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/SessionManager.cpp b/RC9/qpid/cpp/src/qpid/broker/SessionManager.cpp new file mode 100644 index 0000000000..a35488b746 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/SessionManager.cpp @@ -0,0 +1,104 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SessionManager.h" +#include "SessionState.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Statement.h" +#include "qpid/log/Helpers.h" +#include "qpid/memory.h" + +#include <boost/bind.hpp> +#include <boost/range.hpp> + +#include <algorithm> +#include <functional> +#include <ostream> + +namespace qpid { +namespace broker { + +using boost::intrusive_ptr; +using namespace sys; +using namespace framing; + +SessionManager::SessionManager(const SessionState::Configuration& c, Broker& b) + : config(c), broker(b) {} + +SessionManager::~SessionManager() { + detached.clear(); // Must clear before destructor as session dtor will call forget() +} + +std::auto_ptr<SessionState> SessionManager::attach(SessionHandler& h, const SessionId& id, bool/*force*/) { + Mutex::ScopedLock l(lock); + eraseExpired(); // Clean up expired table + std::pair<Attached::iterator, bool> insert = attached.insert(id); + if (!insert.second) + throw SessionBusyException(QPID_MSG("Session already attached: " << id)); + Detached::iterator i = std::find(detached.begin(), detached.end(), id); + std::auto_ptr<SessionState> state; + if (i == detached.end()) + state.reset(new SessionState(broker, h, id, config)); + else { + state.reset(detached.release(i).release()); + state->attach(h); + } + return state; + // FIXME aconway 2008-04-29: implement force +} + +void SessionManager::detach(std::auto_ptr<SessionState> session) { + Mutex::ScopedLock l(lock); + attached.erase(session->getId()); + session->detach(); + if (session->getTimeout() > 0) { + session->expiry = AbsTime(now(),session->getTimeout()*TIME_SEC); + if (session->mgmtObject != 0) + session->mgmtObject->set_expireTime ((uint64_t) Duration (session->expiry)); + detached.push_back(session.release()); // In expiry order + eraseExpired(); +} +} + +void SessionManager::forget(const SessionId& id) { + Mutex::ScopedLock l(lock); + attached.erase(id); +} + +void SessionManager::eraseExpired() { + // Called with lock held. + if (!detached.empty()) { + // This used to use a more elegant invocation of std::lower_bound + // but violated the strict weak ordering rule which Visual Studio + // enforced. See QPID-1424 for more info should you be tempted to + // replace the loop with something more elegant. + AbsTime now = AbsTime::now(); + Detached::iterator keep = detached.begin(); + while ((keep != detached.end()) && ((*keep).expiry < now)) + keep++; + if (detached.begin() != keep) { + QPID_LOG(debug, "Expiring sessions: " << log::formatList(detached.begin(), keep)); + detached.erase(detached.begin(), keep); + } + } +} + +}} // namespace qpid::broker diff --git a/RC9/qpid/cpp/src/qpid/broker/SessionManager.h b/RC9/qpid/cpp/src/qpid/broker/SessionManager.h new file mode 100644 index 0000000000..db88e7ec10 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/SessionManager.h @@ -0,0 +1,87 @@ +#ifndef QPID_BROKER_SESSIONMANAGER_H +#define QPID_BROKER_SESSIONMANAGER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <qpid/SessionState.h> +#include <qpid/sys/Time.h> +#include <qpid/sys/Mutex.h> +#include <qpid/RefCounted.h> + +#include <set> +#include <vector> +#include <memory> + +#include <boost/noncopyable.hpp> +#include <boost/ptr_container/ptr_vector.hpp> +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { +class Broker; +class SessionState; +class SessionHandler; + +/** + * Create and manage SessionState objects. + */ +class SessionManager : private boost::noncopyable { + public: + SessionManager(const qpid::SessionState::Configuration&, Broker&); + + ~SessionManager(); + + /** Open a new active session, caller takes ownership */ + std::auto_ptr<SessionState> attach(SessionHandler& h, const SessionId& id, bool/*force*/); + + /** Return a detached session to the manager, start the timeout counter. */ + void detach(std::auto_ptr<SessionState>); + + /** Forget about an attached session. Called by SessionState destructor. */ + void forget(const SessionId&); + + Broker& getBroker() const { return broker; } + + const qpid::SessionState::Configuration& getSessionConfig() const { return config; } + + private: + typedef boost::ptr_vector<SessionState> Detached; // Sorted in expiry order. + typedef std::set<SessionId> Attached; + + void eraseExpired(); + + sys::Mutex lock; + Detached detached; + Attached attached; + qpid::SessionState::Configuration config; + Broker& broker; +}; + + + +}} // namespace qpid::broker + + + + + +#endif /*!QPID_BROKER_SESSIONMANAGER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/SessionState.cpp b/RC9/qpid/cpp/src/qpid/broker/SessionState.cpp new file mode 100644 index 0000000000..4f088fdf4c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/SessionState.cpp @@ -0,0 +1,271 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "SessionState.h" +#include "Broker.h" +#include "ConnectionState.h" +#include "DeliveryRecord.h" +#include "SessionManager.h" +#include "SessionHandler.h" +#include "qpid/framing/AMQContentBody.h" +#include "qpid/framing/AMQHeaderBody.h" +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/ServerInvoker.h" +#include "qpid/log/Statement.h" + +#include <boost/bind.hpp> +#include <boost/lexical_cast.hpp> + +namespace qpid { +namespace broker { + +using namespace framing; +using sys::Mutex; +using boost::intrusive_ptr; +using qpid::management::ManagementAgent; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; +namespace _qmf = qmf::org::apache::qpid::broker; + +SessionState::SessionState( + Broker& b, SessionHandler& h, const SessionId& id, const SessionState::Configuration& config) + : qpid::SessionState(id, config), + broker(b), handler(&h), + semanticState(*this, *this), + adapter(semanticState), + msgBuilder(&broker.getStore(), broker.getStagingThreshold()), + enqueuedOp(boost::bind(&SessionState::enqueued, this, _1)), + mgmtObject(0) +{ + Manageable* parent = broker.GetVhostObject (); + if (parent != 0) { + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + if (agent != 0) { + mgmtObject = new _qmf::Session + (agent, this, parent, getId().getName()); + mgmtObject->set_attached (0); + mgmtObject->set_detachedLifespan (0); + mgmtObject->clr_expireTime(); + agent->addObject (mgmtObject); + } + } + attach(h); +} + +SessionState::~SessionState() { + if (mgmtObject != 0) + mgmtObject->resourceDestroy (); +} + +AMQP_ClientProxy& SessionState::getProxy() { + assert(isAttached()); + return handler->getProxy(); +} + +ConnectionState& SessionState::getConnection() { + assert(isAttached()); + return handler->getConnection(); +} + +bool SessionState::isLocal(const ConnectionToken* t) const +{ + return isAttached() && &(handler->getConnection()) == t; +} + +void SessionState::detach() { + QPID_LOG(debug, getId() << ": detached on broker."); + disableOutput(); + handler = 0; + if (mgmtObject != 0) + mgmtObject->set_attached (0); +} + +void SessionState::disableOutput() +{ + semanticState.detached();//prevents further activateOutput calls until reattached + getConnection().outputTasks.removeOutputTask(&semanticState); +} + +void SessionState::attach(SessionHandler& h) { + QPID_LOG(debug, getId() << ": attached on broker."); + handler = &h; + if (mgmtObject != 0) + { + mgmtObject->set_attached (1); + mgmtObject->set_connectionRef (h.getConnection().GetManagementObject()->getObjectId()); + mgmtObject->set_channelId (h.getChannel()); + } +} + +void SessionState::activateOutput() { + if (isAttached()) + getConnection().outputTasks.activateOutput(); +} + +void SessionState::giveReadCredit(int32_t credit) { + if (isAttached()) + getConnection().outputTasks.giveReadCredit(credit); +} + +ManagementObject* SessionState::GetManagementObject (void) const +{ + return (ManagementObject*) mgmtObject; +} + +Manageable::status_t SessionState::ManagementMethod (uint32_t methodId, + Args& /*args*/, + string& /*text*/) +{ + Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; + + switch (methodId) + { + case _qmf::Session::METHOD_DETACH : + if (handler != 0) { + handler->sendDetach(); + } + status = Manageable::STATUS_OK; + break; + + case _qmf::Session::METHOD_CLOSE : + /* + if (handler != 0) + { + handler->getConnection().closeChannel(handler->getChannel()); + } + status = Manageable::STATUS_OK; + break; + */ + + case _qmf::Session::METHOD_SOLICITACK : + case _qmf::Session::METHOD_RESETLIFESPAN : + status = Manageable::STATUS_NOT_IMPLEMENTED; + break; + } + + return status; +} + +void SessionState::handleCommand(framing::AMQMethodBody* method, const SequenceNumber& id) { + Invoker::Result invocation = invoke(adapter, *method); + receiverCompleted(id); + if (!invocation.wasHandled()) { + throw NotImplementedException(QPID_MSG("Not implemented: " << *method)); + } else if (invocation.hasResult()) { + getProxy().getExecution().result(id, invocation.getResult()); + } + if (method->isSync()) { + incomplete.process(enqueuedOp, true); + sendCompletion(); + } +} + +void SessionState::handleContent(AMQFrame& frame, const SequenceNumber& id) +{ + if (frame.getBof() && frame.getBos()) //start of frameset + msgBuilder.start(id); + intrusive_ptr<Message> msg(msgBuilder.getMessage()); + msgBuilder.handle(frame); + if (frame.getEof() && frame.getEos()) {//end of frameset + if (frame.getBof()) { + //i.e this is a just a command frame, add a dummy header + AMQFrame header; + header.setBody(AMQHeaderBody()); + header.setBof(false); + header.setEof(false); + msg->getFrames().append(header); + } + msg->setPublisher(&getConnection()); + semanticState.handle(msg); + msgBuilder.end(); + + if (msg->isEnqueueComplete()) { + enqueued(msg); + } else { + incomplete.add(msg); + } + + //hold up execution until async enqueue is complete + if (msg->getFrames().getMethod()->isSync()) { + incomplete.process(enqueuedOp, true); + sendCompletion(); + } else { + incomplete.process(enqueuedOp, false); + } + } +} + +void SessionState::enqueued(boost::intrusive_ptr<Message> msg) +{ + receiverCompleted(msg->getCommandId()); + if (msg->requiresAccept()) + getProxy().getMessage().accept(SequenceSet(msg->getCommandId())); +} + +void SessionState::handleIn(AMQFrame& frame) { + SequenceNumber commandId = receiverGetCurrent(); + //TODO: make command handling more uniform, regardless of whether + //commands carry content. + AMQMethodBody* m = frame.getMethod(); + if (m == 0 || m->isContentBearing()) { + handleContent(frame, commandId); + } else if (frame.getBof() && frame.getEof()) { + handleCommand(frame.getMethod(), commandId); + } else { + throw InternalErrorException("Cannot handle multi-frame command segments yet"); + } +} + +void SessionState::handleOut(AMQFrame& frame) { + assert(handler); + handler->out(frame); +} + +void SessionState::deliver(DeliveryRecord& msg) +{ + uint32_t maxFrameSize = getConnection().getFrameMax(); + assert(senderGetCommandPoint().offset == 0); + SequenceNumber commandId = senderGetCommandPoint().command; + msg.deliver(getProxy().getHandler(), commandId, maxFrameSize); + assert(senderGetCommandPoint() == SessionPoint(commandId+1, 0)); // Delivery has moved sendPoint. +} + +void SessionState::sendCompletion() { handler->sendCompletion(); } + +void SessionState::senderCompleted(const SequenceSet& commands) { + qpid::SessionState::senderCompleted(commands); + for (SequenceSet::RangeIterator i = commands.rangesBegin(); i != commands.rangesEnd(); i++) + semanticState.completed(i->first(), i->last()); +} + +void SessionState::readyToSend() { + QPID_LOG(debug, getId() << ": ready to send, activating output."); + assert(handler); + semanticState.attached(); + sys::AggregateOutput& tasks = handler->getConnection().outputTasks; + tasks.addOutputTask(&semanticState); + tasks.activateOutput(); +} + +Broker& SessionState::getBroker() { return broker; } + +}} // namespace qpid::broker diff --git a/RC9/qpid/cpp/src/qpid/broker/SessionState.h b/RC9/qpid/cpp/src/qpid/broker/SessionState.h new file mode 100644 index 0000000000..035a444127 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/SessionState.h @@ -0,0 +1,144 @@ +#ifndef QPID_BROKER_SESSION_H +#define QPID_BROKER_SESSION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/SessionState.h" +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/SequenceSet.h" +#include "qpid/sys/Time.h" +#include "qpid/management/Manageable.h" +#include "qmf/org/apache/qpid/broker/Session.h" +#include "SessionAdapter.h" +#include "DeliveryAdapter.h" +#include "IncompleteMessageList.h" +#include "MessageBuilder.h" +#include "SessionContext.h" +#include "SemanticState.h" + +#include <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> + +#include <set> +#include <vector> +#include <ostream> + +namespace qpid { + +namespace framing { +class AMQP_ClientProxy; +} + +namespace broker { + +class Broker; +class ConnectionState; +class Message; +class SessionHandler; +class SessionManager; + +/** + * Broker-side session state includes session's handler chains, which + * may themselves have state. + */ +class SessionState : public qpid::SessionState, + public SessionContext, + public DeliveryAdapter, + public management::Manageable, + public framing::FrameHandler::InOutHandler +{ + public: + SessionState(Broker&, SessionHandler&, const SessionId&, const SessionState::Configuration&); + ~SessionState(); + bool isAttached() const { return handler; } + + void detach(); + void attach(SessionHandler& handler); + void disableOutput(); + + /** @pre isAttached() */ + framing::AMQP_ClientProxy& getProxy(); + + /** @pre isAttached() */ + ConnectionState& getConnection(); + bool isLocal(const ConnectionToken* t) const; + + Broker& getBroker(); + + /** OutputControl **/ + void activateOutput(); + void giveReadCredit(int32_t); + + void senderCompleted(const framing::SequenceSet& ranges); + + void sendCompletion(); + + //delivery adapter methods: + void deliver(DeliveryRecord&); + + // Manageable entry points + management::ManagementObject* GetManagementObject (void) const; + management::Manageable::status_t + ManagementMethod (uint32_t methodId, management::Args& args, std::string&); + + void readyToSend(); + + // Used by cluster to create replica sessions. + SemanticState& getSemanticState() { return semanticState; } + boost::intrusive_ptr<Message> getMessageInProgress() { return msgBuilder.getMessage(); } + + private: + + void handleCommand(framing::AMQMethodBody* method, const framing::SequenceNumber& id); + void handleContent(framing::AMQFrame& frame, const framing::SequenceNumber& id); + void enqueued(boost::intrusive_ptr<Message> msg); + + void handleIn(framing::AMQFrame& frame); + void handleOut(framing::AMQFrame& frame); + + // End of the input & output chains. + void handleInLast(framing::AMQFrame& frame); + void handleOutLast(framing::AMQFrame& frame); + + Broker& broker; + SessionHandler* handler; + sys::AbsTime expiry; // Used by SessionManager. + SemanticState semanticState; + SessionAdapter adapter; + MessageBuilder msgBuilder; + IncompleteMessageList incomplete; + IncompleteMessageList::CompletionListener enqueuedOp; + qmf::org::apache::qpid::broker::Session* mgmtObject; + + friend class SessionManager; +}; + + +inline std::ostream& operator<<(std::ostream& out, const SessionState& session) { + return out << session.getId(); +} + +}} // namespace qpid::broker + + + +#endif /*!QPID_BROKER_SESSION_H*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/SignalHandler.cpp b/RC9/qpid/cpp/src/qpid/broker/SignalHandler.cpp new file mode 100644 index 0000000000..7207b0f8ce --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/SignalHandler.cpp @@ -0,0 +1,48 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "SignalHandler.h" +#include "Broker.h" +#include <signal.h> + +namespace qpid { +namespace broker { + +boost::intrusive_ptr<Broker> SignalHandler::broker; + +void SignalHandler::setBroker(const boost::intrusive_ptr<Broker>& b) { + broker = b; + + signal(SIGINT,shutdownHandler); + signal(SIGTERM, shutdownHandler); + + signal(SIGHUP,SIG_IGN); // TODO aconway 2007-07-18: reload config. + + signal(SIGCHLD,SIG_IGN); +} + +void SignalHandler::shutdownHandler(int) { + if (broker.get()) { + broker->shutdown(); + broker = 0; // Release the broker reference. + } +} + +}} // namespace qpid::broker diff --git a/RC9/qpid/cpp/src/qpid/broker/SignalHandler.h b/RC9/qpid/cpp/src/qpid/broker/SignalHandler.h new file mode 100644 index 0000000000..d2cdfae07c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/SignalHandler.h @@ -0,0 +1,47 @@ +#ifndef QPID_BROKER_SIGNALHANDLER_H +#define QPID_BROKER_SIGNALHANDLER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { + +class Broker; + +/** + * Handle signals e.g. to shut-down a broker. + */ +class SignalHandler +{ + public: + /** Set the broker to be shutdown on signals */ + static void setBroker(const boost::intrusive_ptr<Broker>& broker); + + private: + static void shutdownHandler(int); + static boost::intrusive_ptr<Broker> broker; +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_SIGNALHANDLER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/System.cpp b/RC9/qpid/cpp/src/qpid/broker/System.cpp new file mode 100644 index 0000000000..a11ad25bbe --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/System.cpp @@ -0,0 +1,82 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include "System.h" +#include "qpid/agent/ManagementAgent.h" +#include "qpid/framing/Uuid.h" +#include "qpid/sys/SystemInfo.h" +#include <iostream> +#include <fstream> + +using qpid::management::ManagementAgent; +using namespace qpid::broker; +using namespace std; +namespace _qmf = qmf::org::apache::qpid::broker; + +System::System (string _dataDir) : mgmtObject(0) +{ + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + + if (agent != 0) + { + framing::Uuid systemId; + + if (_dataDir.empty ()) + { + systemId.generate (); + } + else + { + string filename (_dataDir + "/systemId"); + ifstream inFile (filename.c_str ()); + + if (inFile.good ()) + { + inFile >> systemId; + inFile.close (); + } + else + { + systemId.generate (); + ofstream outFile (filename.c_str ()); + if (outFile.good ()) + { + outFile << systemId << endl; + outFile.close (); + } + } + } + + mgmtObject = new _qmf::System (agent, this, systemId); + std::string sysname, nodename, release, version, machine; + qpid::sys::SystemInfo::getSystemId (sysname, + nodename, + release, + version, + machine); + mgmtObject->set_osName (sysname); + mgmtObject->set_nodeName (nodename); + mgmtObject->set_release (release); + mgmtObject->set_version (version); + mgmtObject->set_machine (machine); + + agent->addObject (mgmtObject, 0x1000000000000001LL); + } +} + diff --git a/RC9/qpid/cpp/src/qpid/broker/System.h b/RC9/qpid/cpp/src/qpid/broker/System.h new file mode 100644 index 0000000000..42a816e095 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/System.h @@ -0,0 +1,49 @@ +#ifndef _BrokerSystem_ +#define _BrokerSystem_ + +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include "qpid/management/Manageable.h" +#include "qmf/org/apache/qpid/broker/System.h" +#include <boost/shared_ptr.hpp> +#include <string> + +namespace qpid { +namespace broker { + +class System : public management::Manageable +{ + private: + + qmf::org::apache::qpid::broker::System* mgmtObject; + + public: + + typedef boost::shared_ptr<System> shared_ptr; + + System (std::string _dataDir); + + management::ManagementObject* GetManagementObject (void) const + { return mgmtObject; } +}; + +}} + +#endif /*!_BrokerSystem_*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/Timer.cpp b/RC9/qpid/cpp/src/qpid/broker/Timer.cpp new file mode 100644 index 0000000000..0b0d3ba63d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Timer.cpp @@ -0,0 +1,104 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Timer.h" +#include <iostream> + +using boost::intrusive_ptr; +using qpid::sys::AbsTime; +using qpid::sys::Duration; +using qpid::sys::Monitor; +using qpid::sys::Thread; +using namespace qpid::broker; + +TimerTask::TimerTask(Duration timeout) : + duration(timeout), time(AbsTime::now(), timeout), cancelled(false) {} + +TimerTask::TimerTask(AbsTime _time) : + duration(0), time(_time), cancelled(false) {} + +TimerTask::~TimerTask(){} + +void TimerTask::reset() { time = AbsTime(AbsTime::now(), duration); } + +Timer::Timer() : active(false) +{ + start(); +} + +Timer::~Timer() +{ + stop(); +} + +void Timer::run() +{ + Monitor::ScopedLock l(monitor); + while(active){ + if (tasks.empty()) { + monitor.wait(); + } else { + intrusive_ptr<TimerTask> t = tasks.top(); + if (t->cancelled) { + tasks.pop(); + } else if(t->time < AbsTime::now()) { + tasks.pop(); + Monitor::ScopedUnlock u(monitor); + t->fire(); + } else { + monitor.wait(t->time); + } + } + } +} + +void Timer::add(intrusive_ptr<TimerTask> task) +{ + Monitor::ScopedLock l(monitor); + tasks.push(task); + monitor.notify(); +} + +void Timer::start() +{ + Monitor::ScopedLock l(monitor); + if (!active) { + active = true; + runner = Thread(this); + } +} + +void Timer::stop() +{ + { + Monitor::ScopedLock l(monitor); + if (!active) return; + active = false; + monitor.notifyAll(); + } + runner.join(); +} + +bool Later::operator()(const intrusive_ptr<TimerTask>& a, + const intrusive_ptr<TimerTask>& b) const +{ + return a.get() && b.get() && a->time > b->time; +} + diff --git a/RC9/qpid/cpp/src/qpid/broker/Timer.h b/RC9/qpid/cpp/src/qpid/broker/Timer.h new file mode 100644 index 0000000000..f702f0f32d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Timer.h @@ -0,0 +1,79 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _Timer_ +#define _Timer_ + +#include "qpid/sys/Monitor.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Runnable.h" +#include "qpid/RefCounted.h" + +#include <memory> +#include <queue> + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { + +struct TimerTask : public RefCounted { + const qpid::sys::Duration duration; + qpid::sys::AbsTime time; + volatile bool cancelled; + + TimerTask(qpid::sys::Duration timeout); + TimerTask(qpid::sys::AbsTime time); + virtual ~TimerTask(); + void reset(); + virtual void fire() = 0; +}; + +struct Later { + bool operator()(const boost::intrusive_ptr<TimerTask>& a, + const boost::intrusive_ptr<TimerTask>& b) const; +}; + +class Timer : private qpid::sys::Runnable { + protected: + qpid::sys::Monitor monitor; + std::priority_queue<boost::intrusive_ptr<TimerTask>, + std::vector<boost::intrusive_ptr<TimerTask> >, + Later> tasks; + qpid::sys::Thread runner; + bool active; + + virtual void run(); + + public: + Timer(); + virtual ~Timer(); + + void add(boost::intrusive_ptr<TimerTask> task); + void start(); + void stop(); + +}; + + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/TopicExchange.cpp b/RC9/qpid/cpp/src/qpid/broker/TopicExchange.cpp new file mode 100644 index 0000000000..d4f9721162 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/TopicExchange.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 "TopicExchange.h" +#include <algorithm> + +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; +namespace _qmf = qmf::org::apache::qpid::broker; + +// TODO aconway 2006-09-20: More efficient matching algorithm. +// Areas for improvement: +// - excessive string copying: should be 0 copy, match from original buffer. +// - match/lookup: use descision tree or other more efficient structure. + +namespace +{ +const std::string qpidFedOp("qpid.fed.op"); +const std::string qpidFedTags("qpid.fed.tags"); +const std::string qpidFedOrigin("qpid.fed.origin"); + +const std::string fedOpBind("B"); +const std::string fedOpUnbind("U"); +const std::string fedOpReorigin("R"); +const std::string fedOpHello("H"); +} + +Tokens& Tokens::operator=(const std::string& s) { + clear(); + if (s.empty()) return *this; + std::string::const_iterator i = s.begin(); + while (true) { + // Invariant: i is at the beginning of the next untokenized word. + std::string::const_iterator j = std::find(i, s.end(), '.'); + push_back(std::string(i, j)); + if (j == s.end()) return *this; + i = j + 1; + } + return *this; +} + +TopicPattern& TopicPattern::operator=(const Tokens& tokens) { + Tokens::operator=(tokens); + normalize(); + return *this; +} + +void Tokens::key(string& keytext) const +{ + for (std::vector<string>::const_iterator iter = begin(); iter != end(); iter++) { + if (iter != begin()) + keytext += "."; + keytext += *iter; + } +} + +namespace { +const std::string hashmark("#"); +const std::string star("*"); +} + +void TopicPattern::normalize() { + std::string word; + Tokens::iterator i = begin(); + while (i != end()) { + if (*i == hashmark) { + ++i; + while (i != end()) { + // Invariant: *(i-1)==#, [begin()..i-1] is normalized. + if (*i == star) { // Move * before #. + std::swap(*i, *(i-1)); + ++i; + } else if (*i == hashmark) { + erase(i); // Remove extra # + } else { + break; + } + } + } else { + i ++; + } + } +} + + +namespace { +// TODO aconway 2006-09-20: Inefficient to convert every routingKey to a string. +// Need StringRef class that operates on a string in place witout copy. +// Should be applied everywhere strings are extracted from frames. +// +bool do_match(Tokens::const_iterator pattern_begin, Tokens::const_iterator pattern_end, Tokens::const_iterator target_begin, Tokens::const_iterator target_end) +{ + // Invariant: [pattern_begin..p) matches [target_begin..t) + Tokens::const_iterator p = pattern_begin; + Tokens::const_iterator t = target_begin; + while (p != pattern_end && t != target_end) + { + if (*p == star || *p == *t) { + ++p, ++t; + } else if (*p == hashmark) { + ++p; + if (do_match(p, pattern_end, t, target_end)) return true; + while (t != target_end) { + ++t; + if (do_match(p, pattern_end, t, target_end)) return true; + } + return false; + } else { + return false; + } + } + while (p != pattern_end && *p == hashmark) ++p; // Ignore trailing # + return t == target_end && p == pattern_end; +} +} + +bool TopicPattern::match(const Tokens& target) const +{ + return do_match(begin(), end(), target.begin(), target.end()); +} + +TopicExchange::TopicExchange(const string& _name, Manageable* _parent) : Exchange(_name, _parent) +{ + if (mgmtExchange != 0) + mgmtExchange->set_type (typeName); +} + +TopicExchange::TopicExchange(const std::string& _name, bool _durable, + const FieldTable& _args, Manageable* _parent) : + Exchange(_name, _durable, _args, _parent) +{ + if (mgmtExchange != 0) + mgmtExchange->set_type (typeName); +} + +bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* args) +{ + string fedOp(args ? args->getAsString(qpidFedOp) : fedOpBind); + string fedTags(args ? args->getAsString(qpidFedTags) : ""); + string fedOrigin(args ? args->getAsString(qpidFedOrigin) : ""); + bool propagate = false; + bool reallyUnbind; + TopicPattern routingPattern(routingKey); + + if (args == 0 || fedOp.empty() || fedOp == fedOpBind) { + RWlock::ScopedWlock l(lock); + if (isBound(queue, routingPattern)) { + return false; + } else { + Binding::shared_ptr binding (new Binding (routingKey, queue, this, FieldTable(), fedOrigin)); + BoundKey& bk = bindings[routingPattern]; + bk.bindingVector.push_back(binding); + propagate = bk.fedBinding.addOrigin(fedOrigin); + if (mgmtExchange != 0) { + mgmtExchange->inc_bindingCount(); + ((_qmf::Queue*) queue->GetManagementObject())->inc_bindingCount(); + } + } + } else if (fedOp == fedOpUnbind) { + { + RWlock::ScopedWlock l(lock); + BoundKey& bk = bindings[routingPattern]; + propagate = bk.fedBinding.delOrigin(fedOrigin); + reallyUnbind = bk.fedBinding.count() == 0; + } + if (reallyUnbind) + unbind(queue, routingKey, 0); + } else if (fedOp == fedOpReorigin) { + for (std::map<TopicPattern, BoundKey>::iterator iter = bindings.begin(); + iter != bindings.end(); iter++) { + const BoundKey& bk = iter->second; + if (bk.fedBinding.hasLocal()) { + string propKey; + iter->first.key(propKey); + propagateFedOp(propKey, string(), fedOpBind, string()); + } + } + } + + routeIVE(); + if (propagate) + propagateFedOp(routingKey, fedTags, fedOp, fedOrigin); + return true; +} + +bool TopicExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/){ + RWlock::ScopedWlock l(lock); + BindingMap::iterator bi = bindings.find(TopicPattern(routingKey)); + if (bi == bindings.end()) return false; + BoundKey& bk = bi->second; + Binding::vector& qv(bk.bindingVector); + bool propagate = false; + + Binding::vector::iterator q; + for (q = qv.begin(); q != qv.end(); q++) + if ((*q)->queue == queue) + break; + if(q == qv.end()) return false; + qv.erase(q); + propagate = bk.fedBinding.delOrigin(); + if(qv.empty()) bindings.erase(bi); + if (mgmtExchange != 0) { + mgmtExchange->dec_bindingCount(); + ((_qmf::Queue*) queue->GetManagementObject())->dec_bindingCount(); + } + + if (propagate) + propagateFedOp(routingKey, string(), fedOpUnbind, string()); + return true; +} + +bool TopicExchange::isBound(Queue::shared_ptr queue, TopicPattern& pattern) +{ + BindingMap::iterator bi = bindings.find(pattern); + if (bi == bindings.end()) return false; + Binding::vector& qv(bi->second.bindingVector); + Binding::vector::iterator q; + for (q = qv.begin(); q != qv.end(); q++) + if ((*q)->queue == queue) + break; + return q != qv.end(); +} + +void TopicExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/){ + Binding::vector mb; + PreRoute pr(msg, this); + uint32_t count(0); + + { + RWlock::ScopedRlock l(lock); + Tokens tokens(routingKey); + + for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) { + if (i->first.match(tokens)) { + Binding::vector& qv(i->second.bindingVector); + for(Binding::vector::iterator j = qv.begin(); j != qv.end(); j++, count++){ + mb.push_back(*j); + } + } + } + } + + for (Binding::vector::iterator j = mb.begin(); j != mb.end(); ++j) { + msg.deliverTo((*j)->queue); + if ((*j)->mgmtBinding != 0) + (*j)->mgmtBinding->inc_msgMatched (); + } + + if (mgmtExchange != 0) + { + mgmtExchange->inc_msgReceives (); + mgmtExchange->inc_byteReceives (msg.contentSize ()); + if (count == 0) + { + mgmtExchange->inc_msgDrops (); + mgmtExchange->inc_byteDrops (msg.contentSize ()); + } + else + { + mgmtExchange->inc_msgRoutes (count); + mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + } + } +} + +bool TopicExchange::isBound(Queue::shared_ptr queue, const string* const routingKey, const FieldTable* const) +{ + if (routingKey && queue) { + TopicPattern key(*routingKey); + return isBound(queue, key); + } else if (!routingKey && !queue) { + return bindings.size() > 0; + } else if (routingKey) { + for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) { + if (i->first.match(*routingKey)) { + return true; + } + } + } else { + for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) { + Binding::vector& qv(i->second.bindingVector); + Binding::vector::iterator q; + for (q = qv.begin(); q != qv.end(); q++) + if ((*q)->queue == queue) + return true; + } + } + return false; +} + +TopicExchange::~TopicExchange() {} + +const std::string TopicExchange::typeName("topic"); + + diff --git a/RC9/qpid/cpp/src/qpid/broker/TopicExchange.h b/RC9/qpid/cpp/src/qpid/broker/TopicExchange.h new file mode 100644 index 0000000000..f3a2e221f7 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/TopicExchange.h @@ -0,0 +1,110 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TopicExchange_ +#define _TopicExchange_ + +#include <map> +#include <vector> +#include "Exchange.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/Monitor.h" +#include "Queue.h" + +namespace qpid { +namespace broker { + +/** A vector of string tokens */ +class Tokens : public std::vector<std::string> { + public: + Tokens() {}; + // Default copy, assign, dtor are sufficient. + + /** Tokenize s, provides automatic conversion of string to Tokens */ + Tokens(const std::string& s) { operator=(s); } + /** Tokenizing assignment operator s */ + Tokens & operator=(const std::string& s); + void key(std::string& key) const; + + private: + size_t hash; +}; + + +/** + * Tokens that have been normalized as a pattern and can be matched + * with topic Tokens. Normalized meands all sequences of mixed * and + * # are reduced to a series of * followed by at most one #. + */ +class TopicPattern : public Tokens +{ + public: + TopicPattern() {} + // Default copy, assign, dtor are sufficient. + TopicPattern(const Tokens& tokens) { operator=(tokens); } + TopicPattern(const std::string& str) { operator=(str); } + TopicPattern& operator=(const Tokens&); + TopicPattern& operator=(const std::string& str) { return operator=(Tokens(str)); } + + /** Match a topic */ + bool match(const std::string& topic) { return match(Tokens(topic)); } + bool match(const Tokens& topic) const; + + private: + void normalize(); +}; + +class TopicExchange : public virtual Exchange { + struct BoundKey { + Binding::vector bindingVector; + FedBinding fedBinding; + }; + typedef std::map<TopicPattern, BoundKey> BindingMap; + BindingMap bindings; + qpid::sys::RWlock lock; + + bool isBound(Queue::shared_ptr queue, TopicPattern& pattern); + public: + static const std::string typeName; + + TopicExchange(const string& name, management::Manageable* parent = 0); + TopicExchange(const string& _name, bool _durable, + const qpid::framing::FieldTable& _args, management::Manageable* parent = 0); + + virtual std::string getType() const { return typeName; } + + virtual bool bind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool unbind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual void route(Deliverable& msg, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args); + + virtual ~TopicExchange(); + virtual bool supportsDynamicBinding() { return true; } +}; + + + +} +} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/TransactionalStore.h b/RC9/qpid/cpp/src/qpid/broker/TransactionalStore.h new file mode 100644 index 0000000000..2a2bac0c51 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/TransactionalStore.h @@ -0,0 +1,60 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TransactionalStore_ +#define _TransactionalStore_ + +#include <memory> +#include <string> +#include <set> + +namespace qpid { +namespace broker { + +struct InvalidTransactionContextException : public std::exception {}; + +class TransactionContext { +public: + virtual ~TransactionContext(){} +}; + +class TPCTransactionContext : public TransactionContext { +public: + virtual ~TPCTransactionContext(){} +}; + +class TransactionalStore { +public: + virtual std::auto_ptr<TransactionContext> begin() = 0; + virtual std::auto_ptr<TPCTransactionContext> begin(const std::string& xid) = 0; + virtual void prepare(TPCTransactionContext& txn) = 0; + virtual void commit(TransactionContext& txn) = 0; + virtual void abort(TransactionContext& txn) = 0; + + virtual void collectPreparedXids(std::set<std::string>& xids) = 0; + + virtual ~TransactionalStore(){} +}; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/TxAccept.cpp b/RC9/qpid/cpp/src/qpid/broker/TxAccept.cpp new file mode 100644 index 0000000000..c7001e5526 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/TxAccept.cpp @@ -0,0 +1,99 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "TxAccept.h" +#include "qpid/log/Statement.h" + +using std::bind1st; +using std::bind2nd; +using std::mem_fun_ref; +using namespace qpid::broker; +using qpid::framing::SequenceSet; +using qpid::framing::SequenceNumber; + +TxAccept::RangeOp::RangeOp(const AckRange& r) : range(r) {} + +void TxAccept::RangeOp::prepare(TransactionContext* ctxt) +{ + for_each(range.start, range.end, bind(&DeliveryRecord::dequeue, _1, ctxt)); +} + +void TxAccept::RangeOp::commit() +{ + for_each(range.start, range.end, bind(&DeliveryRecord::committed, _1)); + for_each(range.start, range.end, bind(&DeliveryRecord::setEnded, _1)); +} + +TxAccept::RangeOps::RangeOps(DeliveryRecords& u) : unacked(u) {} + +void TxAccept::RangeOps::operator()(SequenceNumber start, SequenceNumber end) +{ + ranges.push_back(RangeOp(DeliveryRecord::findRange(unacked, start, end))); +} + +void TxAccept::RangeOps::prepare(TransactionContext* ctxt) +{ + for_each(ranges.begin(), ranges.end(), bind(&RangeOp::prepare, _1, ctxt)); +} + +void TxAccept::RangeOps::commit() +{ + for_each(ranges.begin(), ranges.end(), bind(&RangeOp::commit, _1)); + //now remove if isRedundant(): + if (!ranges.empty()) { + ack_iterator i = ranges.front().range.start; + ack_iterator end = ranges.back().range.end; + while (i != end) { + if (i->isRedundant()) { + i = unacked.erase(i); + } else { + i++; + } + } + } +} + +TxAccept::TxAccept(const SequenceSet& _acked, DeliveryRecords& _unacked) : + acked(_acked), unacked(_unacked), ops(unacked) +{ + //populate the ops + acked.for_each(ops); +} + +bool TxAccept::prepare(TransactionContext* ctxt) throw() +{ + try{ + ops.prepare(ctxt); + return true; + }catch(const std::exception& e){ + QPID_LOG(error, "Failed to prepare: " << e.what()); + return false; + }catch(...){ + QPID_LOG(error, "Failed to prepare"); + return false; + } +} + +void TxAccept::commit() throw() +{ + ops.commit(); +} + +void TxAccept::rollback() throw() {} diff --git a/RC9/qpid/cpp/src/qpid/broker/TxAccept.h b/RC9/qpid/cpp/src/qpid/broker/TxAccept.h new file mode 100644 index 0000000000..0a5fdedb0a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/TxAccept.h @@ -0,0 +1,83 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TxAccept_ +#define _TxAccept_ + +#include <algorithm> +#include <functional> +#include <list> +#include "qpid/framing/SequenceSet.h" +#include "DeliveryRecord.h" +#include "TxOp.h" + +namespace qpid { + namespace broker { + /** + * Defines the transactional behaviour for accepts received by + * a transactional channel. + */ + class TxAccept : public TxOp { + struct RangeOp + { + AckRange range; + + RangeOp(const AckRange& r); + void prepare(TransactionContext* ctxt); + void commit(); + }; + + struct RangeOps + { + std::vector<RangeOp> ranges; + DeliveryRecords& unacked; + + RangeOps(DeliveryRecords& u); + + void operator()(framing::SequenceNumber start, framing::SequenceNumber end); + void prepare(TransactionContext* ctxt); + void commit(); + }; + + framing::SequenceSet acked; + DeliveryRecords& unacked; + RangeOps ops; + + public: + /** + * @param acked a representation of the accumulation of + * acks received + * @param unacked the record of delivered messages + */ + TxAccept(const framing::SequenceSet& acked, DeliveryRecords& unacked); + virtual bool prepare(TransactionContext* ctxt) throw(); + virtual void commit() throw(); + virtual void rollback() throw(); + virtual ~TxAccept(){} + virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); } + + // Used by cluster replication. + const framing::SequenceSet& getAcked() const { return acked; } + }; + } +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/TxBuffer.cpp b/RC9/qpid/cpp/src/qpid/broker/TxBuffer.cpp new file mode 100644 index 0000000000..ae18e0f318 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/TxBuffer.cpp @@ -0,0 +1,80 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "TxBuffer.h" +#include "qpid/log/Statement.h" + +#include <boost/mem_fn.hpp> +#include <boost/bind.hpp> +using boost::mem_fn; +using namespace qpid::broker; + +bool TxBuffer::prepare(TransactionContext* const ctxt) +{ + for(op_iterator i = ops.begin(); i < ops.end(); i++){ + if(!(*i)->prepare(ctxt)){ + return false; + } + } + return true; +} + +void TxBuffer::commit() +{ + std::for_each(ops.begin(), ops.end(), mem_fn(&TxOp::commit)); + ops.clear(); +} + +void TxBuffer::rollback() +{ + std::for_each(ops.begin(), ops.end(), mem_fn(&TxOp::rollback)); + ops.clear(); +} + +void TxBuffer::enlist(TxOp::shared_ptr op) +{ + ops.push_back(op); +} + +bool TxBuffer::commitLocal(TransactionalStore* const store) +{ + if (!store) return false; + try { + std::auto_ptr<TransactionContext> ctxt = store->begin(); + if (prepare(ctxt.get())) { + store->commit(*ctxt); + commit(); + return true; + } else { + store->abort(*ctxt); + rollback(); + return false; + } + } catch (std::exception& e) { + QPID_LOG(error, "Commit failed with exception: " << e.what()); + } catch (...) { + QPID_LOG(error, "Commit failed with unknown exception"); + } + return false; +} + +void TxBuffer::accept(TxOpConstVisitor& v) const { + std::for_each(ops.begin(), ops.end(), boost::bind(&TxOp::accept, _1, boost::ref(v))); +} diff --git a/RC9/qpid/cpp/src/qpid/broker/TxBuffer.h b/RC9/qpid/cpp/src/qpid/broker/TxBuffer.h new file mode 100644 index 0000000000..aabb5ea0b1 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/TxBuffer.h @@ -0,0 +1,118 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TxBuffer_ +#define _TxBuffer_ + +#include <algorithm> +#include <functional> +#include <vector> +#include "TransactionalStore.h" +#include "TxOp.h" + +/** + * Represents a single transaction. As such, an instance of this class + * will hold a list of operations representing the workload of the + * transaction. This work can be committed or rolled back. Committing + * is a two-stage process: first all the operations should be + * prepared, then if that succeeds they can be committed. + * + * In the 2pc case, a successful prepare may be followed by either a + * commit or a rollback. + * + * Atomicity of prepare is ensured by using a lower level + * transactional facility. This saves explicitly rolling back all the + * successfully prepared ops when one of them fails. i.e. we do not + * use 2pc internally, we instead ensure that prepare is atomic at a + * lower level. This makes individual prepare operations easier to + * code. + * + * Transactions on a messaging broker effect three types of 'action': + * (1) updates to persistent storage (2) updates to transient storage + * or cached data (3) network writes. + * + * Of these, (1) should always occur atomically during prepare to + * ensure that if the broker crashes while a transaction is being + * completed the persistent state (which is all that then remains) is + * consistent. (3) can only be done on commit, after a successful + * prepare. There is a little more flexibility with (2) but any + * changes made during prepare should be subject to the control of the + * TransactionalStore in use. + */ +namespace qpid { + namespace broker { + class TxBuffer{ + typedef std::vector<TxOp::shared_ptr>::iterator op_iterator; + std::vector<TxOp::shared_ptr> ops; + protected: + + public: + typedef boost::shared_ptr<TxBuffer> shared_ptr; + /** + * Adds an operation to the transaction. + */ + void enlist(TxOp::shared_ptr op); + + /** + * Requests that all ops are prepared. This should + * primarily involve making sure that a persistent record + * of the operations is stored where necessary. + * + * Once prepared, a transaction can be committed (or in + * the 2pc case, rolled back). + * + * @returns true if all the operations prepared + * successfully, false if not. + */ + bool prepare(TransactionContext* const ctxt); + + /** + * Signals that the ops all prepared successfully and can + * now commit, i.e. the operation can now be fully carried + * out. + * + * Should only be called after a call to prepare() returns + * true. + */ + void commit(); + + /** + * Signals that all ops can be rolled back. + * + * Should only be called either after a call to prepare() + * returns true (2pc) or instead of a prepare call + * ('server-local') + */ + void rollback(); + + /** + * Helper method for managing the process of server local + * commit + */ + bool commitLocal(TransactionalStore* const store); + + // Used by cluster to replicate transaction status. + void accept(TxOpConstVisitor& v) const; + }; + } +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/TxOp.h b/RC9/qpid/cpp/src/qpid/broker/TxOp.h new file mode 100644 index 0000000000..e0e17fc0dc --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/TxOp.h @@ -0,0 +1,46 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TxOp_ +#define _TxOp_ + +#include "TxOpVisitor.h" +#include "TransactionalStore.h" +#include <boost/shared_ptr.hpp> + +namespace qpid { + namespace broker { + + class TxOp{ + public: + typedef boost::shared_ptr<TxOp> shared_ptr; + + virtual bool prepare(TransactionContext*) throw() = 0; + virtual void commit() throw() = 0; + virtual void rollback() throw() = 0; + virtual ~TxOp(){} + + virtual void accept(TxOpConstVisitor&) const = 0; + }; + +}} // namespace qpid::broker + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/TxOpVisitor.h b/RC9/qpid/cpp/src/qpid/broker/TxOpVisitor.h new file mode 100644 index 0000000000..a5f2a018c9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/TxOpVisitor.h @@ -0,0 +1,100 @@ +#ifndef QPID_BROKER_TXOPVISITOR_H +#define QPID_BROKER_TXOPVISITOR_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/shared_ptr.h" + +namespace qpid { +namespace broker { + +class DtxAck; +class RecoveredDequeue; +class RecoveredEnqueue; +class TxAccept; +class TxPublish; + +/** + * Visitor for TxOp familly of classes. + */ +struct TxOpConstVisitor +{ + virtual ~TxOpConstVisitor() {} + virtual void operator()(const DtxAck&) = 0; + virtual void operator()(const RecoveredDequeue&) = 0; + virtual void operator()(const RecoveredEnqueue&) = 0; + virtual void operator()(const TxAccept&) = 0; + virtual void operator()(const TxPublish&) = 0; +}; + +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_TXOPVISITOR_H*/ +#ifndef QPID_BROKER_TXOPVISITOR_H +#define QPID_BROKER_TXOPVISITOR_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/shared_ptr.h" + +namespace qpid { +namespace broker { + +class DtxAck; +class RecoveredDequeue; +class RecoveredEnqueue; +class TxAccept; +class TxPublish; + +/** + * Visitor for TxOp familly of classes. + */ +struct TxOpConstVisitor +{ + virtual ~TxOpConstVisitor() {} + virtual void operator()(const DtxAck&) = 0; + virtual void operator()(const RecoveredDequeue&) = 0; + virtual void operator()(const RecoveredEnqueue&) = 0; + virtual void operator()(const TxAccept&) = 0; + virtual void operator()(const TxPublish&) = 0; +}; + +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_TXOPVISITOR_H*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/TxPublish.cpp b/RC9/qpid/cpp/src/qpid/broker/TxPublish.cpp new file mode 100644 index 0000000000..25ac691ada --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/TxPublish.cpp @@ -0,0 +1,80 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/log/Statement.h" +#include "TxPublish.h" + +using boost::intrusive_ptr; +using namespace qpid::broker; + +TxPublish::TxPublish(intrusive_ptr<Message> _msg) : msg(_msg) {} + +bool TxPublish::prepare(TransactionContext* ctxt) throw(){ + try{ + for_each(queues.begin(), queues.end(), Prepare(ctxt, msg)); + return true; + }catch(const std::exception& e){ + QPID_LOG(error, "Failed to prepare: " << e.what()); + }catch(...){ + QPID_LOG(error, "Failed to prepare (unknown error)"); + } + return false; +} + +void TxPublish::commit() throw(){ + for_each(queues.begin(), queues.end(), Commit(msg)); +} + +void TxPublish::rollback() throw(){ +} + +void TxPublish::deliverTo(const boost::shared_ptr<Queue>& queue){ + if (!queue->isLocal(msg)) { + queues.push_back(queue); + delivered = true; + } else { + QPID_LOG(debug, "Won't enqueue local message for " << queue->getName()); + } +} + +TxPublish::Prepare::Prepare(TransactionContext* _ctxt, intrusive_ptr<Message>& _msg) + : ctxt(_ctxt), msg(_msg){} + +void TxPublish::Prepare::operator()(const boost::shared_ptr<Queue>& queue){ + if (!queue->enqueue(ctxt, msg)){ + /** + * if not store then mark message for ack and deleivery once + * commit happens, as async IO will never set it when no store + * exists + */ + msg->enqueueComplete(); + } +} + +TxPublish::Commit::Commit(intrusive_ptr<Message>& _msg) : msg(_msg){} + +void TxPublish::Commit::operator()(const boost::shared_ptr<Queue>& queue){ + queue->process(msg); +} + +uint64_t TxPublish::contentSize () +{ + return msg->contentSize (); +} diff --git a/RC9/qpid/cpp/src/qpid/broker/TxPublish.h b/RC9/qpid/cpp/src/qpid/broker/TxPublish.h new file mode 100644 index 0000000000..1f73cb8767 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/TxPublish.h @@ -0,0 +1,89 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TxPublish_ +#define _TxPublish_ + +#include "Queue.h" +#include "Deliverable.h" +#include "Message.h" +#include "MessageStore.h" +#include "TxOp.h" + +#include <algorithm> +#include <functional> +#include <list> + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { + namespace broker { + /** + * Defines the behaviour for publish operations on a + * transactional channel. Messages are routed through + * exchanges when received but are not at that stage delivered + * to the matching queues, rather the queues are held in an + * instance of this class. On prepare() the message is marked + * enqueued to the relevant queues in the MessagesStore. On + * commit() the messages will be passed to the queue for + * dispatch or to be added to the in-memory queue. + */ + class TxPublish : public TxOp, public Deliverable{ + class Prepare{ + TransactionContext* ctxt; + boost::intrusive_ptr<Message>& msg; + public: + Prepare(TransactionContext* ctxt, boost::intrusive_ptr<Message>& msg); + void operator()(const boost::shared_ptr<Queue>& queue); + }; + + class Commit{ + boost::intrusive_ptr<Message>& msg; + public: + Commit(boost::intrusive_ptr<Message>& msg); + void operator()(const boost::shared_ptr<Queue>& queue); + }; + + boost::intrusive_ptr<Message> msg; + std::list<Queue::shared_ptr> queues; + + public: + TxPublish(boost::intrusive_ptr<Message> msg); + virtual bool prepare(TransactionContext* ctxt) throw(); + virtual void commit() throw(); + virtual void rollback() throw(); + + virtual Message& getMessage() { return *msg; }; + + virtual void deliverTo(const boost::shared_ptr<Queue>& queue); + + virtual ~TxPublish(){} + virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); } + + uint64_t contentSize(); + + boost::intrusive_ptr<Message> getMessage() const { return msg; } + const std::list<Queue::shared_ptr> getQueues() const { return queues; } + }; + } +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/broker/Vhost.cpp b/RC9/qpid/cpp/src/qpid/broker/Vhost.cpp new file mode 100644 index 0000000000..c5bb6c5104 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Vhost.cpp @@ -0,0 +1,48 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include "Vhost.h" +#include "qpid/agent/ManagementAgent.h" + +using namespace qpid::broker; +using qpid::management::ManagementAgent; +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace qpid { namespace management { +class Manageable; +}} + +Vhost::Vhost (qpid::management::Manageable* parentBroker) : mgmtObject(0) +{ + if (parentBroker != 0) + { + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + + if (agent != 0) + { + mgmtObject = new _qmf::Vhost(agent, this, parentBroker, "/"); + agent->addObject (mgmtObject, 0x1000000000000003LL); + } + } +} + +void Vhost::setFederationTag(const std::string& tag) +{ + mgmtObject->set_federationTag(tag); +} diff --git a/RC9/qpid/cpp/src/qpid/broker/Vhost.h b/RC9/qpid/cpp/src/qpid/broker/Vhost.h new file mode 100644 index 0000000000..ef59362e4d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/Vhost.h @@ -0,0 +1,49 @@ +#ifndef _Vhost_ +#define _Vhost_ + +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include "qpid/management/Manageable.h" +#include "qmf/org/apache/qpid/broker/Vhost.h" +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace broker { + +class Vhost : public management::Manageable +{ + private: + + qmf::org::apache::qpid::broker::Vhost* mgmtObject; + + public: + + typedef boost::shared_ptr<Vhost> shared_ptr; + + Vhost (management::Manageable* parentBroker); + + management::ManagementObject* GetManagementObject (void) const + { return mgmtObject; } + void setFederationTag(const std::string& tag); +}; + +}} + +#endif /*!_Vhost_*/ diff --git a/RC9/qpid/cpp/src/qpid/broker/posix/BrokerDefaults.cpp b/RC9/qpid/cpp/src/qpid/broker/posix/BrokerDefaults.cpp new file mode 100644 index 0000000000..b3ef48fe58 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/posix/BrokerDefaults.cpp @@ -0,0 +1,30 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/broker/Broker.h" + +namespace qpid { +namespace broker { + +const std::string Broker::Options::DEFAULT_DATA_DIR_LOCATION("/tmp"); +const std::string Broker::Options::DEFAULT_DATA_DIR_NAME("/.qpidd"); + +}} diff --git a/RC9/qpid/cpp/src/qpid/broker/windows/BrokerDefaults.cpp b/RC9/qpid/cpp/src/qpid/broker/windows/BrokerDefaults.cpp new file mode 100644 index 0000000000..138995980a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/windows/BrokerDefaults.cpp @@ -0,0 +1,30 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/broker/Broker.h" + +namespace qpid { +namespace broker { + +const std::string Broker::Options::DEFAULT_DATA_DIR_LOCATION("\\TEMP"); +const std::string Broker::Options::DEFAULT_DATA_DIR_NAME("\\QPIDD.DATA"); + +}} diff --git a/RC9/qpid/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp b/RC9/qpid/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp new file mode 100644 index 0000000000..a239987c0e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp @@ -0,0 +1,175 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +// This source is only used on Windows; SSPI is the Windows mechanism for +// accessing authentication mechanisms, analogous to Cyrus SASL. + +#include "qpid/broker/Connection.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/reply_exceptions.h" + +#include <windows.h> + +using namespace qpid::framing; + +namespace qpid { +namespace broker { + +class NullAuthenticator : public SaslAuthenticator +{ + Connection& connection; + framing::AMQP_ClientProxy::Connection client; +public: + NullAuthenticator(Connection& connection); + ~NullAuthenticator(); + void getMechanisms(framing::Array& mechanisms); + void start(const std::string& mechanism, const std::string& response); + void step(const std::string&) {} +}; + +class SspiAuthenticator : public SaslAuthenticator +{ + HANDLE userToken; + Connection& connection; + framing::AMQP_ClientProxy::Connection client; + +public: + SspiAuthenticator(Connection& connection); + ~SspiAuthenticator(); + void getMechanisms(framing::Array& mechanisms); + void start(const std::string& mechanism, const std::string& response); + void step(const std::string& response); +}; + +bool SaslAuthenticator::available(void) +{ + return true; +} + +// Initialize the SASL mechanism; throw if it fails. +void SaslAuthenticator::init(const std::string& /*saslName*/) +{ + return; +} + +void SaslAuthenticator::fini(void) +{ + return; +} + +std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connection& c) +{ + if (c.getBroker().getOptions().auth) { + return std::auto_ptr<SaslAuthenticator>(new SspiAuthenticator(c)); + } else { + return std::auto_ptr<SaslAuthenticator>(new NullAuthenticator(c)); + } +} + +NullAuthenticator::NullAuthenticator(Connection& c) : connection(c), client(c.getOutput()) {} +NullAuthenticator::~NullAuthenticator() {} + +void NullAuthenticator::getMechanisms(Array& mechanisms) +{ + mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value("ANONYMOUS"))); +} + +void NullAuthenticator::start(const string& mechanism, const string& response) +{ + QPID_LOG(warning, "SASL: No Authentication Performed"); + if (mechanism == "PLAIN") { // Old behavior + if (response.size() > 0 && response[0] == (char) 0) { + string temp = response.substr(1); + string::size_type i = temp.find((char)0); + string uid = temp.substr(0, i); + string pwd = temp.substr(i + 1); + connection.setUserId(uid); + } + } else { + connection.setUserId("anonymous"); + } + client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0); +} + + +SspiAuthenticator::SspiAuthenticator(Connection& c) : userToken(INVALID_HANDLE_VALUE), connection(c), client(c.getOutput()) +{ +} + +SspiAuthenticator::~SspiAuthenticator() +{ + if (INVALID_HANDLE_VALUE != userToken) { + CloseHandle(userToken); + userToken = INVALID_HANDLE_VALUE; + } +} + +void SspiAuthenticator::getMechanisms(Array& mechanisms) +{ + mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value(string("ANONYMOUS")))); + mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value(string("PLAIN")))); + QPID_LOG(info, "SASL: Mechanism list: ANONYMOUS PLAIN"); +} + +void SspiAuthenticator::start(const string& mechanism, const string& response) +{ + QPID_LOG(info, "SASL: Starting authentication with mechanism: " << mechanism); + if (mechanism == "ANONYMOUS") { + connection.setUserId("anonymous"); + client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0); + return; + } + if (mechanism != "PLAIN") + throw ConnectionForcedException("Unsupported mechanism"); + + // PLAIN's response is composed of 3 strings separated by 0 bytes: + // authorization id, authentication id (user), clear-text password. + if (response.size() == 0) + throw ConnectionForcedException("Authentication failed"); + + string::size_type i = response.find((char)0); + string auth = response.substr(0, i); + string::size_type j = response.find((char)0, i+1); + string uid = response.substr(i+1, j-1); + string pwd = response.substr(j+1); + int error = 0; + if (!LogonUser(uid.c_str(), ".", pwd.c_str(), + LOGON32_LOGON_NETWORK, + LOGON32_PROVIDER_DEFAULT, + &userToken)) + error = GetLastError(); + pwd.replace(0, string::npos, 1, (char)0); + if (error != 0) { + QPID_LOG(info, + "SASL: Auth failed [" << error << "]: " << qpid::sys::strError(error)); + throw ConnectionForcedException("Authentication failed"); + } + + connection.setUserId(uid); + client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0); +} + +void SspiAuthenticator::step(const string& response) +{ + QPID_LOG(info, "SASL: Need another step!!!"); +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/client/AckMode.h b/RC9/qpid/cpp/src/qpid/client/AckMode.h new file mode 100644 index 0000000000..b411c322d8 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/AckMode.h @@ -0,0 +1,53 @@ +#ifndef _client_AckMode_h +#define _client_AckMode_h + + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +namespace qpid { +namespace client { + +/** + * DEPRECATED + * + * The available acknowledgement modes for Channel (now also deprecated). + */ +enum AckMode { + /** No acknowledgement will be sent, broker can + discard messages as soon as they are delivered + to a consumer using this mode. **/ + NO_ACK = 0, + /** Each message will be automatically + acknowledged as soon as it is delivered to the + application. **/ + AUTO_ACK = 1, + /** Acknowledgements will be sent automatically, + but not for each message. **/ + LAZY_ACK = 2, + /** The application is responsible for explicitly + acknowledging messages. **/ + CLIENT_ACK = 3 +}; + +}} // namespace qpid::client + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/AsyncSession.h b/RC9/qpid/cpp/src/qpid/client/AsyncSession.h new file mode 100644 index 0000000000..150aabe191 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/AsyncSession.h @@ -0,0 +1,38 @@ +#ifndef QPID_CLIENT_ASYNCSESSION_H +#define QPID_CLIENT_ASYNCSESSION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/AsyncSession_0_10.h" + +namespace qpid { +namespace client { + +/** + * AsyncSession is an alias for Session_0_10 + * + * \ingroup clientapi + */ +typedef AsyncSession_0_10 AsyncSession; + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_ASYNCSESSION_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/Bounds.cpp b/RC9/qpid/cpp/src/qpid/client/Bounds.cpp new file mode 100644 index 0000000000..e2ddb5dfec --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Bounds.cpp @@ -0,0 +1,71 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Bounds.h" + +#include "qpid/log/Statement.h" +#include "qpid/sys/Waitable.h" + +namespace qpid { +namespace client { + +using sys::Waitable; + +Bounds::Bounds(size_t maxSize) : max(maxSize), current(0) {} + +bool Bounds::expand(size_t sizeRequired, bool block) { + if (!max) return true; + Waitable::ScopedLock l(lock); + current += sizeRequired; + if (block) { + Waitable::ScopedWait w(lock); + while (current > max) + lock.wait(); + } + return current <= max; +} + +void Bounds::reduce(size_t size) { + if (!max || size == 0) return; + Waitable::ScopedLock l(lock); + if (current == 0) return; + current -= std::min(size, current); + if (current < max && lock.hasWaiters()) { + lock.notifyAll(); + } +} + +size_t Bounds::getCurrentSize() { + Waitable::ScopedLock l(lock); + return current; +} + +std::ostream& operator<<(std::ostream& out, const Bounds& bounds) { + out << "current=" << bounds.current << ", max=" << bounds.max << " [" << &bounds << "]"; + return out; +} + +void Bounds::setException(const sys::ExceptionHolder& e) { + Waitable::ScopedLock l(lock); + lock.setException(e); + lock.waitWaiters(); // Wait for waiting threads to exit. +} + +}} // namespace qpid::client diff --git a/RC9/qpid/cpp/src/qpid/client/Bounds.h b/RC9/qpid/cpp/src/qpid/client/Bounds.h new file mode 100644 index 0000000000..838fcb8368 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Bounds.h @@ -0,0 +1,49 @@ +#ifndef QPID_CLIENT_BOUNDSCHECKING_H +#define QPID_CLIENT_BOUNDSCHECKING_H +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/sys/Waitable.h" + +namespace qpid{ +namespace client{ + +class Bounds +{ + public: + Bounds(size_t maxSize); + bool expand(size_t, bool block); + void reduce(size_t); + size_t getCurrentSize(); + void setException(const sys::ExceptionHolder&); + + private: + friend std::ostream& operator<<(std::ostream&, const Bounds&); + sys::Waitable lock; + const size_t max; + size_t current; +}; + +std::ostream& operator<<(std::ostream&, const Bounds&); + + +}} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/ChainableFrameHandler.h b/RC9/qpid/cpp/src/qpid/client/ChainableFrameHandler.h new file mode 100644 index 0000000000..29e16d53dc --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/ChainableFrameHandler.h @@ -0,0 +1,47 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef _ChainableFrameHandler_ +#define _ChainableFrameHandler_ + +#include <boost/function.hpp> +#include "qpid/framing/AMQFrame.h" + +namespace qpid { +namespace client { + +struct ChainableFrameHandler +{ + typedef boost::function<void(framing::AMQFrame&)> FrameDelegate; + + FrameDelegate in; + FrameDelegate out; + + ChainableFrameHandler() {} + ChainableFrameHandler(FrameDelegate i, FrameDelegate o): in(i), out(o) {} + virtual ~ChainableFrameHandler() {} +}; + +}} + + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/Completion.h b/RC9/qpid/cpp/src/qpid/client/Completion.h new file mode 100644 index 0000000000..c4979d7934 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Completion.h @@ -0,0 +1,71 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef _Completion_ +#define _Completion_ + +#include <boost/shared_ptr.hpp> +#include "Future.h" +#include "SessionImpl.h" + +namespace qpid { +namespace client { + +/** + * Asynchronous commands that do not return a result will return a + * Completion. You can use the completion to wait for that specific + * command to complete. + * + *@see TypedResult + * + *\ingroup clientapi + */ +class Completion +{ +protected: + Future future; + shared_ptr<SessionImpl> session; + +public: + ///@internal + Completion() {} + + ///@internal + Completion(Future f, shared_ptr<SessionImpl> s) : future(f), session(s) {} + + /** Wait for the asynchronous command that returned this + *Completion to complete. + * + *@exception If the command returns an error, get() throws an exception. + */ + void wait() + { + future.wait(*session); + } + + bool isComplete() { + return future.isComplete(*session); + } +}; + +}} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/Connection.cpp b/RC9/qpid/cpp/src/qpid/client/Connection.cpp new file mode 100644 index 0000000000..f450344aa7 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Connection.cpp @@ -0,0 +1,145 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Connection.h" +#include "ConnectionSettings.h" +#include "Message.h" +#include "SessionImpl.h" +#include "SessionBase_0_10Access.h" +#include "qpid/Url.h" +#include "qpid/log/Logger.h" +#include "qpid/log/Options.h" +#include "qpid/log/Statement.h" +#include "qpid/shared_ptr.h" +#include "qpid/framing/AMQP_HighestVersion.h" + +#include <algorithm> +#include <iostream> +#include <sstream> +#include <functional> +#include <boost/format.hpp> +#include <boost/bind.hpp> + +using namespace qpid::framing; +using namespace qpid::sys; + + +namespace qpid { +namespace client { + +Connection::Connection() : version(framing::highestProtocolVersion) {} + +Connection::~Connection(){ } + +void Connection::open( + const Url& url, + const std::string& uid, const std::string& pwd, + const std::string& vhost, + uint16_t maxFrameSize) +{ + if (url.empty()) + throw Exception(QPID_MSG("Attempt to open URL with no addresses.")); + Url::const_iterator i = url.begin(); + do { + const TcpAddress* tcp = i->get<TcpAddress>(); + i++; + if (tcp) { + try { + ConnectionSettings settings; + settings.host = tcp->host; + settings.port = tcp->port; + settings.username = uid; + settings.password = pwd; + settings.virtualhost = vhost; + settings.maxFrameSize = maxFrameSize; + open(settings); + break; + } + catch (const Exception& /*e*/) { + if (i == url.end()) throw; + } + } + } while (i != url.end()); +} + +void Connection::open( + const std::string& host, int port, + const std::string& uid, const std::string& pwd, + const std::string& vhost, + uint16_t maxFrameSize) +{ + ConnectionSettings settings; + settings.host = host; + settings.port = port; + settings.username = uid; + settings.password = pwd; + settings.virtualhost = vhost; + settings.maxFrameSize = maxFrameSize; + open(settings); +} + +bool Connection::isOpen() const { + return impl && impl->isOpen(); +} + +void +Connection::registerFailureCallback ( boost::function<void ()> fn ) { + failureCallback = fn; + if ( impl ) + impl->registerFailureCallback ( fn ); +} + + + +void Connection::open(const ConnectionSettings& settings) +{ + if (isOpen()) + throw Exception(QPID_MSG("Connection::open() was already called")); + + impl = shared_ptr<ConnectionImpl>(new ConnectionImpl(version, settings)); + impl->open(); + if ( failureCallback ) + impl->registerFailureCallback ( failureCallback ); +} + +Session Connection::newSession(const std::string& name, uint32_t timeout) { + if (!isOpen()) + throw Exception(QPID_MSG("Connection has not yet been opened")); + Session s; + SessionBase_0_10Access(s).set(impl->newSession(name, timeout)); + return s; +} + +void Connection::resume(Session& session) { + if (!isOpen()) + throw Exception(QPID_MSG("Connection is not open.")); + impl->addSession(session.impl); + session.impl->resume(impl); +} + +void Connection::close() { + impl->close(); +} + +std::vector<Url> Connection::getKnownBrokers() { + return impl ? impl->getKnownBrokers() : std::vector<Url>(); +} + +}} // namespace qpid::client diff --git a/RC9/qpid/cpp/src/qpid/client/Connection.h b/RC9/qpid/cpp/src/qpid/client/Connection.h new file mode 100644 index 0000000000..d03542bb5b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Connection.h @@ -0,0 +1,185 @@ +#ifndef QPID_CLIENT_CONNECTION_H +#define QPID_CLIENT_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 <map> +#include <string> +#include "qpid/client/Session.h" + +namespace qpid { + +struct Url; + +namespace client { + +struct ConnectionSettings; +class ConnectionImpl; + +/** + * Represents a connection to an AMQP broker. All communication is + * initiated by establishing a connection, then creating one or more + * Session objects using the connection. @see newSession() + * + * \ingroup clientapi + * + */ +class Connection +{ + framing::ProtocolVersion version; + + boost::function<void ()> failureCallback; + + + protected: + boost::shared_ptr<ConnectionImpl> impl; + + + public: + /** + * Creates a connection object, but does not open the connection. + * @see open() + */ + Connection(); + + ~Connection(); + + /** + * Opens a connection to a broker. + * + * @param host the host on which the broker is running. + * + * @param port the port on the which the broker is listening. + * + * @param uid the userid to connect with. + * + * @param pwd the password to connect with (currently SASL + * PLAIN is the only authentication method supported so this + * is sent in clear text). + * + * @param virtualhost the AMQP virtual host to use (virtual + * hosts, where implemented(!), provide namespace partitioning + * within a single broker). + */ + void open(const std::string& host, int port = 5672, + const std::string& uid = "guest", + const std::string& pwd = "guest", + const std::string& virtualhost = "/", uint16_t maxFrameSize=65535); + + /** + * Opens a connection to a broker using a URL. + * If the URL contains multiple addresses, try each in turn + * till connection is successful. + * + * @url address of the broker to connect to. + * + * @param uid the userid to connect with. + * + * @param pwd the password to connect with (currently SASL + * PLAIN is the only authentication method supported so this + * is sent in clear text). + * + * @param virtualhost the AMQP virtual host to use (virtual + * hosts, where implemented(!), provide namespace partitioning + * within a single broker). + */ + void open(const Url& url, + const std::string& uid = "guest", + const std::string& pwd = "guest", + const std::string& virtualhost = "/", uint16_t maxFrameSize=65535); + + /** + * Opens a connection to a broker. + * + * @param the settings to use (host, port etc). @see ConnectionSettings. + */ + void open(const ConnectionSettings& settings); + + /** + * Close the connection. + * + * Any further use of this connection (without reopening it) will + * not succeed. + */ + void close(); + + /** + * Create a new session on this connection. Sessions allow + * multiple streams of work to be multiplexed over the same + * connection. The returned Session provides functions to send + * commands to the broker. + * + * Session functions are synchronous. In other words, a Session + * function will send a command to the broker and does not return + * until it receives the broker's response confirming the command + * was executed. + * + * AsyncSession provides asynchronous versions of the same + * functions. These functions send a command to the broker but do + * not wait for a response. + * + * You can convert a Session s into an AsyncSession as follows: + * @code + * #include <qpid/client/AsyncSession.h> + * AsyncSession as = async(s); + * @endcode + * + * You can execute a single command asynchronously will a Session s + * like ths: + * @code + * async(s).messageTransfer(...); + * @endcode + * + * Using an AsyncSession is faster for sending large numbers of + * commands, since each command is sent as soon as possible + * without waiting for the previous command to be confirmed. + * + * However with AsyncSession you cannot assume that a command has + * completed until you explicitly synchronize. The simplest way to + * do this is to call Session::sync() or AsyncSession::sync(). + * Both of these functions wait for the broker to confirm all + * commands issued so far on the session. + * + *@param name: A name to identify the session. @see qpid::SessionId + * If the name is empty (the default) then a unique name will be + * chosen using a Universally-unique identifier (UUID) algorithm. + */ + Session newSession(const std::string& name=std::string(), uint32_t timeoutSeconds = 0); + + /** + * Resume a suspended session. A session may be resumed + * on a different connection to the one that created it. + */ + void resume(Session& session); + + bool isOpen() const; + + std::vector<Url> getKnownBrokers(); + void registerFailureCallback ( boost::function<void ()> fn ); + + friend class ConnectionAccess; ///<@internal + friend class SessionBase_0_10; ///<@internal +}; + +}} // namespace qpid::client + + +#endif /*!QPID_CLIENT_CONNECTION_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/ConnectionAccess.h b/RC9/qpid/cpp/src/qpid/client/ConnectionAccess.h new file mode 100644 index 0000000000..b662fd5d8b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/ConnectionAccess.h @@ -0,0 +1,41 @@ +#ifndef QPID_CLIENT_CONNECTIONACCESS_H +#define QPID_CLIENT_CONNECTIONACCESS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/client/Connection.h" + +/**@file @internal Internal use only */ + +namespace qpid { +namespace client { + + + +struct ConnectionAccess { + static void setVersion(Connection& c, const framing::ProtocolVersion& v) { c.version = v; } + static boost::shared_ptr<ConnectionImpl> getImpl(Connection& c) { return c.impl; } +}; + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_CONNECTIONACCESS_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/ConnectionHandler.cpp b/RC9/qpid/cpp/src/qpid/client/ConnectionHandler.cpp new file mode 100644 index 0000000000..db5d006a17 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/ConnectionHandler.cpp @@ -0,0 +1,226 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ConnectionHandler.h" + +#include "qpid/log/Statement.h" +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/all_method_bodies.h" +#include "qpid/framing/ClientInvoker.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Helpers.h" + +using namespace qpid::client; +using namespace qpid::framing; +using namespace qpid::framing::connection; + +namespace { +const std::string OK("OK"); +const std::string PLAIN("PLAIN"); +const std::string en_US("en_US"); + +const std::string INVALID_STATE_START("start received in invalid state"); +const std::string INVALID_STATE_TUNE("tune received in invalid state"); +const std::string INVALID_STATE_OPEN_OK("open-ok received in invalid state"); +const std::string INVALID_STATE_CLOSE_OK("close-ok received in invalid state"); + +} + +CloseCode ConnectionHandler::convert(uint16_t replyCode) +{ + switch (replyCode) { + case 200: return CLOSE_CODE_NORMAL; + case 320: return CLOSE_CODE_CONNECTION_FORCED; + case 402: return CLOSE_CODE_INVALID_PATH; + case 501: default: + return CLOSE_CODE_FRAMING_ERROR; + } +} + +ConnectionHandler::ConnectionHandler(const ConnectionSettings& s, ProtocolVersion& v) + : StateManager(NOT_STARTED), ConnectionSettings(s), outHandler(*this), proxy(outHandler), + errorCode(CLOSE_CODE_NORMAL), version(v) +{ + insist = true; + + ESTABLISHED.insert(FAILED); + ESTABLISHED.insert(CLOSED); + ESTABLISHED.insert(OPEN); + + FINISHED.insert(FAILED); + FINISHED.insert(CLOSED); +} + +void ConnectionHandler::incoming(AMQFrame& frame) +{ + if (getState() == CLOSED) { + throw Exception("Received frame on closed connection"); + } + + + AMQBody* body = frame.getBody(); + try { + if (frame.getChannel() != 0 || !invoke(static_cast<ConnectionOperations&>(*this), *body)) { + switch(getState()) { + case OPEN: + in(frame); + break; + case CLOSING: + QPID_LOG(warning, "Ignoring frame while closing connection: " << frame); + break; + default: + throw Exception("Cannot receive frames on non-zero channel until connection is established."); + } + } + }catch(std::exception& e){ + QPID_LOG(warning, "Closing connection due to " << e.what()); + setState(CLOSING); + errorCode = CLOSE_CODE_FRAMING_ERROR; + errorText = e.what(); + proxy.close(501, e.what()); + } +} + +void ConnectionHandler::outgoing(AMQFrame& frame) +{ + if (getState() == OPEN) + out(frame); + else + throw TransportFailure(errorText.empty() ? "Connection is not open." : errorText); +} + +void ConnectionHandler::waitForOpen() +{ + waitFor(ESTABLISHED); + if (getState() == FAILED || getState() == CLOSED) { + throw ConnectionException(errorCode, errorText); + } +} + +void ConnectionHandler::close() +{ + switch (getState()) { + case NEGOTIATING: + case OPENING: + fail("Connection closed before it was established"); + break; + case OPEN: + setState(CLOSING); + proxy.close(200, OK); + waitFor(FINISHED); + break; + // Nothing to do for CLOSING, CLOSED, FAILED or NOT_STARTED + } +} + +void ConnectionHandler::checkState(STATES s, const std::string& msg) +{ + if (getState() != s) { + throw CommandInvalidException(msg); + } +} + +void ConnectionHandler::fail(const std::string& message) +{ + errorCode = CLOSE_CODE_FRAMING_ERROR; + errorText = message; + QPID_LOG(warning, message); + setState(FAILED); +} + +void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& /*mechanisms*/, const Array& /*locales*/) +{ + checkState(NOT_STARTED, INVALID_STATE_START); + setState(NEGOTIATING); + //TODO: verify that desired mechanism and locale are supported + string response = ((char)0) + username + ((char)0) + password; + proxy.startOk(properties, mechanism, response, locale); +} + +void ConnectionHandler::secure(const std::string& /*challenge*/) +{ + throw NotImplementedException("Challenge-response cycle not yet implemented in client"); +} + +void ConnectionHandler::tune(uint16_t maxChannelsProposed, uint16_t maxFrameSizeProposed, + uint16_t /*heartbeatMin*/, uint16_t /*heartbeatMax*/) +{ + checkState(NEGOTIATING, INVALID_STATE_TUNE); + maxChannels = std::min(maxChannels, maxChannelsProposed); + maxFrameSize = std::min(maxFrameSize, maxFrameSizeProposed); + //TODO: implement heartbeats and check desired value is in valid range + proxy.tuneOk(maxChannels, maxFrameSize, heartbeat); + setState(OPENING); + proxy.open(virtualhost, capabilities, insist); +} + +void ConnectionHandler::openOk ( const Array& knownBrokers ) +{ + checkState(OPENING, INVALID_STATE_OPEN_OK); + knownBrokersUrls.clear(); + framing::Array::ValueVector::const_iterator i; + for ( i = knownBrokers.begin(); i != knownBrokers.end(); ++i ) + knownBrokersUrls.push_back(Url((*i)->get<std::string>())); + setState(OPEN); + QPID_LOG(debug, "Known-brokers for connection: " << log::formatList(knownBrokersUrls)); +} + + +void ConnectionHandler::redirect(const std::string& /*host*/, const Array& /*knownHosts*/) +{ + throw NotImplementedException("Redirection received from broker; not yet implemented in client"); +} + +void ConnectionHandler::close(uint16_t replyCode, const std::string& replyText) +{ + proxy.closeOk(); + errorCode = convert(replyCode); + errorText = replyText; + setState(CLOSED); + QPID_LOG(warning, "Broker closed connection: " << replyCode << ", " << replyText); + if (onError) { + onError(replyCode, replyText); + } +} + +void ConnectionHandler::closeOk() +{ + checkState(CLOSING, INVALID_STATE_CLOSE_OK); + if (onError && errorCode != CLOSE_CODE_NORMAL) { + onError(errorCode, errorText); + } else if (onClose) { + onClose(); + } + setState(CLOSED); +} + +bool ConnectionHandler::isOpen() const +{ + return getState() == OPEN; +} + +bool ConnectionHandler::isClosed() const +{ + int s = getState(); + return s == CLOSED || s == FAILED; +} + +bool ConnectionHandler::isClosing() const { return getState() == CLOSING; } diff --git a/RC9/qpid/cpp/src/qpid/client/ConnectionHandler.h b/RC9/qpid/cpp/src/qpid/client/ConnectionHandler.h new file mode 100644 index 0000000000..12323684a5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/ConnectionHandler.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. + * + */ +#ifndef _ConnectionHandler_ +#define _ConnectionHandler_ + +#include "ChainableFrameHandler.h" +#include "ConnectionSettings.h" +#include "StateManager.h" +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/framing/AMQP_ClientOperations.h" +#include "qpid/framing/AMQP_ServerProxy.h" +#include "qpid/framing/Array.h" +#include "qpid/framing/enum.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/InputHandler.h" +#include "qpid/Url.h" + +namespace qpid { +namespace client { + +class ConnectionHandler : private StateManager, + public ConnectionSettings, + public ChainableFrameHandler, + public framing::InputHandler, + private framing::AMQP_ClientOperations::ConnectionHandler +{ + typedef framing::AMQP_ClientOperations::ConnectionHandler ConnectionOperations; + enum STATES {NOT_STARTED, NEGOTIATING, OPENING, OPEN, CLOSING, CLOSED, FAILED}; + std::set<int> ESTABLISHED, FINISHED; + + class Adapter : public framing::FrameHandler + { + ConnectionHandler& handler; + public: + Adapter(ConnectionHandler& h) : handler(h) {} + void handle(framing::AMQFrame& f) { handler.out(f); } + }; + + Adapter outHandler; + framing::AMQP_ServerProxy::Connection proxy; + framing::connection::CloseCode errorCode; + std::string errorText; + bool insist; + framing::ProtocolVersion version; + framing::Array capabilities; + framing::FieldTable properties; + + void checkState(STATES s, const std::string& msg); + + //methods corresponding to connection controls: + void start(const framing::FieldTable& serverProperties, + const framing::Array& mechanisms, + const framing::Array& locales); + void secure(const std::string& challenge); + void tune(uint16_t channelMax, + uint16_t frameMax, + uint16_t heartbeatMin, + uint16_t heartbeatMax); + void openOk(const framing::Array& knownHosts); + void redirect(const std::string& host, + const framing::Array& knownHosts); + void close(uint16_t replyCode, const std::string& replyText); + void closeOk(); + +public: + using InputHandler::handle; + typedef boost::function<void()> CloseListener; + typedef boost::function<void(uint16_t, const std::string&)> ErrorListener; + + ConnectionHandler(const ConnectionSettings&, framing::ProtocolVersion&); + + void received(framing::AMQFrame& f) { incoming(f); } + + void incoming(framing::AMQFrame& frame); + void outgoing(framing::AMQFrame& frame); + + void waitForOpen(); + void close(); + void fail(const std::string& message); + + // Note that open and closed aren't related by open = !closed + bool isOpen() const; + bool isClosed() const; + bool isClosing() const; + + CloseListener onClose; + ErrorListener onError; + + std::vector<Url> knownBrokersUrls; + + static framing::connection::CloseCode convert(uint16_t replyCode); +}; + +}} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/ConnectionImpl.cpp b/RC9/qpid/cpp/src/qpid/client/ConnectionImpl.cpp new file mode 100644 index 0000000000..0d7ffa0288 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/ConnectionImpl.cpp @@ -0,0 +1,201 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "ConnectionImpl.h" +#include "Connector.h" +#include "ConnectionSettings.h" +#include "SessionImpl.h" +#include "FailoverListener.h" + +#include "qpid/log/Statement.h" +#include "qpid/Url.h" +#include "qpid/framing/enum.h" +#include "qpid/framing/reply_exceptions.h" + +#include <boost/bind.hpp> +#include <boost/format.hpp> + + +namespace qpid { +namespace client { + +using namespace qpid::framing; +using namespace qpid::framing::connection; +using namespace qpid::sys; +using namespace qpid::framing::connection;//for connection error codes + +ConnectionImpl::ConnectionImpl(framing::ProtocolVersion v, const ConnectionSettings& settings) + : Bounds(settings.maxFrameSize * settings.bounds), + handler(settings, v), + version(v), + nextChannel(1) +{ + QPID_LOG(debug, "ConnectionImpl created for " << version.toString()); + handler.in = boost::bind(&ConnectionImpl::incoming, this, _1); + handler.out = boost::bind(&Connector::send, boost::ref(connector), _1); + handler.onClose = boost::bind(&ConnectionImpl::closed, this, + CLOSE_CODE_NORMAL, std::string()); + //only set error handler once open + handler.onError = boost::bind(&ConnectionImpl::closed, this, _1, _2); +} + +ConnectionImpl::~ConnectionImpl() { + // Important to close the connector first, to ensure the + // connector thread does not call on us while the destructor + // is running. + failover.reset(); + if (connector) connector->close(); +} + +void ConnectionImpl::addSession(const boost::shared_ptr<SessionImpl>& session, uint16_t channel) +{ + Mutex::ScopedLock l(lock); + session->setChannel(channel ? channel : nextChannel++); + boost::weak_ptr<SessionImpl>& s = sessions[session->getChannel()]; + boost::shared_ptr<SessionImpl> ss = s.lock(); + if (ss) throw SessionBusyException(QPID_MSG("Channel " << ss->getChannel() << " attachd to " << ss->getId())); + s = session; +} + +void ConnectionImpl::handle(framing::AMQFrame& frame) +{ + handler.outgoing(frame); +} + +void ConnectionImpl::incoming(framing::AMQFrame& frame) +{ + boost::shared_ptr<SessionImpl> s; + { + Mutex::ScopedLock l(lock); + s = sessions[frame.getChannel()].lock(); + } + if (!s) + throw NotAttachedException(QPID_MSG("Invalid channel: " << frame.getChannel())); + s->in(frame); +} + +bool ConnectionImpl::isOpen() const +{ + return handler.isOpen(); +} + + +void ConnectionImpl::open() +{ + const std::string& protocol = handler.protocol; + const std::string& host = handler.host; + int port = handler.port; + QPID_LOG(info, "Connecting to " << protocol << ":" << host << ":" << port); + + connector.reset(Connector::create(protocol, version, handler, this)); + connector->setInputHandler(&handler); + connector->setShutdownHandler(this); + connector->connect(host, port); + connector->init(); + handler.waitForOpen(); + + failover.reset(new FailoverListener(shared_from_this(), handler.knownBrokersUrls)); +} + +void ConnectionImpl::idleIn() +{ + close(); +} + +void ConnectionImpl::idleOut() +{ + AMQFrame frame(in_place<AMQHeartbeatBody>()); + connector->send(frame); +} + +void ConnectionImpl::close() +{ + if (!handler.isOpen()) return; + handler.close(); + closed(CLOSE_CODE_NORMAL, "Closed by client"); +} + + +template <class F> void ConnectionImpl::closeInternal(const F& f) { + { + Mutex::ScopedUnlock u(lock); + connector->close(); + } + //notifying sessions of failure can result in those session being + //deleted which in turn results in a call to erase(); this can + //even happen on this thread, when 's' goes out of scope + //below. Using a copy prevents the map being modified as we + //iterate through. + SessionMap copy; + sessions.swap(copy); + for (SessionMap::iterator i = copy.begin(); i != copy.end(); ++i) { + boost::shared_ptr<SessionImpl> s = i->second.lock(); + if (s) f(s); + } +} + +void ConnectionImpl::closed(uint16_t code, const std::string& text) { + Mutex::ScopedLock l(lock); + setException(new ConnectionException(ConnectionHandler::convert(code), text)); + closeInternal(boost::bind(&SessionImpl::connectionClosed, _1, code, text)); +} + +static const std::string CONN_CLOSED("Connection closed"); + +void ConnectionImpl::shutdown() { + if ( failureCallback ) + failureCallback(); + + if (handler.isClosed()) return; + + // FIXME aconway 2008-06-06: exception use, amqp0-10 does not seem to have + // an appropriate close-code. connection-forced is not right. + bool isClosing = handler.isClosing(); + handler.fail(CONN_CLOSED);//ensure connection is marked as failed before notifying sessions + Mutex::ScopedLock l(lock); + if (!isClosing) + closeInternal(boost::bind(&SessionImpl::connectionBroke, _1, CONN_CLOSED)); + setException(new TransportFailure(CONN_CLOSED)); +} + +void ConnectionImpl::erase(uint16_t ch) { + Mutex::ScopedLock l(lock); + sessions.erase(ch); +} + +const ConnectionSettings& ConnectionImpl::getNegotiatedSettings() +{ + return handler; +} + +std::vector<qpid::Url> ConnectionImpl::getKnownBrokers() { + return failover ? failover->getKnownBrokers() : handler.knownBrokersUrls; +} + +boost::shared_ptr<SessionImpl> ConnectionImpl::newSession(const std::string& name, uint32_t timeout, uint16_t channel) { + boost::shared_ptr<SessionImpl> simpl(new SessionImpl(name, shared_from_this())); + addSession(simpl, channel); + simpl->open(timeout); + return simpl; +} + +void ConnectionImpl::stopFailoverListener() { failover->stop(); } + +}} // namespace qpid::client diff --git a/RC9/qpid/cpp/src/qpid/client/ConnectionImpl.h b/RC9/qpid/cpp/src/qpid/client/ConnectionImpl.h new file mode 100644 index 0000000000..55a4929028 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/ConnectionImpl.h @@ -0,0 +1,99 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef _ConnectionImpl_ +#define _ConnectionImpl_ + +#include "Bounds.h" +#include "ConnectionHandler.h" + +#include "qpid/framing/FrameHandler.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/ShutdownHandler.h" +#include "qpid/sys/TimeoutHandler.h" + +#include <map> +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include <boost/scoped_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> + +namespace qpid { +namespace client { + +class Connector; +struct ConnectionSettings; +class SessionImpl; +class FailoverListener; + +class ConnectionImpl : public Bounds, + public framing::FrameHandler, + public sys::TimeoutHandler, + public sys::ShutdownHandler, + public boost::enable_shared_from_this<ConnectionImpl> + +{ + typedef std::map<uint16_t, boost::weak_ptr<SessionImpl> > SessionMap; + + SessionMap sessions; + ConnectionHandler handler; + boost::scoped_ptr<Connector> connector; + boost::scoped_ptr<FailoverListener> failover; + framing::ProtocolVersion version; + uint16_t nextChannel; + sys::Mutex lock; + + template <class F> void closeInternal(const F&); + + void incoming(framing::AMQFrame& frame); + void closed(uint16_t, const std::string&); + void idleOut(); + void idleIn(); + void shutdown(); + + boost::function<void ()> failureCallback; + + public: + ConnectionImpl(framing::ProtocolVersion version, const ConnectionSettings& settings); + ~ConnectionImpl(); + + void open(); + bool isOpen() const; + + boost::shared_ptr<SessionImpl> newSession(const std::string& name, uint32_t timeout, uint16_t channel=0); + void addSession(const boost::shared_ptr<SessionImpl>&, uint16_t channel=0); + + void close(); + void handle(framing::AMQFrame& frame); + void erase(uint16_t channel); + const ConnectionSettings& getNegotiatedSettings(); + + std::vector<Url> getKnownBrokers(); + void registerFailureCallback ( boost::function<void ()> fn ) { failureCallback = fn; } + void stopFailoverListener(); + + framing::ProtocolVersion getVersion() { return version; } +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/ConnectionSettings.cpp b/RC9/qpid/cpp/src/qpid/client/ConnectionSettings.cpp new file mode 100644 index 0000000000..f5fc62dad2 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/ConnectionSettings.cpp @@ -0,0 +1,54 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "ConnectionSettings.h" + +#include "qpid/log/Logger.h" +#include "qpid/sys/Socket.h" + +namespace qpid { +namespace client { + +ConnectionSettings::ConnectionSettings() : + protocol("tcp"), + host("localhost"), + port(TcpAddress::DEFAULT_PORT), + username("guest"), + password("guest"), + mechanism("PLAIN"), + locale("en_US"), + heartbeat(0), + maxChannels(32767), + maxFrameSize(65535), + bounds(2), + tcpNoDelay(false) +{} + +ConnectionSettings::~ConnectionSettings() {} + +void ConnectionSettings::configureSocket(qpid::sys::Socket& socket) const +{ + if (tcpNoDelay) { + socket.setTcpNoDelay(tcpNoDelay); + QPID_LOG(info, "Set TCP_NODELAY"); + } +} + +}} // namespace qpid::client diff --git a/RC9/qpid/cpp/src/qpid/client/ConnectionSettings.h b/RC9/qpid/cpp/src/qpid/client/ConnectionSettings.h new file mode 100644 index 0000000000..1b994a6da3 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/ConnectionSettings.h @@ -0,0 +1,118 @@ +#ifndef QPID_CLIENT_CONNECTIONSETTINGS_H +#define QPID_CLIENT_CONNECTIONSETTINGS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Options.h" +#include "qpid/log/Options.h" +#include "qpid/Url.h" + +#include <iostream> +#include <exception> + +namespace qpid { + +namespace sys { +class Socket; +} + +namespace client { + +/** + * Settings for a Connection. + */ +struct ConnectionSettings { + + ConnectionSettings(); + virtual ~ConnectionSettings(); + + /** + * Allows socket to be configured; default only sets tcp-nodelay + * based on the flag set. Can be overridden. + */ + virtual void configureSocket(qpid::sys::Socket&) const; + + /** + * The protocol used for the connection (defaults to 'tcp') + */ + std::string protocol; + + /** + * The host (or ip address) to connect to (defaults to 'localhost'). + */ + std::string host; + /** + * The port to connect to (defaults to 5672). + */ + uint16_t port; + /** + * Allows an AMQP 'virtual host' to be specified for the + * connection. + */ + std::string virtualhost; + + /** + * The username to use when authenticating the connection. + */ + std::string username; + /** + * The password to use when authenticating the connection. + */ + std::string password; + /** + * The SASL mechanism to use when authenticating the connection; + * the options are currently PLAIN or ANONYMOUS. + */ + std::string mechanism; + /** + * Allows a locale to be specified for the connection. + */ + std::string locale; + /** + * Allows a heartbeat frequency to be specified (this feature is + * not yet implemented). + */ + uint16_t heartbeat; + /** + * The maximum number of channels that the client will request for + * use on this connection. + */ + uint16_t maxChannels; + /** + * The maximum frame size that the client will request for this + * connection. + */ + uint16_t maxFrameSize; + /** + * Limit the size of the connections send buffer . The buffer + * is limited to bounds * maxFrameSize. + */ + uint bounds; + /** + * If true, TCP_NODELAY will be set for the connection. + */ + bool tcpNoDelay; +}; + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_CONNECTIONSETTINGS_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/Connector.cpp b/RC9/qpid/cpp/src/qpid/client/Connector.cpp new file mode 100644 index 0000000000..bef98863a1 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Connector.cpp @@ -0,0 +1,413 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Connector.h" + +#include "Bounds.h" +#include "ConnectionImpl.h" +#include "ConnectionSettings.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/Time.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/sys/AsynchIO.h" +#include "qpid/sys/Dispatcher.h" +#include "qpid/sys/Poller.h" +#include "qpid/Msg.h" + +#include <iostream> +#include <map> +#include <boost/bind.hpp> +#include <boost/format.hpp> +#include <boost/weak_ptr.hpp> + +namespace qpid { +namespace client { + +using namespace qpid::sys; +using namespace qpid::framing; +using boost::format; +using boost::str; + +// Stuff for the registry of protocol connectors (maybe should be moved to its own file) +namespace { + typedef std::map<std::string, Connector::Factory*> ProtocolRegistry; + + ProtocolRegistry& theProtocolRegistry() { + static ProtocolRegistry protocolRegistry; + + return protocolRegistry; + } +} + +Connector* Connector::create(const std::string& proto, framing::ProtocolVersion v, const ConnectionSettings& s, ConnectionImpl* c) +{ + ProtocolRegistry::const_iterator i = theProtocolRegistry().find(proto); + if (i==theProtocolRegistry().end()) { + throw Exception(QPID_MSG("Unknown protocol: " << proto)); + } + return (i->second)(v, s, c); +} + +void Connector::registerFactory(const std::string& proto, Factory* connectorFactory) +{ + ProtocolRegistry::const_iterator i = theProtocolRegistry().find(proto); + if (i!=theProtocolRegistry().end()) { + QPID_LOG(error, "Tried to register protocol: " << proto << " more than once"); + } + theProtocolRegistry()[proto] = connectorFactory; +} + +class TCPConnector : public Connector, private sys::Runnable +{ + struct Buff; + + /** Batch up frames for writing to aio. */ + class Writer : public framing::FrameHandler { + typedef sys::AsynchIOBufferBase BufferBase; + typedef std::vector<framing::AMQFrame> Frames; + + const uint16_t maxFrameSize; + sys::Mutex lock; + sys::AsynchIO* aio; + BufferBase* buffer; + Frames frames; + size_t lastEof; // Position after last EOF in frames + framing::Buffer encode; + size_t framesEncoded; + std::string identifier; + Bounds* bounds; + + void writeOne(); + void newBuffer(); + + public: + + Writer(uint16_t maxFrameSize, Bounds*); + ~Writer(); + void init(std::string id, sys::AsynchIO*); + void handle(framing::AMQFrame&); + void write(sys::AsynchIO&); + }; + + const uint16_t maxFrameSize; + framing::ProtocolVersion version; + bool initiated; + + sys::Mutex closedLock; + bool closed; + bool joined; + + sys::ShutdownHandler* shutdownHandler; + framing::InputHandler* input; + framing::InitiationHandler* initialiser; + framing::OutputHandler* output; + + Writer writer; + + sys::Thread receiver; + + sys::Socket socket; + + sys::AsynchIO* aio; + boost::shared_ptr<sys::Poller> poller; + + ~TCPConnector(); + + void run(); + void handleClosed(); + bool closeInternal(); + + bool readbuff(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*); + void writebuff(qpid::sys::AsynchIO&); + void writeDataBlock(const framing::AMQDataBlock& data); + void eof(qpid::sys::AsynchIO&); + + std::string identifier; + + boost::weak_ptr<ConnectionImpl> impl; + + void connect(const std::string& host, int port); + void init(); + void close(); + void send(framing::AMQFrame& frame); + + void setInputHandler(framing::InputHandler* handler); + void setShutdownHandler(sys::ShutdownHandler* handler); + sys::ShutdownHandler* getShutdownHandler() const; + framing::OutputHandler* getOutputHandler(); + const std::string& getIdentifier() const; + +public: + TCPConnector(framing::ProtocolVersion pVersion, + const ConnectionSettings&, + ConnectionImpl*); +}; + +// Static constructor which registers connector here +namespace { + Connector* create(framing::ProtocolVersion v, const ConnectionSettings& s, ConnectionImpl* c) { + return new TCPConnector(v, s, c); + } + + struct StaticInit { + StaticInit() { + Connector::registerFactory("tcp", &create); + }; + } init; +} + +TCPConnector::TCPConnector(ProtocolVersion ver, + const ConnectionSettings& settings, + ConnectionImpl* cimpl) + : maxFrameSize(settings.maxFrameSize), + version(ver), + initiated(false), + closed(true), + joined(true), + shutdownHandler(0), + writer(maxFrameSize, cimpl), + aio(0), + impl(cimpl->shared_from_this()) +{ + QPID_LOG(debug, "TCPConnector created for " << version.toString()); + settings.configureSocket(socket); +} + +TCPConnector::~TCPConnector() { + close(); +} + +void TCPConnector::connect(const std::string& host, int port){ + Mutex::ScopedLock l(closedLock); + assert(closed); + try { + socket.connect(host, port); + } catch (const std::exception& /*e*/) { + socket.close(); + throw; + } + + identifier = str(format("[%1% %2%]") % socket.getLocalPort() % socket.getPeerAddress()); + closed = false; + poller = Poller::shared_ptr(new Poller); + aio = AsynchIO::create(socket, + boost::bind(&TCPConnector::readbuff, this, _1, _2), + boost::bind(&TCPConnector::eof, this, _1), + boost::bind(&TCPConnector::eof, this, _1), + 0, // closed + 0, // nobuffs + boost::bind(&TCPConnector::writebuff, this, _1)); + writer.init(identifier, aio); +} + +void TCPConnector::init(){ + Mutex::ScopedLock l(closedLock); + assert(joined); + ProtocolInitiation init(version); + writeDataBlock(init); + joined = false; + receiver = Thread(this); +} + +bool TCPConnector::closeInternal() { + Mutex::ScopedLock l(closedLock); + bool ret = !closed; + if (!closed) { + closed = true; + poller->shutdown(); + } + if (!joined && receiver.id() != Thread::current().id()) { + joined = true; + Mutex::ScopedUnlock u(closedLock); + receiver.join(); + } + return ret; +} + +void TCPConnector::close() { + closeInternal(); +} + +void TCPConnector::setInputHandler(InputHandler* handler){ + input = handler; +} + +void TCPConnector::setShutdownHandler(ShutdownHandler* handler){ + shutdownHandler = handler; +} + +OutputHandler* TCPConnector::getOutputHandler() { + return this; +} + +sys::ShutdownHandler* TCPConnector::getShutdownHandler() const { + return shutdownHandler; +} + +const std::string& TCPConnector::getIdentifier() const { + return identifier; +} + +void TCPConnector::send(AMQFrame& frame) { + writer.handle(frame); +} + +void TCPConnector::handleClosed() { + if (closeInternal() && shutdownHandler) + shutdownHandler->shutdown(); +} + +struct TCPConnector::Buff : public AsynchIO::BufferBase { + Buff(size_t size) : AsynchIO::BufferBase(new char[size], size) {} + ~Buff() { delete [] bytes;} +}; + +TCPConnector::Writer::Writer(uint16_t s, Bounds* b) : maxFrameSize(s), aio(0), buffer(0), lastEof(0), bounds(b) +{ +} + +TCPConnector::Writer::~Writer() { delete buffer; } + +void TCPConnector::Writer::init(std::string id, sys::AsynchIO* a) { + Mutex::ScopedLock l(lock); + identifier = id; + aio = a; + newBuffer(); +} +void TCPConnector::Writer::handle(framing::AMQFrame& frame) { + Mutex::ScopedLock l(lock); + frames.push_back(frame); + //only try to write if this is the end of a frameset or if we + //already have a buffers worth of data + if (frame.getEof() || (bounds && bounds->getCurrentSize() >= maxFrameSize)) { + lastEof = frames.size(); + aio->notifyPendingWrite(); + } + QPID_LOG(trace, "SENT " << identifier << ": " << frame); +} + +void TCPConnector::Writer::writeOne() { + assert(buffer); + framesEncoded = 0; + + buffer->dataStart = 0; + buffer->dataCount = encode.getPosition(); + aio->queueWrite(buffer); + newBuffer(); +} + +void TCPConnector::Writer::newBuffer() { + buffer = aio->getQueuedBuffer(); + if (!buffer) buffer = new Buff(maxFrameSize); + encode = framing::Buffer(buffer->bytes, buffer->byteCount); + framesEncoded = 0; +} + +// Called in IO thread. +void TCPConnector::Writer::write(sys::AsynchIO&) { + Mutex::ScopedLock l(lock); + assert(buffer); + size_t bytesWritten(0); + for (size_t i = 0; i < lastEof; ++i) { + AMQFrame& frame = frames[i]; + uint32_t size = frame.encodedSize(); + if (size > encode.available()) writeOne(); + assert(size <= encode.available()); + frame.encode(encode); + ++framesEncoded; + bytesWritten += size; + } + frames.erase(frames.begin(), frames.begin()+lastEof); + lastEof = 0; + if (bounds) bounds->reduce(bytesWritten); + if (encode.getPosition() > 0) writeOne(); +} + +bool TCPConnector::readbuff(AsynchIO& aio, AsynchIO::BufferBase* buff) { + framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount); + + if (!initiated) { + framing::ProtocolInitiation protocolInit; + if (protocolInit.decode(in)) { + //TODO: check the version is correct + QPID_LOG(debug, "RECV " << identifier << " INIT(" << protocolInit << ")"); + } + initiated = true; + } + AMQFrame frame; + while(frame.decode(in)){ + QPID_LOG(trace, "RECV " << identifier << ": " << frame); + input->received(frame); + } + // TODO: unreading needs to go away, and when we can cope + // with multiple sub-buffers in the general buffer scheme, it will + if (in.available() != 0) { + // Adjust buffer for used bytes and then "unread them" + buff->dataStart += buff->dataCount-in.available(); + buff->dataCount = in.available(); + aio.unread(buff); + } else { + // Give whole buffer back to aio subsystem + aio.queueReadBuffer(buff); + } + return true; +} + +void TCPConnector::writebuff(AsynchIO& aio_) { + writer.write(aio_); +} + +void TCPConnector::writeDataBlock(const AMQDataBlock& data) { + AsynchIO::BufferBase* buff = new Buff(maxFrameSize); + framing::Buffer out(buff->bytes, buff->byteCount); + data.encode(out); + buff->dataCount = data.encodedSize(); + aio->queueWrite(buff); +} + +void TCPConnector::eof(AsynchIO&) { + handleClosed(); +} + +// TODO: astitcher 20070908 This version of the code can never time out, so the idle processing +// will never be called +void TCPConnector::run(){ + // Keep the connection impl in memory until run() completes. + boost::shared_ptr<ConnectionImpl> protect = impl.lock(); + assert(protect); + try { + Dispatcher d(poller); + + for (int i = 0; i < 32; i++) { + aio->queueReadBuffer(new Buff(maxFrameSize)); + } + + aio->start(poller); + d.run(); + aio->queueForDeletion(); + socket.close(); + } catch (const std::exception& e) { + QPID_LOG(error, QPID_MSG("FAIL " << identifier << ": " << e.what())); + handleClosed(); + } +} + + +}} // namespace qpid::client diff --git a/RC9/qpid/cpp/src/qpid/client/Connector.h b/RC9/qpid/cpp/src/qpid/client/Connector.h new file mode 100644 index 0000000000..5c37d95300 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Connector.h @@ -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. + * + */ +#ifndef _Connector_ +#define _Connector_ + + +#include "qpid/framing/InputHandler.h" +#include "qpid/framing/OutputHandler.h" +#include "qpid/framing/InitiationHandler.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/sys/ShutdownHandler.h" +#include "qpid/sys/TimeoutHandler.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Socket.h" +#include "qpid/sys/Time.h" + +#include <queue> +#include <boost/weak_ptr.hpp> +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace client { + +struct ConnectionSettings; +class ConnectionImpl; + +///@internal +class Connector : public framing::OutputHandler +{ + public: + // Protocol connector factory related stuff (it might be better to separate this code from the TCP Connector in the future) + typedef Connector* Factory(framing::ProtocolVersion, const ConnectionSettings&, ConnectionImpl*); + static Connector* create(const std::string& proto, framing::ProtocolVersion, const ConnectionSettings&, ConnectionImpl*); + static void registerFactory(const std::string& proto, Factory* connectorFactory); + + virtual ~Connector() {}; + virtual void connect(const std::string& host, int port) = 0; + virtual void init() {}; + virtual void close() = 0; + virtual void send(framing::AMQFrame& frame) = 0; + + virtual void setInputHandler(framing::InputHandler* handler) = 0; + virtual void setShutdownHandler(sys::ShutdownHandler* handler) = 0; + virtual sys::ShutdownHandler* getShutdownHandler() const = 0; + virtual framing::OutputHandler* getOutputHandler() = 0; + virtual const std::string& getIdentifier() const = 0; +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/Demux.cpp b/RC9/qpid/cpp/src/qpid/client/Demux.cpp new file mode 100644 index 0000000000..b774214ae4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Demux.cpp @@ -0,0 +1,132 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Demux.h" +#include "qpid/Exception.h" +#include "qpid/framing/MessageTransferBody.h" + +#include <iostream> + +namespace qpid { +namespace client { + +ByTransferDest::ByTransferDest(const std::string& d) : dest(d) {} +bool ByTransferDest::operator()(const framing::FrameSet& frameset) const +{ + return frameset.isA<framing::MessageTransferBody>() && + frameset.as<framing::MessageTransferBody>()->getDestination() == dest; +} + +ScopedDivert::ScopedDivert(const std::string& _dest, Demux& _demuxer) : dest(_dest), demuxer(_demuxer) +{ + queue = demuxer.add(dest, ByTransferDest(dest)); +} + +ScopedDivert::~ScopedDivert() +{ + demuxer.remove(dest); +} + +Demux::Demux() : defaultQueue(new Queue()) {} + +Demux::~Demux() { close(sys::ExceptionHolder(new ClosedException())); } + +Demux::QueuePtr ScopedDivert::getQueue() +{ + return queue; +} + +void Demux::handle(framing::FrameSet::shared_ptr frameset) +{ + sys::Mutex::ScopedLock l(lock); + bool matched = false; + for (iterator i = records.begin(); i != records.end() && !matched; i++) { + if (i->condition && i->condition(*frameset)) { + matched = true; + i->queue->push(frameset); + } + } + if (!matched) { + defaultQueue->push(frameset); + } +} + +void Demux::close(const sys::ExceptionHolder& ex) +{ + sys::Mutex::ScopedLock l(lock); + for (iterator i = records.begin(); i != records.end(); i++) { + i->queue->close(ex); + } + defaultQueue->close(ex); +} + +void Demux::open() +{ + sys::Mutex::ScopedLock l(lock); + for (iterator i = records.begin(); i != records.end(); i++) { + i->queue->open(); + } + defaultQueue->open(); +} + +Demux::QueuePtr Demux::add(const std::string& name, Condition condition) +{ + sys::Mutex::ScopedLock l(lock); + iterator i = std::find_if(records.begin(), records.end(), Find(name)); + if (i == records.end()) { + Record r(name, condition); + records.push_back(r); + return r.queue; + } else { + throw Exception("Queue already exists for " + name); + } +} + +void Demux::remove(const std::string& name) +{ + sys::Mutex::ScopedLock l(lock); + records.remove_if(Find(name)); +} + +Demux::QueuePtr Demux::get(const std::string& name) +{ + sys::Mutex::ScopedLock l(lock); + iterator i = std::find_if(records.begin(), records.end(), Find(name)); + if (i == records.end()) { + throw Exception("No queue for " + name); + } + return i->queue; +} + +Demux::QueuePtr Demux::getDefault() +{ + return defaultQueue; +} + +Demux::Find::Find(const std::string& n) : name(n) {} + +bool Demux::Find::operator()(const Record& record) const +{ + return record.name == name; +} + +}} + diff --git a/RC9/qpid/cpp/src/qpid/client/Demux.h b/RC9/qpid/cpp/src/qpid/client/Demux.h new file mode 100644 index 0000000000..7304e799bb --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Demux.h @@ -0,0 +1,102 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <list> +#include <boost/function.hpp> +#include <boost/shared_ptr.hpp> +#include "qpid/framing/FrameSet.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/BlockingQueue.h" + +#ifndef _Demux_ +#define _Demux_ + +namespace qpid { +namespace client { + +///@internal +class ByTransferDest +{ + const std::string dest; +public: + ByTransferDest(const std::string& dest); + bool operator()(const framing::FrameSet& frameset) const; +}; + +///@internal +class Demux +{ +public: + typedef boost::function<bool(const framing::FrameSet&)> Condition; + typedef sys::BlockingQueue<framing::FrameSet::shared_ptr> Queue; + typedef boost::shared_ptr<Queue> QueuePtr; + + Demux(); + ~Demux(); + + void handle(framing::FrameSet::shared_ptr); + void close(const sys::ExceptionHolder& ex); + void open(); + + QueuePtr add(const std::string& name, Condition); + void remove(const std::string& name); + QueuePtr get(const std::string& name); + QueuePtr getDefault(); + +private: + struct Record + { + const std::string name; + Condition condition; + QueuePtr queue; + + Record(const std::string& n, Condition c) : name(n), condition(c), queue(new Queue()) {} + }; + + sys::Mutex lock; + std::list<Record> records; + QueuePtr defaultQueue; + + typedef std::list<Record>::iterator iterator; + + struct Find + { + const std::string name; + Find(const std::string& name); + bool operator()(const Record& record) const; + }; +}; + +class ScopedDivert +{ + const std::string dest; + Demux& demuxer; + Demux::QueuePtr queue; +public: + ScopedDivert(const std::string& dest, Demux& demuxer); + ~ScopedDivert(); + Demux::QueuePtr getQueue(); +}; + +}} // namespace qpid::client + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/Dispatcher.cpp b/RC9/qpid/cpp/src/qpid/client/Dispatcher.cpp new file mode 100644 index 0000000000..27cc4184f9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Dispatcher.cpp @@ -0,0 +1,144 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Dispatcher.h" +#include "SubscriptionImpl.h" + +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/BlockingQueue.h" +#include "Message.h" + +#include <boost/state_saver.hpp> + +using qpid::framing::FrameSet; +using qpid::framing::MessageTransferBody; +using qpid::sys::Mutex; +using qpid::sys::ScopedLock; +using qpid::sys::Thread; + +namespace qpid { +namespace client { + +Dispatcher::Dispatcher(const Session& s, const std::string& q) + : session(s), + running(false), + autoStop(true), + failoverHandler(0) +{ + queue = q.empty() ? + session.getExecution().getDemux().getDefault() : + session.getExecution().getDemux().get(q); +} + +void Dispatcher::start() +{ + worker = Thread(this); +} + +void Dispatcher::wait() +{ + worker.join(); +} + +void Dispatcher::run() +{ + Mutex::ScopedLock l(lock); + if (running) + throw Exception("Dispatcher is already running."); + boost::state_saver<bool> reset(running); // Reset to false on exit. + running = true; + try { + while (!queue->isClosed()) { + Mutex::ScopedUnlock u(lock); + FrameSet::shared_ptr content = queue->pop(); + if (content->isA<MessageTransferBody>()) { + Message msg(*content); + boost::intrusive_ptr<SubscriptionImpl> listener = find(msg.getDestination()); + if (!listener) { + QPID_LOG(error, "No listener found for destination " << msg.getDestination()); + } else { + assert(listener); + listener->received(msg); + } + } else { + if (handler.get()) { + handler->handle(*content); + } else { + QPID_LOG(warning, "No handler found for " << *(content->getMethod())); + } + } + } + session.sync(); // Make sure all our acks are received before returning. + } + catch (const ClosedException&) { + QPID_LOG(debug, QPID_MSG(session.getId() << ": closed by peer")); + } + catch (const TransportFailure&) { + QPID_LOG(info, QPID_MSG(session.getId() << ": transport failure")); + throw; + } + catch (const std::exception& e) { + if ( failoverHandler ) { + QPID_LOG(debug, QPID_MSG(session.getId() << " failover: " << e.what())); + failoverHandler(); + } else { + QPID_LOG(error, session.getId() << " error: " << e.what()); + throw; + } + } +} + +void Dispatcher::stop() +{ + ScopedLock<Mutex> l(lock); + queue->close(); // Will interrupt thread blocked in pop() +} + +void Dispatcher::setAutoStop(bool b) +{ + ScopedLock<Mutex> l(lock); + autoStop = b; +} + +boost::intrusive_ptr<SubscriptionImpl> Dispatcher::find(const std::string& name) +{ + ScopedLock<Mutex> l(lock); + Listeners::iterator i = listeners.find(name); + if (i == listeners.end()) { + return defaultListener; + } + return i->second; +} + +void Dispatcher::listen(const boost::intrusive_ptr<SubscriptionImpl>& subscription) { + ScopedLock<Mutex> l(lock); + listeners[subscription->getName()] = subscription; +} + +void Dispatcher::cancel(const std::string& destination) { + ScopedLock<Mutex> l(lock); + listeners.erase(destination); + if (autoStop && listeners.empty()) + queue->close(); +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/client/Dispatcher.h b/RC9/qpid/cpp/src/qpid/client/Dispatcher.h new file mode 100644 index 0000000000..e84f8f303d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Dispatcher.h @@ -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. + * + */ +#ifndef _Dispatcher_ +#define _Dispatcher_ + +#include <map> +#include <memory> +#include <string> +#include <boost/shared_ptr.hpp> +#include "qpid/client/Session.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Thread.h" +#include "MessageListener.h" +#include "SubscriptionImpl.h" + +namespace qpid { +namespace client { + +class SubscriptionImpl; + +///@internal +typedef framing::Handler<framing::FrameSet> FrameSetHandler; + +///@internal +class Dispatcher : public sys::Runnable +{ + typedef std::map<std::string, boost::intrusive_ptr<SubscriptionImpl> >Listeners; + sys::Mutex lock; + sys::Thread worker; + Session session; + Demux::QueuePtr queue; + bool running; + bool autoStop; + Listeners listeners; + boost::intrusive_ptr<SubscriptionImpl> defaultListener; + std::auto_ptr<FrameSetHandler> handler; + + boost::intrusive_ptr<SubscriptionImpl> find(const std::string& name); + bool isStopped(); + + boost::function<void ()> failoverHandler; + +public: + Dispatcher(const Session& session, const std::string& queue = ""); + + void start(); + void wait(); + void run(); + void stop(); + void setAutoStop(bool b); + + void registerFailoverHandler ( boost::function<void ()> fh ) + { + failoverHandler = fh; + } + + void listen(const boost::intrusive_ptr<SubscriptionImpl>& subscription); + void cancel(const std::string& destination); +}; + +}} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/Execution.h b/RC9/qpid/cpp/src/qpid/client/Execution.h new file mode 100644 index 0000000000..10674afde0 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Execution.h @@ -0,0 +1,53 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _Execution_ +#define _Execution_ + +#include "qpid/framing/SequenceNumber.h" +#include "Demux.h" + +namespace qpid { +namespace client { + +/**@internal + * + * Provides access to more detailed aspects of the session + * implementation. + */ +class Execution +{ +public: + virtual ~Execution() {} + /** + * Provides access to the demultiplexing function within the + * session implementation + */ + virtual Demux& getDemux() = 0; + /** + * Wait until notification has been received of completion of the + * outgoing command with the specified id. + */ + void waitForCompletion(const framing::SequenceNumber& id); +}; + +}} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/FailoverListener.cpp b/RC9/qpid/cpp/src/qpid/client/FailoverListener.cpp new file mode 100644 index 0000000000..16370f8912 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/FailoverListener.cpp @@ -0,0 +1,115 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "FailoverListener.h" +#include "SessionBase_0_10Access.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/framing/Uuid.h" +#include "qpid/log/Statement.h" +#include "qpid/log/Helpers.h" + +namespace qpid { +namespace client { + +static const std::string AMQ_FAILOVER("amq.failover"); + +static Session makeSession(boost::shared_ptr<SessionImpl> si) { + // Hold only a weak pointer to the ConnectionImpl so a + // FailoverListener in a ConnectionImpl won't createa a shared_ptr + // cycle. + // + si->setWeakPtr(true); + Session s; + SessionBase_0_10Access(s).set(si); + return s; +} + +FailoverListener::FailoverListener(const boost::shared_ptr<ConnectionImpl>& c, const std::vector<Url>& initUrls) + : knownBrokers(initUrls) + { + // Special versions used to mark cluster catch-up connections + // which do not need a FailoverListener + if (c->getVersion().getMajor() >= 0x80) { + QPID_LOG(debug, "No failover listener for catch-up connection."); + return; + } + + Session session = makeSession(c->newSession(AMQ_FAILOVER+framing::Uuid(true).str(), 0)); + if (session.exchangeQuery(arg::name=AMQ_FAILOVER).getNotFound()) { + session.close(); + return; + } + subscriptions.reset(new SubscriptionManager(session)); + std::string qname=session.getId().getName(); + session.queueDeclare(arg::queue=qname, arg::exclusive=true, arg::autoDelete=true); + session.exchangeBind(arg::queue=qname, arg::exchange=AMQ_FAILOVER); + subscriptions->subscribe(*this, qname, SubscriptionSettings(FlowControl::unlimited(), ACCEPT_MODE_NONE)); + thread = sys::Thread(*this); +} + +void FailoverListener::run() +{ + try { + subscriptions->run(); + } catch (const TransportFailure&) { + } catch (const std::exception& e) { + QPID_LOG(error, QPID_MSG(e.what())); + } +} + +FailoverListener::~FailoverListener() { + try { stop(); } + catch (const std::exception& /*e*/) {} +} + +void FailoverListener::stop() { + if (subscriptions.get()) + subscriptions->stop(); + + if (thread.id() == sys::Thread::current().id()) { + // FIXME aconway 2008-10-16: this can happen if ConnectionImpl + // dtor runs when my session drops its weak pointer lock. + // For now, leak subscriptions to prevent a core if we delete + // without joining. + subscriptions.release(); + } + else if (thread.id()) { + thread.join(); + thread=sys::Thread(); + subscriptions.reset(); // Safe to delete after join. + } +} + +void FailoverListener::received(Message& msg) { + sys::Mutex::ScopedLock l(lock); + knownBrokers.clear(); + framing::Array urlArray; + msg.getHeaders().getArray("amq.failover", urlArray); + for (framing::Array::ValueVector::const_iterator i = urlArray.begin(); i != urlArray.end(); ++i ) + knownBrokers.push_back(Url((*i)->get<std::string>())); + QPID_LOG(info, "Known-brokers update: " << log::formatList(knownBrokers)); +} + +std::vector<Url> FailoverListener::getKnownBrokers() const { + sys::Mutex::ScopedLock l(lock); + return knownBrokers; +} + +}} // namespace qpid::client diff --git a/RC9/qpid/cpp/src/qpid/client/FailoverListener.h b/RC9/qpid/cpp/src/qpid/client/FailoverListener.h new file mode 100644 index 0000000000..fe73a26611 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/FailoverListener.h @@ -0,0 +1,59 @@ +#ifndef QPID_CLIENT_FAILOVERLISTENER_H +#define QPID_CLIENT_FAILOVERLISTENER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/client/MessageListener.h" +#include "qpid/Url.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Thread.h" +#include <vector> + +namespace qpid { +namespace client { + +class SubscriptionManager; + +/** + * @internal Listen for failover updates from the amq.failover exchange. + */ +class FailoverListener : public MessageListener, private qpid::sys::Runnable +{ + public: + FailoverListener(const boost::shared_ptr<ConnectionImpl>&, const std::vector<Url>& initUrls); + ~FailoverListener(); + void stop(); + + std::vector<Url> getKnownBrokers() const; + void received(Message& msg); + void run(); + + private: + mutable sys::Mutex lock; + std::auto_ptr<SubscriptionManager> subscriptions; + sys::Thread thread; + std::vector<Url> knownBrokers; +}; +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_FAILOVERLISTENER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/FailoverManager.cpp b/RC9/qpid/cpp/src/qpid/client/FailoverManager.cpp new file mode 100644 index 0000000000..ab9dbca70f --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/FailoverManager.cpp @@ -0,0 +1,119 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "FailoverManager.h" +#include "qpid/Exception.h" +#include "qpid/log/Statement.h" + + +namespace qpid { +namespace client { + +using qpid::sys::Monitor; + +FailoverManager::FailoverManager(const ConnectionSettings& s, + ReconnectionStrategy* rs) : settings(s), strategy(rs), state(IDLE) {} + +void FailoverManager::execute(Command& c) +{ + bool retry = false; + bool completed = false; + while (!completed) { + try { + AsyncSession session = connect().newSession(); + c.execute(session, retry); + session.sync();//TODO: shouldn't be required + session.close(); + completed = true; + } catch(const TransportFailure&) { + retry = true; + } + } +} + +void FailoverManager::close() +{ + Monitor::ScopedLock l(lock); + connection.close(); +} + +Connection& FailoverManager::connect(std::vector<Url> brokers) +{ + Monitor::ScopedLock l(lock); + if (state == CANT_CONNECT) { + state = IDLE;//retry + } + while (!connection.isOpen()) { + if (state == CONNECTING) { + lock.wait(); + } else if (state == CANT_CONNECT) { + throw CannotConnectException("Cannot establish a connection"); + } else { + state = CONNECTING; + Connection c; + attempt(c, settings, brokers.empty() ? connection.getKnownBrokers() : brokers); + if (c.isOpen()) state = IDLE; + else state = CANT_CONNECT; + connection = c; + lock.notifyAll(); + } + } + return connection; +} + +Connection& FailoverManager::getConnection() +{ + Monitor::ScopedLock l(lock); + return connection; +} + +void FailoverManager::attempt(Connection& c, ConnectionSettings s, std::vector<Url> urls) +{ + Monitor::ScopedUnlock u(lock); + if (strategy) strategy->editUrlList(urls); + if (urls.empty()) { + attempt(c, s); + } else { + for (std::vector<Url>::const_iterator i = urls.begin(); i != urls.end() && !c.isOpen(); ++i) { + for (Url::const_iterator j = i->begin(); j != i->end() && !c.isOpen(); ++j) { + const TcpAddress* tcp = j->get<TcpAddress>(); + if (tcp) { + s.host = tcp->host; + s.port = tcp->port; + attempt(c, s); + } + } + } + } +} + +void FailoverManager::attempt(Connection& c, ConnectionSettings s) +{ + try { + QPID_LOG(info, "Attempting to connect to " << s.host << " on " << s.port << "..."); + c.open(s); + QPID_LOG(info, "Connected to " << s.host << " on " << s.port); + } catch (const Exception& e) { + QPID_LOG(info, "Could not connect to " << s.host << " on " << s.port << ": " << e.what()); + } +} + + +}} // namespace qpid::client diff --git a/RC9/qpid/cpp/src/qpid/client/FailoverManager.h b/RC9/qpid/cpp/src/qpid/client/FailoverManager.h new file mode 100644 index 0000000000..8b6eeda8a1 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/FailoverManager.h @@ -0,0 +1,134 @@ +#ifndef QPID_CLIENT_FAILOVERMANAGER_H +#define QPID_CLIENT_FAILOVERMANAGER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES 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 "ConnectionSettings.h" +#include "qpid/Exception.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/sys/Monitor.h" +#include <vector> + +namespace qpid { +namespace client { + +struct CannotConnectException : qpid::Exception +{ + CannotConnectException(const std::string& m) : qpid::Exception(m) {} +}; + +/** + * Utility to manage failover. + */ +class FailoverManager +{ + public: + /** + * Interface to implement for doing work that can be resumed on + * failover + */ + struct Command + { + /** + * This method will be called with isRetry=false when the + * command is first executed. The session to use for the work + * will be passed to the implementing class. If the connection + * fails while the execute call is in progress, the + * FailoverManager controlling the execution will re-establish + * a connection, open a new session and call back to the + * Command implementations execute method with the new session + * and isRetry=true. + */ + virtual void execute(AsyncSession& session, bool isRetry) = 0; + virtual ~Command() {} + }; + + struct ReconnectionStrategy + { + /** + * This method is called by the FailoverManager prior to + * establishing a connection (or re-connection) and can be + * used if the application wishes to edit or re-order the list + * which will default to the list of known brokers for the + * last connection. + */ + virtual void editUrlList(std::vector<Url>& urls) = 0; + virtual ~ReconnectionStrategy() {} + }; + + /** + * Create a manager to control failover for a logical connection. + * + * @param settings the initial connection settings + * @param strategy optional stratgey callback allowing application + * to edit or reorder the list of urls to which reconnection is + * attempted + */ + FailoverManager(const ConnectionSettings& settings, ReconnectionStrategy* strategy = 0); + /** + * Return the current connection if open or attept to reconnect to + * the specified list of urls. If no list is specified the list of + * known brokers from the last connection will be used. If no list + * is specified and this is the first connect attempt, the host + * and port from the initial settings will be used. + * + * If the full list is tried and all attempts fail, + * CannotConnectException is thrown. + */ + Connection& connect(std::vector<Url> brokers = std::vector<Url>()); + /** + * Return the current connection whether open or not + */ + Connection& getConnection(); + /** + * Close the current connection + */ + void close(); + /** + * Reliably execute the specified command. This involves creating + * a session on which to carry out the work of the command, + * handling failover occuring while exeuting that command and + * re-starting the work. + * + * Multiple concurrent threads can call execute with different + * commands; each thread will be allocated its own + * session. FailoverManager will coordinate the different threads + * on failover to ensure they continue to use the same logical + * connection. + */ + void execute(Command&); + private: + enum State {IDLE, CONNECTING, CANT_CONNECT}; + + qpid::sys::Monitor lock; + Connection connection; + ConnectionSettings settings; + ReconnectionStrategy* strategy; + State state; + + void attempt(Connection&, ConnectionSettings settings, std::vector<Url> urls); + void attempt(Connection&, ConnectionSettings settings); +}; +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_FAILOVERMANAGER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/FlowControl.h b/RC9/qpid/cpp/src/qpid/client/FlowControl.h new file mode 100644 index 0000000000..d2205aaa78 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/FlowControl.h @@ -0,0 +1,75 @@ +#ifndef QPID_CLIENT_FLOWCONTROL_H +#define QPID_CLIENT_FLOWCONTROL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <qpid/sys/IntegerTypes.h> + +namespace qpid { +namespace client { + +/** + * Flow control works by associating a finite amount of "credit" + * with a subscription. + * + * Credit includes a message count and a byte count. Each message + * received decreases the message count by one, and the byte count by + * the size of the message. Either count can have the special value + * UNLIMITED which is never decreased. + * + * A subscription's credit is exhausted when the message count is 0 or + * the byte count is too small for the next available message. The + * subscription will not receive any further messages until is credit + * is renewed. + * + * In "window mode" credit is automatically renewed when a message is + * completed (which by default happens when it is accepted). In + * non-window mode credit is not automatically renewed, it must be + * explicitly re-set (@see Subscription) + */ +struct FlowControl { + static const uint32_t UNLIMITED=0xFFFFFFFF; + FlowControl(uint32_t messages_=0, uint32_t bytes_=0, bool window_=false) + : messages(messages_), bytes(bytes_), window(window_) {} + + static FlowControl messageCredit(uint32_t messages_) { return FlowControl(messages_,UNLIMITED,false); } + static FlowControl messageWindow(uint32_t messages_) { return FlowControl(messages_,UNLIMITED,true); } + static FlowControl byteCredit(uint32_t bytes_) { return FlowControl(UNLIMITED,bytes_,false); } + static FlowControl byteWindow(uint32_t bytes_) { return FlowControl(UNLIMITED,bytes_,true); } + static FlowControl unlimited() { return FlowControl(UNLIMITED, UNLIMITED, false); } + static FlowControl zero() { return FlowControl(0, 0, false); } + + /** Message credit: subscription can accept up to this many messages. */ + uint32_t messages; + /** Byte credit: subscription can accept up to this many bytes of message content. */ + uint32_t bytes; + /** Window mode. If true credit is automatically renewed as messages are acknowledged. */ + bool window; + + bool operator==(const FlowControl& x) { + return messages == x.messages && bytes == x.bytes && window == x.window; + }; +}; + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_FLOWCONTROL_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/Future.cpp b/RC9/qpid/cpp/src/qpid/client/Future.cpp new file mode 100644 index 0000000000..6a0c78ae4b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Future.cpp @@ -0,0 +1,45 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Future.h" + +namespace qpid { +namespace client { + +void Future::wait(SessionImpl& session) +{ + if (!complete) { + session.waitForCompletion(command); + } + complete = true; +} + +bool Future::isComplete(SessionImpl& session) +{ + return complete || session.isComplete(command); +} + +void Future::setFutureResult(boost::shared_ptr<FutureResult> r) +{ + result = r; +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/client/Future.h b/RC9/qpid/cpp/src/qpid/client/Future.h new file mode 100644 index 0000000000..67f39cdf3f --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Future.h @@ -0,0 +1,64 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef _Future_ +#define _Future_ + +#include <boost/bind.hpp> +#include <boost/shared_ptr.hpp> +#include "qpid/Exception.h" +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/StructHelper.h" +#include "FutureCompletion.h" +#include "FutureResult.h" +#include "SessionImpl.h" + +namespace qpid { +namespace client { + +/**@internal */ +class Future : private framing::StructHelper +{ + framing::SequenceNumber command; + boost::shared_ptr<FutureResult> result; + bool complete; + +public: + Future() : complete(false) {} + Future(const framing::SequenceNumber& id) : command(id), complete(false) {} + + template <class T> void decodeResult(T& value, SessionImpl& session) + { + if (result) { + decode(value, result->getResult(session)); + } else { + throw Exception("Result not expected"); + } + } + + void wait(SessionImpl& session); + bool isComplete(SessionImpl& session); + void setFutureResult(boost::shared_ptr<FutureResult> r); +}; + +}} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/FutureCompletion.cpp b/RC9/qpid/cpp/src/qpid/client/FutureCompletion.cpp new file mode 100644 index 0000000000..130c65d6aa --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/FutureCompletion.cpp @@ -0,0 +1,48 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "FutureCompletion.h" + +using namespace qpid::client; +using namespace qpid::sys; + +FutureCompletion::FutureCompletion() : complete(false) {} + +bool FutureCompletion::isComplete() const +{ + Monitor::ScopedLock l(lock); + return complete; +} + +void FutureCompletion::completed() +{ + Monitor::ScopedLock l(lock); + complete = true; + lock.notifyAll(); +} + +void FutureCompletion::waitForCompletion() const +{ + Monitor::ScopedLock l(lock); + while (!complete) { + lock.wait(); + } +} diff --git a/RC9/qpid/cpp/src/qpid/client/FutureCompletion.h b/RC9/qpid/cpp/src/qpid/client/FutureCompletion.h new file mode 100644 index 0000000000..4248ddeab8 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/FutureCompletion.h @@ -0,0 +1,49 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef _FutureCompletion_ +#define _FutureCompletion_ + +#include "qpid/framing/amqp_framing.h" +#include "qpid/sys/Monitor.h" + +namespace qpid { +namespace client { + +///@internal +class FutureCompletion +{ +protected: + mutable sys::Monitor lock; + bool complete; + +public: + FutureCompletion(); + virtual ~FutureCompletion(){} + bool isComplete() const; + void waitForCompletion() const; + void completed(); +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/FutureResult.cpp b/RC9/qpid/cpp/src/qpid/client/FutureResult.cpp new file mode 100644 index 0000000000..007f278051 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/FutureResult.cpp @@ -0,0 +1,43 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "FutureResult.h" + +#include "SessionImpl.h" + +using namespace qpid::client; +using namespace qpid::framing; +using namespace qpid::sys; + +const std::string& FutureResult::getResult(SessionImpl& session) const +{ + waitForCompletion(); + session.assertOpen(); + return result; +} + +void FutureResult::received(const std::string& r) +{ + Monitor::ScopedLock l(lock); + result = r; + complete = true; + lock.notifyAll(); +} diff --git a/RC9/qpid/cpp/src/qpid/client/FutureResult.h b/RC9/qpid/cpp/src/qpid/client/FutureResult.h new file mode 100644 index 0000000000..e97d80476d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/FutureResult.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 _FutureResult_ +#define _FutureResult_ + +#include <string> +#include "qpid/framing/amqp_framing.h" +#include "FutureCompletion.h" + +namespace qpid { +namespace client { + +class SessionImpl; + +///@internal +class FutureResult : public FutureCompletion +{ + std::string result; +public: + const std::string& getResult(SessionImpl& session) const; + void received(const std::string& result); +}; + +}} + + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/Handle.h b/RC9/qpid/cpp/src/qpid/client/Handle.h new file mode 100644 index 0000000000..4fd82b7646 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Handle.h @@ -0,0 +1,61 @@ +#ifndef QPID_CLIENT_HANDLE_H +#define QPID_CLIENT_HANDLE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +namespace qpid { +namespace client { + +template <class T> class HandlePrivate; + +/** + * A handle is like a pointer: it points to some underlying object. + * Handles can be null, like a 0 pointer. Use isValid(), isNull() or the + * implicit conversion to bool to test for a null handle. + */ +template <class T> class Handle { + public: + ~Handle(); + Handle(const Handle&); + Handle& operator=(const Handle&); + + /**@return true if handle is valid, i.e. not null. */ + bool isValid() const { return impl; } + + /**@return true if handle is null. It is an error to call any function on a null handle. */ + bool isNull() const { return !impl; } + + operator bool() const { return impl; } + bool operator !() const { return impl; } + + void swap(Handle<T>&); + + protected: + Handle(T* =0); + T* impl; + + friend class HandlePrivate<T>; +}; + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_HANDLE_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/HandleAccess.h b/RC9/qpid/cpp/src/qpid/client/HandleAccess.h new file mode 100644 index 0000000000..f1747db638 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/HandleAccess.h @@ -0,0 +1,41 @@ +#ifndef QPID_CLIENT_HANDLEACCESS_H +#define QPID_CLIENT_HANDLEACCESS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <Handle.h> + +namespace qpid { +namespace client { + +/** + * Provide access to the private impl member of a Handle. + */ +template <class T> +class HandleAccess +{ + public: + static boost::shared_ptr<T> getImpl(Handle<T>& h) { return h.impl; } +}; +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_HANDLEACCESS_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/HandlePrivate.h b/RC9/qpid/cpp/src/qpid/client/HandlePrivate.h new file mode 100644 index 0000000000..488ce48075 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/HandlePrivate.h @@ -0,0 +1,61 @@ +#ifndef QPID_CLIENT_HANDLEPRIVATE_H +#define QPID_CLIENT_HANDLEPRIVATE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <algorithm> + +namespace qpid { +namespace client { + +/** @file + * Private implementation of handle, include in .cpp file of handle + * subclasses _after_ including the declaration of class T. + * T can be any class that can be used with boost::intrusive_ptr. + */ + +template <class T> +Handle<T>::Handle(T* p) : impl(p) { if (impl) boost::intrusive_ptr_add_ref(impl); } + +template <class T> +Handle<T>::~Handle() { if(impl) boost::intrusive_ptr_release(impl); } + +template <class T> +Handle<T>::Handle(const Handle& h) : impl(h.impl) { if(impl) boost::intrusive_ptr_add_ref(impl); } + +template <class T> +Handle<T>& Handle<T>::operator=(const Handle<T>& h) { Handle<T>(h).swap(*this); return *this; } + +template <class T> +void Handle<T>::swap(Handle<T>& h) { std::swap(impl, h.impl); } + + +/** Access to private impl of a Handle */ +template <class T> +class HandlePrivate { + public: + static boost::intrusive_ptr<T> get(Handle<T>& h) { return boost::intrusive_ptr<T>(h.impl); } +}; + + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_HANDLEPRIVATE_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/LoadPlugins.cpp b/RC9/qpid/cpp/src/qpid/client/LoadPlugins.cpp new file mode 100644 index 0000000000..b395226859 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/LoadPlugins.cpp @@ -0,0 +1,49 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Modules.h" +#include "qpid/sys/Shlib.h" +#include <string> +#include <vector> +using std::vector; +using std::string; + +namespace { + +struct LoadtimeInitialise { + LoadtimeInitialise() { + qpid::ModuleOptions moduleOptions(MODULE_DIR); + string defaultPath (moduleOptions.loadDir); + moduleOptions.parse (0, 0, CONF_FILE, true); + + for (vector<string>::iterator iter = moduleOptions.load.begin(); + iter != moduleOptions.load.end(); + iter++) + qpid::tryShlib (iter->data(), false); + + if (!moduleOptions.noLoad) { + bool isDefault = defaultPath == moduleOptions.loadDir; + qpid::loadModuleDir (moduleOptions.loadDir, isDefault); + } + } +} init; + +} // namespace diff --git a/RC9/qpid/cpp/src/qpid/client/LocalQueue.cpp b/RC9/qpid/cpp/src/qpid/client/LocalQueue.cpp new file mode 100644 index 0000000000..e449c9f795 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/LocalQueue.cpp @@ -0,0 +1,77 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "LocalQueue.h" +#include "qpid/Exception.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/reply_exceptions.h" +#include "HandlePrivate.h" +#include "SubscriptionImpl.h" + +namespace qpid { +namespace client { + +using namespace framing; + +LocalQueue::LocalQueue() {} +LocalQueue::~LocalQueue() {} + +Message LocalQueue::pop(sys::Duration timeout) { return get(timeout); } + +Message LocalQueue::get(sys::Duration timeout) { + Message result; + bool ok = get(result, timeout); + if (!ok) throw Exception("Timed out waiting for a message"); + return result; +} + +bool LocalQueue::get(Message& result, sys::Duration timeout) { + if (!queue) + throw ClosedException(); + FrameSet::shared_ptr content; + bool ok = queue->pop(content, timeout); + if (!ok) return false; + if (content->isA<MessageTransferBody>()) { + result = Message(*content); + boost::intrusive_ptr<SubscriptionImpl> si = HandlePrivate<SubscriptionImpl>::get(subscription); + assert(si); + if (si) si->received(result); + return true; + } + else + throw CommandInvalidException( + QPID_MSG("Unexpected method: " << content->getMethod())); +} + +bool LocalQueue::empty() const +{ + if (!queue) + throw ClosedException(); + return queue->empty(); +} + +size_t LocalQueue::size() const +{ + if (!queue) + throw ClosedException(); + return queue->size(); +} + +}} // namespace qpid::client diff --git a/RC9/qpid/cpp/src/qpid/client/LocalQueue.h b/RC9/qpid/cpp/src/qpid/client/LocalQueue.h new file mode 100644 index 0000000000..30ea00612d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/LocalQueue.h @@ -0,0 +1,115 @@ +#ifndef QPID_CLIENT_LOCALQUEUE_H +#define QPID_CLIENT_LOCALQUEUE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/client/Message.h" +#include "qpid/client/Subscription.h" +#include "qpid/client/Demux.h" +#include "qpid/sys/Time.h" + +namespace qpid { +namespace client { + +/** + * A local queue to collect messages retrieved from a remote broker + * queue. Create a queue and subscribe it using the SubscriptionManager. + * Messages from the remote queue on the broker will be stored in the + * local queue until you retrieve them. + * + * \ingroup clientapi + * + * \details Using a Local Queue + * + * <pre> + * LocalQueue local_queue; + * subscriptions.subscribe(local_queue, string("message_queue")); + * for (int i=0; i<10; i++) { + * Message message = local_queue.get(); + * std::cout << message.getData() << std::endl; + * } + * </pre> + * + * <h2>Getting Messages</h2> + * + * <ul><li> + * <p>get()</p> + * <pre>Message message = local_queue.get();</pre> + * <pre>// Specifying timeouts (TIME_SEC, TIME_MSEC, TIME_USEC, TIME_NSEC) + *#include <qpid/sys/Time.h> + *Message message; + *local_queue.get(message, 5*sys::TIME_SEC);</pre></li></ul> + * + * <h2>Checking size</h2> + * <ul><li> + * <p>empty()</p> + * <pre>if (local_queue.empty()) { ... }</pre></li> + * <li><p>size()</p> + * <pre>std::cout << local_queue.size();</pre></li> + * </ul> + */ + +class LocalQueue { + public: + /** Create a local queue. Subscribe the local queue to a remote broker + * queue with a SubscriptionManager. + * + * LocalQueue is an alternative to implementing a MessageListener. + */ + LocalQueue(); + + ~LocalQueue(); + + /** Wait up to timeout for the next message from the local queue. + *@param result Set to the message from the queue. + *@param timeout wait up this timeout for a message to appear. + *@return true if result was set, false if queue was empty after timeout. + */ + bool get(Message& result, sys::Duration timeout=0); + + /** Get the next message off the local queue, or wait up to the timeout + * for message from the broker queue. + *@param timeout wait up this timeout for a message to appear. + *@return message from the queue. + *@throw ClosedException if subscription is closed or timeout exceeded. + */ + Message get(sys::Duration timeout=sys::TIME_INFINITE); + + /** Synonym for get() */ + Message pop(sys::Duration timeout=sys::TIME_INFINITE); + + /** Return true if local queue is empty. */ + bool empty() const; + + /** Number of messages on the local queue */ + size_t size() const; + + private: + Demux::QueuePtr queue; + Subscription subscription; + + friend class SubscriptionManager; +}; + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_LOCALQUEUE_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/Message.cpp b/RC9/qpid/cpp/src/qpid/client/Message.cpp new file mode 100644 index 0000000000..13caaecefd --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Message.cpp @@ -0,0 +1,71 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Message.h" + +namespace qpid { +namespace client { + +Message::Message(const std::string& data, const std::string& routingKey) : TransferContent(data, routingKey) {} + +std::string Message::getDestination() const +{ + return method.getDestination(); +} + +bool Message::isRedelivered() const +{ + return hasDeliveryProperties() && getDeliveryProperties().getRedelivered(); +} + +void Message::setRedelivered(bool redelivered) +{ + getDeliveryProperties().setRedelivered(redelivered); +} + +framing::FieldTable& Message::getHeaders() +{ + return getMessageProperties().getApplicationHeaders(); +} + +const framing::FieldTable& Message::getHeaders() const +{ + return getMessageProperties().getApplicationHeaders(); +} + +const framing::MessageTransferBody& Message::getMethod() const +{ + return method; +} + +const framing::SequenceNumber& Message::getId() const +{ + return id; +} + +/**@internal for incoming messages */ +Message::Message(const framing::FrameSet& frameset) : + method(*frameset.as<framing::MessageTransferBody>()), id(frameset.getId()) +{ + populate(frameset); +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/client/Message.h b/RC9/qpid/cpp/src/qpid/client/Message.h new file mode 100644 index 0000000000..3f932efd8b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Message.h @@ -0,0 +1,151 @@ +#ifndef _client_Message_h +#define _client_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 <string> +#include "qpid/client/Session.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/TransferContent.h" + +namespace qpid { +namespace client { + +/** + * A message sent to or received from the broker. + * + * \ingroup clientapi + * \details + * + * <h2>Getting and setting message contents</h2> + * + * <ul> + * <li> + * <p>getData()</p> + * <pre>std::cout << "Response: " << message.getData() << std::endl;</pre> + * </li> + * <li> + * <p>setData()</p> + * <pre>message.setData("That's all, folks!");</pre></li> + * <li> + * <p>appendData()</p> + * <pre>message.appendData(" ... let's add a bit more ...");</pre></li> + * </ul> + * + * <h2>Getting and Setting Delivery Properties</h2> + * + * <ul> + * <li> + * <p>getDeliveryProperties()</p> + * <pre>message.getDeliveryProperties().setRoutingKey("control");</pre> + * <pre>message.getDeliveryProperties().setDeliveryMode(PERSISTENT);</pre> + * <pre>message.getDeliveryProperties().setPriority(9);</pre> + * <pre>message.getDeliveryProperties().setTtl(100);</pre></li> + * + * <li> + * <p>hasDeliveryProperties()</p> + * <pre>if (! message.hasDeliveryProperties()) { + * ... + *}</pre></li> + * </ul> + * + * <h2>Getting and Setting Message Properties</h2> + * + * <ul> + * <li> + * <p>getMessageProperties()</p> + * <pre> + *request.getMessageProperties().setReplyTo(ReplyTo("amq.direct", response_queue.str())); + * </pre> + * <pre> + *routingKey = request.getMessageProperties().getReplyTo().getRoutingKey(); + *exchange = request.getMessageProperties().getReplyTo().getExchange(); + * </pre> + * <pre>message.getMessageProperties().setContentType("text/plain");</pre> + * <pre>message.getMessageProperties().setContentEncoding("text/plain");</pre> + * </li> + * <li> + * <p>hasMessageProperties()</p> + * <pre>request.getMessageProperties().hasReplyTo();</pre> + * </li> + * </ul> + * + * <h2>Getting and Setting Application Headers</h2> + * + * <ul> + * <li> + * <p>getHeaders()</p> + * <pre> + *message.getHeaders().getString("control"); + * </pre> + * <pre> + *message.getHeaders().setString("control","continue"); + * </pre></li> + * </ul> + * + * + */ + +class Message : public framing::TransferContent +{ +public: + /** Create a Message. + *@param data Data for the message body. + *@param routingKey Passed to the exchange that routes the message. + */ + Message(const std::string& data=std::string(), + const std::string& routingKey=std::string()); + + /** The destination of messages sent to the broker is the exchange + * name. The destination of messages received from the broker is + * the delivery tag identifyig the local subscription (often this + * is the name of the subscribed queue.) + */ + std::string getDestination() const; + + /** Check the redelivered flag. */ + bool isRedelivered() const; + /** Set the redelivered flag. */ + void setRedelivered(bool redelivered); + + /** Get a modifyable reference to the message headers. */ + framing::FieldTable& getHeaders(); + + /** Get a non-modifyable reference to the message headers. */ + const framing::FieldTable& getHeaders() const; + + ///@internal + const framing::MessageTransferBody& getMethod() const; + ///@internal + const framing::SequenceNumber& getId() const; + + /**@internal for incoming messages */ + Message(const framing::FrameSet& frameset); + +private: + //method and id are only set for received messages: + framing::MessageTransferBody method; + framing::SequenceNumber id; +}; + +}} + +#endif /*!_client_Message_h*/ diff --git a/RC9/qpid/cpp/src/qpid/client/MessageListener.cpp b/RC9/qpid/cpp/src/qpid/client/MessageListener.cpp new file mode 100644 index 0000000000..68ebedeb0d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/MessageListener.cpp @@ -0,0 +1,24 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "MessageListener.h" + +qpid::client::MessageListener::~MessageListener() {} diff --git a/RC9/qpid/cpp/src/qpid/client/MessageListener.h b/RC9/qpid/cpp/src/qpid/client/MessageListener.h new file mode 100644 index 0000000000..75aad6521b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/MessageListener.h @@ -0,0 +1,100 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <string> + +#ifndef _MessageListener_ +#define _MessageListener_ + +#include "Message.h" + +namespace qpid { +namespace client { + + /** + * Implement a subclass of MessageListener and subscribe it using + * the SubscriptionManager to receive messages. + * + * Another way to receive messages is by using a LocalQueue. + * + * \ingroup clientapi + * \details + * + * <h2>Using a MessageListener</h2> + * + * <ul> + * <li> + * <p>The received() function is called when a message arrives:</p> + * <pre>virtual void received(Message& message)=0;</pre> + * </li> + * <li> + * <p>Derive your own listener, implement the received() function:</p> + * <pre> + * class Listener : public MessageListener { + * private: + * SubscriptionManager& subscriptions; + * public: + * Listener(SubscriptionManager& subscriptions); + * virtual void received(Message& message); + * }; + * + * Listener::Listener(SubscriptionManager& subs) : subscriptions(subs) + * {} + * + * void Listener::received(Message& message) { + * std::cout << "Message: " << message.getData() << std::endl; + * if (message.getData() == "That's all, folks!") { + * std::cout << "Shutting down listener for " << message.getDestination() + * << std::endl; + * subscriptions.cancel(message.getDestination()); + * } + * } + *</pre> + * <pre> + * SubscriptionManager subscriptions(session); + * + * // Create a listener and subscribe it to the queue named "message_queue" + * Listener listener(subscriptions); + * subscriptions.subscribe(listener, "message_queue"); + * + * // Receive messages until the subscription is cancelled + * // by Listener::received() + * subscriptions.run(); + * </pre> + * </li> + * </ul> + * + */ + + class MessageListener{ + public: + virtual ~MessageListener(); + + /** Called for each message arriving from the broker. Override + * in your own subclass to process messages. + */ + virtual void received(Message& msg) = 0; + }; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/MessageReplayTracker.cpp b/RC9/qpid/cpp/src/qpid/client/MessageReplayTracker.cpp new file mode 100644 index 0000000000..9ffbb76837 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/MessageReplayTracker.cpp @@ -0,0 +1,80 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "MessageReplayTracker.h" +#include <boost/bind.hpp> + +namespace qpid { +namespace client { + +MessageReplayTracker::MessageReplayTracker(uint f) : flushInterval(f), count(0) {} + +void MessageReplayTracker::send(const Message& message, const std::string& destination) +{ + buffer.push_back(ReplayRecord(message, destination)); + buffer.back().send(*this); + if (flushInterval && ++count >= flushInterval) { + checkCompletion(); + if (!buffer.empty()) session.flush(); + } +} +void MessageReplayTracker::init(AsyncSession s) +{ + session = s; +} + +void MessageReplayTracker::replay(AsyncSession s) +{ + session = s; + std::list<ReplayRecord> copy; + buffer.swap(copy); + std::for_each(copy.begin(), copy.end(), boost::bind(&ReplayRecord::send, _1, boost::ref(*this))); + session.flush(); + count = 0; +} + +void MessageReplayTracker::setFlushInterval(uint f) +{ + flushInterval = f; +} + +uint MessageReplayTracker::getFlushInterval() +{ + return flushInterval; +} + +void MessageReplayTracker::checkCompletion() +{ + buffer.remove_if(boost::bind(&ReplayRecord::isComplete, _1)); +} + +MessageReplayTracker::ReplayRecord::ReplayRecord(const Message& m, const std::string& d) : message(m), destination(d) {} + +void MessageReplayTracker::ReplayRecord::send(MessageReplayTracker& tracker) +{ + status = tracker.session.messageTransfer(arg::destination=destination, arg::content=message); +} + +bool MessageReplayTracker::ReplayRecord::isComplete() +{ + return status.isComplete(); +} + +}} // namespace qpid::client diff --git a/RC9/qpid/cpp/src/qpid/client/MessageReplayTracker.h b/RC9/qpid/cpp/src/qpid/client/MessageReplayTracker.h new file mode 100644 index 0000000000..45b16fb704 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/MessageReplayTracker.h @@ -0,0 +1,73 @@ +#ifndef QPID_CLIENT_MESSAGEREPLAYTRACKER_H +#define QPID_CLIENT_MESSAGEREPLAYTRACKER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "AsyncSession.h" +#include "Message.h" + +#include <list> +#include <string> + +namespace qpid { +namespace client { + +/** + * Utility to track messages sent asynchronously, allowing those that + * are indoubt to be replayed over a new session. + */ +class MessageReplayTracker +{ + public: + MessageReplayTracker(uint flushInterval); + void send(const Message& message, const std::string& destination = ""); + void init(AsyncSession session); + void replay(AsyncSession session); + void setFlushInterval(uint interval); + uint getFlushInterval(); + void checkCompletion(); + + template <class F> void foreach(F& f) { + for (std::list<ReplayRecord>::const_iterator i = buffer.begin(); i != buffer.end(); i++) { + f(i->message); + } + } + + private: + struct ReplayRecord + { + Completion status; + Message message; + std::string destination; + + ReplayRecord(const Message& message, const std::string& destination); + void send(MessageReplayTracker&); + bool isComplete(); + }; + + AsyncSession session; + uint flushInterval; + uint count; + std::list<ReplayRecord> buffer; +}; +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_MESSAGEREPLAYTRACKER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/QueueOptions.cpp b/RC9/qpid/cpp/src/qpid/client/QueueOptions.cpp new file mode 100644 index 0000000000..ac65b0bc22 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/QueueOptions.cpp @@ -0,0 +1,116 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "QueueOptions.h" + +namespace qpid { +namespace client { + + +QueueOptions::QueueOptions() +{} + +const std::string QueueOptions::strMaxCountKey("qpid.max_count"); +const std::string QueueOptions::strMaxSizeKey("qpid.max_size"); +const std::string QueueOptions::strTypeKey("qpid.policy_type"); +const std::string QueueOptions::strREJECT("reject"); +const std::string QueueOptions::strFLOW_TO_DISK("flow_to_disk"); +const std::string QueueOptions::strRING("ring"); +const std::string QueueOptions::strRING_STRICT("ring_strict"); +const std::string QueueOptions::strLastValueQueue("qpid.last_value_queue"); +const std::string QueueOptions::strPersistLastNode("qpid.persist_last_node"); +const std::string QueueOptions::strLVQMatchProperty("qpid.LVQ_key"); +const std::string QueueOptions::strLastValueQueueNoBrowse("qpid.last_value_queue_no_browse"); + + +QueueOptions::~QueueOptions() +{} + +void QueueOptions::setSizePolicy(QueueSizePolicy sp, uint64_t maxSize, uint32_t maxCount) +{ + if (maxCount) setInt(strMaxCountKey, maxCount); + if (maxSize) setInt(strMaxSizeKey, maxSize); + if (maxSize || maxCount){ + switch (sp) + { + case REJECT: + setString(strTypeKey, strREJECT); + break; + case FLOW_TO_DISK: + setString(strTypeKey, strFLOW_TO_DISK); + break; + case RING: + setString(strTypeKey, strRING); + break; + case RING_STRICT: + setString(strTypeKey, strRING_STRICT); + break; + case NONE: + clearSizePolicy(); + break; + } + } +} + + +void QueueOptions::setPersistLastNode() +{ + setInt(strPersistLastNode, 1); +} + +void QueueOptions::setOrdering(QueueOrderingPolicy op) +{ + if (op == LVQ){ + setInt(strLastValueQueue, 1); + }else if (op == LVQ_NO_BROWSE){ + setInt(strLastValueQueueNoBrowse, 1); + }else { + clearOrdering(); + } +} + +void QueueOptions::getLVQKey(std::string& key) +{ + key.assign(strLVQMatchProperty); +} + +void QueueOptions::clearSizePolicy() +{ + erase(strMaxCountKey); + erase(strMaxSizeKey); + erase(strTypeKey); +} + +void QueueOptions::clearPersistLastNode() +{ + erase(strPersistLastNode); +} + +void QueueOptions::clearOrdering() +{ + erase(strLastValueQueue); +} + + +} +} + + diff --git a/RC9/qpid/cpp/src/qpid/client/QueueOptions.h b/RC9/qpid/cpp/src/qpid/client/QueueOptions.h new file mode 100644 index 0000000000..114e1e49c2 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/QueueOptions.h @@ -0,0 +1,104 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/framing/FieldTable.h" + +#ifndef _QueueOptions_ +#define _QueueOptions_ + +namespace qpid { +namespace client { + +enum QueueSizePolicy {NONE, REJECT, FLOW_TO_DISK, RING, RING_STRICT}; +enum QueueOrderingPolicy {FIFO, LVQ, LVQ_NO_BROWSE}; + +/** + * A help class to set options on the Queue. Create a configured args while + * still allowing any custom configuration via the FieldTable base class + */ +class QueueOptions: public framing::FieldTable +{ + public: + QueueOptions(); + virtual ~QueueOptions(); + + /** + * Sets the queue sizing plocy + * + * @param sp SizePolicy + * REJECT - reject if queue greater than size/count + * FLOW_TO_DISK - page messages to disk from this point is greater than size/count + * RING - limit the queue to size/count and over-write old messages round a ring + * RING_STRICT - limit the queue to size/count and reject is head == tail + * NONE - Use default broker sizing policy + * @param maxSize Set the max number of bytes for the sizing policies + * @param setMaxCount Set the max number of messages for the sizing policies + */ + void setSizePolicy(QueueSizePolicy sp, uint64_t maxSize, uint32_t maxCount ); + + /** + * Enables the persisting of a queue to the store module when a cluster fails down to it's last + * node. Does so optimistically. Will start persisting when cluster count >1 again. + */ + void setPersistLastNode(); + + /** + * Sets the odering policy on the Queue, default ordering is FIFO. + */ + void setOrdering(QueueOrderingPolicy op); + + /** + * Use broker defualt sizing ploicy + */ + void clearSizePolicy(); + + /** + * Clear Persist Last Node Policy + */ + void clearPersistLastNode(); + + /** + * get the key used match LVQ in args for message transfer + */ + void getLVQKey(std::string& key); + + /** + * Use default odering policy + */ + void clearOrdering(); + + static const std::string strMaxCountKey; + static const std::string strMaxSizeKey; + static const std::string strTypeKey; + static const std::string strREJECT; + static const std::string strFLOW_TO_DISK; + static const std::string strRING; + static const std::string strRING_STRICT; + static const std::string strLastValueQueue; + static const std::string strPersistLastNode; + static const std::string strLVQMatchProperty; + static const std::string strLastValueQueueNoBrowse; +}; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/RdmaConnector.cpp b/RC9/qpid/cpp/src/qpid/client/RdmaConnector.cpp new file mode 100644 index 0000000000..98fe762f31 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/RdmaConnector.cpp @@ -0,0 +1,428 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Connector.h" + +#include "Bounds.h" +#include "ConnectionImpl.h" +#include "ConnectionSettings.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/Time.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/sys/rdma/RdmaIO.h" +#include "qpid/sys/Dispatcher.h" +#include "qpid/sys/Poller.h" +#include "qpid/Msg.h" + +#include <iostream> +#include <boost/bind.hpp> +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> + +// This stuff needs to abstracted out of here to a platform specific file +#include <netdb.h> + +namespace qpid { +namespace client { + +using namespace qpid::sys; +using namespace qpid::framing; +using boost::format; +using boost::str; + +class RdmaConnector : public Connector, private sys::Runnable +{ + struct Buff; + + /** Batch up frames for writing to aio. */ + class Writer : public framing::FrameHandler { + typedef Rdma::Buffer BufferBase; + typedef std::deque<framing::AMQFrame> Frames; + + const uint16_t maxFrameSize; + sys::Mutex lock; + Rdma::AsynchIO* aio; + BufferBase* buffer; + Frames frames; + size_t lastEof; // Position after last EOF in frames + framing::Buffer encode; + size_t framesEncoded; + std::string identifier; + Bounds* bounds; + + void writeOne(); + void newBuffer(); + + public: + + Writer(uint16_t maxFrameSize, Bounds*); + ~Writer(); + void init(std::string id, Rdma::AsynchIO*); + void handle(framing::AMQFrame&); + void write(Rdma::AsynchIO&); + }; + + const uint16_t maxFrameSize; + framing::ProtocolVersion version; + bool initiated; + + sys::Mutex pollingLock; + bool polling; + bool joined; + + sys::ShutdownHandler* shutdownHandler; + framing::InputHandler* input; + framing::InitiationHandler* initialiser; + framing::OutputHandler* output; + + Writer writer; + + sys::Thread receiver; + + Rdma::AsynchIO* aio; + sys::Poller::shared_ptr poller; + + ~RdmaConnector(); + + void run(); + void handleClosed(); + bool closeInternal(); + + // Callbacks + void connected(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr&, const Rdma::ConnectionParams&); + void connectionError(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr&, Rdma::ErrorType); + void disconnected(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr&); + void rejected(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr&, const Rdma::ConnectionParams&); + + void readbuff(Rdma::AsynchIO&, Rdma::Buffer*); + void writebuff(Rdma::AsynchIO&); + void writeDataBlock(const framing::AMQDataBlock& data); + void eof(Rdma::AsynchIO&); + + std::string identifier; + + ConnectionImpl* impl; + + void connect(const std::string& host, int port); + void close(); + void send(framing::AMQFrame& frame); + + void setInputHandler(framing::InputHandler* handler); + void setShutdownHandler(sys::ShutdownHandler* handler); + sys::ShutdownHandler* getShutdownHandler() const; + framing::OutputHandler* getOutputHandler(); + const std::string& getIdentifier() const; + +public: + RdmaConnector(framing::ProtocolVersion pVersion, + const ConnectionSettings&, + ConnectionImpl*); +}; + +// Static constructor which registers connector here +namespace { + Connector* create(framing::ProtocolVersion v, const ConnectionSettings& s, ConnectionImpl* c) { + return new RdmaConnector(v, s, c); + } + + struct StaticInit { + StaticInit() { + Connector::registerFactory("rdma", &create); + Connector::registerFactory("ib", &create); + }; + } init; +} + + +RdmaConnector::RdmaConnector(ProtocolVersion ver, + const ConnectionSettings& settings, + ConnectionImpl* cimpl) + : maxFrameSize(settings.maxFrameSize), + version(ver), + initiated(false), + polling(false), + joined(true), + shutdownHandler(0), + writer(maxFrameSize, cimpl), + aio(0), + impl(cimpl) +{ + QPID_LOG(debug, "RdmaConnector created for " << version); +} + +RdmaConnector::~RdmaConnector() { + close(); +} + +void RdmaConnector::connect(const std::string& host, int port){ + Mutex::ScopedLock l(pollingLock); + assert(!polling); + assert(joined); + poller = Poller::shared_ptr(new Poller); + + // This stuff needs to abstracted out of here to a platform specific file + ::addrinfo *res; + ::addrinfo hints; + hints.ai_flags = 0; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + int n = ::getaddrinfo(host.c_str(), boost::lexical_cast<std::string>(port).c_str(), &hints, &res); + if (n<0) { + throw Exception(QPID_MSG("Cannot resolve " << host << ": " << ::gai_strerror(n))); + } + + Rdma::Connector* c = new Rdma::Connector( + *res->ai_addr, + Rdma::ConnectionParams(maxFrameSize, Rdma::DEFAULT_WR_ENTRIES), + boost::bind(&RdmaConnector::connected, this, poller, _1, _2), + boost::bind(&RdmaConnector::connectionError, this, poller, _1, _2), + boost::bind(&RdmaConnector::disconnected, this, poller, _1), + boost::bind(&RdmaConnector::rejected, this, poller, _1, _2)); + c->start(poller); + + polling = true; + joined = false; + receiver = Thread(this); +} + +// The following only gets run when connected +void RdmaConnector::connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci, const Rdma::ConnectionParams& cp) { + Rdma::QueuePair::intrusive_ptr q = ci->getQueuePair(); + + aio = new Rdma::AsynchIO(ci->getQueuePair(), + cp.maxRecvBufferSize, cp.initialXmitCredit , Rdma::DEFAULT_WR_ENTRIES, + boost::bind(&RdmaConnector::readbuff, this, _1, _2), + boost::bind(&RdmaConnector::writebuff, this, _1), + 0, // write buffers full + boost::bind(&RdmaConnector::eof, this, _1)); // data error - just close connection + aio->start(poller); + + identifier = str(format("[%1% %2%]") % ci->getLocalName() % ci->getPeerName()); + writer.init(identifier, aio); + ProtocolInitiation init(version); + writeDataBlock(init); +} + +void RdmaConnector::connectionError(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr&, Rdma::ErrorType) { + QPID_LOG(trace, "Connection Error " << identifier); + eof(*aio); +} + +void RdmaConnector::disconnected(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr&) { + eof(*aio); +} + +void RdmaConnector::rejected(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr&, const Rdma::ConnectionParams& cp) { + QPID_LOG(trace, "Connection Rejected " << identifier << ": " << cp.maxRecvBufferSize); + eof(*aio); +} + +bool RdmaConnector::closeInternal() { + bool ret; + { + Mutex::ScopedLock l(pollingLock); + ret = polling; + if (polling) { + polling = false; + poller->shutdown(); + } + if (joined || receiver.id() == Thread::current().id()) { + return ret; + } + joined = true; + } + + receiver.join(); + return ret; +} + +void RdmaConnector::close() { + closeInternal(); +} + +void RdmaConnector::setInputHandler(InputHandler* handler){ + input = handler; +} + +void RdmaConnector::setShutdownHandler(ShutdownHandler* handler){ + shutdownHandler = handler; +} + +OutputHandler* RdmaConnector::getOutputHandler(){ + return this; +} + +sys::ShutdownHandler* RdmaConnector::getShutdownHandler() const { + return shutdownHandler; +} + +const std::string& RdmaConnector::getIdentifier() const { + return identifier; +} + +void RdmaConnector::send(AMQFrame& frame) { + writer.handle(frame); +} + +void RdmaConnector::handleClosed() { + if (closeInternal() && shutdownHandler) + shutdownHandler->shutdown(); +} + +RdmaConnector::Writer::Writer(uint16_t s, Bounds* b) : + maxFrameSize(s), + aio(0), + buffer(0), + lastEof(0), + bounds(b) +{ +} + +RdmaConnector::Writer::~Writer() { + if (aio) + aio->returnBuffer(buffer); +} + +void RdmaConnector::Writer::init(std::string id, Rdma::AsynchIO* a) { + Mutex::ScopedLock l(lock); + identifier = id; + aio = a; + assert(aio->bufferAvailable()); + newBuffer(); +} +void RdmaConnector::Writer::handle(framing::AMQFrame& frame) { + Mutex::ScopedLock l(lock); + frames.push_back(frame); + // Don't bother to send anything unless we're at the end of a frameset (assembly in 0-10 terminology) + if (frame.getEof()) { + lastEof = frames.size(); + QPID_LOG(debug, "Requesting write: lastEof=" << lastEof); + aio->notifyPendingWrite(); + } + QPID_LOG(trace, "SENT " << identifier << ": " << frame); +} + +void RdmaConnector::Writer::writeOne() { + assert(buffer); + QPID_LOG(trace, "Write buffer " << encode.getPosition() + << " bytes " << framesEncoded << " frames "); + framesEncoded = 0; + + buffer->dataStart = 0; + buffer->dataCount = encode.getPosition(); + aio->queueWrite(buffer); + newBuffer(); +} + +void RdmaConnector::Writer::newBuffer() { + buffer = aio->getBuffer(); + encode = framing::Buffer(buffer->bytes, buffer->byteCount); + framesEncoded = 0; +} + +// Called in IO thread. (write idle routine) +// This is NOT only called in response to previously calling notifyPendingWrite +void RdmaConnector::Writer::write(Rdma::AsynchIO&) { + Mutex::ScopedLock l(lock); + assert(buffer); + // If nothing to do return immediately + if (lastEof==0) + return; + size_t bytesWritten = 0; + while (aio->writable() && aio->bufferAvailable() && !frames.empty()) { + const AMQFrame* frame = &frames.front(); + uint32_t size = frame->encodedSize(); + while (size <= encode.available()) { + frame->encode(encode); + frames.pop_front(); + ++framesEncoded; + bytesWritten += size; + if (frames.empty()) + break; + frame = &frames.front(); + size = frame->encodedSize(); + } + lastEof -= framesEncoded; + writeOne(); + } + if (bounds) bounds->reduce(bytesWritten); +} + +void RdmaConnector::readbuff(Rdma::AsynchIO&, Rdma::Buffer* buff) { + framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount); + + if (!initiated) { + framing::ProtocolInitiation protocolInit; + if (protocolInit.decode(in)) { + //TODO: check the version is correct + QPID_LOG(debug, "RECV " << identifier << " INIT(" << protocolInit << ")"); + } + initiated = true; + } + AMQFrame frame; + while(frame.decode(in)){ + QPID_LOG(trace, "RECV " << identifier << ": " << frame); + input->received(frame); + } +} + +void RdmaConnector::writebuff(Rdma::AsynchIO& aio_) { + writer.write(aio_); +} + +void RdmaConnector::writeDataBlock(const AMQDataBlock& data) { + Rdma::Buffer* buff = aio->getBuffer(); + framing::Buffer out(buff->bytes, buff->byteCount); + data.encode(out); + buff->dataCount = data.encodedSize(); + aio->queueWrite(buff); +} + +void RdmaConnector::eof(Rdma::AsynchIO&) { + handleClosed(); +} + +// TODO: astitcher 20070908 This version of the code can never time out, so the idle processing +// will never be called +void RdmaConnector::run(){ + // Keep the connection impl in memory until run() completes. + //GRS: currently the ConnectionImpls destructor is where the Io thread is joined + //boost::shared_ptr<ConnectionImpl> protect = impl->shared_from_this(); + //assert(protect); + try { + Dispatcher d(poller); + + //aio->start(poller); + d.run(); + //aio->queueForDeletion(); + } catch (const std::exception& e) { + { + // We're no longer polling + Mutex::ScopedLock l(pollingLock); + polling = false; + } + QPID_LOG(error, e.what()); + handleClosed(); + } +} + + +}} // namespace qpid::client diff --git a/RC9/qpid/cpp/src/qpid/client/Results.cpp b/RC9/qpid/cpp/src/qpid/client/Results.cpp new file mode 100644 index 0000000000..1b98d6c98a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Results.cpp @@ -0,0 +1,76 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Results.h" +#include "FutureResult.h" +#include "qpid/framing/SequenceSet.h" + +using namespace qpid::framing; +using namespace boost; + +namespace qpid { +namespace client { + +Results::Results() {} + +Results::~Results() { + try { close(); } catch (const std::exception& /*e*/) { assert(0); } +} + +void Results::close() +{ + for (Listeners::iterator i = listeners.begin(); i != listeners.end(); i++) { + i->second->completed(); + } + listeners.clear(); +} + +void Results::completed(const SequenceSet& set) +{ + //call complete on those listeners whose ids fall within the set + Listeners::iterator i = listeners.begin(); + while (i != listeners.end()) { + if (set.contains(i->first)) { + i->second->completed(); + listeners.erase(i++); + } else { + i++; + } + } +} + +void Results::received(const SequenceNumber& id, const std::string& result) +{ + Listeners::iterator i = listeners.find(id); + if (i != listeners.end()) { + i->second->received(result); + listeners.erase(i); + } +} + +Results::FutureResultPtr Results::listenForResult(const SequenceNumber& id) +{ + FutureResultPtr l(new FutureResult()); + listeners[id] = l; + return l; +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/client/Results.h b/RC9/qpid/cpp/src/qpid/client/Results.h new file mode 100644 index 0000000000..4c49f6b05b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Results.h @@ -0,0 +1,56 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/framing/SequenceNumber.h" +#include <map> +#include <boost/shared_ptr.hpp> + +#ifndef _Results_ +#define _Results_ + +namespace qpid { +namespace client { + +class FutureResult; + +///@internal +class Results +{ +public: + typedef boost::shared_ptr<FutureResult> FutureResultPtr; + + Results(); + ~Results(); + void completed(const framing::SequenceSet& set); + void received(const framing::SequenceNumber& id, const std::string& result); + FutureResultPtr listenForResult(const framing::SequenceNumber& point); + void close(); + +private: + typedef std::map<framing::SequenceNumber, FutureResultPtr> Listeners; + Listeners listeners; +}; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/Session.h b/RC9/qpid/cpp/src/qpid/client/Session.h new file mode 100644 index 0000000000..bdabd26c82 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Session.h @@ -0,0 +1,39 @@ +#ifndef QPID_CLIENT_SESSION_H +#define QPID_CLIENT_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/client/Session_0_10.h" + +namespace qpid { +namespace client { + +/** + * Session is an alias for Session_0_10 + * + * \ingroup clientapi + */ +typedef Session_0_10 Session; + + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_SESSION_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/SessionBase_0_10.cpp b/RC9/qpid/cpp/src/qpid/client/SessionBase_0_10.cpp new file mode 100644 index 0000000000..c933a64f07 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/SessionBase_0_10.cpp @@ -0,0 +1,77 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "SessionBase_0_10.h" +#include "Connection.h" +#include "qpid/framing/all_method_bodies.h" + +namespace qpid { +namespace client { + +using namespace framing; + +SessionBase_0_10::SessionBase_0_10() {} +SessionBase_0_10::~SessionBase_0_10() {} + +void SessionBase_0_10::close() { impl->close(); } + +Execution& SessionBase_0_10::getExecution() +{ + return *impl; +} + +void SessionBase_0_10::flush() +{ + impl->sendFlush(); +} + +void SessionBase_0_10::sync() +{ + ExecutionSyncBody b; + b.setSync(true); + impl->send(b).wait(*impl); +} + +void SessionBase_0_10::markCompleted(const framing::SequenceSet& ids, bool notifyPeer) +{ + impl->markCompleted(ids, notifyPeer); +} + +void SessionBase_0_10::markCompleted(const framing::SequenceNumber& id, bool cumulative, bool notifyPeer) +{ + impl->markCompleted(id, cumulative, notifyPeer); +} + +void SessionBase_0_10::sendCompletion() +{ + impl->sendCompletion(); +} + +uint16_t SessionBase_0_10::getChannel() const { return impl->getChannel(); } + +void SessionBase_0_10::suspend() { impl->suspend(); } +void SessionBase_0_10::resume(Connection c) { impl->resume(c.impl); } +uint32_t SessionBase_0_10::timeout(uint32_t seconds) { return impl->setTimeout(seconds); } + +SessionId SessionBase_0_10::getId() const { return impl->getId(); } +framing::FrameSet::shared_ptr SessionBase_0_10::get() { return impl->get(); } + + +}} // namespace qpid::client diff --git a/RC9/qpid/cpp/src/qpid/client/SessionBase_0_10.h b/RC9/qpid/cpp/src/qpid/client/SessionBase_0_10.h new file mode 100644 index 0000000000..091c977053 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/SessionBase_0_10.h @@ -0,0 +1,118 @@ +#ifndef QPID_CLIENT_SESSIONBASE_H +#define QPID_CLIENT_SESSIONBASE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/SessionId.h" +#include "qpid/framing/amqp_structs.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/framing/MethodContent.h" +#include "qpid/framing/TransferContent.h" +#include "qpid/client/Completion.h" +#include "qpid/client/ConnectionImpl.h" +#include "qpid/client/Execution.h" +#include "qpid/client/SessionImpl.h" +#include "qpid/client/TypedResult.h" +#include "qpid/shared_ptr.h" +#include <string> + +namespace qpid { +namespace client { + +class Connection; + +using std::string; +using framing::Content; +using framing::FieldTable; +using framing::MethodContent; +using framing::SequenceNumber; +using framing::SequenceSet; +using framing::SequenceNumberSet; +using qpid::SessionId; +using framing::Xid; + +/** Unit of message credit: messages or bytes */ +enum CreditUnit { MESSAGE_CREDIT=0, BYTE_CREDIT=1, UNLIMITED_CREDIT=0xFFFFFFFF }; + +/** + * Base class for handles to an AMQP session. + * + * Subclasses provide the AMQP commands for a given + * version of the protocol. + */ +class SessionBase_0_10 { + public: + + typedef framing::TransferContent DefaultContent; + + ///@internal + SessionBase_0_10(); + ~SessionBase_0_10(); + + /** Get the next message frame-set from the session. */ + framing::FrameSet::shared_ptr get(); + + /** Get the session ID */ + SessionId getId() const; + + /** Close the session. + * A session is automatically closed when all handles to it are destroyed. + */ + void close(); + + /** + * Synchronize the session: sync() waits until all commands issued + * on this session so far have been completed by the broker. + * + * Note sync() is always synchronous, even on an AsyncSession object + * because that's almost always what you want. You can call + * AsyncSession::executionSync() directly in the unusual event + * that you want to do an asynchronous sync. + */ + void sync(); + + /** Set the timeout for this session. */ + uint32_t timeout(uint32_t seconds); + + /** Suspend the session - detach it from its connection */ + void suspend(); + + /** Resume a suspended session with a new connection */ + void resume(Connection); + + /** Get the channel associated with this session */ + uint16_t getChannel() const; + + Execution& getExecution(); + void flush(); + void markCompleted(const framing::SequenceSet& ids, bool notifyPeer); + void markCompleted(const framing::SequenceNumber& id, bool cumulative, bool notifyPeer); + void sendCompletion(); + + protected: + boost::shared_ptr<SessionImpl> impl; + friend class SessionBase_0_10Access; +}; + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_SESSIONBASE_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/SessionBase_0_10Access.h b/RC9/qpid/cpp/src/qpid/client/SessionBase_0_10Access.h new file mode 100644 index 0000000000..e2189a53dd --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/SessionBase_0_10Access.h @@ -0,0 +1,42 @@ +#ifndef QPID_CLIENT_SESSIONBASEACCESS_H +#define QPID_CLIENT_SESSIONBASEACCESS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/client/SessionBase_0_10.h" + +/**@file @internal Internal use only */ + +namespace qpid { +namespace client { + +class SessionBase_0_10Access { + public: + SessionBase_0_10Access(SessionBase_0_10& sb_) : sb(sb_) {} + void set(const boost::shared_ptr<SessionImpl>& si) { sb.impl = si; } + boost::shared_ptr<SessionImpl> get() { return sb.impl; } + private: + SessionBase_0_10& sb; +}; +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_SESSIONBASEACCESS_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/SessionImpl.cpp b/RC9/qpid/cpp/src/qpid/client/SessionImpl.cpp new file mode 100644 index 0000000000..ab8c1bddb8 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/SessionImpl.cpp @@ -0,0 +1,695 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SessionImpl.h" + +#include "ConnectionImpl.h" +#include "Future.h" + +#include "qpid/framing/all_method_bodies.h" +#include "qpid/framing/ClientInvoker.h" +#include "qpid/framing/enum.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/MethodContent.h" +#include "qpid/framing/SequenceSet.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/DeliveryProperties.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/IntegerTypes.h" + +#include <boost/bind.hpp> + +namespace { const std::string EMPTY; } + +namespace qpid { +namespace client { + +using namespace qpid::framing; +using namespace qpid::framing::session; //for detach codes + +typedef sys::Monitor::ScopedLock Lock; +typedef sys::Monitor::ScopedUnlock UnLock; +typedef sys::ScopedLock<sys::Semaphore> Acquire; + + +SessionImpl::SessionImpl(const std::string& name, shared_ptr<ConnectionImpl> conn) + : state(INACTIVE), + detachedLifetime(0), + maxFrameSize(conn->getNegotiatedSettings().maxFrameSize), + id(conn->getNegotiatedSettings().username, name.empty() ? Uuid(true).str() : name), + connectionShared(conn), + connectionWeak(conn), + weakPtr(false), + ioHandler(*this), + proxy(ioHandler), + nextIn(0), + nextOut(0) +{ + channel.next = connectionShared.get(); +} + +SessionImpl::~SessionImpl() { + { + Lock l(state); + if (state != DETACHED) { + QPID_LOG(warning, "Session was not closed cleanly"); + setState(DETACHED); + handleClosed(); + state.waitWaiters(); + } + } + boost::shared_ptr<ConnectionImpl> c = connectionWeak.lock(); + if (c) c->erase(channel); +} + + +FrameSet::shared_ptr SessionImpl::get() // user thread +{ + // No lock here: pop does a blocking wait. + return demux.getDefault()->pop(); +} + +const SessionId SessionImpl::getId() const //user thread +{ + return id; //id is immutable +} + +void SessionImpl::open(uint32_t timeout) // user thread +{ + Lock l(state); + if (state == INACTIVE) { + setState(ATTACHING); + proxy.attach(id.getName(), false); + waitFor(ATTACHED); + //TODO: timeout will not be set locally until get response to + //confirm, should we wait for that? + setTimeout(timeout); + proxy.commandPoint(nextOut, 0); + } else { + throw Exception("Open already called for this session"); + } +} + +void SessionImpl::close() //user thread +{ + Lock l(state); + if (detachedLifetime) setTimeout(0); + detach(); + waitFor(DETACHED); +} + +void SessionImpl::resume(shared_ptr<ConnectionImpl>) // user thread +{ + // weakPtr sessions should not be resumed. + if (weakPtr) return; + throw NotImplementedException("Resume not yet implemented by client!"); +} + +void SessionImpl::suspend() //user thread +{ + Lock l(state); + detach(); +} + +void SessionImpl::detach() //call with lock held +{ + if (state == ATTACHED) { + setState(DETACHING); + proxy.detach(id.getName()); + } +} + + +uint16_t SessionImpl::getChannel() const // user thread +{ + return channel; +} + +void SessionImpl::setChannel(uint16_t c) // user thread +{ + //channel will only ever be set when session is detached (and + //about to be resumed) + channel = c; +} + +Demux& SessionImpl::getDemux() +{ + return demux; +} + +void SessionImpl::waitForCompletion(const SequenceNumber& id) +{ + Lock l(state); + waitForCompletionImpl(id); +} + +void SessionImpl::waitForCompletionImpl(const SequenceNumber& id) //call with lock held +{ + while (incompleteOut.contains(id)) { + checkOpen(); + state.wait(); + } +} + +bool SessionImpl::isComplete(const SequenceNumber& id) +{ + Lock l(state); + return !incompleteOut.contains(id); +} + +struct IsCompleteUpTo +{ + const SequenceNumber& id; + bool result; + + IsCompleteUpTo(const SequenceNumber& _id) : id(_id), result(true) {} + void operator()(const SequenceNumber& start, const SequenceNumber&) + { + if (start <= id) result = false; + } + +}; + +bool SessionImpl::isCompleteUpTo(const SequenceNumber& id) +{ + Lock l(state); + //return false if incompleteOut contains anything less than id, + //true otherwise + IsCompleteUpTo f(id); + incompleteIn.for_each(f); + return f.result; +} + +struct MarkCompleted +{ + const SequenceNumber& id; + SequenceSet& completedIn; + + MarkCompleted(const SequenceNumber& _id, SequenceSet& set) : id(_id), completedIn(set) {} + + void operator()(const SequenceNumber& start, const SequenceNumber& end) + { + if (id >= end) { + completedIn.add(start, end); + } else if (id >= start) { + completedIn.add(start, id); + } + } + +}; + +void SessionImpl::markCompleted(const SequenceSet& ids, bool notifyPeer) +{ + Lock l(state); + incompleteIn.remove(ids); + completedIn.add(ids); + if (notifyPeer) { + sendCompletion(); + } +} + +void SessionImpl::markCompleted(const SequenceNumber& id, bool cumulative, bool notifyPeer) +{ + Lock l(state); + if (cumulative) { + //everything in incompleteIn less than or equal to id is now complete + MarkCompleted f(id, completedIn); + incompleteIn.for_each(f); + //make sure id itself is in + completedIn.add(id); + //then remove anything thats completed from the incomplete set + incompleteIn.remove(completedIn); + } else if (incompleteIn.contains(id)) { + incompleteIn.remove(id); + completedIn.add(id); + } + if (notifyPeer) { + sendCompletion(); + } +} + +void SessionImpl::setException(const sys::ExceptionHolder& ex) { + Lock l(state); + setExceptionLH(ex); +} + +void SessionImpl::setExceptionLH(const sys::ExceptionHolder& ex) { // Call with lock held. + exceptionHolder = ex; + setState(DETACHED); +} + +/** + * Called by ConnectionImpl to notify active sessions when connection + * is explictly closed + */ +void SessionImpl::connectionClosed(uint16_t code, const std::string& text) { + setException(createConnectionException(code, text)); + handleClosed(); +} + +/** + * Called by ConnectionImpl to notify active sessions when connection + * is disconnected + */ +void SessionImpl::connectionBroke(const std::string& _text) { + setException(sys::ExceptionHolder(new TransportFailure(_text))); + handleClosed(); +} + +Future SessionImpl::send(const AMQBody& command) +{ + return sendCommand(command); +} + +Future SessionImpl::send(const AMQBody& command, const MethodContent& content) +{ + return sendCommand(command, &content); +} + +namespace { +// Functor for FrameSet::map to send header + content frames but, not method frames. +struct SendContentFn { + FrameHandler& handler; + void operator()(const AMQFrame& f) { + if (!f.getMethod()) + handler(const_cast<AMQFrame&>(f)); + } + SendContentFn(FrameHandler& h) : handler(h) {} +}; +} + +Future SessionImpl::send(const AMQBody& command, const FrameSet& content) { + Acquire a(sendLock); + SequenceNumber id = nextOut++; + { + Lock l(state); + checkOpen(); + incompleteOut.add(id); + } + Future f(id); + if (command.getMethod()->resultExpected()) { + Lock l(state); + //result listener must be set before the command is sent + f.setFutureResult(results.listenForResult(id)); + } + AMQFrame frame(command); + frame.setEof(false); + handleOut(frame); + + SendContentFn send(out); + content.map(send); + return f; +} + +Future SessionImpl::sendCommand(const AMQBody& command, const MethodContent* content) +{ + Acquire a(sendLock); + SequenceNumber id = nextOut++; + { + Lock l(state); + checkOpen(); + incompleteOut.add(id); + } + Future f(id); + if (command.getMethod()->resultExpected()) { + Lock l(state); + //result listener must be set before the command is sent + f.setFutureResult(results.listenForResult(id)); + } + AMQFrame frame(command); + if (content) { + frame.setEof(false); + } + handleOut(frame); + if (content) { + sendContent(*content); + } + return f; +} + +void SessionImpl::sendContent(const MethodContent& content) +{ + AMQFrame header(content.getHeader()); + + // Client is not allowed to set the delivery-properties.exchange. + AMQHeaderBody* headerp = static_cast<AMQHeaderBody*>(header.getBody()); + if (headerp && headerp->get<DeliveryProperties>()) + headerp->get<DeliveryProperties>(true)->clearExchangeFlag(); + + header.setFirstSegment(false); + uint64_t data_length = content.getData().length(); + if(data_length > 0){ + header.setLastSegment(false); + handleOut(header); + /*Note: end of frame marker included in overhead but not in size*/ + const uint32_t frag_size = maxFrameSize - AMQFrame::frameOverhead(); + + if(data_length < frag_size){ + AMQFrame frame(in_place<AMQContentBody>(content.getData())); + frame.setFirstSegment(false); + handleOut(frame); + }else{ + uint32_t offset = 0; + uint32_t remaining = data_length - offset; + while (remaining > 0) { + uint32_t length = remaining > frag_size ? frag_size : remaining; + string frag(content.getData().substr(offset, length)); + AMQFrame frame(in_place<AMQContentBody>(frag)); + frame.setFirstSegment(false); + frame.setLastSegment(true); + if (offset > 0) { + frame.setFirstFrame(false); + } + offset += length; + remaining = data_length - offset; + if (remaining) { + frame.setLastFrame(false); + } + handleOut(frame); + } + } + } else { + handleOut(header); + } +} + + +bool isMessageMethod(AMQMethodBody* method) +{ + return method->isA<MessageTransferBody>(); +} + +bool isMessageMethod(AMQBody* body) +{ + AMQMethodBody* method=body->getMethod(); + return method && isMessageMethod(method); +} + +bool isContentFrame(AMQFrame& frame) +{ + AMQBody* body = frame.getBody(); + uint8_t type = body->type(); + return type == HEADER_BODY || type == CONTENT_BODY || isMessageMethod(body); +} + +void SessionImpl::handleIn(AMQFrame& frame) // network thread +{ + try { + if (!invoke(static_cast<SessionHandler&>(*this), *frame.getBody())) { + if (invoke(static_cast<ExecutionHandler&>(*this), *frame.getBody())) { + //make sure the command id sequence and completion + //tracking takes account of execution commands + Lock l(state); + completedIn.add(nextIn++); + } else { + //if not handled by this class, its for the application: + deliver(frame); + } + } + } + catch (const SessionException& e) { + setException(createSessionException(e.code, e.getMessage())); + } + catch (const ChannelException& e) { + setException(createChannelException(e.code, e.getMessage())); + } +} + +void SessionImpl::handleOut(AMQFrame& frame) // user thread +{ + sendFrame(frame, true); +} + +void SessionImpl::proxyOut(AMQFrame& frame) // network thread +{ + //Note: this case is treated slightly differently that command + //frames sent by application; session controls should not be + //blocked by bounds checking on the outgoing frame queue. + sendFrame(frame, false); +} + +void SessionImpl::sendFrame(AMQFrame& frame, bool canBlock) +{ + boost::shared_ptr<ConnectionImpl> c = connectionWeak.lock(); + if (c) { + channel.handle(frame); + c->expand(frame.encodedSize(), canBlock); + } +} + +void SessionImpl::deliver(AMQFrame& frame) // network thread +{ + if (!arriving) { + arriving = FrameSet::shared_ptr(new FrameSet(nextIn++)); + } + arriving->append(frame); + if (arriving->isComplete()) { + //message.transfers will be marked completed only when 'acked' + //as completion affects flow control; other commands will be + //considered completed as soon as processed here + if (arriving->isA<MessageTransferBody>()) { + Lock l(state); + incompleteIn.add(arriving->getId()); + } else { + Lock l(state); + completedIn.add(arriving->getId()); + } + demux.handle(arriving); + arriving.reset(); + } +} + +//control handler methods (called by network thread when controls are +//received from peer): + +void SessionImpl::attach(const std::string& /*name*/, bool /*force*/) +{ + throw NotImplementedException("Client does not support attach"); +} + +void SessionImpl::attached(const std::string& _name) +{ + Lock l(state); + if (id.getName() != _name) throw InternalErrorException("Incorrect session name"); + setState(ATTACHED); +} + +void SessionImpl::detach(const std::string& _name) +{ + Lock l(state); + if (id.getName() != _name) throw InternalErrorException("Incorrect session name"); + setState(DETACHED); + QPID_LOG(info, "Session detached by peer: " << id); +} + +void SessionImpl::detached(const std::string& _name, uint8_t _code) { + Lock l(state); + if (id.getName() != _name) throw InternalErrorException("Incorrect session name"); + setState(DETACHED); + if (_code) { + //TODO: make sure this works with execution.exception - don't + //want to overwrite the code from that + setExceptionLH(createChannelException(_code, "Session detached by peer")); + QPID_LOG(error, exceptionHolder.what()); + } + if (detachedLifetime == 0) { + handleClosed(); +} +} + +void SessionImpl::requestTimeout(uint32_t t) +{ + Lock l(state); + detachedLifetime = t; + proxy.timeout(t); +} + +void SessionImpl::timeout(uint32_t t) +{ + Lock l(state); + detachedLifetime = t; +} + +void SessionImpl::commandPoint(const framing::SequenceNumber& id, uint64_t offset) +{ + if (offset) throw NotImplementedException("Non-zero byte offset not yet supported for command-point"); + + Lock l(state); + nextIn = id; +} + +void SessionImpl::expected(const framing::SequenceSet& commands, const framing::Array& fragments) +{ + if (!commands.empty() || fragments.encodedSize()) { + throw NotImplementedException("Session resumption not yet supported"); + } +} + +void SessionImpl::confirmed(const framing::SequenceSet& /*commands*/, const framing::Array& /*fragments*/) +{ + //don't really care too much about this yet +} + +void SessionImpl::completed(const framing::SequenceSet& commands, bool timelyReply) +{ + Lock l(state); + incompleteOut.remove(commands); + state.notifyAll();//notify any waiters of completion + completedOut.add(commands); + //notify any waiting results of completion + results.completed(commands); + + if (timelyReply) { + proxy.knownCompleted(completedOut); + completedOut.clear(); + } +} + +void SessionImpl::knownCompleted(const framing::SequenceSet& commands) +{ + Lock l(state); + completedIn.remove(commands); +} + +void SessionImpl::flush(bool expected, bool confirmed, bool completed) +{ + Lock l(state); + if (expected) { + proxy.expected(SequenceSet(nextIn), Array()); + } + if (confirmed) { + proxy.confirmed(completedIn, Array()); + } + if (completed) { + proxy.completed(completedIn, true); + } +} + +void SessionImpl::sendCompletion() +{ + Lock l(state); + sendCompletionImpl(); +} + +void SessionImpl::sendFlush() +{ + Lock l(state); + proxy.flush(false, false, true); +} + +void SessionImpl::sendCompletionImpl() +{ + proxy.completed(completedIn, completedIn.span() > 1000); +} + +void SessionImpl::gap(const framing::SequenceSet& /*commands*/) +{ + throw NotImplementedException("gap not yet supported"); +} + +void SessionImpl::sync() {} + +void SessionImpl::result(const framing::SequenceNumber& commandId, const std::string& value) +{ + Lock l(state); + results.received(commandId, value); +} + +void SessionImpl::exception(uint16_t errorCode, + const framing::SequenceNumber& commandId, + uint8_t classCode, + uint8_t commandCode, + uint8_t /*fieldIndex*/, + const std::string& description, + const framing::FieldTable& /*errorInfo*/) +{ + Lock l(state); + setExceptionLH(createSessionException(errorCode, description)); + QPID_LOG(warning, "Exception received from broker: " << exceptionHolder.what() + << " [caused by " << commandId << " " << classCode << ":" << commandCode << "]"); + + if (detachedLifetime) + setTimeout(0); +} + + +//private utility methods: + +inline void SessionImpl::setState(State s) //call with lock held +{ + state = s; +} + +inline void SessionImpl::waitFor(State s) //call with lock held +{ + // We can be DETACHED at any time + if (s == DETACHED) state.waitFor(DETACHED); + else state.waitFor(States(s, DETACHED)); + check(); +} + +void SessionImpl::check() const //call with lock held. +{ + exceptionHolder.raise(); +} + +void SessionImpl::checkOpen() const //call with lock held. +{ + check(); + if (state != ATTACHED) { + throw NotAttachedException(QPID_MSG("Session " << getId() << " isn't attached")); + } +} + +void SessionImpl::assertOpen() const +{ + Lock l(state); + checkOpen(); +} + +void SessionImpl::handleClosed() +{ + demux.close(exceptionHolder.empty() ? new ClosedException() : exceptionHolder); + results.close(); +} + +uint32_t SessionImpl::setTimeout(uint32_t seconds) { + proxy.requestTimeout(seconds); + // FIXME aconway 2008-10-07: wait for timeout response from broker + // and use value retured by broker. + detachedLifetime = seconds; + return detachedLifetime; +} + +uint32_t SessionImpl::getTimeout() const { + return detachedLifetime; +} + +void SessionImpl::setWeakPtr(bool weak) { + weakPtr = weak; + if (weakPtr) + connectionShared.reset(); // Only keep weak pointer + else + connectionShared = connectionWeak.lock(); +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/client/SessionImpl.h b/RC9/qpid/cpp/src/qpid/client/SessionImpl.h new file mode 100644 index 0000000000..ea7776634a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/SessionImpl.h @@ -0,0 +1,219 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef _SessionImpl_ +#define _SessionImpl_ + +#include "Demux.h" +#include "Execution.h" +#include "Results.h" + +#include "qpid/SessionId.h" +#include "qpid/SessionState.h" +#include "boost/shared_ptr.hpp" +#include "boost/weak_ptr.hpp" +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/ChannelHandler.h" +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/AMQP_ClientOperations.h" +#include "qpid/framing/AMQP_ServerProxy.h" +#include "qpid/sys/Semaphore.h" +#include "qpid/sys/StateMonitor.h" +#include "qpid/sys/ExceptionHolder.h" + +#include <boost/optional.hpp> + +namespace qpid { + +namespace framing { + +class FrameSet; +class MethodContent; +class SequenceSet; + +} + +namespace client { + +class Future; +class ConnectionImpl; +class SessionHandler; + +///@internal +class SessionImpl : public framing::FrameHandler::InOutHandler, + public Execution, + private framing::AMQP_ClientOperations::SessionHandler, + private framing::AMQP_ClientOperations::ExecutionHandler +{ +public: + SessionImpl(const std::string& name, shared_ptr<ConnectionImpl>); + ~SessionImpl(); + + + //NOTE: Public functions called in user thread. + framing::FrameSet::shared_ptr get(); + + const SessionId getId() const; + + uint16_t getChannel() const; + void setChannel(uint16_t channel); + + void open(uint32_t detachedLifetime); + void close(); + void resume(shared_ptr<ConnectionImpl>); + void suspend(); + + void assertOpen() const; + + Future send(const framing::AMQBody& command); + Future send(const framing::AMQBody& command, const framing::MethodContent& content); + Future send(const framing::AMQBody& command, const framing::FrameSet& content); + + Demux& getDemux(); + void markCompleted(const framing::SequenceNumber& id, bool cumulative, bool notifyPeer); + void markCompleted(const framing::SequenceSet& ids, bool notifyPeer); + bool isComplete(const framing::SequenceNumber& id); + bool isCompleteUpTo(const framing::SequenceNumber& id); + void waitForCompletion(const framing::SequenceNumber& id); + void sendCompletion(); + void sendFlush(); + + void setException(const sys::ExceptionHolder&); + + //NOTE: these are called by the network thread when the connection is closed or dies + void connectionClosed(uint16_t code, const std::string& text); + void connectionBroke(const std::string& text); + + /** Set timeout in seconds, returns actual timeout allowed by broker */ + uint32_t setTimeout(uint32_t requestedSeconds); + + /** Get timeout in seconds. */ + uint32_t getTimeout() const; + + /** Make this session use a weak_ptr to the ConnectionImpl. + * Used for sessions created by the ConnectionImpl itself. + */ + void setWeakPtr(bool weak=true); + +private: + enum State { + INACTIVE, + ATTACHING, + ATTACHED, + DETACHING, + DETACHED + }; + typedef framing::AMQP_ClientOperations::SessionHandler SessionHandler; + typedef framing::AMQP_ClientOperations::ExecutionHandler ExecutionHandler; + typedef sys::StateMonitor<State, DETACHED> StateMonitor; + typedef StateMonitor::Set States; + + inline void setState(State s); + inline void waitFor(State); + + void setExceptionLH(const sys::ExceptionHolder&); // LH = lock held when called. + void detach(); + + void check() const; + void checkOpen() const; + void handleClosed(); + + void handleIn(framing::AMQFrame& frame); + void handleOut(framing::AMQFrame& frame); + /** + * Sends session controls. This case is treated slightly + * differently than command frames sent by the application via + * handleOut(); session controlsare not subject to bounds checking + * on the outgoing frame queue. + */ + void proxyOut(framing::AMQFrame& frame); + void sendFrame(framing::AMQFrame& frame, bool canBlock); + void deliver(framing::AMQFrame& frame); + + Future sendCommand(const framing::AMQBody&, const framing::MethodContent* = 0); + void sendContent(const framing::MethodContent&); + void waitForCompletionImpl(const framing::SequenceNumber& id); + + void sendCompletionImpl(); + + // Note: Following methods are called by network thread in + // response to session controls from the broker + void attach(const std::string& name, bool force); + void attached(const std::string& name); + void detach(const std::string& name); + void detached(const std::string& name, uint8_t detachCode); + void requestTimeout(uint32_t timeout); + void timeout(uint32_t timeout); + void commandPoint(const framing::SequenceNumber& commandId, uint64_t commandOffset); + void expected(const framing::SequenceSet& commands, const framing::Array& fragments); + void confirmed(const framing::SequenceSet& commands, const framing::Array& fragments); + void completed(const framing::SequenceSet& commands, bool timelyReply); + void knownCompleted(const framing::SequenceSet& commands); + void flush(bool expected, bool confirmed, bool completed); + void gap(const framing::SequenceSet& commands); + + // Note: Following methods are called by network thread in + // response to execution commands from the broker + void sync(); + void result(const framing::SequenceNumber& commandId, const std::string& value); + void exception(uint16_t errorCode, + const framing::SequenceNumber& commandId, + uint8_t classCode, + uint8_t commandCode, + uint8_t fieldIndex, + const std::string& description, + const framing::FieldTable& errorInfo); + + sys::ExceptionHolder exceptionHolder; + mutable StateMonitor state; + mutable sys::Semaphore sendLock; + uint32_t detachedLifetime; + const uint64_t maxFrameSize; + const SessionId id; + + shared_ptr<ConnectionImpl> connection(); + shared_ptr<ConnectionImpl> connectionShared; + boost::weak_ptr<ConnectionImpl> connectionWeak; + bool weakPtr; + + framing::FrameHandler::MemFunRef<SessionImpl, &SessionImpl::proxyOut> ioHandler; + framing::ChannelHandler channel; + framing::AMQP_ServerProxy::Session proxy; + + Results results; + Demux demux; + framing::FrameSet::shared_ptr arriving; + + framing::SequenceSet incompleteIn;//incoming commands that are as yet incomplete + framing::SequenceSet completedIn;//incoming commands that are have completed + framing::SequenceSet incompleteOut;//outgoing commands not yet known to be complete + framing::SequenceSet completedOut;//outgoing commands that we know to be completed + framing::SequenceNumber nextIn; + framing::SequenceNumber nextOut; + + SessionState sessionState; + + friend class client::SessionHandler; +}; + +}} // namespace qpid::client + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/SslConnector.cpp b/RC9/qpid/cpp/src/qpid/client/SslConnector.cpp new file mode 100644 index 0000000000..6dbdbd003e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/SslConnector.cpp @@ -0,0 +1,399 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Connector.h" + +#include "Bounds.h" +#include "ConnectionImpl.h" +#include "ConnectionSettings.h" +#include "qpid/Options.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/Time.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/sys/ssl/util.h" +#include "qpid/sys/ssl/SslIo.h" +#include "qpid/sys/ssl/SslSocket.h" +#include "qpid/sys/Dispatcher.h" +#include "qpid/sys/Poller.h" +#include "qpid/Msg.h" + +#include <iostream> +#include <map> +#include <boost/bind.hpp> +#include <boost/format.hpp> + +namespace qpid { +namespace client { + +using namespace qpid::sys; +using namespace qpid::sys::ssl; +using namespace qpid::framing; +using boost::format; +using boost::str; + + +class SslConnector : public Connector, private sys::Runnable +{ + struct Buff; + + /** Batch up frames for writing to aio. */ + class Writer : public framing::FrameHandler { + typedef sys::ssl::SslIOBufferBase BufferBase; + typedef std::vector<framing::AMQFrame> Frames; + + const uint16_t maxFrameSize; + sys::Mutex lock; + sys::ssl::SslIO* aio; + BufferBase* buffer; + Frames frames; + size_t lastEof; // Position after last EOF in frames + framing::Buffer encode; + size_t framesEncoded; + std::string identifier; + Bounds* bounds; + + void writeOne(); + void newBuffer(); + + public: + + Writer(uint16_t maxFrameSize, Bounds*); + ~Writer(); + void init(std::string id, sys::ssl::SslIO*); + void handle(framing::AMQFrame&); + void write(sys::ssl::SslIO&); + }; + + const uint16_t maxFrameSize; + framing::ProtocolVersion version; + bool initiated; + + sys::Mutex closedLock; + bool closed; + bool joined; + + sys::ShutdownHandler* shutdownHandler; + framing::InputHandler* input; + framing::InitiationHandler* initialiser; + framing::OutputHandler* output; + + Writer writer; + + sys::Thread receiver; + + sys::ssl::SslSocket socket; + + sys::ssl::SslIO* aio; + boost::shared_ptr<sys::Poller> poller; + + ~SslConnector(); + + void run(); + void handleClosed(); + bool closeInternal(); + + void readbuff(qpid::sys::ssl::SslIO&, qpid::sys::ssl::SslIOBufferBase*); + void writebuff(qpid::sys::ssl::SslIO&); + void writeDataBlock(const framing::AMQDataBlock& data); + void eof(qpid::sys::ssl::SslIO&); + + std::string identifier; + + ConnectionImpl* impl; + + void connect(const std::string& host, int port); + void init(); + void close(); + void send(framing::AMQFrame& frame); + + void setInputHandler(framing::InputHandler* handler); + void setShutdownHandler(sys::ShutdownHandler* handler); + sys::ShutdownHandler* getShutdownHandler() const; + framing::OutputHandler* getOutputHandler(); + const std::string& getIdentifier() const; + +public: + SslConnector(framing::ProtocolVersion pVersion, + const ConnectionSettings&, + ConnectionImpl*); +}; + +// Static constructor which registers connector here +namespace { + Connector* create(framing::ProtocolVersion v, const ConnectionSettings& s, ConnectionImpl* c) { + return new SslConnector(v, s, c); + } + + struct StaticInit { + StaticInit() { + try { + SslOptions options; + options.parse (0, 0, CONF_FILE, true); + if (options.certDbPath.empty()) { + QPID_LOG(warning, "SSL connector not enabled, you must set QPID_SSL_CERT_DB to enable it."); + } else { + initNSS(options); + Connector::registerFactory("ssl", &create); + } + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to initialise SSL connector: " << e.what()); + } + }; + + ~StaticInit() { shutdownNSS(); } + } init; +} + +SslConnector::SslConnector(ProtocolVersion ver, + const ConnectionSettings& settings, + ConnectionImpl* cimpl) + : maxFrameSize(settings.maxFrameSize), + version(ver), + initiated(false), + closed(true), + joined(true), + shutdownHandler(0), + writer(maxFrameSize, cimpl), + aio(0), + impl(cimpl) +{ + QPID_LOG(debug, "SslConnector created for " << version.toString()); + //TODO: how do we want to handle socket configuration with ssl? + //settings.configureSocket(socket); +} + +SslConnector::~SslConnector() { + close(); +} + +void SslConnector::connect(const std::string& host, int port){ + Mutex::ScopedLock l(closedLock); + assert(closed); + try { + socket.connect(host, port); + } catch (const std::exception& e) { + socket.close(); + throw; + } + + identifier = str(format("[%1% %2%]") % socket.getLocalPort() % socket.getPeerAddress()); + closed = false; + poller = Poller::shared_ptr(new Poller); + aio = new SslIO(socket, + boost::bind(&SslConnector::readbuff, this, _1, _2), + boost::bind(&SslConnector::eof, this, _1), + boost::bind(&SslConnector::eof, this, _1), + 0, // closed + 0, // nobuffs + boost::bind(&SslConnector::writebuff, this, _1)); + writer.init(identifier, aio); +} + +void SslConnector::init(){ + Mutex::ScopedLock l(closedLock); + assert(joined); + ProtocolInitiation init(version); + writeDataBlock(init); + joined = false; + receiver = Thread(this); +} + +bool SslConnector::closeInternal() { + Mutex::ScopedLock l(closedLock); + bool ret = !closed; + if (!closed) { + closed = true; + poller->shutdown(); + } + if (!joined && receiver.id() != Thread::current().id()) { + joined = true; + Mutex::ScopedUnlock u(closedLock); + receiver.join(); + } + return ret; +} + +void SslConnector::close() { + closeInternal(); +} + +void SslConnector::setInputHandler(InputHandler* handler){ + input = handler; +} + +void SslConnector::setShutdownHandler(ShutdownHandler* handler){ + shutdownHandler = handler; +} + +OutputHandler* SslConnector::getOutputHandler() { + return this; +} + +sys::ShutdownHandler* SslConnector::getShutdownHandler() const { + return shutdownHandler; +} + +const std::string& SslConnector::getIdentifier() const { + return identifier; +} + +void SslConnector::send(AMQFrame& frame) { + writer.handle(frame); +} + +void SslConnector::handleClosed() { + if (closeInternal() && shutdownHandler) + shutdownHandler->shutdown(); +} + +struct SslConnector::Buff : public SslIO::BufferBase { + Buff(size_t size) : SslIO::BufferBase(new char[size], size) {} + ~Buff() { delete [] bytes;} +}; + +SslConnector::Writer::Writer(uint16_t s, Bounds* b) : maxFrameSize(s), aio(0), buffer(0), lastEof(0), bounds(b) +{ +} + +SslConnector::Writer::~Writer() { delete buffer; } + +void SslConnector::Writer::init(std::string id, sys::ssl::SslIO* a) { + Mutex::ScopedLock l(lock); + identifier = id; + aio = a; + newBuffer(); +} +void SslConnector::Writer::handle(framing::AMQFrame& frame) { + Mutex::ScopedLock l(lock); + frames.push_back(frame); + if (frame.getEof() || (bounds && bounds->getCurrentSize() >= maxFrameSize)) { + lastEof = frames.size(); + aio->notifyPendingWrite(); + } + QPID_LOG(trace, "SENT " << identifier << ": " << frame); +} + +void SslConnector::Writer::writeOne() { + assert(buffer); + framesEncoded = 0; + + buffer->dataStart = 0; + buffer->dataCount = encode.getPosition(); + aio->queueWrite(buffer); + newBuffer(); +} + +void SslConnector::Writer::newBuffer() { + buffer = aio->getQueuedBuffer(); + if (!buffer) buffer = new Buff(maxFrameSize); + encode = framing::Buffer(buffer->bytes, buffer->byteCount); + framesEncoded = 0; +} + +// Called in IO thread. +void SslConnector::Writer::write(sys::ssl::SslIO&) { + Mutex::ScopedLock l(lock); + assert(buffer); + size_t bytesWritten(0); + for (size_t i = 0; i < lastEof; ++i) { + AMQFrame& frame = frames[i]; + uint32_t size = frame.encodedSize(); + if (size > encode.available()) writeOne(); + assert(size <= encode.available()); + frame.encode(encode); + ++framesEncoded; + bytesWritten += size; + } + frames.erase(frames.begin(), frames.begin()+lastEof); + lastEof = 0; + if (bounds) bounds->reduce(bytesWritten); + if (encode.getPosition() > 0) writeOne(); +} + +void SslConnector::readbuff(SslIO& aio, SslIO::BufferBase* buff) { + framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount); + + if (!initiated) { + framing::ProtocolInitiation protocolInit; + if (protocolInit.decode(in)) { + //TODO: check the version is correct + QPID_LOG(debug, "RECV " << identifier << " INIT(" << protocolInit << ")"); + } + initiated = true; + } + AMQFrame frame; + while(frame.decode(in)){ + QPID_LOG(trace, "RECV " << identifier << ": " << frame); + input->received(frame); + } + // TODO: unreading needs to go away, and when we can cope + // with multiple sub-buffers in the general buffer scheme, it will + if (in.available() != 0) { + // Adjust buffer for used bytes and then "unread them" + buff->dataStart += buff->dataCount-in.available(); + buff->dataCount = in.available(); + aio.unread(buff); + } else { + // Give whole buffer back to aio subsystem + aio.queueReadBuffer(buff); + } +} + +void SslConnector::writebuff(SslIO& aio_) { + writer.write(aio_); +} + +void SslConnector::writeDataBlock(const AMQDataBlock& data) { + SslIO::BufferBase* buff = new Buff(maxFrameSize); + framing::Buffer out(buff->bytes, buff->byteCount); + data.encode(out); + buff->dataCount = data.encodedSize(); + aio->queueWrite(buff); +} + +void SslConnector::eof(SslIO&) { + handleClosed(); +} + +// TODO: astitcher 20070908 This version of the code can never time out, so the idle processing +// will never be called +void SslConnector::run(){ + // Keep the connection impl in memory until run() completes. + boost::shared_ptr<ConnectionImpl> protect = impl->shared_from_this(); + assert(protect); + try { + Dispatcher d(poller); + + for (int i = 0; i < 32; i++) { + aio->queueReadBuffer(new Buff(maxFrameSize)); + } + + aio->start(poller); + d.run(); + aio->queueForDeletion(); + socket.close(); + } catch (const std::exception& e) { + QPID_LOG(error, e.what()); + handleClosed(); + } +} + + +}} // namespace qpid::client diff --git a/RC9/qpid/cpp/src/qpid/client/StateManager.cpp b/RC9/qpid/cpp/src/qpid/client/StateManager.cpp new file mode 100644 index 0000000000..0cb3c6b9d4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/StateManager.cpp @@ -0,0 +1,68 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "StateManager.h" +#include "qpid/framing/amqp_framing.h" + +using namespace qpid::client; +using namespace qpid::framing; +using namespace qpid::sys; + +StateManager::StateManager(int s) : state(s) {} + +void StateManager::waitForStateChange(int current) +{ + Monitor::ScopedLock l(stateLock); + while (state == current) { + stateLock.wait(); + } +} + +void StateManager::waitFor(int desired) +{ + Monitor::ScopedLock l(stateLock); + while (state != desired) { + stateLock.wait(); + } +} + +void StateManager::waitFor(std::set<int> desired) +{ + Monitor::ScopedLock l(stateLock); + while (desired.find(state) == desired.end()) { + stateLock.wait(); + } +} + + +void StateManager::setState(int s) +{ + Monitor::ScopedLock l(stateLock); + state = s; + stateLock.notifyAll(); +} + +int StateManager::getState() const +{ + Monitor::ScopedLock l(stateLock); + return state; +} + diff --git a/RC9/qpid/cpp/src/qpid/client/StateManager.h b/RC9/qpid/cpp/src/qpid/client/StateManager.h new file mode 100644 index 0000000000..b01664a0c1 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/StateManager.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 _StateManager_ +#define _StateManager_ + +#include <set> +#include "qpid/sys/Monitor.h" + +namespace qpid { +namespace client { + +///@internal +class StateManager +{ + int state; + mutable sys::Monitor stateLock; + +public: + StateManager(int initial); + void setState(int state); + int getState() const ; + void waitForStateChange(int current); + void waitFor(std::set<int> states); + void waitFor(int state); +}; + +}} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/Subscription.cpp b/RC9/qpid/cpp/src/qpid/client/Subscription.cpp new file mode 100644 index 0000000000..1f1b5ac6c6 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Subscription.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 "Subscription.h" +#include "SubscriptionImpl.h" +#include "HandlePrivate.h" +#include "qpid/framing/enum.h" + +namespace qpid { +namespace client { + +template class Handle<SubscriptionImpl>; + + +std::string Subscription::getName() const { return impl->getName(); } +std::string Subscription::getQueue() const { return impl->getQueue(); } +const SubscriptionSettings& Subscription::getSettings() const { return impl->getSettings(); } +void Subscription::setFlowControl(const FlowControl& f) { impl->setFlowControl(f); } +void Subscription::setAutoAck(unsigned int n) { impl->setAutoAck(n); } +SequenceSet Subscription::getUnacquired() const { return impl->getUnacquired(); } +SequenceSet Subscription::getUnaccepted() const { return impl->getUnaccepted(); } +void Subscription::acquire(const SequenceSet& messageIds) { impl->acquire(messageIds); } +void Subscription::accept(const SequenceSet& messageIds) { impl->accept(messageIds); } +void Subscription::release(const SequenceSet& messageIds) { impl->release(messageIds); } +Session Subscription::getSession() const { return impl->getSession(); } +SubscriptionManager&Subscription:: getSubscriptionManager() const { return impl->getSubscriptionManager(); } +void Subscription::cancel() { impl->cancel(); } +void Subscription::grantMessageCredit(uint32_t value) { impl->grantCredit(framing::message::CREDIT_UNIT_MESSAGE, value); } +void Subscription::grantByteCredit(uint32_t value) { impl->grantCredit(framing::message::CREDIT_UNIT_BYTE, value); } +}} // namespace qpid::client + + diff --git a/RC9/qpid/cpp/src/qpid/client/Subscription.h b/RC9/qpid/cpp/src/qpid/client/Subscription.h new file mode 100644 index 0000000000..6d9342bf09 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/Subscription.h @@ -0,0 +1,113 @@ +#ifndef QPID_CLIENT_SUBSCRIPTION_H +#define QPID_CLIENT_SUBSCRIPTION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionSettings.h" +#include "qpid/client/Handle.h" +#include "qpid/client/Message.h" + +namespace qpid { +namespace client { + +class SubscriptionImpl; +class SubscriptionManager; + +/** + * A handle to an active subscription. Provides methods to query the subscription status + * and control acknowledgement (acquire and accept) of messages. + */ +class Subscription : public Handle<SubscriptionImpl> { + public: + Subscription(SubscriptionImpl* si=0) : Handle<SubscriptionImpl>(si) {} + + /** The name of the subsctription, used as the "destination" for messages from the broker. + * Usually the same as the queue name but can be set differently. + */ + std::string getName() const; + + /** Name of the queue this subscription subscribes to */ + std::string getQueue() const; + + /** Get the flow control and acknowledgement settings for this subscription */ + const SubscriptionSettings& getSettings() const; + + /** Set the flow control parameters */ + void setFlowControl(const FlowControl&); + + /** Automatically acknowledge (acquire and accept) batches of n messages. + * You can disable auto-acknowledgement by setting n=0, and use acquire() and accept() + * to manually acquire and accept messages. + */ + void setAutoAck(unsigned int n); + + /** Get the set of ID's for messages received by this subscription but not yet acquired. + * This will always be empty if getSettings().acquireMode=ACQUIRE_MODE_PRE_ACQUIRED + */ + SequenceSet getUnacquired() const; + + /** Get the set of ID's for messages received by this subscription but not yet accepted. */ + SequenceSet getUnaccepted() const; + + /** Acquire messageIds and remove them from the unacquired set. + * oAdd them to the unaccepted set if getSettings().acceptMode == ACCEPT_MODE_EXPLICIT. + */ + void acquire(const SequenceSet& messageIds); + + /** Accept messageIds and remove them from the unaccepted set. + *@pre messageIds is a subset of getUnaccepted() + */ + void accept(const SequenceSet& messageIds); + + /** Release messageIds and remove them from the unaccepted set. + *@pre messageIds is a subset of getUnaccepted() + */ + void release(const SequenceSet& messageIds); + + /* Acquire a single message */ + void acquire(const Message& m) { acquire(SequenceSet(m.getId())); } + + /* Accept a single message */ + void accept(const Message& m) { accept(SequenceSet(m.getId())); } + + /* Release a single message */ + void release(const Message& m) { release(SequenceSet(m.getId())); } + + /** Get the session associated with this subscription */ + Session getSession() const; + + /** Get the subscription manager associated with this subscription */ + SubscriptionManager& getSubscriptionManager() const; + + /** Cancel the subscription. */ + void cancel(); + + /** Grant the specified amount of message credit */ + void grantMessageCredit(uint32_t); + + /** Grant the specified amount of byte credit */ + void grantByteCredit(uint32_t); +}; +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_SUBSCRIPTION_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/SubscriptionImpl.cpp b/RC9/qpid/cpp/src/qpid/client/SubscriptionImpl.cpp new file mode 100644 index 0000000000..5ea87110c2 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/SubscriptionImpl.cpp @@ -0,0 +1,149 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SubscriptionImpl.h" +#include "SubscriptionManager.h" +#include "SubscriptionSettings.h" + +namespace qpid { +namespace client { + +using sys::Mutex; +using framing::MessageAcquireResult; + +SubscriptionImpl::SubscriptionImpl(SubscriptionManager& m, const std::string& q, const SubscriptionSettings& s, const std::string& n, MessageListener* l) + : manager(m), name(n), queue(q), settings(s), listener(l) +{} + +void SubscriptionImpl::subscribe() +{ + async(manager.getSession()).messageSubscribe( + arg::queue=queue, + arg::destination=name, + arg::acceptMode=settings.acceptMode, + arg::acquireMode=settings.acquireMode, + arg::exclusive=settings.exclusive); + setFlowControl(settings.flowControl); +} + +std::string SubscriptionImpl::getName() const { return name; } + +std::string SubscriptionImpl::getQueue() const { return queue; } + +const SubscriptionSettings& SubscriptionImpl::getSettings() const { + Mutex::ScopedLock l(lock); + return settings; +} + +void SubscriptionImpl::setFlowControl(const FlowControl& f) { + Mutex::ScopedLock l(lock); + AsyncSession s=manager.getSession(); + if (&settings.flowControl != &f) settings.flowControl = f; + s.messageSetFlowMode(name, f.window); + s.messageFlow(name, CREDIT_UNIT_MESSAGE, f.messages); + s.messageFlow(name, CREDIT_UNIT_BYTE, f.bytes); + s.sync(); +} + +void SubscriptionImpl::grantCredit(framing::message::CreditUnit unit, uint32_t value) { + async(manager.getSession()).messageFlow(name, unit, value); +} + +void SubscriptionImpl::setAutoAck(size_t n) { + Mutex::ScopedLock l(lock); + settings.autoAck = n; +} + +SequenceSet SubscriptionImpl::getUnacquired() const { Mutex::ScopedLock l(lock); return unacquired; } +SequenceSet SubscriptionImpl::getUnaccepted() const { Mutex::ScopedLock l(lock); return unaccepted; } + +void SubscriptionImpl::acquire(const SequenceSet& messageIds) { + Mutex::ScopedLock l(lock); + MessageAcquireResult result = manager.getSession().messageAcquire(messageIds); + unacquired.remove(result.getTransfers()); + if (settings.acceptMode == ACCEPT_MODE_EXPLICIT) + unaccepted.add(result.getTransfers()); +} + +void SubscriptionImpl::accept(const SequenceSet& messageIds) { + Mutex::ScopedLock l(lock); + manager.getSession().messageAccept(messageIds); + unaccepted.remove(messageIds); + switch (settings.completionMode) { + case COMPLETE_ON_ACCEPT: + manager.getSession().markCompleted(messageIds, true); + break; + case COMPLETE_ON_DELIVERY: + manager.getSession().sendCompletion(); + break; + default://do nothing + break; + } +} + +void SubscriptionImpl::release(const SequenceSet& messageIds) { + Mutex::ScopedLock l(lock); + manager.getSession().messageRelease(messageIds); + if (settings.acceptMode == ACCEPT_MODE_EXPLICIT) + unaccepted.remove(messageIds); +} + +Session SubscriptionImpl::getSession() const { return manager.getSession(); } + +SubscriptionManager& SubscriptionImpl::getSubscriptionManager() const { return manager; } + +void SubscriptionImpl::cancel() { manager.cancel(name); } + +void SubscriptionImpl::received(Message& m) { + Mutex::ScopedLock l(lock); + if (m.getMethod().getAcquireMode() == ACQUIRE_MODE_NOT_ACQUIRED) + unacquired.add(m.getId()); + else if (m.getMethod().getAcceptMode() == ACCEPT_MODE_EXPLICIT) + unaccepted.add(m.getId()); + + if (listener) { + Mutex::ScopedUnlock u(lock); + listener->received(m); + } + + if (settings.completionMode == COMPLETE_ON_DELIVERY) { + manager.getSession().markCompleted(m.getId(), false, false); + } + if (settings.autoAck) { + if (unaccepted.size() >= settings.autoAck) { + async(manager.getSession()).messageAccept(unaccepted); + switch (settings.completionMode) { + case COMPLETE_ON_ACCEPT: + manager.getSession().markCompleted(unaccepted, true); + break; + case COMPLETE_ON_DELIVERY: + manager.getSession().sendCompletion(); + break; + default://do nothing + break; + } + unaccepted.clear(); + } + } +} + +}} // namespace qpid::client + diff --git a/RC9/qpid/cpp/src/qpid/client/SubscriptionImpl.h b/RC9/qpid/cpp/src/qpid/client/SubscriptionImpl.h new file mode 100644 index 0000000000..c4c486daeb --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/SubscriptionImpl.h @@ -0,0 +1,109 @@ +#ifndef QPID_CLIENT_SUBSCRIPTIONIMPL_H +#define QPID_CLIENT_SUBSCRIPTIONIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/client/SubscriptionSettings.h" +#include "qpid/client/Session.h" +#include "qpid/client/MessageListener.h" +#include "qpid/framing/enum.h" +#include "qpid/framing/SequenceSet.h" +#include "qpid/sys/Mutex.h" +#include "qpid/RefCounted.h" + +namespace qpid { +namespace client { + +class SubscriptionManager; + +class SubscriptionImpl : public RefCounted, public MessageListener { + public: + SubscriptionImpl(SubscriptionManager&, const std::string& queue, + const SubscriptionSettings&, const std::string& name, MessageListener* =0); + + /** The name of the subsctription, used as the "destination" for messages from the broker. + * Usually the same as the queue name but can be set differently. + */ + std::string getName() const; + + /** Name of the queue this subscription subscribes to */ + std::string getQueue() const; + + /** Get the flow control and acknowledgement settings for this subscription */ + const SubscriptionSettings& getSettings() const; + + /** Set the flow control parameters */ + void setFlowControl(const FlowControl&); + + /** Automatically acknowledge (acquire and accept) batches of n messages. + * You can disable auto-acknowledgement by setting n=0, and use acquire() and accept() + * to manually acquire and accept messages. + */ + void setAutoAck(size_t n); + + /** Get the set of ID's for messages received by this subscription but not yet acquired. + * This will always be empty if acquireMode=ACQUIRE_MODE_PRE_ACQUIRED + */ + SequenceSet getUnacquired() const; + + /** Get the set of ID's for messages acquired by this subscription but not yet accepted. */ + SequenceSet getUnaccepted() const; + + /** Acquire messageIds and remove them from the un-acquired set for the session. */ + void acquire(const SequenceSet& messageIds); + + /** Accept messageIds and remove them from the un-accepted set for the session. */ + void accept(const SequenceSet& messageIds); + + /** Release messageIds and remove them from the un-accepted set for the session. */ + void release(const SequenceSet& messageIds); + + /** Get the session associated with this subscription */ + Session getSession() const; + + /** Get the subscription manager associated with this subscription */ + SubscriptionManager& getSubscriptionManager() const; + + /** Send subscription request and issue appropriate flow control commands. */ + void subscribe(); + + /** Cancel the subscription. */ + void cancel(); + + /** Grant specified credit for this subscription **/ + void grantCredit(framing::message::CreditUnit unit, uint32_t value); + + void received(Message&); + + private: + + mutable sys::Mutex lock; + SubscriptionManager& manager; + std::string name, queue; + SubscriptionSettings settings; + framing::SequenceSet unacquired, unaccepted; + MessageListener* listener; +}; + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_SUBSCRIPTIONIMPL_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/SubscriptionManager.cpp b/RC9/qpid/cpp/src/qpid/client/SubscriptionManager.cpp new file mode 100644 index 0000000000..c91ae178ac --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/SubscriptionManager.cpp @@ -0,0 +1,140 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _Subscription_ +#define _Subscription_ + +#include "SubscriptionManager.h" +#include "SubscriptionImpl.h" +#include <qpid/client/Dispatcher.h> +#include <qpid/client/Session.h> +#include <qpid/client/MessageListener.h> +#include <qpid/framing/Uuid.h> +#include <set> +#include <sstream> + + +namespace qpid { +namespace client { + +SubscriptionManager::SubscriptionManager(const Session& s) + : dispatcher(s), session(s), autoStop(true) +{} + +Subscription SubscriptionManager::subscribe( + MessageListener& listener, const std::string& q, const SubscriptionSettings& ss, const std::string& n) +{ + std::string name=n.empty() ? q:n; + boost::intrusive_ptr<SubscriptionImpl> si = new SubscriptionImpl(*this, q, ss, name, &listener); + dispatcher.listen(si); + //issue subscription request after listener is registered with dispatcher + si->subscribe(); + return subscriptions[name] = Subscription(si.get()); +} + +Subscription SubscriptionManager::subscribe( + LocalQueue& lq, const std::string& q, const SubscriptionSettings& ss, const std::string& n) +{ + std::string name=n.empty() ? q:n; + lq.queue=session.getExecution().getDemux().add(name, ByTransferDest(name)); + boost::intrusive_ptr<SubscriptionImpl> si = new SubscriptionImpl(*this, q, ss, name, 0); + si->subscribe(); + lq.subscription = Subscription(si.get()); + return subscriptions[name] = lq.subscription; +} + +Subscription SubscriptionManager::subscribe( + MessageListener& listener, const std::string& q, const std::string& n) +{ + return subscribe(listener, q, defaultSettings, n); +} + +Subscription SubscriptionManager::subscribe( + LocalQueue& lq, const std::string& q, const std::string& n) +{ + return subscribe(lq, q, defaultSettings, n); +} + +void SubscriptionManager::cancel(const std::string& dest) +{ + sync(session).messageCancel(dest); + dispatcher.cancel(dest); +} + +void SubscriptionManager::setAutoStop(bool set) { autoStop=set; } + +void SubscriptionManager::run() +{ + dispatcher.setAutoStop(autoStop); + dispatcher.run(); +} + +void SubscriptionManager::start() +{ + dispatcher.setAutoStop(autoStop); + dispatcher.start(); +} + +void SubscriptionManager::wait() +{ + dispatcher.wait(); +} + +void SubscriptionManager::stop() +{ + dispatcher.stop(); +} + +bool SubscriptionManager::get(Message& result, const std::string& queue, sys::Duration timeout) { + LocalQueue lq; + std::string unique = framing::Uuid(true).str(); + subscribe(lq, queue, SubscriptionSettings(FlowControl::messageCredit(1)), unique); + AutoCancel ac(*this, unique); + //first wait for message to be delivered if a timeout has been specified + if (timeout && lq.get(result, timeout)) + return true; + //make sure message is not on queue before final check + sync(session).messageFlush(unique); + return lq.get(result, 0); +} + +Message SubscriptionManager::get(const std::string& queue, sys::Duration timeout) { + Message result; + if (!get(result, queue, timeout)) + throw Exception("Timed out waiting for a message"); + return result; +} + +Session SubscriptionManager::getSession() const { return session; } + +Subscription SubscriptionManager::getSubscription(const std::string& name) const { + std::map<std::string, Subscription>::const_iterator i = subscriptions.find(name); + if (i == subscriptions.end()) + throw Exception(QPID_MSG("Subscription not found: " << name)); + return i->second; +} + +void SubscriptionManager::registerFailoverHandler (boost::function<void ()> fh) { + dispatcher.registerFailoverHandler(fh); +} + +}} // namespace qpid::client + +#endif diff --git a/RC9/qpid/cpp/src/qpid/client/SubscriptionManager.h b/RC9/qpid/cpp/src/qpid/client/SubscriptionManager.h new file mode 100644 index 0000000000..1017480257 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/SubscriptionManager.h @@ -0,0 +1,293 @@ +#ifndef QPID_CLIENT_SUBSCRIPTIONMANAGER_H +#define QPID_CLIENT_SUBSCRIPTIONMANAGER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/sys/Mutex.h" +#include <qpid/client/Dispatcher.h> +#include <qpid/client/Completion.h> +#include <qpid/client/Session.h> +#include <qpid/client/AsyncSession.h> +#include <qpid/client/MessageListener.h> +#include <qpid/client/LocalQueue.h> +#include <qpid/client/Subscription.h> +#include <qpid/sys/Runnable.h> +#include <set> +#include <sstream> + +namespace qpid { +namespace client { + +/** + * A class to help create and manage subscriptions. + * + * Set up your subscriptions, then call run() to have messages + * delivered. + * + * \ingroup clientapi + * + * \details + * + * <h2>Subscribing and canceling subscriptions</h2> + * + * <ul> + * <li> + * <p>subscribe()</p> + * <pre> SubscriptionManager subscriptions(session); + * Listener listener(subscriptions); + * subscriptions.subscribe(listener, myQueue);</pre> + * <pre> SubscriptionManager subscriptions(session); + * LocalQueue local_queue; + * subscriptions.subscribe(local_queue, string("message_queue"));</pre></li> + * <li> + * <p>cancel()</p> + * <pre>subscriptions.cancel();</pre></li> + * </ul> + * + * <h2>Waiting for messages (and returning)</h2> + * + * <ul> + * <li> + * <p>run()</p> + * <pre> // Give up control to receive messages + * subscriptions.run();</pre></li> + * <li> + * <p>stop()</p> + * <pre>.// Use this code in a listener to return from run() + * subscriptions.stop();</pre></li> + * <li> + * <p>setAutoStop()</p> + * <pre>.// Return from subscriptions.run() when last subscription is cancelled + *.subscriptions.setAutoStop(true); + *.subscriptons.run(); + * </pre></li> + * <li> + * <p>Ending a subscription in a listener</p> + * <pre> + * void Listener::received(Message& message) { + * + * if (message.getData() == "That's all, folks!") { + * subscriptions.cancel(message.getDestination()); + * } + * } + * </pre> + * </li> + * </ul> + * + */ +class SubscriptionManager : public sys::Runnable +{ + typedef sys::Mutex::ScopedLock Lock; + typedef sys::Mutex::ScopedUnlock Unlock; + + qpid::client::Dispatcher dispatcher; + qpid::client::AsyncSession session; + bool autoStop; + SubscriptionSettings defaultSettings; + + public: + /** Create a new SubscriptionManager associated with a session */ + SubscriptionManager(const Session& session); + + /** + * Subscribe a MessagesListener to receive messages from queue. + * + * Provide your own subclass of MessagesListener to process + * incoming messages. It will be called for each message received. + * + *@param listener Listener object to receive messages. + *@param queue Name of the queue to subscribe to. + *@param settings settings for the subscription. + *@param name unique destination name for the subscription, defaults to queue name. + */ + Subscription subscribe(MessageListener& listener, + const std::string& queue, + const SubscriptionSettings& settings, + const std::string& name=std::string()); + + /** + * Subscribe a LocalQueue to receive messages from queue. + * + * Incoming messages are stored in the queue for you to retrieve. + * + *@param queue Name of the queue to subscribe to. + *@param flow initial FlowControl for the subscription. + *@param name unique destination name for the subscription, defaults to queue name. + * If not specified, the queue name is used. + */ + Subscription subscribe(LocalQueue& localQueue, + const std::string& queue, + const SubscriptionSettings& settings, + const std::string& name=std::string()); + + /** + * Subscribe a MessagesListener to receive messages from queue. + * + * Provide your own subclass of MessagesListener to process + * incoming messages. It will be called for each message received. + * + *@param listener Listener object to receive messages. + *@param queue Name of the queue to subscribe to. + *@param name unique destination name for the subscription, defaults to queue name. + * If not specified, the queue name is used. + */ + Subscription subscribe(MessageListener& listener, + const std::string& queue, + const std::string& name=std::string()); + + /** + * Subscribe a LocalQueue to receive messages from queue. + * + * Incoming messages are stored in the queue for you to retrieve. + * + *@param queue Name of the queue to subscribe to. + *@param name unique destination name for the subscription, defaults to queue name. + * If not specified, the queue name is used. + */ + Subscription subscribe(LocalQueue& localQueue, + const std::string& queue, + const std::string& name=std::string()); + + + /** Get a single message from a queue. + *@param result is set to the message from the queue. + *@param timeout wait up this timeout for a message to appear. + *@return true if result was set, false if no message available after timeout. + */ + bool get(Message& result, const std::string& queue, sys::Duration timeout=0); + + /** Get a single message from a queue. + *@param timeout wait up this timeout for a message to appear. + *@return message from the queue. + *@throw Exception if the timeout is exceeded. + */ + Message get(const std::string& queue, sys::Duration timeout=sys::TIME_INFINITE); + + /** Get a subscription by name. + *@throw Exception if not found. + */ + Subscription getSubscription(const std::string& name) const; + + /** Cancel a subscription. See also: Subscription.cancel() */ + void cancel(const std::string& name); + + /** Deliver messages in the current thread until stop() is called. + * Only one thread may be running in a SubscriptionManager at a time. + * @see run + */ + void run(); + + /** Start a new thread to deliver messages. + * Only one thread may be running in a SubscriptionManager at a time. + * @see start + */ + void start(); + + /** + * Wait for the thread started by a call to start() to complete. + */ + void wait(); + + /** If set true, run() will stop when all subscriptions + * are cancelled. If false, run will only stop when stop() + * is called. True by default. + */ + void setAutoStop(bool set=true); + + /** Stop delivery. Causes run() to return, or the thread started with start() to exit. */ + void stop(); + + static const uint32_t UNLIMITED=0xFFFFFFFF; + + /** Set the flow control for a subscription. */ + void setFlowControl(const std::string& name, const FlowControl& flow) { + getSubscription(name).setFlowControl(flow); + } + + /** Set the flow control for a subscription. + *@param name: name of the subscription. + *@param messages: message credit. + *@param bytes: byte credit. + *@param window: if true use window-based flow control. + */ + void setFlowControl(const std::string& name, uint32_t messages, uint32_t bytes, bool window=true) { + setFlowControl(name, messages, bytes, window); + } + + /** Set the default settings for subscribe() calls that don't + * include a SubscriptionSettings parameter. + */ + void setDefaultSettings(const SubscriptionSettings& s) { defaultSettings = s; } + + /** Get the default settings for subscribe() calls that don't + * include a SubscriptionSettings parameter. + */ + const SubscriptionSettings& getDefaultSettings() const { return defaultSettings; } + + /** Get the default settings for subscribe() calls that don't + * include a SubscriptionSettings parameter. + */ + SubscriptionSettings& getDefaultSettings() { return defaultSettings; } + + /** + * Set the default flow control settings for subscribe() calls + * that don't include a SubscriptionSettings parameter. + * + *@param messages: message credit. + *@param bytes: byte credit. + *@param window: if true use window-based flow control. + */ + void setFlowControl(uint32_t messages, uint32_t bytes, bool window=true) { + defaultSettings.flowControl = FlowControl(messages, bytes, window); + } + + /** + *Set the default accept-mode for subscribe() calls that don't + *include a SubscriptionSettings parameter. + */ + void setAcceptMode(AcceptMode mode) { defaultSettings.acceptMode = mode; } + + /** + * Set the default acquire-mode subscribe()s that don't specify SubscriptionSettings. + */ + void setAcquireMode(AcquireMode mode) { defaultSettings.acquireMode = mode; } + + void registerFailoverHandler ( boost::function<void ()> fh ); + + Session getSession() const; + + private: + std::map<std::string, Subscription> subscriptions; +}; + +/** AutoCancel cancels a subscription in its destructor */ +class AutoCancel { + public: + AutoCancel(SubscriptionManager& sm_, const std::string& tag_) : sm(sm_), tag(tag_) {} + ~AutoCancel() { sm.cancel(tag); } + private: + SubscriptionManager& sm; + std::string tag; +}; + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_SUBSCRIPTIONMANAGER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/SubscriptionSettings.h b/RC9/qpid/cpp/src/qpid/client/SubscriptionSettings.h new file mode 100644 index 0000000000..ade539b376 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/SubscriptionSettings.h @@ -0,0 +1,92 @@ +#ifndef QPID_CLIENT_SUBSCRIPTIONSETTINGS_H +#define QPID_CLIENT_SUBSCRIPTIONSETTINGS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/FlowControl.h" +#include "qpid/framing/enum.h" + +namespace qpid { +namespace client { + +/** Bring AMQP enum definitions for message class into this namespace. */ +using namespace qpid::framing::message; + +enum CompletionMode { + MANUAL_COMPLETION = 0, + COMPLETE_ON_DELIVERY = 1, + COMPLETE_ON_ACCEPT = 2 +}; +/** + * Settings for a subscription. + */ +struct SubscriptionSettings +{ + SubscriptionSettings( + FlowControl flow=FlowControl::unlimited(), + AcceptMode accept=ACCEPT_MODE_EXPLICIT, + AcquireMode acquire=ACQUIRE_MODE_PRE_ACQUIRED, + unsigned int autoAck_=1, + CompletionMode completion=COMPLETE_ON_DELIVERY + ) : flowControl(flow), acceptMode(accept), acquireMode(acquire), autoAck(autoAck_), completionMode(completion), exclusive(false) {} + + FlowControl flowControl; ///@< Flow control settings. @see FlowControl + AcceptMode acceptMode; ///@< ACCEPT_MODE_EXPLICIT or ACCEPT_MODE_NONE + AcquireMode acquireMode; ///@< ACQUIRE_MODE_PRE_ACQUIRED or ACQUIRE_MODE_NOT_ACQUIRED + + /** Automatically acknowledge (accept) batches of autoAck + * messages. 0 means no automatic acknowledgement. This has no + * effect for messsages received for a subscription with + * ACCEPT_MODE_NODE.*/ + unsigned int autoAck; + /** + * In windowing mode, completion of a message will cause the + * credit used up by that message to be reallocated. The + * subscriptions completion mode controls how completion is + * managed. + * + * If set to COMPLETE_ON_DELIVERY (which is the default), messages + * will be marked as completed once they have been received. The + * server will be explicitly notified of all completed messages + * for the session when the next accept is sent through the + * subscription (either explictly or through autAck). However the + * server may also periodically request information on the + * completed messages. + * + * If set to COMPLETE_ON_ACCEPT, messages will be marked as + * completed once they are accepted (via the Subscription class) + * and the server will also be notified of all completed messages + * for the session. + * + * If set to MANUAL_COMPLETION the application is responsible for + * completing messages (@see Session::markCompleted()). + */ + CompletionMode completionMode; + /** + * If set, requests that no other subscriber be allowed to access + * the queue while this subscription is active. + */ + bool exclusive; +}; + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_SUBSCRIPTIONSETTINGS_H*/ diff --git a/RC9/qpid/cpp/src/qpid/client/TypedResult.h b/RC9/qpid/cpp/src/qpid/client/TypedResult.h new file mode 100644 index 0000000000..5306997d74 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/client/TypedResult.h @@ -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. + * + */ + +#ifndef _TypedResult_ +#define _TypedResult_ + +#include "Completion.h" + +namespace qpid { +namespace client { + +/** + * Returned by asynchronous commands that return a result. + * You can use get() to wait for completion and get the result value. + * \ingroup clientapi + */ +template <class T> class TypedResult : public Completion +{ + T result; + bool decoded; + +public: + ///@internal + TypedResult(Future f, shared_ptr<SessionImpl> s) : Completion(f, s), decoded(false) {} + + /** + * Wait for the asynchronous command that returned this TypedResult to complete + * and return its result. + * + *@return The result returned by the command. + *@exception If the command returns an error, get() throws an exception. + * + */ + T& get() + { + if (!decoded) { + future.decodeResult(result, *session); + decoded = true; + } + + return result; + } +}; + +}} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/cluster/Cluster.cpp b/RC9/qpid/cpp/src/qpid/cluster/Cluster.cpp new file mode 100644 index 0000000000..dd9de68bf5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/Cluster.cpp @@ -0,0 +1,548 @@ +/* + * + * 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 "Cluster.h" +#include "Connection.h" +#include "DumpClient.h" +#include "FailoverExchange.h" + +#include "qpid/broker/Broker.h" +#include "qpid/broker/SessionState.h" +#include "qpid/broker/Connection.h" +#include "qpid/broker/QueueRegistry.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/AMQP_AllOperations.h" +#include "qpid/framing/AllInvoker.h" +#include "qpid/framing/ClusterDumpRequestBody.h" +#include "qpid/framing/ClusterReadyBody.h" +#include "qpid/framing/ClusterConfigChangeBody.h" +#include "qpid/framing/ClusterDumpOfferBody.h" +#include "qpid/framing/ClusterShutdownBody.h" +#include "qpid/framing/ClusterConnectionDeliverCloseBody.h" +#include "qpid/framing/ClusterConnectionDeliverDoOutputBody.h" +#include "qpid/log/Statement.h" +#include "qpid/log/Helpers.h" +#include "qpid/sys/Thread.h" +#include "qpid/memory.h" +#include "qpid/shared_ptr.h" +#include "qmf/org/apache/qpid/cluster/Package.h" + +#include <boost/bind.hpp> +#include <boost/cast.hpp> +#include <boost/current_function.hpp> +#include <algorithm> +#include <iterator> +#include <map> +#include <ostream> + +namespace qpid { +namespace cluster { +using namespace qpid::framing; +using namespace qpid::sys; +using namespace std; +using namespace qpid::cluster; +using qpid::management::ManagementAgent; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; +namespace qmf = qmf::org::apache::qpid::cluster; + +/**@file + Threading notes: + - Public functions may be called in local connection IO threads. + see .h. +*/ + +struct ClusterDispatcher : public framing::AMQP_AllOperations::ClusterHandler { + qpid::cluster::Cluster& cluster; + MemberId member; + Cluster::Lock& l; + ClusterDispatcher(Cluster& c, const MemberId& id, Cluster::Lock& l_) : cluster(c), member(id), l(l_) {} + + void dumpRequest(const std::string& url) { cluster.dumpRequest(member, url, l); } + void ready(const std::string& url) { cluster.ready(member, url, l); } + void configChange(const std::string& addresses) { cluster.configChange(member, addresses, l); } + void dumpOffer(uint64_t dumpee, const Uuid& id) { cluster.dumpOffer(member, dumpee, id, l); } + void shutdown() { cluster.shutdown(member, l); } + + bool invoke(AMQBody& body) { return framing::invoke(*this, body).wasHandled(); } +}; + +Cluster::Cluster(const std::string& name_, const Url& url_, broker::Broker& b, bool quorum_, size_t readMax_, size_t writeEstimate_) : + broker(b), + poller(b.getPoller()), + cpg(*this), + name(name_), + myUrl(url_), + myId(cpg.self()), + readMax(readMax_), + writeEstimate(writeEstimate_), + cpgDispatchHandle( + cpg, + boost::bind(&Cluster::dispatch, this, _1), // read + 0, // write + boost::bind(&Cluster::disconnect, this, _1) // disconnect + ), + mcast(cpg, poller), + mgmtObject(0), + deliverQueue(boost::bind(&Cluster::delivered, this, _1), poller), + state(INIT), + lastSize(0), + lastBroker(false) +{ + ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + if (agent != 0){ + qmf::Package packageInit(agent); + mgmtObject = new qmf::Cluster (agent, this, &broker,name,myUrl.str()); + agent->addObject (mgmtObject); + mgmtObject->set_status("JOINING"); + } + broker.getKnownBrokers = boost::bind(&Cluster::getUrls, this); + failoverExchange.reset(new FailoverExchange(this)); + cpgDispatchHandle.startWatch(poller); + deliverQueue.start(); + QPID_LOG(notice, *this << " joining cluster " << name << " with url=" << myUrl); + if (quorum_) quorum.init(); + cpg.join(name); + broker.addFinalizer(boost::bind(&Cluster::brokerShutdown, this)); // Must be last for exception safety. +} + +Cluster::~Cluster() { + if (dumpThread.id()) dumpThread.join(); // Join the previous dumpthread. +} + +void Cluster::insert(const boost::intrusive_ptr<Connection>& c) { + connections.insert(c->getId(), c); +} + +void Cluster::erase(ConnectionId id) { + connections.erase(id); +} + +std::vector<Url> Cluster::getUrls() const { + Lock l(lock); + return getUrls(l); +} + +std::vector<Url> Cluster::getUrls(Lock&) const { + return map.memberUrls(); +} + +void Cluster::leave() { + Lock l(lock); + leave(l); +} + +void Cluster::leave(Lock&) { + if (state != LEFT) { + state = LEFT; + QPID_LOG(notice, *this << " leaving cluster " << name); + if (mgmtObject!=0) mgmtObject->set_status("SHUTDOWN"); + if (!deliverQueue.isStopped()) deliverQueue.stop(); + try { cpg.leave(); } + catch (const std::exception& e) { + QPID_LOG(critical, *this << " error leaving process group: " << e.what()); + } + try { broker.shutdown(); } + catch (const std::exception& e) { + QPID_LOG(critical, *this << " error during shutdown: " << e.what()); + } + } +} + +boost::intrusive_ptr<Connection> Cluster::getConnection(const ConnectionId& connectionId) { + boost::intrusive_ptr<Connection> cp = connections.find(connectionId); + if (!cp && connectionId.getMember() != myId) { // New shadow connection + std::ostringstream mgmtId; + mgmtId << name << ":" << connectionId; + cp = new Connection(*this, shadowOut, mgmtId.str(), connectionId); + connections.insert(connectionId, cp); + } + return cp; +} + +void Cluster::deliver( + cpg_handle_t /*handle*/, + cpg_name* /*group*/, + uint32_t nodeid, + uint32_t pid, + void* msg, + int msg_len) +{ + Mutex::ScopedLock l(lock); + MemberId from(nodeid, pid); + framing::Buffer buf(static_cast<char*>(msg), msg_len); + deliver(Event::decode(from, buf), l); +} + +void Cluster::deliver(const Event& e, Lock&) { + if (state == LEFT) return; + QPID_LOG(trace, *this << " PUSH: " << e); + deliverQueue.push(e); // Otherwise enqueue for processing. +} + +// Entry point: called when deliverQueue has events to process. +void Cluster::delivered(PollableEventQueue::Queue& events) { + try { + for_each(events.begin(), events.end(), boost::bind(&Cluster::deliveredEvent, this, _1)); + events.clear(); + } catch (const std::exception& e) { + QPID_LOG(critical, *this << " error in cluster delivery: " << e.what()); + leave(); + } +} + +void Cluster::deliveredEvent(const Event& e) { + Buffer buf(e); + AMQFrame frame; + if (e.isCluster()) { + while (frame.decode(buf)) { + QPID_LOG(trace, *this << " DLVR: " << e << " " << frame); + Mutex::ScopedLock l(lock); // FIXME aconway 2008-12-11: lock scope is too big. + ClusterDispatcher dispatch(*this, e.getMemberId(), l); + if (!framing::invoke(dispatch, *frame.getBody()).wasHandled()) + throw Exception(QPID_MSG("Invalid cluster control")); + } + } + else { // e.isConnection() + if (state == NEWBIE) { + QPID_LOG(trace, *this << " DROP: " << e); + } + else { + boost::intrusive_ptr<Connection> connection = getConnection(e.getConnectionId()); + if (!connection) return; + if (e.getType() == CONTROL) { + while (frame.decode(buf)) { + QPID_LOG(trace, *this << " DLVR: " << e << " " << frame); + connection->delivered(frame); + } + } + else { + QPID_LOG(trace, *this << " DLVR: " << e); + connection->deliverBuffer(buf); + } + } + } +} + +struct AddrList { + const cpg_address* addrs; + int count; + const char *prefix, *suffix; + AddrList(const cpg_address* a, int n, const char* p="", const char* s="") + : addrs(a), count(n), prefix(p), suffix(s) {} +}; + +ostream& operator<<(ostream& o, const AddrList& a) { + if (!a.count) return o; + o << a.prefix; + for (const cpg_address* p = a.addrs; p < a.addrs+a.count; ++p) { + const char* reasonString; + switch (p->reason) { + case CPG_REASON_JOIN: reasonString = " (joined) "; break; + case CPG_REASON_LEAVE: reasonString = " (left) "; break; + case CPG_REASON_NODEDOWN: reasonString = " (node-down) "; break; + case CPG_REASON_NODEUP: reasonString = " (node-up) "; break; + case CPG_REASON_PROCDOWN: reasonString = " (process-down) "; break; + default: reasonString = " "; + } + qpid::cluster::MemberId member(*p); + o << member << reasonString; + } + return o << a.suffix; +} + +// Entry point: called by IO to dispatch CPG events. +void Cluster::dispatch(sys::DispatchHandle& h) { + try { + cpg.dispatchAll(); + h.rewatch(); + } catch (const std::exception& e) { + QPID_LOG(critical, *this << " error in cluster dispatch: " << e.what()); + leave(); + } +} + +// Entry point: called if disconnected from CPG. +void Cluster::disconnect(sys::DispatchHandle& ) { + QPID_LOG(critical, *this << " error disconnected from cluster"); + try { + broker.shutdown(); + } catch (const std::exception& e) { + QPID_LOG(error, *this << " error in shutdown: " << e.what()); + } +} + +void Cluster::configChange ( + cpg_handle_t /*handle*/, + cpg_name */*group*/, + cpg_address *current, int nCurrent, + cpg_address *left, int nLeft, + cpg_address */*joined*/, int /*nJoined*/) +{ + Mutex::ScopedLock l(lock); + QPID_LOG(debug, *this << " config change: " << AddrList(current, nCurrent) + << AddrList(left, nLeft, "( ", ")")); + std::string addresses; + for (cpg_address* p = current; p < current+nCurrent; ++p) + addresses.append(MemberId(*p).str()); + deliver(Event::control(ClusterConfigChangeBody(ProtocolVersion(), addresses), myId), l); +} + +void Cluster::configChange(const MemberId&, const std::string& addresses, Lock& l) { + bool memberChange = map.configChange(addresses); + if (state == LEFT) return; + + if (!map.isAlive(myId)) { // Final config change. + leave(l); + return; + } + + if (state == INIT) { // First configChange + if (map.aliveCount() == 1) { + setClusterId(true); + // FIXME aconway 2008-12-11: Centralize transition to READY and associated actions eg mcast.release() + state = READY; + mcast.release(); + QPID_LOG(notice, *this << " first in cluster"); + if (mgmtObject!=0) mgmtObject->set_status("ACTIVE"); + map = ClusterMap(myId, myUrl, true); + memberUpdate(l); + } + else { // Joining established group. + state = NEWBIE; + QPID_LOG(info, *this << " joining cluster: " << map); + mcast.mcastControl(ClusterDumpRequestBody(ProtocolVersion(), myUrl.str()), myId); + } + } + else if (state >= READY && memberChange) + memberUpdate(l); +} + + + + +void Cluster::tryMakeOffer(const MemberId& id, Lock& ) { + if (state == READY && map.isNewbie(id)) { + state = OFFER; + QPID_LOG(info, *this << " send dump-offer to " << id); + mcast.mcastControl(ClusterDumpOfferBody(ProtocolVersion(), id, clusterId), myId); + } +} + +// Called from Broker::~Broker when broker is shut down. At this +// point we know the poller has stopped so no poller callbacks will be +// invoked. We must ensure that CPG has also shut down so no CPG +// callbacks will be invoked. +// +void Cluster::brokerShutdown() { + QPID_LOG(notice, *this << " shutting down "); + if (state != LEFT) { + try { cpg.shutdown(); } + catch (const std::exception& e) { + QPID_LOG(error, *this << " during shutdown: " << e.what()); + } + } + delete this; +} + +void Cluster::dumpRequest(const MemberId& id, const std::string& url, Lock& l) { + map.dumpRequest(id, url); + tryMakeOffer(id, l); +} + +void Cluster::ready(const MemberId& id, const std::string& url, Lock& l) { + if (map.ready(id, Url(url))) + memberUpdate(l); + if (state == CATCHUP && id == myId) { + state = READY; + mcast.release(); + QPID_LOG(notice, *this << " caught up, active cluster member"); + if (mgmtObject!=0) mgmtObject->set_status("ACTIVE"); + mcast.release(); + } +} + +void Cluster::dumpOffer(const MemberId& dumper, uint64_t dumpeeInt, const Uuid& uuid, Lock& l) { + if (state == LEFT) return; + MemberId dumpee(dumpeeInt); + boost::optional<Url> url = map.dumpOffer(dumper, dumpee); + if (dumper == myId) { + assert(state == OFFER); + if (url) { // My offer was first. + dumpStart(dumpee, *url, l); + } + else { // Another offer was first. + state = READY; + mcast.release(); + QPID_LOG(info, *this << " cancelled dump offer to " << dumpee); + tryMakeOffer(map.firstNewbie(), l); // Maybe make another offer. + } + } + else if (dumpee == myId && url) { + assert(state == NEWBIE); + setClusterId(uuid); + state = DUMPEE; + QPID_LOG(info, *this << " receiving dump from " << dumper); + deliverQueue.stop(); + checkDumpIn(l); + } +} + +// FIXME aconway 2008-10-15: no longer need a separate control now +// that the dump control is in the deliver queue. +void Cluster::dumpStart(const MemberId& dumpee, const Url& url, Lock&) { + if (state == LEFT) return; + assert(state == OFFER); + state = DUMPER; + QPID_LOG(info, *this << " stall for dump to " << dumpee << " at " << url); + deliverQueue.stop(); + if (dumpThread.id()) dumpThread.join(); // Join the previous dumpthread. + dumpThread = Thread( + new DumpClient(myId, dumpee, url, broker, map, connections.values(), + boost::bind(&Cluster::dumpOutDone, this), + boost::bind(&Cluster::dumpOutError, this, _1))); +} + +// Called in dump thread. +void Cluster::dumpInDone(const ClusterMap& m) { + Lock l(lock); + dumpedMap = m; + checkDumpIn(l); +} + +void Cluster::checkDumpIn(Lock& ) { + if (state == LEFT) return; + if (state == DUMPEE && dumpedMap) { + map = *dumpedMap; + mcast.mcastControl(ClusterReadyBody(ProtocolVersion(), myUrl.str()), myId); + state = CATCHUP; + QPID_LOG(info, *this << " received dump, starting catch-up"); + deliverQueue.start(); + } +} + +void Cluster::dumpOutDone() { + Monitor::ScopedLock l(lock); + dumpOutDone(l); +} + +void Cluster::dumpOutDone(Lock& l) { + assert(state == DUMPER); + state = READY; + mcast.release(); + QPID_LOG(info, *this << " sent dump"); + deliverQueue.start(); + tryMakeOffer(map.firstNewbie(), l); // Try another offer +} + +void Cluster::dumpOutError(const std::exception& e) { + Monitor::ScopedLock l(lock); + QPID_LOG(error, *this << " error sending dump: " << e.what()); + dumpOutDone(l); +} + +void Cluster ::shutdown(const MemberId& id, Lock& l) { + QPID_LOG(notice, *this << " received shutdown from " << id); + leave(l); +} + +ManagementObject* Cluster::GetManagementObject() const { return mgmtObject; } + +Manageable::status_t Cluster::ManagementMethod (uint32_t methodId, Args&, string&) { + Lock l(lock); + QPID_LOG(debug, *this << " managementMethod [id=" << methodId << "]"); + switch (methodId) { + case qmf::Cluster::METHOD_STOPCLUSTERNODE: stopClusterNode(l); break; + case qmf::Cluster::METHOD_STOPFULLCLUSTER: stopFullCluster(l); break; + default: return Manageable::STATUS_UNKNOWN_METHOD; + } + return Manageable::STATUS_OK; +} + +void Cluster::stopClusterNode(Lock& l) { + QPID_LOG(notice, *this << " stopped by admin"); + leave(l); +} + +void Cluster::stopFullCluster(Lock& ) { + QPID_LOG(notice, *this << " shutting down cluster " << name); + mcast.mcastControl(ClusterShutdownBody(), myId); +} + +void Cluster::memberUpdate(Lock& l) { + QPID_LOG(info, *this << " member update: " << map); + std::vector<Url> urls = getUrls(l); + size_t size = urls.size(); + failoverExchange->setUrls(urls); + + if (size == 1 && lastSize > 1 && state >= READY) { + QPID_LOG(info, *this << " last broker standing, update queue policies"); + lastBroker = true; + broker.getQueues().updateQueueClusterState(true); + } + else if (size > 1 && lastBroker) { + QPID_LOG(info, *this << " last broker standing joined by " << size-1 << " replicas, updating queue policies" << size); + lastBroker = false; + broker.getQueues().updateQueueClusterState(false); + } + lastSize = size; + + if (mgmtObject) { + mgmtObject->set_clusterSize(size); + string urlstr; + for(std::vector<Url>::iterator iter = urls.begin(); iter != urls.end(); iter++ ) { + if (iter != urls.begin()) urlstr += "\n"; + urlstr += iter->str(); + } + mgmtObject->set_members(urlstr); + } + + // Close connections belonging to members that have now been excluded + connections.update(myId, map); +} + +std::ostream& operator<<(std::ostream& o, const Cluster& cluster) { + static const char* STATE[] = { "INIT", "NEWBIE", "DUMPEE", "CATCHUP", "READY", "OFFER", "DUMPER", "LEFT" }; + return o << cluster.myId << "(" << STATE[cluster.state] << ")"; +} + +MemberId Cluster::getId() const { + return myId; // Immutable, no need to lock. +} + +broker::Broker& Cluster::getBroker() const { + return broker; // Immutable, no need to lock. +} + +void Cluster::checkQuorum() { + if (!quorum.isQuorate()) { + QPID_LOG(critical, *this << " disconnected from cluster quorum, shutting down"); + leave(); + throw Exception(QPID_MSG(*this << " disconnected from cluster quorum.")); + } +} + +void Cluster::setClusterId(const Uuid& uuid) { + clusterId = uuid; + if (mgmtObject) + mgmtObject->set_clusterID(clusterId.str()); + QPID_LOG(debug, *this << " cluster-id = " << clusterId); +} + +}} // namespace qpid::cluster diff --git a/RC9/qpid/cpp/src/qpid/cluster/Cluster.h b/RC9/qpid/cpp/src/qpid/cluster/Cluster.h new file mode 100644 index 0000000000..b8fe61bf15 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/Cluster.h @@ -0,0 +1,230 @@ +#ifndef QPID_CLUSTER_CLUSTER_H +#define QPID_CLUSTER_CLUSTER_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "Cpg.h" +#include "Event.h" +#include "NoOpConnectionOutputHandler.h" +#include "ClusterMap.h" +#include "ConnectionMap.h" +#include "FailoverExchange.h" +#include "Quorum.h" +#include "Multicaster.h" + +#include "qpid/broker/Broker.h" +#include "qpid/sys/PollableQueue.h" +#include "qpid/sys/Monitor.h" +#include "qpid/sys/LockPtr.h" +#include "qpid/management/Manageable.h" +#include "qpid/Url.h" +#include "qmf/org/apache/qpid/cluster/Cluster.h" + +#include <boost/intrusive_ptr.hpp> +#include <boost/bind.hpp> +#include <boost/optional.hpp> + +#include <algorithm> +#include <vector> +#include <map> + +namespace qpid { + +namespace framing { +class AMQBody; +class Uuid; +} + +namespace cluster { + +class Connection; + +/** + * Connection to the cluster + * + * Threading notes: 3 thread categories: connection, deliver, dump. + * + */ +class Cluster : private Cpg::Handler, public management::Manageable { + public: + typedef boost::intrusive_ptr<Connection> ConnectionPtr; + typedef std::vector<ConnectionPtr> Connections; + + /** + * Join a cluster. + */ + Cluster(const std::string& name, const Url& url, broker::Broker&, bool useQuorum, + size_t readMax, size_t writeEstimate); + + virtual ~Cluster(); + + // Connection map - called in connection threads. + void insert(const ConnectionPtr&); + void erase(ConnectionId); + + // URLs of current cluster members - called in connection threads. + std::vector<Url> getUrls() const; + boost::shared_ptr<FailoverExchange> getFailoverExchange() const { return failoverExchange; } + + // Leave the cluster - called in any thread. + void leave(); + + // Dump completed - called in dump thread + void dumpInDone(const ClusterMap&); + + MemberId getId() const; + broker::Broker& getBroker() const; + Multicaster& getMulticast() { return mcast; } + + boost::function<bool ()> isQuorate; + void checkQuorum(); // called in connection threads. + + size_t getReadMax() { return readMax; } + size_t getWriteEstimate() { return writeEstimate; } + + private: + typedef sys::LockPtr<Cluster,sys::Monitor> LockPtr; + typedef sys::LockPtr<const Cluster,sys::Monitor> ConstLockPtr; + typedef sys::Monitor::ScopedLock Lock; + + typedef sys::PollableQueue<Event> PollableEventQueue; + typedef std::deque<Event> PlainEventQueue; + + // NB: The final Lock& parameter on functions below is used to mark functions + // that should only be called by a function that already holds the lock. + // The parameter makes it hard to forget since you have to have an instance of + // a Lock to call the unlocked functions. + + void leave(Lock&); + std::vector<Url> getUrls(Lock&) const; + + // Make an offer if we can - called in deliver thread. + void tryMakeOffer(const MemberId&, Lock&); + + // Called in main thread in ~Broker. + void brokerShutdown(); + + // Cluster controls implement XML methods from cluster.xml. + // Called in deliver thread. + // + void dumpRequest(const MemberId&, const std::string&, Lock&); + void dumpOffer(const MemberId& dumper, uint64_t dumpee, const framing::Uuid&, Lock&); + void ready(const MemberId&, const std::string&, Lock&); + void configChange(const MemberId&, const std::string& addresses, Lock& l); + void shutdown(const MemberId&, Lock&); + void delivered(PollableEventQueue::Queue&); // deliverQueue callback + void deliveredEvent(const Event&); + + // Helper, called in deliver thread. + void dumpStart(const MemberId& dumpee, const Url& url, Lock&); + + // CPG callbacks, called in CPG IO thread. + void dispatch(sys::DispatchHandle&); // Dispatch CPG events. + void disconnect(sys::DispatchHandle&); // PG was disconnected + + void deliver( // CPG deliver callback. + cpg_handle_t /*handle*/, + struct cpg_name *group, + uint32_t /*nodeid*/, + uint32_t /*pid*/, + void* /*msg*/, + int /*msg_len*/); + + void deliver(const Event& e, Lock&); + + void configChange( // CPG config change callback. + cpg_handle_t /*handle*/, + struct cpg_name */*group*/, + struct cpg_address */*members*/, int /*nMembers*/, + struct cpg_address */*left*/, int /*nLeft*/, + struct cpg_address */*joined*/, int /*nJoined*/ + ); + + boost::intrusive_ptr<cluster::Connection> getConnection(const ConnectionId&); + + virtual qpid::management::ManagementObject* GetManagementObject() const; + virtual management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); + + void stopClusterNode(Lock&); + void stopFullCluster(Lock&); + void memberUpdate(Lock&); + + // Called in connection IO threads . + void checkDumpIn(Lock&); + + // Called in DumpClient thread. + void dumpOutDone(); + void dumpOutError(const std::exception&); + void dumpOutDone(Lock&); + + void setClusterId(const framing::Uuid&); + + // Immutable members set on construction, never changed. + broker::Broker& broker; + boost::shared_ptr<sys::Poller> poller; + Cpg cpg; + const std::string name; + const Url myUrl; + const MemberId myId; + const size_t readMax; + const size_t writeEstimate; + framing::Uuid clusterId; + NoOpConnectionOutputHandler shadowOut; + sys::DispatchHandle cpgDispatchHandle; + + + // Thread safe members + Multicaster mcast; + qmf::org::apache::qpid::cluster::Cluster* mgmtObject; // mgnt owns lifecycle + PollableEventQueue deliverQueue; + ConnectionMap connections; + boost::shared_ptr<FailoverExchange> failoverExchange; + Quorum quorum; + + // Remaining members are protected by lock. + mutable sys::Monitor lock; + + // Local cluster state, cluster map + enum { + INIT, ///< Initial state, no CPG messages received. + NEWBIE, ///< Sent dump request, waiting for dump offer. + DUMPEE, ///< Stalled receive queue at dump offer, waiting for dump to complete. + CATCHUP, ///< Dump complete, unstalled but has not yet seen own "ready" event. + READY, ///< Fully operational + OFFER, ///< Sent an offer, waiting for accept/reject. + DUMPER, ///< Offer accepted, sending a state dump. + LEFT ///< Final state, left the cluster. + } state; + ClusterMap map; + size_t lastSize; + bool lastBroker; + + // Dump related + sys::Thread dumpThread; + boost::optional<ClusterMap> dumpedMap; + + friend std::ostream& operator<<(std::ostream&, const Cluster&); + friend class ClusterDispatcher; +}; + +}} // namespace qpid::cluster + + + +#endif /*!QPID_CLUSTER_CLUSTER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/ClusterLeaveException.h b/RC9/qpid/cpp/src/qpid/cluster/ClusterLeaveException.h new file mode 100644 index 0000000000..e5bdbc560a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/ClusterLeaveException.h @@ -0,0 +1,35 @@ +#ifndef QPID_CLUSTER_CLUSTERLEAVEEXCEPTION_H +#define QPID_CLUSTER_CLUSTERLEAVEEXCEPTION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/Exception.h" + +namespace qpid { +namespace cluster { + +struct ClusterLeaveException : public Exception +{ + ClusterLeaveException(const std::string& message=std::string()) : Exception(message) {} +}; +}} // namespace qpid::cluster + +#endif /*!QPID_CLUSTER_CLUSTERLEAVEEXCEPTION_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/ClusterMap.cpp b/RC9/qpid/cpp/src/qpid/cluster/ClusterMap.cpp new file mode 100644 index 0000000000..873f0be928 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/ClusterMap.cpp @@ -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. + * + */ +#include "ClusterMap.h" +#include "qpid/Url.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/log/Statement.h" +#include <boost/bind.hpp> +#include <algorithm> +#include <functional> +#include <iterator> +#include <ostream> + +namespace qpid { +using namespace framing; + +namespace cluster { + +namespace { + +void addFieldTableValue(FieldTable::ValueMap::value_type vt, ClusterMap::Map& map, ClusterMap::Set& set) { + MemberId id(vt.first); + set.insert(id); + std::string url = vt.second->get<std::string>(); + if (!url.empty()) + map.insert(ClusterMap::Map::value_type(id, Url(url))); +} + +void insertFieldTableFromMapValue(FieldTable& ft, const ClusterMap::Map::value_type& vt) { + ft.setString(vt.first.str(), vt.second.str()); +} + +void assignFieldTable(FieldTable& ft, const ClusterMap::Map& map) { + ft.clear(); + std::for_each(map.begin(), map.end(), boost::bind(&insertFieldTableFromMapValue, boost::ref(ft), _1)); +} + +} + +ClusterMap::ClusterMap() {} + +ClusterMap::ClusterMap(const MemberId& id, const Url& url , bool isMember) { + alive.insert(id); + if (isMember) + members[id] = url; + else + newbies[id] = url; +} + +ClusterMap::ClusterMap(const FieldTable& newbiesFt, const FieldTable& membersFt) { + std::for_each(newbiesFt.begin(), newbiesFt.end(), boost::bind(&addFieldTableValue, _1, boost::ref(newbies), boost::ref(alive))); + std::for_each(membersFt.begin(), membersFt.end(), boost::bind(&addFieldTableValue, _1, boost::ref(members), boost::ref(alive))); +} + +ClusterConnectionMembershipBody ClusterMap::asMethodBody() const { + framing::ClusterConnectionMembershipBody b; + b.getNewbies().clear(); + std::for_each(newbies.begin(), newbies.end(), boost::bind(&insertFieldTableFromMapValue, boost::ref(b.getNewbies()), _1)); + for(Set::const_iterator i = alive.begin(); i != alive.end(); ++i) { + if (!isMember(*i) && !isNewbie(*i)) + b.getNewbies().setString(i->str(), std::string()); + } + b.getMembers().clear(); + std::for_each(members.begin(), members.end(), boost::bind(&insertFieldTableFromMapValue, boost::ref(b.getMembers()), _1)); + return b; +} + +bool ClusterMap::configChange( + cpg_address *current, int nCurrent, + cpg_address *left, int nLeft, + cpg_address */*joined*/, int /*nJoined*/) +{ + cpg_address* a; + bool memberChange=false; + for (a = left; a != left+nLeft; ++a) { + memberChange = memberChange || members.erase(*a); + newbies.erase(*a); + } + alive.clear(); + std::copy(current, current+nCurrent, std::inserter(alive, alive.end())); + return memberChange; +} + +Url ClusterMap::getUrl(const Map& map, const MemberId& id) { + Map::const_iterator i = map.find(id); + return i == map.end() ? Url() : i->second; +} + +MemberId ClusterMap::firstNewbie() const { + return newbies.empty() ? MemberId() : newbies.begin()->first; +} + +std::vector<Url> ClusterMap::memberUrls() const { + std::vector<Url> urls(members.size()); + std::transform(members.begin(), members.end(), urls.begin(), + boost::bind(&Map::value_type::second, _1)); + return urls; +} + +std::ostream& operator<<(std::ostream& o, const ClusterMap::Map& m) { + std::ostream_iterator<MemberId> oi(o); + std::transform(m.begin(), m.end(), oi, boost::bind(&ClusterMap::Map::value_type::first, _1)); + return o; +} + +std::ostream& operator<<(std::ostream& o, const ClusterMap& m) { + for (ClusterMap::Set::const_iterator i = m.alive.begin(); i != m.alive.end(); ++i) { + o << *i; + if (m.isMember(*i)) o << "(member)"; + else if (m.isNewbie(*i)) o << "(newbie)"; + else o << "(unknown)"; + o << " "; + } + return o; +} + +bool ClusterMap::dumpRequest(const MemberId& id, const std::string& url) { + if (isAlive(id)) { + newbies[id] = Url(url); + return true; + } + return false; +} + +bool ClusterMap::ready(const MemberId& id, const Url& url) { + return isAlive(id) && members.insert(Map::value_type(id,url)).second; +} + +bool ClusterMap::configChange(const std::string& addresses) { + bool memberChange = false; + Set update; + for (std::string::const_iterator i = addresses.begin(); i < addresses.end(); i += 8) + update.insert(MemberId(std::string(i, i+8))); + Set removed; + std::set_difference(alive.begin(), alive.end(), + update.begin(), update.end(), + std::inserter(removed, removed.begin())); + alive = update; + for (Set::const_iterator i = removed.begin(); i != removed.end(); ++i) { + memberChange = memberChange || members.erase(*i); + newbies.erase(*i); + } + return memberChange; +} + +boost::optional<Url> ClusterMap::dumpOffer(const MemberId& from, const MemberId& to) { + Map::iterator i = newbies.find(to); + if (isAlive(from) && i != newbies.end()) { + Url url= i->second; + newbies.erase(i); // No longer a potential dumpee. + return url; + } + return boost::optional<Url>(); +} + +}} // namespace qpid::cluster diff --git a/RC9/qpid/cpp/src/qpid/cluster/ClusterMap.h b/RC9/qpid/cpp/src/qpid/cluster/ClusterMap.h new file mode 100644 index 0000000000..5c1981269f --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/ClusterMap.h @@ -0,0 +1,99 @@ +#ifndef QPID_CLUSTER_CLUSTERMAP_H +#define QPID_CLUSTER_CLUSTERMAP_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "types.h" +#include "qpid/Url.h" +#include "qpid/framing/ClusterConnectionMembershipBody.h" + +#include <boost/function.hpp> +#include <boost/optional.hpp> + +#include <vector> +#include <deque> +#include <map> +#include <set> +#include <iosfwd> + +namespace qpid { +namespace cluster { + +/** + * Map of established cluster members and newbies waiting for a brain dump. + */ +class ClusterMap { + public: + typedef std::map<MemberId, Url> Map; + typedef std::set<MemberId> Set; + + ClusterMap(); + ClusterMap(const MemberId& id, const Url& url, bool isReady); + ClusterMap(const framing::FieldTable& urls, const framing::FieldTable& states); + + /** Update from config change. + *@return true if member set changed. + */ + bool configChange( + cpg_address *current, int nCurrent, + cpg_address *left, int nLeft, + cpg_address *joined, int nJoined); + + bool configChange(const std::string& addresses); + + bool isNewbie(const MemberId& id) const { return newbies.find(id) != newbies.end(); } + bool isMember(const MemberId& id) const { return members.find(id) != members.end(); } + bool isAlive(const MemberId& id) const { return alive.find(id) != alive.end(); } + + Url getNewbieUrl(const MemberId& id) { return getUrl(newbies, id); } + Url getMemberUrl(const MemberId& id) { return getUrl(members, id); } + + /** First newbie in the cluster in ID order, target for offers */ + MemberId firstNewbie() const; + + /** Convert map contents to a cluster control body. */ + framing::ClusterConnectionMembershipBody asMethodBody() const; + + size_t aliveCount() const { return alive.size(); } + size_t memberCount() const { return members.size(); } + std::vector<Url> memberUrls() const; + + bool dumpRequest(const MemberId& id, const std::string& url); + /** Return non-empty Url if accepted */ + boost::optional<Url> dumpOffer(const MemberId& from, const MemberId& to); + + /**@return true If this is a new member */ + bool ready(const MemberId& id, const Url&); + + private: + Url getUrl(const Map& map, const MemberId& id); + + Map newbies, members; + Set alive; + + friend std::ostream& operator<<(std::ostream&, const Map&); + friend std::ostream& operator<<(std::ostream&, const ClusterMap&); +}; + +}} // namespace qpid::cluster + +#endif /*!QPID_CLUSTER_CLUSTERMAP_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/ClusterPlugin.cpp b/RC9/qpid/cpp/src/qpid/cluster/ClusterPlugin.cpp new file mode 100644 index 0000000000..0f4944d392 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/ClusterPlugin.cpp @@ -0,0 +1,106 @@ +/* + * + * 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 "Connection.h" +#include "ConnectionCodec.h" + +#include "qpid/cluster/Cluster.h" +#include "qpid/cluster/ConnectionCodec.h" + +#include "qpid/broker/Broker.h" +#include "qpid/Plugin.h" +#include "qpid/Options.h" +#include "qpid/shared_ptr.h" +#include "qpid/log/Statement.h" + +#include <boost/utility/in_place_factory.hpp> +#include <boost/scoped_ptr.hpp> + +namespace qpid { +namespace cluster { + +using namespace std; +using broker::Broker; + +struct ClusterValues { + string name; + string url; + bool quorum; + size_t readMax, writeEstimate; + + // FIXME aconway 2008-12-09: revisit default. + ClusterValues() : quorum(false), readMax(0), writeEstimate(64) {} + + Url getUrl(uint16_t port) const { + if (url.empty()) return Url::getIpAddressesUrl(port); + return Url(url); + } +}; + +/** Note separating options from values to work around boost version differences. + * Old boost takes a reference to options objects, but new boost makes a copy. + * New boost allows a shared_ptr but that's not compatible with old boost. + */ +struct ClusterOptions : public Options { + ClusterValues& values; + + ClusterOptions(ClusterValues& v) : Options("Cluster Options"), values(v) { + addOptions() + ("cluster-name", optValue(values.name, "NAME"), "Name of cluster to join") + ("cluster-url", optValue(values.url,"URL"), + "URL of this broker, advertized to the cluster.\n" + "Defaults to a URL listing all the local IP addresses\n") +#if HAVE_LIBCMAN + ("cluster-cman", optValue(values.quorum), "Integrate with Cluster Manager (CMAN) cluster.") +#endif + ("cluster-read-max", optValue(values.readMax,"N"), + "Throttle read rate from client connections.") + ("cluster-write-estimate", optValue(values.writeEstimate, "Kb"), + "Estimate connection write rate per multicast cycle") + ; + } +}; + +struct ClusterPlugin : public Plugin { + + ClusterValues values; + ClusterOptions options; + Cluster* cluster; + boost::scoped_ptr<ConnectionCodec::Factory> factory; + + ClusterPlugin() : options(values), cluster(0) {} + + Options* getOptions() { return &options; } + + void initialize(Plugin::Target& target) { + if (values.name.empty()) return; // Only if --cluster-name option was specified. + Broker* broker = dynamic_cast<Broker*>(&target); + if (!broker) return; + cluster = new Cluster(values.name, values.getUrl(broker->getPort(Broker::TCP_TRANSPORT)), *broker, values.quorum, values.readMax, values.writeEstimate*1024); + broker->setConnectionFactory( + boost::shared_ptr<sys::ConnectionCodec::Factory>( + new ConnectionCodec::Factory(broker->getConnectionFactory(), *cluster))); + broker->getExchanges().registerExchange(cluster->getFailoverExchange()); + } + + void earlyInitialize(Plugin::Target&) {} +}; + +static ClusterPlugin instance; // Static initialization. + +}} // namespace qpid::cluster diff --git a/RC9/qpid/cpp/src/qpid/cluster/Connection.cpp b/RC9/qpid/cpp/src/qpid/cluster/Connection.cpp new file mode 100644 index 0000000000..f0d38bf299 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/Connection.cpp @@ -0,0 +1,370 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES 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 "DumpClient.h" +#include "Cluster.h" + +#include "qpid/broker/SessionState.h" +#include "qpid/broker/SemanticState.h" +#include "qpid/broker/TxBuffer.h" +#include "qpid/broker/TxPublish.h" +#include "qpid/broker/TxAccept.h" +#include "qpid/broker/RecoveredEnqueue.h" +#include "qpid/broker/RecoveredDequeue.h" +#include "qpid/broker/Exchange.h" +#include "qpid/broker/Queue.h" +#include "qpid/framing/enum.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/AllInvoker.h" +#include "qpid/framing/DeliveryProperties.h" +#include "qpid/framing/ClusterConnectionDeliverCloseBody.h" +#include "qpid/framing/ConnectionCloseBody.h" +#include "qpid/framing/ConnectionCloseOkBody.h" +#include "qpid/log/Statement.h" + +#include <boost/current_function.hpp> + +// TODO aconway 2008-11-03: +// +// Disproportionate amount of code here is dedicated to receiving a +// brain-dump when joining a cluster and building initial +// state. Should be separated out into its own classes. +// + + +namespace qpid { +namespace cluster { + +using namespace framing; + +NoOpConnectionOutputHandler Connection::discardHandler; + +// Shadow connections +Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out, + const std::string& wrappedId, ConnectionId myId) + : cluster(c), self(myId), catchUp(false), output(*this, out), + connection(&output, cluster.getBroker(), wrappedId) +{ init(); } + +// Local connections +Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out, + const std::string& wrappedId, MemberId myId, bool isCatchUp) + : cluster(c), self(myId, this), catchUp(isCatchUp), output(*this, out), + connection(&output, cluster.getBroker(), wrappedId) +{ init(); } + +void Connection::init() { + QPID_LOG(debug, cluster << " new connection: " << *this); + if (isLocal() && !isCatchUp()) { + output.giveReadCredit(cluster.getReadMax()); + } +} + +Connection::~Connection() { + QPID_LOG(debug, cluster << " deleted connection: " << *this); +} + +bool Connection::doOutput() { + return output.doOutput(); +} + +// Delivery of doOutput allows us to run the real connection doOutput() +// which stocks up the write buffers with data. +// +void Connection::deliverDoOutput(uint32_t requested) { + assert(!catchUp); + output.deliverDoOutput(requested); +} + +// Received from a directly connected client. +void Connection::received(framing::AMQFrame& f) { + QPID_LOG(trace, cluster << " RECV " << *this << ": " << f); + if (isLocal()) { + currentChannel = f.getChannel(); + if (!framing::invoke(*this, *f.getBody()).wasHandled()) + connection.received(f); + } + else { // Shadow or dumped ex catch-up connection. + if (f.getMethod() && f.getMethod()->isA<ConnectionCloseBody>()) { + if (isShadow()) { + QPID_LOG(debug, cluster << " inserting connection " << *this); + cluster.insert(boost::intrusive_ptr<Connection>(this)); + } + AMQFrame ok(in_place<ConnectionCloseOkBody>()); + connection.getOutput().send(ok); + output.setOutputHandler(discardHandler); + catchUp = false; + } + else + QPID_LOG(warning, cluster << " ignoring unexpected frame " << *this << ": " << f); + } +} + +bool Connection::checkUnsupported(const AMQBody& body) { + std::string message; + if (body.getMethod()) { + switch (body.getMethod()->amqpClassId()) { + case DTX_CLASS_ID: message = "DTX transactions are not currently supported by cluster."; break; + } + } + else if (body.type() == HEADER_BODY) { + const DeliveryProperties* dp = static_cast<const AMQHeaderBody&>(body).get<DeliveryProperties>(); + if (dp && dp->getTtl()) message = "Message TTL is not currently supported by cluster."; + } + if (!message.empty()) + connection.close(connection::CLOSE_CODE_FRAMING_ERROR, message); + return !message.empty(); +} + +// Delivered from cluster. +void Connection::delivered(framing::AMQFrame& f) { + QPID_LOG(trace, cluster << " RECV: " << *this << ": " << f); + assert(!catchUp); + currentChannel = f.getChannel(); + if (!framing::invoke(*this, *f.getBody()).wasHandled() // Connection contol. + && !checkUnsupported(*f.getBody())) // Unsupported operation. + { + connection.received(f); // Pass to broker connection. + } +} + +// A local connection is closed by the network layer. +void Connection::closed() { + try { + if (catchUp) { + QPID_LOG(critical, cluster << " catch-up connection closed prematurely " << *this); + cluster.leave(); + } + else if (isDumped()) { + QPID_LOG(debug, cluster << " closed dump connection " << *this); + connection.closed(); + } + else if (isLocal()) { + QPID_LOG(debug, cluster << " local close of replicated connection " << *this); + // This was a local replicated connection. Multicast a deliver + // closed and process any outstanding frames from the cluster + // until self-delivery of deliver-close. + output.setOutputHandler(discardHandler); + cluster.getMulticast().mcastControl(ClusterConnectionDeliverCloseBody(), self); + } + } + catch (const std::exception& e) { + QPID_LOG(error, cluster << " error closing connection " << *this << ": " << e.what()); + } +} + +// Self-delivery of close message, close the connection. +void Connection::deliverClose () { + assert(!catchUp); + connection.closed(); + cluster.erase(self); +} + +// Member of a shadow connection left the cluster. +void Connection::left() { + assert(isShadow()); + connection.closed(); +} + +// Decode data from local clients. +size_t Connection::decode(const char* buffer, size_t size) { + if (catchUp) { // Handle catch-up locally. + Buffer buf(const_cast<char*>(buffer), size); + while (localDecoder.decode(buf)) + received(localDecoder.frame); + } + else { // Multicast local connections. + assert(isLocal()); + cluster.getMulticast().mcastBuffer(buffer, size, self); + } + return size; +} + +void Connection::deliverBuffer(Buffer& buf) { + assert(!catchUp); + ++deliverSeq; + while (mcastDecoder.decode(buf)) + delivered(mcastDecoder.frame); + if (cluster.getReadMax()) + output.giveReadCredit(1); +} + +broker::SessionState& Connection::sessionState() { + return *connection.getChannel(currentChannel).getSession(); +} + +broker::SemanticState& Connection::semanticState() { + return sessionState().getSemanticState(); +} + +void Connection::consumerState(const string& name, bool blocked, bool notifyEnabled) { + broker::SemanticState::ConsumerImpl& c = semanticState().find(name); + c.setBlocked(blocked); + if (notifyEnabled) c.enableNotify(); else c.disableNotify(); +} + +void Connection::sessionState( + const SequenceNumber& replayStart, + const SequenceNumber& sendCommandPoint, + const SequenceSet& sentIncomplete, + const SequenceNumber& expected, + const SequenceNumber& received, + const SequenceSet& unknownCompleted, + const SequenceSet& receivedIncomplete) +{ + sessionState().setState( + replayStart, + sendCommandPoint, + sentIncomplete, + expected, + received, + unknownCompleted, + receivedIncomplete); + QPID_LOG(debug, cluster << " received session state dump for " << sessionState().getId()); +} + +void Connection::shadowReady(uint64_t memberId, uint64_t connectionId) { + ConnectionId shadow = ConnectionId(memberId, connectionId); + QPID_LOG(debug, cluster << " catch-up connection " << *this << " becomes shadow " << shadow); + self = shadow; +} + +void Connection::membership(const FieldTable& newbies, const FieldTable& members) { + QPID_LOG(debug, cluster << " incoming dump complete on connection " << *this); + cluster.dumpInDone(ClusterMap(newbies, members)); + self.second = 0; // Mark this as completed dump connection. +} + +bool Connection::isLocal() const { + return self.first == cluster.getId() && self.second == this; +} + +bool Connection::isShadow() const { + return self.first != cluster.getId(); +} + +bool Connection::isDumped() const { + return self.first == cluster.getId() && self.second == 0; +} + + +shared_ptr<broker::Queue> Connection::findQueue(const std::string& qname) { + shared_ptr<broker::Queue> queue = cluster.getBroker().getQueues().find(qname); + if (!queue) throw Exception(QPID_MSG(cluster << " can't find queue " << qname)); + return queue; +} + +broker::QueuedMessage Connection::getDumpMessage() { + broker::QueuedMessage m = findQueue(DumpClient::DUMP)->get(); + if (!m.payload) throw Exception(QPID_MSG(cluster << " empty dump queue")); + return m; +} + +void Connection::deliveryRecord(const string& qname, + const SequenceNumber& position, + const string& tag, + const SequenceNumber& id, + bool acquired, + bool accepted, + bool cancelled, + bool completed, + bool ended, + bool windowing, + uint32_t credit) +{ + broker::QueuedMessage m; + broker::Queue::shared_ptr queue = findQueue(qname); + if (!ended) { // Has a message + if (acquired) // Message is on the dump queue + m = getDumpMessage(); + else // Message at original position in original queue + m = queue->find(position); + if (!m.payload) + throw Exception(QPID_MSG("deliveryRecord no dump message")); + } + + broker::DeliveryRecord dr(m, queue, tag, acquired, accepted, windowing, credit); + dr.setId(id); + if (cancelled) dr.cancel(dr.getTag()); + if (completed) dr.complete(); + if (ended) dr.setEnded(); // Exsitance of message + semanticState().record(dr); // Part of the session's unacked list. +} + +void Connection::queuePosition(const string& qname, const SequenceNumber& position) { + shared_ptr<broker::Queue> q = cluster.getBroker().getQueues().find(qname); + if (!q) throw InvalidArgumentException(QPID_MSG("Invalid queue name " << qname)); + q->setPosition(position); +} + +std::ostream& operator<<(std::ostream& o, const Connection& c) { + const char* type="unknown"; + if (c.isLocal()) type = "local"; + else if (c.isShadow()) type = "shadow"; + else if (c.isDumped()) type = "dumped"; + return o << c.getId() << "(" << type << (c.isCatchUp() ? ",catchup" : "") << ")"; +} + +void Connection::txStart() { + txBuffer = make_shared_ptr(new broker::TxBuffer()); +} +void Connection::txAccept(const framing::SequenceSet& acked) { + txBuffer->enlist(make_shared_ptr(new broker::TxAccept(acked, semanticState().getUnacked()))); +} + +void Connection::txDequeue(const std::string& queue) { + txBuffer->enlist(make_shared_ptr(new broker::RecoveredDequeue(findQueue(queue), getDumpMessage().payload))); +} + +void Connection::txEnqueue(const std::string& queue) { + txBuffer->enlist(make_shared_ptr(new broker::RecoveredEnqueue(findQueue(queue), getDumpMessage().payload))); +} + +void Connection::txPublish(const framing::Array& queues, bool delivered) { + boost::shared_ptr<broker::TxPublish> txPub(new broker::TxPublish(getDumpMessage().payload)); + for (framing::Array::const_iterator i = queues.begin(); i != queues.end(); ++i) + txPub->deliverTo(findQueue((*i)->get<std::string>())); + txPub->delivered = delivered; + txBuffer->enlist(txPub); +} + +void Connection::txEnd() { + semanticState().setTxBuffer(txBuffer); +} + +void Connection::accumulatedAck(const qpid::framing::SequenceSet& s) { + semanticState().setAccumulatedAck(s); +} + +void Connection::exchange(const std::string& encoded) { + Buffer buf(const_cast<char*>(encoded.data()), encoded.size()); + broker::Exchange::shared_ptr ex = broker::Exchange::decode(cluster.getBroker().getExchanges(), buf); + QPID_LOG(debug, cluster << " decoded exchange " << ex->getName()); +} + +void Connection::queue(const std::string& encoded) { + Buffer buf(const_cast<char*>(encoded.data()), encoded.size()); + broker::Queue::shared_ptr q = broker::Queue::decode(cluster.getBroker().getQueues(), buf); + QPID_LOG(debug, cluster << " decoded queue " << q->getName()); +} + +}} // namespace qpid::cluster + diff --git a/RC9/qpid/cpp/src/qpid/cluster/Connection.h b/RC9/qpid/cpp/src/qpid/cluster/Connection.h new file mode 100644 index 0000000000..29e42ce534 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/Connection.h @@ -0,0 +1,177 @@ +#ifndef QPID_CLUSTER_CONNECTION_H +#define QPID_CLUSTER_CONNECTION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "types.h" +#include "WriteEstimate.h" +#include "OutputInterceptor.h" +#include "NoOpConnectionOutputHandler.h" + +#include "qpid/broker/Connection.h" +#include "qpid/amqp_0_10/Connection.h" +#include "qpid/sys/ConnectionInputHandler.h" +#include "qpid/sys/ConnectionOutputHandler.h" +#include "qpid/framing/FrameDecoder.h" +#include "qpid/framing/SequenceNumber.h" + +#include <iosfwd> + +namespace qpid { + +namespace framing { class AMQFrame; } + +namespace broker { +class SemanticState; +class QueuedMessage; +class TxBuffer; +class TxAccept; +} + +namespace cluster { +class Cluster; + +/** Intercept broker::Connection calls for shadow and local cluster connections. */ +class Connection : + public RefCounted, + public sys::ConnectionInputHandler, + public framing::AMQP_AllOperations::ClusterConnectionHandler + +{ + public: + /** Local connection, use this in ConnectionId */ + Connection(Cluster&, sys::ConnectionOutputHandler& out, const std::string& id, MemberId, bool catchUp); + /** Shadow connection */ + Connection(Cluster&, sys::ConnectionOutputHandler& out, const std::string& id, ConnectionId); + ~Connection(); + + ConnectionId getId() const { return self; } + broker::Connection& getBrokerConnection() { return connection; } + + /** True for connections from direct clients of local broker */ + bool isLocal() const; + + /** True for connections that are shadowing remote broker connections */ + bool isShadow() const; + + /** True if the connection is in "catch-up" mode: building initial broker state. */ + bool isCatchUp() const { return catchUp; } + + /** True if the connection is a completed shared dump connection */ + bool isDumped() const; + + Cluster& getCluster() { return cluster; } + + // ConnectionInputHandler methods + void received(framing::AMQFrame&); + void closed(); + bool doOutput(); + bool hasOutput() { return connection.hasOutput(); } + void idleOut() { connection.idleOut(); } + void idleIn() { connection.idleIn(); } + + /** Called if the connectors member has left the cluster */ + void left(); + + // ConnectionCodec methods + size_t decode(const char* buffer, size_t size); + + // Called for data delivered from the cluster. + void deliverBuffer(framing::Buffer&); + void delivered(framing::AMQFrame&); + + void consumerState(const std::string& name, bool blocked, bool notifyEnabled); + + // ==== Used in catch-up mode to build initial state. + // + // State dump methods. + void sessionState(const framing::SequenceNumber& replayStart, + const framing::SequenceNumber& sendCommandPoint, + const framing::SequenceSet& sentIncomplete, + const framing::SequenceNumber& expected, + const framing::SequenceNumber& received, + const framing::SequenceSet& unknownCompleted, const SequenceSet& receivedIncomplete); + + void shadowReady(uint64_t memberId, uint64_t connectionId); + + void membership(const framing::FieldTable&, const framing::FieldTable&); + + void deliveryRecord(const std::string& queue, + const framing::SequenceNumber& position, + const std::string& tag, + const framing::SequenceNumber& id, + bool acquired, + bool accepted, + bool cancelled, + bool completed, + bool ended, + bool windowing, + uint32_t credit); + + void queuePosition(const std::string&, const framing::SequenceNumber&); + + void txStart(); + void txAccept(const framing::SequenceSet&); + void txDequeue(const std::string&); + void txEnqueue(const std::string&); + void txPublish(const qpid::framing::Array&, bool); + void txEnd(); + void accumulatedAck(const qpid::framing::SequenceSet&); + + // Encoded queue/exchange replication. + void queue(const std::string& encoded); + void exchange(const std::string& encoded); + + private: + void init(); + bool checkUnsupported(const framing::AMQBody& body); + void deliverClose(); + void deliverDoOutput(uint32_t requested); + void sendDoOutput(); + + boost::shared_ptr<broker::Queue> findQueue(const std::string& qname); + broker::SessionState& sessionState(); + broker::SemanticState& semanticState(); + broker::QueuedMessage getDumpMessage(); + + static NoOpConnectionOutputHandler discardHandler; + + Cluster& cluster; + ConnectionId self; + bool catchUp; + WriteEstimate writeEstimate; + OutputInterceptor output; + framing::FrameDecoder localDecoder; + framing::FrameDecoder mcastDecoder; + broker::Connection connection; + framing::SequenceNumber deliverSeq; + framing::ChannelId currentChannel; + boost::shared_ptr<broker::TxBuffer> txBuffer; + + int FIXMEcredit; // FIXME aconway 2008-12-05: remove + + friend std::ostream& operator<<(std::ostream&, const Connection&); +}; + +}} // namespace qpid::cluster + +#endif /*!QPID_CLUSTER_CONNECTION_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp b/RC9/qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp new file mode 100644 index 0000000000..44e40f0591 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp @@ -0,0 +1,82 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "ConnectionCodec.h" +#include "Connection.h" +#include "Cluster.h" +#include "ProxyInputHandler.h" +#include "qpid/broker/Connection.h" +#include "qpid/framing/ConnectionCloseBody.h" +#include "qpid/framing/ConnectionCloseOkBody.h" +#include "qpid/log/Statement.h" +#include "qpid/memory.h" +#include <stdexcept> +#include <boost/utility/in_place_factory.hpp> + +namespace qpid { +namespace cluster { + +using namespace framing; + +sys::ConnectionCodec* +ConnectionCodec::Factory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id) { + if (v == ProtocolVersion(0, 10)) + return new ConnectionCodec(out, id, cluster, false); + else if (v == ProtocolVersion(0x80 + 0, 0x80 + 10)) + return new ConnectionCodec(out, id, cluster, true); // Catch-up connection + return 0; +} + +// Used for outgoing Link connections, we don't care. +sys::ConnectionCodec* +ConnectionCodec::Factory::create(sys::OutputControl& out, const std::string& id) { + return next->create(out, id); +} + +ConnectionCodec::ConnectionCodec(sys::OutputControl& out, const std::string& id, Cluster& cluster, bool catchUp) + : codec(out, id, false), + interceptor(new Connection(cluster, codec, id, cluster.getId(), catchUp)), + id(interceptor->getId()), + localId(id) +{ + std::auto_ptr<sys::ConnectionInputHandler> ih(new ProxyInputHandler(interceptor)); + codec.setInputHandler(ih); + if (!catchUp) // Don't put catchUp connections in the cluster map. + cluster.insert(interceptor); +} + +ConnectionCodec::~ConnectionCodec() {} + +size_t ConnectionCodec::decode(const char* buffer, size_t size) { + QPID_LOG(trace, "RECVB [" << localId << "]: " << size << " bytes"); + return interceptor->decode(buffer, size); +} + +bool ConnectionCodec::isClosed() const { return codec.isClosed(); } + +size_t ConnectionCodec::encode(const char* buffer, size_t size) { return codec.encode(buffer, size); } + +bool ConnectionCodec::canEncode() { return codec.canEncode(); } + +void ConnectionCodec::closed() { codec.closed(); } + +ProtocolVersion ConnectionCodec::getVersion() const { return codec.getVersion(); } + +}} // namespace qpid::cluster diff --git a/RC9/qpid/cpp/src/qpid/cluster/ConnectionCodec.h b/RC9/qpid/cpp/src/qpid/cluster/ConnectionCodec.h new file mode 100644 index 0000000000..86fac270fa --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/ConnectionCodec.h @@ -0,0 +1,80 @@ +#ifndef QPID_CLUSTER_CONNCTIONCODEC_H +#define QPID_CLUSTER_CONNCTIONCODEC_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/amqp_0_10/Connection.h" +#include "qpid/cluster/Connection.h" +#include <boost/shared_ptr.hpp> +#include <boost/intrusive_ptr.hpp> + +namespace qpid { + +namespace broker { +class Connection; +} + +namespace cluster { +class Cluster; + +/** + * Encapsulates the standard amqp_0_10::ConnectionCodec and sets up + * a cluster::Connection for the connection. + * + * The ConnectionCodec is deleted by the network layer when the + * connection closes. The cluster::Connection needs to be kept + * around until all cluster business on the connection is complete. + * + */ +class ConnectionCodec : public sys::ConnectionCodec { + public: + struct Factory : public sys::ConnectionCodec::Factory { + boost::shared_ptr<sys::ConnectionCodec::Factory> next; + Cluster& cluster; + Factory(boost::shared_ptr<sys::ConnectionCodec::Factory> f, Cluster& c) + : next(f), cluster(c) {} + sys::ConnectionCodec* create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id); + sys::ConnectionCodec* create(sys::OutputControl&, const std::string& id); + }; + + ConnectionCodec(sys::OutputControl& out, const std::string& id, Cluster& c, bool catchUp); + ~ConnectionCodec(); + + // ConnectionCodec functions. + size_t decode(const char* buffer, size_t size); + size_t encode(const char* buffer, size_t size); + bool canEncode(); + void closed(); + bool isClosed() const; + framing::ProtocolVersion getVersion() const; + + + private: + amqp_0_10::Connection codec; + boost::intrusive_ptr<cluster::Connection> interceptor; + cluster::ConnectionId id; + std::string localId; +}; + +}} // namespace qpid::cluster + +#endif /*!QPID_CLUSTER_CONNCTIONCODEC_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/ConnectionMap.h b/RC9/qpid/cpp/src/qpid/cluster/ConnectionMap.h new file mode 100644 index 0000000000..f1862e2e75 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/ConnectionMap.h @@ -0,0 +1,90 @@ +#ifndef QPID_CLUSTER_CONNECTIONMAP_H +#define QPID_CLUSTER_CONNECTIONMAP_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "types.h" +#include "Connection.h" +#include "ClusterMap.h" +#include "qpid/sys/Mutex.h" +#include <boost/intrusive_ptr.hpp> +#include <map> + +namespace qpid { +namespace cluster { + +/** + * Thread safe map of connections. + */ +class ConnectionMap +{ + public: + typedef boost::intrusive_ptr<cluster::Connection> ConnectionPtr; + typedef std::vector<ConnectionPtr> Vector; + + void insert(ConnectionId id, ConnectionPtr p) { + ScopedLock l(lock); + map.insert(Map::value_type(id,p)); + } + + void erase(ConnectionId id) { + ScopedLock l(lock); + map.erase(id); + } + + ConnectionPtr find(ConnectionId id) const { + ScopedLock l(lock); + Map::const_iterator i = map.find(id); + return i == map.end() ? ConnectionPtr() : i->second; + } + + Vector values() const { + Vector result(map.size()); + std::transform(map.begin(), map.end(), result.begin(), + boost::bind(&Map::value_type::second, _1)); + return result; + } + + void update(MemberId myId, const ClusterMap& cluster) { + for (Map::iterator i = map.begin(); i != map.end(); ) { + MemberId member = i->first.getMember(); + if (member != myId && !cluster.isMember(member)) { + i->second->left(); + map.erase(i++); + } else { + i++; + } + } + } + + size_t size() const { return map.size(); } + private: + typedef std::map<ConnectionId, ConnectionPtr> Map; + typedef sys::Mutex::ScopedLock ScopedLock; + + mutable sys::Mutex lock; + Map map; +}; + + +}} // namespace qpid::cluster + +#endif /*!QPID_CLUSTER_CONNECTIONMAP_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/Cpg.cpp b/RC9/qpid/cpp/src/qpid/cluster/Cpg.cpp new file mode 100644 index 0000000000..48c3b483f9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/Cpg.cpp @@ -0,0 +1,197 @@ +/* + * + * 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 "Cpg.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/posix/PrivatePosix.h" +#include "qpid/log/Statement.h" + +#include <vector> +#include <limits> +#include <iterator> + +#include <unistd.h> + +namespace qpid { +namespace cluster { + +using namespace std; + +Cpg* Cpg::cpgFromHandle(cpg_handle_t handle) { + void* cpg=0; + check(cpg_context_get(handle, &cpg), "Cannot get CPG instance."); + if (!cpg) throw Exception("Cannot get CPG instance."); + return reinterpret_cast<Cpg*>(cpg); +} + +// Global callback functions. +void Cpg::globalDeliver ( + cpg_handle_t handle, + struct cpg_name *group, + uint32_t nodeid, + uint32_t pid, + void* msg, + int msg_len) +{ + cpgFromHandle(handle)->handler.deliver(handle, group, nodeid, pid, msg, msg_len); +} + +void Cpg::globalConfigChange( + cpg_handle_t handle, + struct cpg_name *group, + struct cpg_address *members, int nMembers, + struct cpg_address *left, int nLeft, + struct cpg_address *joined, int nJoined +) +{ + cpgFromHandle(handle)->handler.configChange(handle, group, members, nMembers, left, nLeft, joined, nJoined); +} + +int Cpg::getFd() { + int fd; + check(cpg_fd_get(handle, &fd), "Cannot get CPG file descriptor"); + return fd; +} + +Cpg::Cpg(Handler& h) : IOHandle(new sys::IOHandlePrivate), handler(h), isShutdown(false) { + cpg_callbacks_t callbacks; + ::memset(&callbacks, sizeof(callbacks), 0); + callbacks.cpg_deliver_fn = &globalDeliver; + callbacks.cpg_confchg_fn = &globalConfigChange; + check(cpg_initialize(&handle, &callbacks), "Cannot initialize CPG"); + check(cpg_context_set(handle, this), "Cannot set CPG context"); + // Note: CPG is currently unix-specific. If CPG is ported to + // windows then this needs to be refactored into + // qpid::sys::<platform> + IOHandle::impl->fd = getFd(); + QPID_LOG(debug, "Initialized CPG handle 0x" << std::hex << handle); +} + +Cpg::~Cpg() { + try { + shutdown(); + } catch (const std::exception& e) { + QPID_LOG(error, "Exception in Cpg destructor: " << e.what()); + } +} + +void Cpg::join(const std::string& name) { + group = name; + check(cpg_join(handle, &group), cantJoinMsg(group)); +} + +void Cpg::leave() { + check(cpg_leave(handle, &group), cantLeaveMsg(group)); +} + +bool Cpg::isFlowControlEnabled() { + cpg_flow_control_state_t flowState; + check(cpg_flow_control_state_get(handle, &flowState), "Cannot get CPG flow control status."); + return flowState == CPG_FLOW_CONTROL_ENABLED; +} + +bool Cpg::mcast(const iovec* iov, int iovLen) { + if (isFlowControlEnabled()) { + QPID_LOG(warning, "CPG flow control enabled") + return false; + } + cpg_error_t result; + do { + result = cpg_mcast_joined(handle, CPG_TYPE_AGREED, const_cast<iovec*>(iov), iovLen); + if (result != CPG_ERR_TRY_AGAIN) check(result, cantMcastMsg(group)); + } while(result == CPG_ERR_TRY_AGAIN); + return true; +} + +void Cpg::shutdown() { + if (!isShutdown) { + QPID_LOG(debug,"Shutting down CPG"); + isShutdown=true; + check(cpg_finalize(handle), "Error in shutdown of CPG"); + } +} + +void Cpg::dispatch(cpg_dispatch_t type) { + check(cpg_dispatch(handle,type), "Error in CPG dispatch"); +} + +string Cpg::errorStr(cpg_error_t err, const std::string& msg) { + switch (err) { + case CPG_OK: return msg+": ok"; + case CPG_ERR_LIBRARY: return msg+": library error"; + case CPG_ERR_TIMEOUT: return msg+": timeout"; + case CPG_ERR_TRY_AGAIN: return msg+": timeout. The aisexec daemon may not be running"; + case CPG_ERR_INVALID_PARAM: return msg+": invalid param"; + case CPG_ERR_NO_MEMORY: return msg+": no memory"; + case CPG_ERR_BAD_HANDLE: return msg+": bad handle"; + case CPG_ERR_ACCESS: return msg+": access denied."; + case CPG_ERR_NOT_EXIST: return msg+": not exist"; + case CPG_ERR_EXIST: return msg+": exist"; + case CPG_ERR_NOT_SUPPORTED: return msg+": not supported"; + case CPG_ERR_SECURITY: return msg+": security"; + case CPG_ERR_TOO_MANY_GROUPS: return msg+": too many groups"; + default: + assert(0); + return ": unknown"; + }; +} + +std::string Cpg::cantJoinMsg(const Name& group) { + return "Cannot join CPG group "+group.str(); +} + +std::string Cpg::cantLeaveMsg(const Name& group) { + return "Cannot leave CPG group "+group.str(); +} + +std::string Cpg::cantMcastMsg(const Name& group) { + return "Cannot mcast to CPG group "+group.str(); +} + +MemberId Cpg::self() const { + unsigned int nodeid; + check(cpg_local_get(handle, &nodeid), "Cannot get local CPG identity"); + return MemberId(nodeid, getpid()); +} + +namespace { int byte(uint32_t value, int i) { return (value >> (i*8)) & 0xff; } } + +ostream& operator <<(ostream& out, const MemberId& id) { + out << byte(id.first, 0) << "." + << byte(id.first, 1) << "." + << byte(id.first, 2) << "." + << byte(id.first, 3); + return out << ":" << id.second; +} + +ostream& operator<<(ostream& o, const ConnectionId& c) { + return o << c.first << "-" << c.second; +} + +std::string MemberId::str() const { + char s[8]; + reinterpret_cast<uint32_t&>(s[0]) = htonl(first); + reinterpret_cast<uint32_t&>(s[4]) = htonl(second); + return std::string(s,8); +} + +MemberId::MemberId(const std::string& s) { + first = ntohl(reinterpret_cast<const uint32_t&>(s[0])); + second = ntohl(reinterpret_cast<const uint32_t&>(s[4])); +} +}} // namespace qpid::cluster diff --git a/RC9/qpid/cpp/src/qpid/cluster/Cpg.h b/RC9/qpid/cpp/src/qpid/cluster/Cpg.h new file mode 100644 index 0000000000..5de2b516d5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/Cpg.h @@ -0,0 +1,169 @@ +#ifndef CPG_H +#define CPG_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/Exception.h" +#include "qpid/cluster/Dispatchable.h" +#include "qpid/cluster/types.h" +#include "qpid/sys/IOHandle.h" +#include "qpid/sys/Mutex.h" + +#include <boost/scoped_ptr.hpp> + +#include <cassert> +#include <string.h> + +namespace qpid { +namespace cluster { + +/** + * Lightweight C++ interface to cpg.h operations. + * + * Manages a single CPG handle, initialized in ctor, finialzed in destructor. + * On error all functions throw Cpg::Exception. + * + */ +class Cpg : public sys::IOHandle { + public: + struct Exception : public ::qpid::Exception { + Exception(const std::string& msg) : ::qpid::Exception(msg) {} + }; + + struct Name : public cpg_name { + Name() { length = 0; } + Name(const char* s) { copy(s, strlen(s)); } + Name(const char* s, size_t n) { copy(s,n); } + Name(const std::string& s) { copy(s.data(), s.size()); } + void copy(const char* s, size_t n) { + assert(n < CPG_MAX_NAME_LENGTH); + memcpy(value, s, n); + length=n; + } + + std::string str() const { return std::string(value, length); } + }; + + static std::string str(const cpg_name& n) { + return std::string(n.value, n.length); + } + + struct Handler { + virtual ~Handler() {}; + virtual void deliver( + cpg_handle_t /*handle*/, + struct cpg_name *group, + uint32_t /*nodeid*/, + uint32_t /*pid*/, + void* /*msg*/, + int /*msg_len*/) = 0; + + virtual void configChange( + cpg_handle_t /*handle*/, + struct cpg_name */*group*/, + struct cpg_address */*members*/, int /*nMembers*/, + struct cpg_address */*left*/, int /*nLeft*/, + struct cpg_address */*joined*/, int /*nJoined*/ + ) = 0; + }; + + /** Open a CPG handle. + *@param handler for CPG events. + */ + Cpg(Handler&); + + /** Destructor calls shutdown if not already calledx. */ + ~Cpg(); + + /** Disconnect from CPG */ + void shutdown(); + + /** Dispatch CPG events. + *@param type one of + * - CPG_DISPATCH_ONE - dispatch exactly one event. + * - CPG_DISPATCH_ALL - dispatch all available events, don't wait. + * - CPG_DISPATCH_BLOCKING - blocking dispatch loop. + */ + void dispatch(cpg_dispatch_t type); + + void dispatchOne() { dispatch(CPG_DISPATCH_ONE); } + void dispatchAll() { dispatch(CPG_DISPATCH_ALL); } + void dispatchBlocking() { dispatch(CPG_DISPATCH_BLOCKING); } + + void join(const std::string& group); + void leave(); + + /** Multicast to the group. NB: must not be called concurrently. + * + *@return true if the message was multi-cast, false if + * it was not sent due to flow control. + */ + bool mcast(const iovec* iov, int iovLen); + + cpg_handle_t getHandle() const { return handle; } + + MemberId self() const; + + int getFd(); + + bool isFlowControlEnabled(); + + private: + static std::string errorStr(cpg_error_t err, const std::string& msg); + static std::string cantJoinMsg(const Name&); + static std::string cantLeaveMsg(const Name&); + static std::string cantMcastMsg(const Name&); + + static void check(cpg_error_t result, const std::string& msg) { + if (result != CPG_OK) throw Exception(errorStr(result, msg)); + } + + static Cpg* cpgFromHandle(cpg_handle_t); + + static void globalDeliver( + cpg_handle_t handle, + struct cpg_name *group, + uint32_t nodeid, + uint32_t pid, + void* msg, + int msg_len); + + static void globalConfigChange( + cpg_handle_t handle, + struct cpg_name *group, + struct cpg_address *members, int nMembers, + struct cpg_address *left, int nLeft, + struct cpg_address *joined, int nJoined + ); + + cpg_handle_t handle; + Handler& handler; + bool isShutdown; + Name group; + sys::Mutex dispatchLock; +}; + +inline bool operator==(const cpg_name& a, const cpg_name& b) { + return a.length==b.length && strncmp(a.value, b.value, a.length) == 0; +} +inline bool operator!=(const cpg_name& a, const cpg_name& b) { return !(a == b); } + +}} // namespace qpid::cluster + +#endif /*!CPG_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/Dispatchable.h b/RC9/qpid/cpp/src/qpid/cluster/Dispatchable.h new file mode 100644 index 0000000000..e7f0df4218 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/Dispatchable.h @@ -0,0 +1,52 @@ +#ifndef QPID_CLUSTER_DISPATCHABLE_H +#define QPID_CLUSTER_DISPATCHABLE_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +namespace qpid { +namespace cluster { + +/** + * Interface for classes that have some "events" that need dispatching + * in a thread. + */ +class Dispatchable +{ + public: + virtual ~Dispatchable() {} + + /** Dispatch one event in current thread. */ + virtual void dispatchOne() = 0; + /** Dispatch all available events, don't block. */ + virtual void dispatchAll() = 0; + /** Blocking loop to dispatch cluster events */ + virtual void dispatchBlocking() = 0; + + /** Wait for at least one event, then dispatch all available events. + * Don't block. Useful for tests. + */ + virtual void dispatchSome() { dispatchOne(); dispatchAll(); } + +}; + +}} // namespace qpid::cluster + + + +#endif /*!QPID_CLUSTER_DISPATCHABLE_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/DumpClient.cpp b/RC9/qpid/cpp/src/qpid/cluster/DumpClient.cpp new file mode 100644 index 0000000000..3f3212470d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/DumpClient.cpp @@ -0,0 +1,369 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "DumpClient.h" +#include "Cluster.h" +#include "ClusterMap.h" +#include "Connection.h" +#include "qpid/client/SessionBase_0_10Access.h" +#include "qpid/client/ConnectionAccess.h" +#include "qpid/broker/Broker.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/QueueRegistry.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/Exchange.h" +#include "qpid/broker/ExchangeRegistry.h" +#include "qpid/broker/SessionHandler.h" +#include "qpid/broker/SessionState.h" +#include "qpid/broker/TxOpVisitor.h" +#include "qpid/broker/DtxAck.h" +#include "qpid/broker/TxAccept.h" +#include "qpid/broker/TxPublish.h" +#include "qpid/broker/RecoveredDequeue.h" +#include "qpid/broker/RecoveredEnqueue.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/ClusterConnectionMembershipBody.h" +#include "qpid/framing/ClusterConnectionShadowReadyBody.h" +#include "qpid/framing/ClusterConnectionSessionStateBody.h" +#include "qpid/framing/ClusterConnectionConsumerStateBody.h" +#include "qpid/framing/enum.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/framing/TypeCode.h" +#include "qpid/log/Statement.h" +#include "qpid/Url.h" +#include <boost/bind.hpp> +#include <algorithm> + +namespace qpid { +namespace cluster { + +using broker::Broker; +using broker::Exchange; +using broker::Queue; +using broker::QueueBinding; +using broker::Message; +using namespace framing; +namespace arg=client::arg; +using client::SessionBase_0_10Access; + +struct ClusterConnectionProxy : public AMQP_AllProxy::ClusterConnection { + ClusterConnectionProxy(client::Connection c) : + AMQP_AllProxy::ClusterConnection(*client::ConnectionAccess::getImpl(c)) {} + ClusterConnectionProxy(client::AsyncSession s) : + AMQP_AllProxy::ClusterConnection(SessionBase_0_10Access(s).get()->out) {} +}; + +// Create a connection with special version that marks it as a catch-up connection. +client::Connection catchUpConnection() { + client::Connection c; + client::ConnectionAccess::setVersion(c, ProtocolVersion(0x80 , 0x80 + 10)); + return c; +} + +// Send a control body directly to the session. +void send(client::AsyncSession& s, const AMQBody& body) { + client::SessionBase_0_10Access sb(s); + sb.get()->send(body); +} + +// TODO aconway 2008-09-24: optimization: dump connections/sessions in parallel. + +DumpClient::DumpClient(const MemberId& dumper, const MemberId& dumpee, const Url& url, + broker::Broker& broker, const ClusterMap& m, const Cluster::Connections& cons, + const boost::function<void()>& ok, + const boost::function<void(const std::exception&)>& fail) + : dumperId(dumper), dumpeeId(dumpee), dumpeeUrl(url), dumperBroker(broker), map(m), connections(cons), + connection(catchUpConnection()), shadowConnection(catchUpConnection()), + done(ok), failed(fail) +{ + connection.open(url); + session = connection.newSession("dump_shared"); +} + +DumpClient::~DumpClient() {} + +// Illegal exchange/queue name for catch-up, avoid clashes with user queues/exchanges. +static const char DUMP_CHARS[] = "\000qpid-dump"; +const std::string DumpClient::DUMP(DUMP_CHARS, sizeof(DUMP_CHARS)); + +void DumpClient::dump() { + QPID_LOG(debug, dumperId << " dumping state to " << dumpeeId << " at " << dumpeeUrl); + Broker& b = dumperBroker; + b.getExchanges().eachExchange(boost::bind(&DumpClient::dumpExchange, this, _1)); + + // Dump exchange is used to route messages to the proper queue without modifying routing key. + session.exchangeDeclare(arg::exchange=DUMP, arg::type="fanout", arg::autoDelete=true); + b.getQueues().eachQueue(boost::bind(&DumpClient::dumpQueue, this, _1)); + // Dump queue is used to transfer acquired messages that are no longer on their original queue. + session.queueDeclare(arg::queue=DUMP, arg::autoDelete=true); + session.sync(); + session.close(); + + std::for_each(connections.begin(), connections.end(), boost::bind(&DumpClient::dumpConnection, this, _1)); + AMQFrame frame(map.asMethodBody()); + client::ConnectionAccess::getImpl(connection)->handle(frame); + connection.close(); + QPID_LOG(debug, dumperId << " dumped state to " << dumpeeId << " at " << dumpeeUrl); +} + +void DumpClient::run() { + try { + dump(); + done(); + } catch (const std::exception& e) { + failed(e); + } + delete this; +} + +namespace { +template <class T> std::string encode(const T& t) { + std::string encoded; + encoded.resize(t.encodedSize()); + framing::Buffer buf(const_cast<char*>(encoded.data()), encoded.size()); + t.encode(buf); + return encoded; +} +} // namespace + +void DumpClient::dumpExchange(const boost::shared_ptr<Exchange>& ex) { + QPID_LOG(debug, dumperId << " dumping exchange " << ex->getName()); + ClusterConnectionProxy proxy(session); + proxy.exchange(encode(*ex)); +} + +/** Bind a queue to the dump exchange and dump messges to it + * setting the message possition as needed. + */ +class MessageDumper { + std::string queue; + bool haveLastPos; + framing::SequenceNumber lastPos; + client::AsyncSession session; + + public: + + MessageDumper(const string& q, const client::AsyncSession s) : queue(q), haveLastPos(false), session(s) { + session.exchangeBind(queue, DumpClient::DUMP); + } + + ~MessageDumper() { + session.exchangeUnbind(queue, DumpClient::DUMP); + } + + void dumpQueuedMessage(const broker::QueuedMessage& message) { + if (!haveLastPos || message.position - lastPos != 1) { + ClusterConnectionProxy(session).queuePosition(queue, message.position.getValue()-1); + haveLastPos = true; + } + lastPos = message.position; + SessionBase_0_10Access sb(session); + framing::MessageTransferBody transfer( + framing::ProtocolVersion(), DumpClient::DUMP, message::ACCEPT_MODE_NONE, message::ACQUIRE_MODE_PRE_ACQUIRED); + sb.get()->send(transfer, message.payload->getFrames()); + } + + void dumpMessage(const boost::intrusive_ptr<broker::Message>& message) { + dumpQueuedMessage(broker::QueuedMessage(0, message, haveLastPos? lastPos.getValue()+1 : 1)); + } +}; + + +void DumpClient::dumpQueue(const boost::shared_ptr<Queue>& q) { + QPID_LOG(debug, dumperId << " dumping queue " << q->getName()); + ClusterConnectionProxy proxy(session); + proxy.queue(encode(*q)); + MessageDumper dumper(q->getName(), session); + q->eachMessage(boost::bind(&MessageDumper::dumpQueuedMessage, &dumper, _1)); + q->eachBinding(boost::bind(&DumpClient::dumpBinding, this, q->getName(), _1)); +} + + +void DumpClient::dumpBinding(const std::string& queue, const QueueBinding& binding) { + session.exchangeBind(queue, binding.exchange, binding.key, binding.args); +} + +void DumpClient::dumpConnection(const boost::intrusive_ptr<Connection>& dumpConnection) { + QPID_LOG(debug, dumperId << " dumping connection " << *dumpConnection); + shadowConnection = catchUpConnection(); + + broker::Connection& bc = dumpConnection->getBrokerConnection(); + // FIXME aconway 2008-10-20: What authentication info to use on reconnect? + shadowConnection.open(dumpeeUrl, bc.getUserId(), ""/*password*/, "/"/*vhost*/, bc.getFrameMax()); + bc.eachSessionHandler(boost::bind(&DumpClient::dumpSession, this, _1)); + ClusterConnectionProxy(shadowConnection).shadowReady( + dumpConnection->getId().getMember(), + reinterpret_cast<uint64_t>(dumpConnection->getId().getPointer())); + shadowConnection.close(); + QPID_LOG(debug, dumperId << " dumped connection " << *dumpConnection); +} + +void DumpClient::dumpSession(broker::SessionHandler& sh) { + QPID_LOG(debug, dumperId << " dumping session " << &sh.getConnection() << "[" << sh.getChannel() << "] = " + << sh.getSession()->getId()); + broker::SessionState* ss = sh.getSession(); + if (!ss) return; // no session. + + // Create a client session to dump session state. + boost::shared_ptr<client::ConnectionImpl> cimpl = client::ConnectionAccess::getImpl(shadowConnection); + boost::shared_ptr<client::SessionImpl> simpl = cimpl->newSession(ss->getId().getName(), ss->getTimeout(), sh.getChannel()); + client::SessionBase_0_10Access(shadowSession).set(simpl); + AMQP_AllProxy::ClusterConnection proxy(simpl->out); + + // Re-create session state on remote connection. + + // Dump consumers. For reasons unknown, boost::bind does not work here with boost 1.33. + QPID_LOG(debug, dumperId << " dumping consumers."); + ss->getSemanticState().eachConsumer(std::bind1st(std::mem_fun(&DumpClient::dumpConsumer),this)); + + QPID_LOG(debug, dumperId << " dumping unacknowledged messages."); + broker::DeliveryRecords& drs = ss->getSemanticState().getUnacked(); + std::for_each(drs.begin(), drs.end(), boost::bind(&DumpClient::dumpUnacked, this, _1)); + + dumpTxState(ss->getSemanticState()); // Tx transaction state. + + // Adjust for command counter for message in progress, will be sent after state update. + boost::intrusive_ptr<Message> inProgress = ss->getMessageInProgress(); + SequenceNumber received = ss->receiverGetReceived().command; + if (inProgress) + --received; + + // Reset command-sequence state. + proxy.sessionState( + ss->senderGetReplayPoint().command, + ss->senderGetCommandPoint().command, + ss->senderGetIncomplete(), + std::max(received, ss->receiverGetExpected().command), + received, + ss->receiverGetUnknownComplete(), + ss->receiverGetIncomplete() + ); + + // Send frames for partial message in progress. + if (inProgress) { + inProgress->getFrames().map(simpl->out); + } + + // FIXME aconway 2008-09-23: update session replay list. + + QPID_LOG(debug, dumperId << " dumped session " << sh.getSession()->getId()); +} + +void DumpClient::dumpConsumer(const broker::SemanticState::ConsumerImpl* ci) { + QPID_LOG(debug, dumperId << " dumping consumer " << ci->getName() << " on " << shadowSession.getId()); + using namespace message; + shadowSession.messageSubscribe( + arg::queue = ci->getQueue()->getName(), + arg::destination = ci->getName(), + arg::acceptMode = ci->isAckExpected() ? ACCEPT_MODE_EXPLICIT : ACCEPT_MODE_NONE, + arg::acquireMode = ci->isAcquire() ? ACQUIRE_MODE_PRE_ACQUIRED : ACQUIRE_MODE_NOT_ACQUIRED, + arg::exclusive = ci->isExclusive(), + arg::resumeId = ci->getResumeId(), + arg::resumeTtl = ci->getResumeTtl(), + arg::arguments = ci->getArguments() + ); + shadowSession.messageSetFlowMode(ci->getName(), ci->isWindowing() ? FLOW_MODE_WINDOW : FLOW_MODE_CREDIT); + shadowSession.messageFlow(ci->getName(), CREDIT_UNIT_MESSAGE, ci->getMsgCredit()); + shadowSession.messageFlow(ci->getName(), CREDIT_UNIT_BYTE, ci->getByteCredit()); + ClusterConnectionConsumerStateBody state( + ProtocolVersion(), + ci->getName(), + ci->isBlocked(), + ci->isNotifyEnabled() + ); + client::SessionBase_0_10Access(shadowSession).get()->send(state); + QPID_LOG(debug, dumperId << " dumped consumer " << ci->getName() << " on " << shadowSession.getId()); +} + +void DumpClient::dumpUnacked(const broker::DeliveryRecord& dr) { + if (!dr.isEnded() && dr.isAcquired() && dr.getMessage().payload) { + // If the message is acquired then it is no longer on the + // dumpees queue, put it on the dump queue for dumpee to pick up. + // + MessageDumper(DUMP, shadowSession).dumpQueuedMessage(dr.getMessage()); + } + ClusterConnectionProxy(shadowSession).deliveryRecord( + dr.getQueue()->getName(), + dr.getMessage().position, + dr.getTag(), + dr.getId(), + dr.isAcquired(), + dr.isAccepted(), + dr.isCancelled(), + dr.isComplete(), + dr.isEnded(), + dr.isWindowing(), + dr.getCredit() + ); +} + +class TxOpDumper : public broker::TxOpConstVisitor, public MessageDumper { + public: + TxOpDumper(DumpClient& dc, client::AsyncSession s) + : MessageDumper(DumpClient::DUMP, s), parent(dc), session(s), proxy(s) {} + + void operator()(const broker::DtxAck& ) { + throw InternalErrorException("DTX transactions not currently supported by cluster."); + } + + void operator()(const broker::RecoveredDequeue& rdeq) { + dumpMessage(rdeq.getMessage()); + proxy.txEnqueue(rdeq.getQueue()->getName()); + } + + void operator()(const broker::RecoveredEnqueue& renq) { + dumpMessage(renq.getMessage()); + proxy.txEnqueue(renq.getQueue()->getName()); + } + + void operator()(const broker::TxAccept& txAccept) { + proxy.txAccept(txAccept.getAcked()); + } + + void operator()(const broker::TxPublish& txPub) { + dumpMessage(txPub.getMessage()); + typedef std::list<Queue::shared_ptr> QueueList; + const QueueList& qlist = txPub.getQueues(); + Array qarray(TYPE_CODE_STR8); + for (QueueList::const_iterator i = qlist.begin(); i != qlist.end(); ++i) + qarray.push_back(Array::ValuePtr(new Str8Value((*i)->getName()))); + proxy.txPublish(qarray, txPub.delivered); + } + + private: + DumpClient& parent; + client::AsyncSession session; + ClusterConnectionProxy proxy; +}; + +void DumpClient::dumpTxState(broker::SemanticState& s) { + QPID_LOG(debug, dumperId << " dumping TX transaction state."); + ClusterConnectionProxy proxy(shadowSession); + proxy.accumulatedAck(s.getAccumulatedAck()); + broker::TxBuffer::shared_ptr txBuffer = s.getTxBuffer(); + if (txBuffer) { + proxy.txStart(); + TxOpDumper dumper(*this, shadowSession); + txBuffer->accept(dumper); + proxy.txEnd(); + } +} + +}} // namespace qpid::cluster diff --git a/RC9/qpid/cpp/src/qpid/cluster/DumpClient.h b/RC9/qpid/cpp/src/qpid/cluster/DumpClient.h new file mode 100644 index 0000000000..23676e7646 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/DumpClient.h @@ -0,0 +1,101 @@ +#ifndef QPID_CLUSTER_DUMPCLIENT_H +#define QPID_CLUSTER_DUMPCLIENT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ClusterMap.h" +#include "qpid/client/Connection.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/broker/SemanticState.h" +#include "qpid/sys/Runnable.h" +#include <boost/shared_ptr.hpp> + + +namespace qpid { + +class Url; + +namespace broker { + +class Broker; +class Queue; +class Exchange; +class QueueBindings; +class QueueBinding; +class QueuedMessage; +class SessionHandler; +class DeliveryRecord; +class SessionState; +class SemanticState; + +} // namespace broker + +namespace cluster { + +class Cluster; +class Connection; +class ClusterMap; + +/** + * A client that dumps the contents of a local broker to a remote one using AMQP. + */ +class DumpClient : public sys::Runnable { + public: + static const std::string DUMP; // Name for special dump queue and exchange. + + DumpClient(const MemberId& dumper, const MemberId& dumpee, const Url&, + broker::Broker& donor, const ClusterMap& map, const std::vector<boost::intrusive_ptr<Connection> >& , + const boost::function<void()>& done, + const boost::function<void(const std::exception&)>& fail); + + ~DumpClient(); + void dump(); + void run(); // Will delete this when finished. + + void dumpUnacked(const broker::DeliveryRecord&); + + private: + void dumpQueue(const boost::shared_ptr<broker::Queue>&); + void dumpExchange(const boost::shared_ptr<broker::Exchange>&); + void dumpMessage(const broker::QueuedMessage&); + void dumpMessageTo(const broker::QueuedMessage&, const std::string& queue, client::Session s); + void dumpBinding(const std::string& queue, const broker::QueueBinding& binding); + void dumpConnection(const boost::intrusive_ptr<Connection>& connection); + void dumpSession(broker::SessionHandler& s); + void dumpTxState(broker::SemanticState& s); + void dumpConsumer(const broker::SemanticState::ConsumerImpl*); + + MemberId dumperId; + MemberId dumpeeId; + Url dumpeeUrl; + broker::Broker& dumperBroker; + ClusterMap map; + std::vector<boost::intrusive_ptr<Connection> > connections; + client::Connection connection, shadowConnection; + client::AsyncSession session, shadowSession; + boost::function<void()> done; + boost::function<void(const std::exception& e)> failed; +}; + +}} // namespace qpid::cluster + +#endif /*!QPID_CLUSTER_DUMPCLIENT_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/Event.cpp b/RC9/qpid/cpp/src/qpid/cluster/Event.cpp new file mode 100644 index 0000000000..cfa8fe05f1 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/Event.cpp @@ -0,0 +1,89 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "types.h" +#include "Event.h" +#include "Cpg.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/AMQFrame.h" +#include <ostream> +#include <iterator> +#include <algorithm> + +namespace qpid { +namespace cluster { + +using framing::Buffer; + +const size_t Event::HEADER_SIZE = + sizeof(uint8_t) + // type + sizeof(uint64_t) + // connection pointer only, CPG provides member ID. + sizeof(uint32_t); // payload size + +Event::Event(EventType t, const ConnectionId& c, size_t s) + : type(t), connectionId(c), size(s), store(RefCountedBuffer::create(s+HEADER_SIZE)) { + encodeHeader(); +} + +Event Event::decode(const MemberId& m, framing::Buffer& buf) { + if (buf.available() <= HEADER_SIZE) + throw ClusterLeaveException("Not enough for multicast header"); + EventType type((EventType)buf.getOctet()); + if(type != DATA && type != CONTROL) + throw ClusterLeaveException("Invalid multicast event type"); + ConnectionId connection(m, reinterpret_cast<Connection*>(buf.getLongLong())); + uint32_t size = buf.getLong(); + Event e(type, connection, size); + if (buf.available() < size) + throw ClusterLeaveException("Not enough data for multicast event"); + memcpy(e.getData(), buf.getPointer() + buf.getPosition(), size); + return e; +} + +Event Event::control(const framing::AMQBody& body, const ConnectionId& cid) { + framing::AMQFrame f(body); + Event e(CONTROL, cid, f.encodedSize()); + Buffer buf(e); + f.encode(buf); + return e; +} + +void Event::encodeHeader () { + Buffer b(getStore(), HEADER_SIZE); + b.putOctet(type); + b.putLongLong(reinterpret_cast<uint64_t>(connectionId.getPointer())); + b.putLong(size); + assert(b.getPosition() == HEADER_SIZE); +} + +Event::operator Buffer() const { + return Buffer(const_cast<char*>(getData()), getSize()); +} + +static const char* EVENT_TYPE_NAMES[] = { "data", "control" }; + +std::ostream& operator << (std::ostream& o, const Event& e) { + o << "[event " << e.getConnectionId() + << " " << EVENT_TYPE_NAMES[e.getType()] + << " " << e.getSize() << " bytes]"; + return o; +} + +}} // namespace qpid::cluster diff --git a/RC9/qpid/cpp/src/qpid/cluster/Event.h b/RC9/qpid/cpp/src/qpid/cluster/Event.h new file mode 100644 index 0000000000..427410923b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/Event.h @@ -0,0 +1,87 @@ +#ifndef QPID_CLUSTER_EVENT_H +#define QPID_CLUSTER_EVENT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "types.h" +#include "Cpg.h" +#include "Connection.h" +#include "qpid/RefCountedBuffer.h" +#include "qpid/framing/Buffer.h" +#include <iosfwd> + +namespace qpid { +namespace cluster { + +// TODO aconway 2008-09-03: more efficient solution for shared +// byte-stream data. +// + +/** + * Events are sent to/received from the cluster. + * Refcounted so they can be stored on queues. + */ +class Event { + public: + /** Create an event with a buffer that can hold size bytes plus an event header. */ + Event(EventType t=DATA, const ConnectionId& c=ConnectionId(), size_t size=0); + + /** Create an event copied from delivered data. */ + static Event decode(const MemberId& m, framing::Buffer&); + + /** Create an event containing a control */ + static Event control(const framing::AMQBody&, const ConnectionId&); + + EventType getType() const { return type; } + ConnectionId getConnectionId() const { return connectionId; } + MemberId getMemberId() const { return connectionId.getMember(); } + size_t getSize() const { return size; } + + // Data excluding header. + char* getData() { return store + HEADER_SIZE; } + const char* getData() const { return store + HEADER_SIZE; } + + // Store including header + char* getStore() { return store; } + const char* getStore() const { return store; } + size_t getStoreSize() { return size + HEADER_SIZE; } + + bool isCluster() const { return connectionId.getPointer() == 0; } + bool isConnection() const { return connectionId.getPointer() != 0; } + + operator framing::Buffer() const; + + private: + static const size_t HEADER_SIZE; + + void encodeHeader(); + + EventType type; + ConnectionId connectionId; + size_t size; + RefCountedBuffer::pointer store; +}; + +std::ostream& operator << (std::ostream&, const Event&); +}} // namespace qpid::cluster + +#endif /*!QPID_CLUSTER_EVENT_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/FailoverExchange.cpp b/RC9/qpid/cpp/src/qpid/cluster/FailoverExchange.cpp new file mode 100644 index 0000000000..abc7f5df6f --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/FailoverExchange.cpp @@ -0,0 +1,99 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "FailoverExchange.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/DeliverableMessage.h" +#include "qpid/broker/Queue.h" +#include "qpid/framing/MessageProperties.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/AMQHeaderBody.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/Array.h" +#include <boost/bind.hpp> +#include <algorithm> + +namespace qpid { +namespace cluster { +using namespace std; + +using namespace broker; +using namespace framing; + +const string FailoverExchange::TYPE_NAME("amq.failover"); + +FailoverExchange::FailoverExchange(management::Manageable* parent) : Exchange(TYPE_NAME, parent) { + if (mgmtExchange != 0) + mgmtExchange->set_type(TYPE_NAME); +} + + +void FailoverExchange::setUrls(const vector<Url>& u) { + Lock l(lock); + urls=u; + if (urls.empty()) return; + std::for_each(queues.begin(), queues.end(), + boost::bind(&FailoverExchange::sendUpdate, this, _1)); +} + +string FailoverExchange::getType() const { return TYPE_NAME; } + +bool FailoverExchange::bind(Queue::shared_ptr queue, const string&, const framing::FieldTable*) { + Lock l(lock); + sendUpdate(queue); + return queues.insert(queue).second; +} + +bool FailoverExchange::unbind(Queue::shared_ptr queue, const string&, const framing::FieldTable*) { + Lock l(lock); + return queues.erase(queue); +} + +bool FailoverExchange::isBound(Queue::shared_ptr queue, const string* const, const framing::FieldTable*) { + Lock l(lock); + return queues.find(queue) != queues.end(); +} + +void FailoverExchange::route(Deliverable&, const string& , const framing::FieldTable* ) { + QPID_LOG(warning, "Message received by exchange " << TYPE_NAME << " ignoring"); +} + +void FailoverExchange::sendUpdate(const Queue::shared_ptr& queue) { + // Called with lock held. + if (urls.empty()) return; + framing::Array array(0x95); // FIXME aconway 2008-10-06: Array is unusable like this. Need type constants or better mapping. + for (Urls::const_iterator i = urls.begin(); i != urls.end(); ++i) + array.add(boost::shared_ptr<Str16Value>(new Str16Value(i->str()))); + const ProtocolVersion v; + boost::intrusive_ptr<Message> msg(new Message); + AMQFrame command(MessageTransferBody(v, TYPE_NAME, 1, 0)); + command.setLastSegment(false); + msg->getFrames().append(command); + AMQHeaderBody header; + header.get<MessageProperties>(true)->setContentLength(0); + header.get<MessageProperties>(true)->getApplicationHeaders().setArray(TYPE_NAME, array); + AMQFrame headerFrame(header); + headerFrame.setFirstSegment(false); + msg->getFrames().append(headerFrame); + DeliverableMessage(msg).deliverTo(queue); +} + +}} // namespace cluster diff --git a/RC9/qpid/cpp/src/qpid/cluster/FailoverExchange.h b/RC9/qpid/cpp/src/qpid/cluster/FailoverExchange.h new file mode 100644 index 0000000000..738cd2a602 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/FailoverExchange.h @@ -0,0 +1,68 @@ +#ifndef QPID_CLUSTER_FAILOVEREXCHANGE_H +#define QPID_CLUSTER_FAILOVEREXCHANGE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/broker/Exchange.h" +#include "qpid/broker/DeliverableMessage.h" +#include "qpid/Url.h" + +#include <vector> +#include <set> + +namespace qpid { +namespace cluster { + +/** + * Failover exchange provides failover host list, as specified in AMQP 0-10. + */ +class FailoverExchange : public broker::Exchange +{ + public: + static const std::string TYPE_NAME; + + FailoverExchange(management::Manageable* parent); + + void setUrls(const std::vector<Url>&); + + // Exchange overrides + std::string getType() const; + bool bind(broker::Queue::shared_ptr queue, const std::string& routingKey, const framing::FieldTable* args); + bool unbind(broker::Queue::shared_ptr queue, const std::string& routingKey, const framing::FieldTable* args); + bool isBound(broker::Queue::shared_ptr queue, const std::string* const routingKey, const framing::FieldTable* const args); + void route(broker::Deliverable& msg, const std::string& routingKey, const framing::FieldTable* args); + + private: + void sendUpdate(const broker::Queue::shared_ptr&); + + typedef sys::Mutex::ScopedLock Lock; + typedef std::vector<Url> Urls; + typedef std::set<broker::Queue::shared_ptr> Queues; + + sys::Mutex lock; + Urls urls; + Queues queues; + +}; +}} // namespace qpid::cluster + +#endif /*!QPID_CLUSTER_FAILOVEREXCHANGE_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/Multicaster.cpp b/RC9/qpid/cpp/src/qpid/cluster/Multicaster.cpp new file mode 100644 index 0000000000..37d2f81b39 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/Multicaster.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 "Multicaster.h" +#include "Cpg.h" +#include "ClusterLeaveException.h" +#include "qpid/log/Statement.h" + + +namespace qpid { +namespace cluster { + +Multicaster::Multicaster(Cpg& cpg_, const boost::shared_ptr<sys::Poller>& poller) : + cpg(cpg_), queue(boost::bind(&Multicaster::sendMcast, this, _1), poller), + holding(true) +{ + queue.start(); +} + +void Multicaster::mcastControl(const framing::AMQBody& body, const ConnectionId& id) { + mcast(Event::control(body, id)); +} + +void Multicaster::mcastBuffer(const char* data, size_t size, const ConnectionId& id) { + Event e(DATA, id, size); + memcpy(e.getData(), data, size); + mcast(e); +} + +void Multicaster::mcast(const Event& e) { + { + sys::Mutex::ScopedLock l(lock); + if (e.getType() == DATA && e.isConnection() && holding) { + holdingQueue.push_back(e); + QPID_LOG(trace, " MCAST held: " << e ); + return; + } + } + queue.push(e); +} + + +void Multicaster::sendMcast(PollableEventQueue::Queue& values) { + try { + PollableEventQueue::Queue::iterator i = values.begin(); + while( i != values.end()) { + iovec iov = { const_cast<char*>(i->getStore()), i->getStoreSize() }; + if (!cpg.mcast(&iov, 1)) break; // returns false for flow control + QPID_LOG(trace, " MCAST " << *i); + ++i; + } + values.erase(values.begin(), i); + } + catch (const std::exception& e) { + throw ClusterLeaveException(e.what()); + } +} + +void Multicaster::release() { + sys::Mutex::ScopedLock l(lock); + holding = false; + std::for_each(holdingQueue.begin(), holdingQueue.end(), boost::bind(&Multicaster::mcast, this, _1)); + holdingQueue.clear(); +} + +}} // namespace qpid::cluster diff --git a/RC9/qpid/cpp/src/qpid/cluster/Multicaster.h b/RC9/qpid/cpp/src/qpid/cluster/Multicaster.h new file mode 100644 index 0000000000..8b306ce10e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/Multicaster.h @@ -0,0 +1,71 @@ +#ifndef QPID_CLUSTER_MULTICASTER_H +#define QPID_CLUSTER_MULTICASTER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "types.h" +#include "Event.h" +#include "qpid/sys/PollableQueue.h" +#include "qpid/sys/Mutex.h" +#include <boost/shared_ptr.hpp> +#include <sys/uio.h> // For iovec + +namespace qpid { + +namespace sys { +class Poller; +} + +namespace cluster { + +class Cpg; + +/** + * Multicast to the cluster. Shared, thread safe object. + */ +class Multicaster +{ + public: + /** Starts in holding mode: connection data events are held, other events are mcast */ + Multicaster(Cpg& cpg_, const boost::shared_ptr<sys::Poller>& ); + void mcastControl(const framing::AMQBody& controlBody, const ConnectionId&); + void mcastBuffer(const char*, size_t, const ConnectionId&); + void mcast(const Event& e); + /** End holding mode, held events are mcast */ + void release(); + + private: + typedef sys::PollableQueue<Event> PollableEventQueue; + typedef std::deque<Event> PlainEventQueue; + + void sendMcast(PollableEventQueue::Queue& ); + + sys::Mutex lock; + Cpg& cpg; + PollableEventQueue queue; + bool holding; + PlainEventQueue holdingQueue; + std::vector<struct ::iovec> ioVector; +}; +}} // namespace qpid::cluster + +#endif /*!QPID_CLUSTER_MULTICASTER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/NoOpConnectionOutputHandler.h b/RC9/qpid/cpp/src/qpid/cluster/NoOpConnectionOutputHandler.h new file mode 100644 index 0000000000..74a376a657 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/NoOpConnectionOutputHandler.h @@ -0,0 +1,47 @@ +#ifndef QPID_CLUSTER_NOOPCONNECTIONOUTPUTHANDLER_H +#define QPID_CLUSTER_NOOPCONNECTIONOUTPUTHANDLER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <qpid/sys/ConnectionOutputHandler.h> + +namespace qpid { + +namespace framing { class AMQFrame; } + +namespace cluster { + +/** + * Output handler for frames sent to noop connections. + * Simply discards frames. + */ +class NoOpConnectionOutputHandler : public sys::ConnectionOutputHandler +{ + public: + virtual void send(framing::AMQFrame&) {} + virtual void close() {} + virtual void activateOutput() {} + virtual void giveReadCredit(int32_t) {} +}; + +}} // namespace qpid::cluster + +#endif /*!QPID_CLUSTER_NOOPCONNECTIONOUTPUTHANDLER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp b/RC9/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp new file mode 100644 index 0000000000..075023caea --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/OutputInterceptor.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 "OutputInterceptor.h" +#include "Connection.h" +#include "Cluster.h" +#include "qpid/framing/ClusterConnectionDeliverDoOutputBody.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/log/Statement.h" +#include <boost/current_function.hpp> + + +namespace qpid { +namespace cluster { + +using namespace framing; + +OutputInterceptor::OutputInterceptor( + cluster::Connection& p, sys::ConnectionOutputHandler& h) + : parent(p), next(&h), sent(), writeEstimate(p.getCluster().getWriteEstimate()), + moreOutput(), doingOutput() +{} + +void OutputInterceptor::send(framing::AMQFrame& f) { + parent.getCluster().checkQuorum(); + next->send(f); + if (!parent.isCatchUp()) + sent += f.encodedSize(); +} + +void OutputInterceptor::activateOutput() { + if (parent.isCatchUp()) + next->activateOutput(); + else { + QPID_LOG(trace, parent << " activateOutput - sending doOutput"); + moreOutput = true; + sendDoOutput(); + } +} + +void OutputInterceptor::giveReadCredit(int32_t credit) { next->giveReadCredit(credit); } + +// Called in write thread when the IO layer has no more data to write. +// We do nothing in the write thread, we run doOutput only on delivery +// of doOutput requests. +bool OutputInterceptor::doOutput() { + QPID_LOG(trace, parent << " write idle."); + return false; +} + +// Delivery of doOutput allows us to run the real connection doOutput() +// which tranfers frames to the codec for writing. +// +void OutputInterceptor::deliverDoOutput(size_t requested) { + size_t buf = next->getBuffered(); + if (parent.isLocal()) + writeEstimate.delivered(requested, sent, buf); // Update the estimate. + + // Run the real doOutput() till we have added the requested data or there's nothing to output. + sent = 0; + do { + moreOutput = parent.getBrokerConnection().doOutput(); + } while (sent < requested && moreOutput); + sent += buf; // Include buffered data in the sent total. + + QPID_LOG(trace, "Delivered doOutput: requested=" << requested << " output=" << sent << " more=" << moreOutput); + + if (parent.isLocal() && moreOutput) { + QPID_LOG(trace, parent << " deliverDoOutput - sending doOutput, more output available."); + sendDoOutput(); + } + else + doingOutput = false; +} + +// Send a doOutput request if one is not already in flight. +void OutputInterceptor::sendDoOutput() { + if (!parent.isLocal()) return; + + doingOutput = true; + size_t request = writeEstimate.sending(getBuffered()); + + // Note we may send 0 size request if there's more than 2*estimate in the buffer. + // Send it anyway to keep the doOutput chain going until we are sure there's no more output + // (in deliverDoOutput) + // + parent.getCluster().getMulticast().mcastControl( + ClusterConnectionDeliverDoOutputBody(ProtocolVersion(), request), parent.getId()); + QPID_LOG(trace, parent << "Send doOutput request for " << request); +} + +void OutputInterceptor::setOutputHandler(sys::ConnectionOutputHandler& h) { + next = &h; +} + +void OutputInterceptor::close() { + next->close(); +} + +size_t OutputInterceptor::getBuffered() const { + return next->getBuffered(); +} + +}} // namespace qpid::cluster diff --git a/RC9/qpid/cpp/src/qpid/cluster/OutputInterceptor.h b/RC9/qpid/cpp/src/qpid/cluster/OutputInterceptor.h new file mode 100644 index 0000000000..0ac15e747a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/OutputInterceptor.h @@ -0,0 +1,75 @@ +#ifndef QPID_CLUSTER_OUTPUTINTERCEPTOR_H +#define QPID_CLUSTER_OUTPUTINTERCEPTOR_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "WriteEstimate.h" +#include "qpid/sys/ConnectionOutputHandler.h" +#include "qpid/broker/ConnectionFactory.h" +#include <boost/function.hpp> + +namespace qpid { +namespace framing { class AMQFrame; } +namespace cluster { + +class Connection; + +/** + * Interceptor for connection OutputHandler, manages outgoing message replication. + */ +class OutputInterceptor : public sys::ConnectionOutputHandler { + public: + OutputInterceptor(cluster::Connection& p, sys::ConnectionOutputHandler& h); + + // sys::ConnectionOutputHandler functions + void send(framing::AMQFrame& f); + void activateOutput(); + void giveReadCredit(int32_t); + void close(); + size_t getBuffered() const; + + // Delivery point for doOutput requests. + void deliverDoOutput(size_t requested); + // Intercept doOutput requests on Connection. + bool doOutput(); + + void setOutputHandler(sys::ConnectionOutputHandler& h); + + cluster::Connection& parent; + + private: + typedef sys::Mutex::ScopedLock Locker; + + void sendDoOutput(); + + mutable sys::Mutex lock; + sys::ConnectionOutputHandler* next; + size_t sent; + size_t lastDoOutput; + WriteEstimate writeEstimate; + bool moreOutput; + bool doingOutput; +}; + +}} // namespace qpid::cluster + +#endif /*!QPID_CLUSTER_OUTPUTINTERCEPTOR_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/ProxyInputHandler.h b/RC9/qpid/cpp/src/qpid/cluster/ProxyInputHandler.h new file mode 100644 index 0000000000..228f8d092d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/ProxyInputHandler.h @@ -0,0 +1,57 @@ +#ifndef QPID_CLUSTER_PROXYINPUTHANDLER_H +#define QPID_CLUSTER_PROXYINPUTHANDLER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/ConnectionInputHandler.h" +#include <boost/intrusive_ptr.hpp> + +namespace qpid { + +namespace framing { class AMQFrame; } + +namespace cluster { + +/** + * Proxies ConnectionInputHandler functions and ensures target.closed() + * is called, on deletion if not before. + */ +class ProxyInputHandler : public sys::ConnectionInputHandler +{ + public: + ProxyInputHandler(boost::intrusive_ptr<cluster::Connection> t) : target(t) {} + ~ProxyInputHandler() { closed(); } + + void received(framing::AMQFrame& f) { target->received(f); } + void closed() { if (target) target->closed(); target = 0; } + void idleOut() { target->idleOut(); } + void idleIn() { target->idleIn(); } + bool doOutput() { return target->doOutput(); } + bool hasOutput() { return target->hasOutput(); } + + private: + boost::intrusive_ptr<cluster::Connection> target; +}; + +}} // namespace qpid::cluster + +#endif /*!QPID_CLUSTER_PROXYINPUTHANDLER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/Quorum.h b/RC9/qpid/cpp/src/qpid/cluster/Quorum.h new file mode 100644 index 0000000000..f07b58dfa6 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/Quorum.h @@ -0,0 +1,32 @@ +#ifndef QPID_CLUSTER_QUORUM_H +#define QPID_CLUSTER_QUORUM_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "config.h" + +#if HAVE_LIBCMAN +#include "Quorum_cman.h" +#else +#include "Quorum_null.h" +#endif + +#endif /*!QPID_CLUSTER_QUORUM_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp b/RC9/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp new file mode 100644 index 0000000000..d5df758b40 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp @@ -0,0 +1,49 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Quorum_cman.h" +#include "qpid/log/Statement.h" +#include "qpid/Options.h" +#include "qpid/sys/Time.h" + +namespace qpid { +namespace cluster { + +Quorum::Quorum() : enable(false), cman(0) {} + +Quorum::~Quorum() { if (cman) cman_finish(cman); } + +void Quorum::init() { + QPID_LOG(info, "Waiting for cluster quorum"); + enable = true; + cman = cman_init(0); + if (cman == 0) throw ErrnoException("Can't connect to cman service"); + // FIXME aconway 2008-11-13: configure max wait. + for (int retry = 0; !cman_is_quorate(cman) && retry < 30; retry++) { + QPID_LOG(info, "Waiting for cluster quorum: " << sys::strError(errno)); + sys::sleep(1); + } + if (!cman_is_quorate(cman)) + throw ErrnoException("Timed out waiting for cluster quorum."); +} + +bool Quorum::isQuorate() { return enable ? cman_is_quorate(cman) : true; } + +}} // namespace qpid::cluster diff --git a/RC9/qpid/cpp/src/qpid/cluster/Quorum_cman.h b/RC9/qpid/cpp/src/qpid/cluster/Quorum_cman.h new file mode 100644 index 0000000000..d0f8b2c954 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/Quorum_cman.h @@ -0,0 +1,50 @@ +#ifndef QPID_CLUSTER_QUORUM_CMAN_H +#define QPID_CLUSTER_QUORUM_CMAN_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +extern "C" { +#include <libcman.h> +} + +namespace qpid { + +class Options; + +namespace cluster { + +class Quorum { + public: + Quorum(); + ~Quorum(); + void init(); + bool isQuorate(); + + private: + bool enable; + cman_handle_t cman; +}; + + +}} // namespace qpid::cluster + +#endif /*!QPID_CLUSTER_QUORUM_CMAN_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/Quorum_null.h b/RC9/qpid/cpp/src/qpid/cluster/Quorum_null.h new file mode 100644 index 0000000000..cbb6c20708 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/Quorum_null.h @@ -0,0 +1,37 @@ +#ifndef QPID_CLUSTER_QUORUM_NULL_H +#define QPID_CLUSTER_QUORUM_NULL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +namespace qpid { +namespace cluster { + +/** Null implementation of quorum. */ + +class Quorum { + public: + void init() {} + bool isQuorate() { return true; } +}; + +#endif /*!QPID_CLUSTER_QUORUM_NULL_H*/ + +}} // namespace qpid::cluster diff --git a/RC9/qpid/cpp/src/qpid/cluster/WriteEstimate.cpp b/RC9/qpid/cpp/src/qpid/cluster/WriteEstimate.cpp new file mode 100644 index 0000000000..4d840947f3 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/WriteEstimate.cpp @@ -0,0 +1,64 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "WriteEstimate.h" +#include "qpid/log/Statement.h" +#include <boost/current_function.hpp> + +namespace qpid { +namespace cluster { + +WriteEstimate::WriteEstimate(size_t initial) + : growing(true), estimate(initial), lastEstimate(initial) {} + +size_t WriteEstimate::sending(size_t buffered) { + // We want to send a doOutput request for enough data such + // that if estimate bytes are written before it is self + // delivered then what is left in the buffer plus the doOutput + // request will be estimate bytes. + + size_t predictLeft = (buffered > estimate) ? buffered - estimate : 0; + size_t request = (estimate > predictLeft) ? estimate - predictLeft : 0; + return request; +} + +size_t pad(size_t value) { return value + value/2; } + +void WriteEstimate::delivered(size_t last, size_t sent, size_t buffered) { + lastEstimate = last; + size_t wrote = sent > buffered ? sent - buffered : 0; + if (wrote == 0) // No change + return; + if (buffered > 0) { // Buffer was over-stocked, we wrote to capacity. + growing = false; + estimate = pad(wrote); // Estimate at 1.5 write for padding. + } + else if (wrote > estimate) { // Wrote everything, buffer was under-stocked + if (growing) + estimate = std::max(estimate*2, pad(wrote)); // Grow quickly if we have not yet seen an over-stock. + else + estimate = pad(wrote); + } +} + +}} // namespace qpid::cluster + + diff --git a/RC9/qpid/cpp/src/qpid/cluster/WriteEstimate.h b/RC9/qpid/cpp/src/qpid/cluster/WriteEstimate.h new file mode 100644 index 0000000000..97b1435fcc --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/WriteEstimate.h @@ -0,0 +1,69 @@ +#ifndef QPID_CLUSTER_WRITEESTIMATE_H +#define QPID_CLUSTER_WRITEESTIMATE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Mutex.h" + +namespace qpid { +namespace cluster { + +/** + * Estimate the amount of data that a connection can write between sending + * a doOutput notice and re-receiving it. + * + * The goal is to avoid ever write-idling the connection by sending + * the next doOutput request as soon as we've processed the previous + * one, such that data generated by the previous request will keep the + * writer busy till the next one is delivered. + * + */ +class WriteEstimate +{ + public: + WriteEstimate(size_t initial=4096); + + /** About to send a doOutput request. + * Update estimation state and return size for next request. + */ + size_t sending(size_t buffered); + + /** + * doOutput request just delivered, not yet executed. Update the estimate. + * and estimate how much data to request in the next onOutput + * request. 0 means don't send an onOutput request. + * + * @param delivered value in doOutput control. + */ + void delivered(size_t delivered, size_t sent, size_t buffered); + + /** Last estimate delivered, i.e. known to cluster */ + size_t getLastEstimate() const { return estimate; } + + private: + bool growing; + size_t estimate, lastEstimate; +}; + +}} // namespace qpid::cluster + +#endif /*!QPID_CLUSTER_WRITEESTIMATE_H*/ diff --git a/RC9/qpid/cpp/src/qpid/cluster/management-schema.xml b/RC9/qpid/cpp/src/qpid/cluster/management-schema.xml new file mode 100644 index 0000000000..da19387cc6 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/management-schema.xml @@ -0,0 +1,57 @@ +<schema package="org.apache.qpid.cluster"> + + <!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + --> + + <!-- Type information: + +Numeric types with "_wm" suffix are watermarked numbers. These are compound +values containing a current value, and a low and high water mark for the reporting +interval. The low and high water marks are set to the current value at the +beginning of each interval and track the minimum and maximum values of the statistic +over the interval respectively. + +Access rights for configuration elements: + +RO => Read Only +RC => Read/Create, can be set at create time only, read-only thereafter +RW => Read/Write + +If access rights are omitted for a property, they are assumed to be RO. + + --> + + <class name="Cluster"> + <property name="brokerRef" type="objId" references="Broker" access="RC" index="y" parentRef="y"/> + <property name="clusterName" type="sstr" access="RC" desc="Name of cluster this server is a member of"/> + <property name="clusterID" type="sstr" access="RO" desc="Globally uniquie ID (UUID) for this cluster instance"/> + <property name="publishedURL" type="sstr" access="RC" desc="URL this node advertizes itself as"/> + <property name="clusterSize" type="uint16" access="RO" desc="Number of brokers currently in the cluster"/> + <property name="status" type="sstr" access="RO" desc="Cluster node status (STALLED,ACTIVE,JOINING)"/> + <property name="members" type="lstr" access="RO" desc="List of member URLs delimited by ';'"/> + + <method name="stopClusterNode"/> + <method name="stopFullCluster"/> + + </class> + + + +</schema> + diff --git a/RC9/qpid/cpp/src/qpid/cluster/types.h b/RC9/qpid/cpp/src/qpid/cluster/types.h new file mode 100644 index 0000000000..857b19cc8a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/cluster/types.h @@ -0,0 +1,84 @@ +#ifndef QPID_CLUSTER_TYPES_H +#define QPID_CLUSTER_TYPES_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +#include "ClusterLeaveException.h" +#include "config.h" +#include "qpid/Url.h" + +#include <utility> +#include <iosfwd> +#include <string> + +#include <stdint.h> + +extern "C" { +#ifdef HAVE_OPENAIS_CPG_H +#include <openais/cpg.h> +#elif HAVE_COROSYNC_CPG_H +# include <corosync/cpg.h> +#else +# error "No cpg.h header file available" +#endif +} + +namespace qpid { +namespace cluster { + +class Connection; + +/** Types of cluster event. */ +enum EventType { DATA, CONTROL }; + +/** first=node-id, second=pid */ +struct MemberId : std::pair<uint32_t, uint32_t> { + explicit MemberId(uint64_t n) : std::pair<uint32_t,uint32_t>( n >> 32, n & 0xffffffff) {} + explicit MemberId(uint32_t node=0, uint32_t pid=0) : std::pair<uint32_t,uint32_t>(node, pid) {} + MemberId(const cpg_address& caddr) : std::pair<uint32_t,uint32_t>(caddr.nodeid, caddr.pid) {} + MemberId(const std::string&); // Decode from string. + uint32_t getNode() const { return first; } + uint32_t getPid() const { return second; } + operator uint64_t() const { return (uint64_t(first)<<32ull) + second; } + + // AsMethodBody as string, network byte order. + std::string str() const; +}; + +inline bool operator==(const cpg_address& caddr, const MemberId& id) { return id == MemberId(caddr); } + +std::ostream& operator<<(std::ostream&, const MemberId&); + +struct ConnectionId : public std::pair<MemberId, Connection*> { + ConnectionId(const MemberId& m=MemberId(), Connection* c=0) : std::pair<MemberId, Connection*> (m,c) {} + ConnectionId(uint64_t m, uint64_t c) + : std::pair<MemberId, Connection*>(MemberId(m), reinterpret_cast<Connection*>(c)) {} + MemberId getMember() const { return first; } + Connection* getPointer() const { return second; } +}; + +std::ostream& operator<<(std::ostream&, const ConnectionId&); + +}} // namespace qpid::cluster + +#endif /*!QPID_CLUSTER_TYPES_H*/ diff --git a/RC9/qpid/cpp/src/qpid/console/Agent.cpp b/RC9/qpid/cpp/src/qpid/console/Agent.cpp new file mode 100644 index 0000000000..8b5a8adbb4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/Agent.cpp @@ -0,0 +1,30 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Agent.h" + +std::ostream& qpid::console::operator<<(std::ostream& o, const Agent& agent) +{ + o << "Agent at bank " << agent.getBrokerBank() << "." << agent.getAgentBank() << + " (" << agent.getLabel() << ")"; + return o; +} + diff --git a/RC9/qpid/cpp/src/qpid/console/Agent.h b/RC9/qpid/cpp/src/qpid/console/Agent.h new file mode 100644 index 0000000000..3307a1b44b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/Agent.h @@ -0,0 +1,57 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _QPID_CONSOLE_AGENT_H_ +#define _QPID_CONSOLE_AGENT_H_ + +#include "Broker.h" + +namespace qpid { +namespace console { + + /** + * + * \ingroup qmfconsoleapi + */ + class Agent { + public: + typedef std::vector<Agent*> Vector; + + Agent(Broker* _broker, uint32_t _bank, const std::string& _label) : + broker(_broker), brokerBank(broker->getBrokerBank()), + agentBank(_bank), label(_label) {} + Broker* getBroker() const { return broker; } + uint32_t getBrokerBank() const { return brokerBank; } + uint32_t getAgentBank() const { return agentBank; } + const std::string& getLabel() const { return label; } + + private: + Broker* broker; + const uint32_t brokerBank; + const uint32_t agentBank; + const std::string label; + }; + + std::ostream& operator<<(std::ostream& o, const Agent& agent); +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/console/Broker.cpp b/RC9/qpid/cpp/src/qpid/console/Broker.cpp new file mode 100644 index 0000000000..c6b1be1d31 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/Broker.cpp @@ -0,0 +1,300 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Broker.h" +#include "Object.h" +#include "Value.h" +#include "SessionManager.h" +#include "ConsoleListener.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/SystemInfo.h" + +using namespace qpid::client; +using namespace qpid::console; +using namespace qpid::framing; +using namespace qpid::sys; +using namespace std; + +Broker::Broker(SessionManager& sm, ConnectionSettings& settings) : + sessionManager(sm), connected(false), connectionSettings(settings), + reqsOutstanding(1), syncInFlight(false), topicBound(false), methodObject(0), + connThreadBody(*this), connThread(connThreadBody) +{ + string osName; + string nodeName; + string release; + string version; + string machine; + + sys::SystemInfo::getSystemId(osName, nodeName, release, version, machine); + uint32_t pid = sys::SystemInfo::getParentProcessId(); + + stringstream text; + + text << "qmfc-cpp-" << nodeName << "-" << pid; + amqpSessionId = string(text.str()); + + QPID_LOG(debug, "Broker::Broker: constructed, amqpSessionId=" << amqpSessionId); +} + +Broker::~Broker() +{ +} + +string Broker::getUrl() const +{ + stringstream url; + url << connectionSettings.host << ":" << connectionSettings.port; + return url.str(); +} + +void Broker::encodeHeader(framing::Buffer& buf, uint8_t opcode, uint32_t seq) const +{ + buf.putOctet('A'); + buf.putOctet('M'); + buf.putOctet('2'); + buf.putOctet(opcode); + buf.putLong (seq); +} + +bool Broker::checkHeader(framing::Buffer& buf, uint8_t *opcode, uint32_t *seq) const +{ + if (buf.getSize() < 8) + return false; + + uint8_t h1 = buf.getOctet(); + uint8_t h2 = buf.getOctet(); + uint8_t h3 = buf.getOctet(); + + *opcode = buf.getOctet(); + *seq = buf.getLong(); + + return h1 == 'A' && h2 == 'M' && h3 == '2'; +} + +void Broker::received(client::Message& msg) +{ + string data = msg.getData(); + Buffer inBuffer(const_cast<char*>(data.c_str()), data.size()); + uint8_t opcode; + uint32_t sequence; + + if (checkHeader(inBuffer, &opcode, &sequence)) { + QPID_LOG(trace, "Broker::received: opcode=" << opcode << " seq=" << sequence); + + if (opcode == 'b') sessionManager.handleBrokerResp(this, inBuffer, sequence); + else if (opcode == 'p') sessionManager.handlePackageInd(this, inBuffer, sequence); + else if (opcode == 'z') sessionManager.handleCommandComplete(this, inBuffer, sequence); + else if (opcode == 'q') sessionManager.handleClassInd(this, inBuffer, sequence); + else if (opcode == 'm') sessionManager.handleMethodResp(this, inBuffer, sequence); + else if (opcode == 'h') sessionManager.handleHeartbeatInd(this, inBuffer, sequence); + else if (opcode == 'e') sessionManager.handleEventInd(this, inBuffer, sequence); + else if (opcode == 's') sessionManager.handleSchemaResp(this, inBuffer, sequence); + else if (opcode == 'c') sessionManager.handleContentInd(this, inBuffer, sequence, true, false); + else if (opcode == 'i') sessionManager.handleContentInd(this, inBuffer, sequence, false, true); + else if (opcode == 'g') sessionManager.handleContentInd(this, inBuffer, sequence, true, true); + } +} + +void Broker::resetAgents() +{ + for (AgentMap::iterator iter = agents.begin(); iter != agents.end(); iter++) { + if (sessionManager.listener != 0) + sessionManager.listener->delAgent(*(iter->second)); + delete iter->second; + } + + agents.clear(); + agents[0x0000000100000000LL] = new Agent(this, 0, "BrokerAgent"); +} + +void Broker::updateAgent(const Object& object) +{ + uint32_t brokerBank = object.attrUint("brokerBank"); + uint32_t agentBank = object.attrUint("agentBank"); + uint64_t agentKey = ((uint64_t) brokerBank << 32) | (uint64_t) agentBank; + AgentMap::iterator iter = agents.find(agentKey); + + if (object.isDeleted()) { + if (iter != agents.end()) { + if (sessionManager.listener != 0) + sessionManager.listener->delAgent(*(iter->second)); + delete iter->second; + agents.erase(iter); + } + } else { + if (iter == agents.end()) { + Agent* agent = new Agent(this, agentBank, object.attrString("label")); + agents[agentKey] = agent; + if (sessionManager.listener != 0) + sessionManager.listener->newAgent(*agent); + } + } +} + +void Broker::ConnectionThread::run() +{ + static const int delayMin(1); + static const int delayMax(128); + static const int delayFactor(2); + int delay(delayMin); + string dest("qmfc"); + + sessionId.generate(); + queueName << "qmfc-" << sessionId; + + while (true) { + try { + broker.topicBound = false; + broker.reqsOutstanding = 1; + connection.open(broker.connectionSettings); + session = connection.newSession(queueName.str()); + subscriptions = new client::SubscriptionManager(session); + + session.queueDeclare(arg::queue=queueName.str(), arg::autoDelete=true, + arg::exclusive=true); + session.exchangeBind(arg::exchange="amq.direct", arg::queue=queueName.str(), + arg::bindingKey=queueName.str()); + + subscriptions->setAcceptMode(ACCEPT_MODE_NONE); + subscriptions->setAcquireMode(ACQUIRE_MODE_PRE_ACQUIRED); + subscriptions->subscribe(broker, queueName.str(), dest); + subscriptions->setFlowControl(dest, FlowControl::unlimited()); + { + Mutex::ScopedLock _lock(connLock); + operational = true; + broker.resetAgents(); + broker.connected = true; + broker.sessionManager.handleBrokerConnect(&broker); + broker.sessionManager.startProtocol(&broker); + try { + Mutex::ScopedUnlock _unlock(connLock); + subscriptions->run(); + } catch (std::exception) {} + + operational = false; + broker.connected = false; + broker.sessionManager.handleBrokerDisconnect(&broker); + } + delay = delayMin; + delete subscriptions; + subscriptions = 0; + session.close(); + } catch (std::exception &e) { + QPID_LOG(debug, " outer exception: " << e.what()); + if (delay < delayMax) + delay *= delayFactor; + } + + ::sleep(delay); + } +} + +Broker::ConnectionThread::~ConnectionThread() +{ + if (subscriptions != 0) { + delete subscriptions; + } +} + +void Broker::ConnectionThread::sendBuffer(Buffer& buf, uint32_t length, + const string& exchange, const string& routingKey) +{ + { + Mutex::ScopedLock _lock(connLock); + if (!operational) + return; + } + + client::Message msg; + string data; + + buf.getRawData(data, length); + msg.getDeliveryProperties().setRoutingKey(routingKey); + msg.getMessageProperties().setReplyTo(ReplyTo("amq.direct", queueName.str())); + msg.setData(data); + try { + session.messageTransfer(arg::content=msg, arg::destination=exchange); + } catch(std::exception&) {} +} + +void Broker::ConnectionThread::bindExchange(const std::string& exchange, const std::string& key) +{ + { + Mutex::ScopedLock _lock(connLock); + if (!operational) + return; + } + + QPID_LOG(debug, "Broker::ConnectionThread::bindExchange: exchange=" << exchange << " key=" << key); + session.exchangeBind(arg::exchange=exchange, arg::queue=queueName.str(), + arg::bindingKey=key); +} + +void Broker::waitForStable() +{ + Mutex::ScopedLock l(lock); + if (reqsOutstanding == 0) + return; + syncInFlight = true; + while (reqsOutstanding != 0) { + bool result = cond.wait(lock, AbsTime(now(), TIME_SEC * sessionManager.settings.getTimeout)); + if (!result) + throw(Exception("Timed out waiting for broker to synchronize")); + } +} + +void Broker::incOutstanding() +{ + Mutex::ScopedLock l(lock); + reqsOutstanding++; +} + +void Broker::decOutstanding() +{ + Mutex::ScopedLock l(lock); + reqsOutstanding--; + if (reqsOutstanding == 0) { + if (!topicBound) { + topicBound = true; + for (vector<string>::const_iterator iter = sessionManager.bindingKeyList.begin(); + iter != sessionManager.bindingKeyList.end(); iter++) + connThreadBody.bindExchange("qpid.management", *iter); + } + if (syncInFlight) { + syncInFlight = false; + cond.notify(); + } + } +} + +void Broker::appendAgents(Agent::Vector& agentlist) const +{ + for (AgentMap::const_iterator iter = agents.begin(); iter != agents.end(); iter++) { + agentlist.push_back(iter->second); + } +} + +ostream& qpid::console::operator<<(ostream& o, const Broker& k) +{ + o << "Broker: " << k.connectionSettings.host << ":" << k.connectionSettings.port; + return o; +} diff --git a/RC9/qpid/cpp/src/qpid/console/Broker.h b/RC9/qpid/cpp/src/qpid/console/Broker.h new file mode 100644 index 0000000000..9df2380dff --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/Broker.h @@ -0,0 +1,130 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _QPID_CONSOLE_BROKER_H_ +#define _QPID_CONSOLE_BROKER_H_ + +#include "qpid/client/Connection.h" +#include "qpid/client/ConnectionSettings.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Session.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/client/Message.h" +#include "qpid/client/MessageListener.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Condition.h" +#include "qpid/Url.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/Uuid.h" +#include <string> +#include <iostream> + +namespace qpid { +namespace console { + class SessionManager; + class Agent; + class Object; + + /** + * + * \ingroup qpidconsoleapi + */ + class Broker : public client::MessageListener { + public: + Broker(SessionManager& sm, client::ConnectionSettings& settings); + ~Broker(); + + bool isConnected() const { return connected; } + const std::string& getError() const { return error; } + const std::string& getSessionId() const { return amqpSessionId; } + const framing::Uuid& getBrokerId() const { return brokerId; } + uint32_t getBrokerBank() const { return 1; } + void addBinding(const std::string& key) { + connThreadBody.bindExchange("qpid.management", key); + } + std::string getUrl() const; + + private: + friend class SessionManager; + friend class Object; + typedef std::map<uint64_t,Agent*> AgentMap; + static const int SYNC_TIME = 60; + + SessionManager& sessionManager; + AgentMap agents; + client::SubscriptionManager* subscription; + bool connected; + std::string error; + std::string amqpSessionId; + client::ConnectionSettings connectionSettings; + sys::Mutex lock; + sys::Condition cond; + framing::Uuid brokerId; + uint32_t reqsOutstanding; + bool syncInFlight; + bool topicBound; + Object* methodObject; + + friend class ConnectionThread; + class ConnectionThread : public sys::Runnable { + bool operational; + Broker& broker; + framing::Uuid sessionId; + client::Connection connection; + client::Session session; + client::SubscriptionManager* subscriptions; + std::stringstream queueName; + sys::Mutex connLock; + void run(); + public: + ConnectionThread(Broker& _broker) : + operational(false), broker(_broker), subscriptions(0) {} + ~ConnectionThread(); + void sendBuffer(qpid::framing::Buffer& buf, + uint32_t length, + const std::string& exchange = "qpid.management", + const std::string& routingKey = "broker"); + void bindExchange(const std::string& exchange, const std::string& key); + }; + + ConnectionThread connThreadBody; + sys::Thread connThread; + + void encodeHeader(framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0) const; + bool checkHeader(framing::Buffer& buf, uint8_t *opcode, uint32_t *seq) const; + void received(client::Message& msg); + void resetAgents(); + void updateAgent(const Object& object); + void waitForStable(); + void incOutstanding(); + void decOutstanding(); + void setBrokerId(const framing::Uuid& id) { brokerId = id; } + void appendAgents(std::vector<Agent*>& agents) const; + + friend std::ostream& operator<<(std::ostream& o, const Broker& k); + }; + + std::ostream& operator<<(std::ostream& o, const Broker& k); +} +} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/console/ClassKey.cpp b/RC9/qpid/cpp/src/qpid/console/ClassKey.cpp new file mode 100644 index 0000000000..6aa2bcb117 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/ClassKey.cpp @@ -0,0 +1,104 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ClassKey.h" +#include <string.h> + +using namespace std; +using namespace qpid::console; + +ClassKey::ClassKey(const string& _package, const string& _name, const uint8_t* _hash) : + package(_package), name(_name) +{ + ::memcpy(hash, _hash, HASH_SIZE); +} + +string ClassKey::getHashString() const +{ + char cstr[36]; + ::sprintf(cstr, "%02x%02x%02x%02x-%02x%02x%02x%02x-%02x%02x%02x%02x-%02x%02x%02x%02x", + hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], + hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]); + return string(cstr); +} + +string ClassKey::str() const +{ + string result(package + ":" + name + "(" + getHashString() + ")"); + return result; +} + +bool ClassKey::operator==(const ClassKey& other) const +{ + return ::memcmp(hash, other.hash, HASH_SIZE) == 0 && + name == other.name && + package == other.package; +} + +bool ClassKey::operator!=(const ClassKey& other) const +{ + return !(*this == other); +} + +bool ClassKey::operator<(const ClassKey& other) const +{ + int cmp = ::memcmp(hash, other.hash, HASH_SIZE); + if (cmp != 0) + return cmp < 0; + cmp = name.compare(other.name); + if (cmp != 0) + return cmp < 0; + return package < other.package; +} + +bool ClassKey::operator>(const ClassKey& other) const +{ + int cmp = ::memcmp(hash, other.hash, HASH_SIZE); + if (cmp != 0) + return cmp > 0; + cmp = name.compare(other.name); + if (cmp != 0) + return cmp > 0; + return package > other.package; +} + +bool ClassKey::operator<=(const ClassKey& other) const +{ + return !(*this > other); +} + +bool ClassKey::operator>=(const ClassKey& other) const +{ + return !(*this < other); +} + +void ClassKey::encode(framing::Buffer& buffer) const +{ + buffer.putShortString(package); + buffer.putShortString(name); + buffer.putBin128(const_cast<uint8_t*>(hash)); +} + +ostream& qpid::console::operator<<(ostream& o, const ClassKey& k) +{ + o << k.str(); + return o; +} diff --git a/RC9/qpid/cpp/src/qpid/console/ClassKey.h b/RC9/qpid/cpp/src/qpid/console/ClassKey.h new file mode 100644 index 0000000000..f6617e22d5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/ClassKey.h @@ -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. + * + */ +#ifndef _QPID_CONSOLE_CLASSKEY_H_ +#define _QPID_CONSOLE_CLASSKEY_H_ + +#include <string> +#include "Package.h" +#include "qpid/framing/Buffer.h" + +namespace qpid { +namespace console { + + /** + * + * \ingroup qmfconsoleapi + */ + class ClassKey { + public: + static const int HASH_SIZE = 16; + + ClassKey(const std::string& package, const std::string& name, const uint8_t* hash); + + const std::string& getPackageName() const { return package; } + const std::string& getClassName() const { return name; } + const uint8_t* getHash() const { return hash; } + std::string getHashString() const; + std::string str() const; + bool operator==(const ClassKey& other) const; + bool operator!=(const ClassKey& other) const; + bool operator<(const ClassKey& other) const; + bool operator>(const ClassKey& other) const; + bool operator<=(const ClassKey& other) const; + bool operator>=(const ClassKey& other) const; + void encode(framing::Buffer& buffer) const; + + private: + std::string package; + std::string name; + uint8_t hash[HASH_SIZE]; + }; + + std::ostream& operator<<(std::ostream& o, const ClassKey& k); +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/console/ConsoleListener.h b/RC9/qpid/cpp/src/qpid/console/ConsoleListener.h new file mode 100644 index 0000000000..d0db6034f6 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/ConsoleListener.h @@ -0,0 +1,96 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _QPID_CONSOLE_CONSOLE_LISTENER_H_ +#define _QPID_CONSOLE_CONSOLE_LISTENER_H_ + +#include <string> +#include "Broker.h" +#include "ClassKey.h" +#include "Object.h" +#include "Event.h" + +namespace qpid { +namespace console { + + /** + * Implement a subclass of ConsoleListener and subscribe it using + * the SessionManager to receive indications. + * + * \ingroup qmfconsoleapi + */ + class ConsoleListener{ + public: + virtual ~ConsoleListener() {}; + + /** Invoked when a connection is established to a broker + */ + virtual void brokerConnected(const Broker&) {} + + /** Invoked when the connection to a broker is lost + */ + virtual void brokerDisconnected(const Broker&) {} + + /** Invoked when a QMF package is discovered. + */ + virtual void newPackage(const std::string&) {} + + /** Invoked when a new class is discovered. Session.getSchema can be + * used to obtain details about the class. + */ + virtual void newClass(const ClassKey&) {} + + /** Invoked when a QMF agent is discovered. + */ + virtual void newAgent(const Agent&) {} + + /** Invoked when a QMF agent disconects. + */ + virtual void delAgent(const Agent&) {} + + /** Invoked when an object is updated. + */ + virtual void objectProps(Broker&, Object&) {} + + /** Invoked when an object is updated. + */ + virtual void objectStats(Broker&, Object&) {} + + /** Invoked when an event is raised. + */ + virtual void event(Event&) {} + + /** + */ + //virtual void heartbeat(Agent&, uint64_t) {} + + /** + */ + virtual void brokerInfo(Broker&) {} + + /** + */ + //virtual void methodResponse(Broker&, uint32_t seq, MethodResponse&) {} + }; +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/console/Event.cpp b/RC9/qpid/cpp/src/qpid/console/Event.cpp new file mode 100644 index 0000000000..51f043159c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/Event.cpp @@ -0,0 +1,205 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Broker.h" +#include "ClassKey.h" +#include "Schema.h" +#include "Event.h" +#include "Value.h" +#include "qpid/sys/Time.h" +#include "qpid/framing/Buffer.h" + +using namespace qpid::console; +using namespace std; +using qpid::framing::Uuid; +using qpid::framing::FieldTable; + +Event::Event(Broker* _broker, SchemaClass* _schema, framing::Buffer& buffer) : + broker(_broker), schema(_schema) +{ + timestamp = buffer.getLongLong(); + severity = (Severity) buffer.getOctet(); + for (vector<SchemaArgument*>::const_iterator aIter = schema->arguments.begin(); + aIter != schema->arguments.end(); aIter++) { + SchemaArgument* argument = *aIter; + attributes[argument->name] = argument->decodeValue(buffer); + } +} + +const ClassKey& Event::getClassKey() const +{ + return schema->getClassKey(); +} + +string Event::getSeverityString() const +{ + switch (severity) { + case EMERGENCY : return string("EMER"); + case ALERT : return string("ALERT"); + case CRITICAL : return string("CRIT"); + case ERROR : return string("ERROR"); + case WARNING : return string("WARN"); + case NOTICE : return string("NOTIC"); + case INFO : return string("INFO"); + case DEBUG : return string("DEBUG"); + } + return string("<UNKNOWN>"); +} + +ObjectId Event::attrRef(const string& key) const +{ + Object::AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return ObjectId(); + Value::Ptr val = iter->second; + if (!val->isObjectId()) + return ObjectId(); + return val->asObjectId(); +} + +uint32_t Event::attrUint(const string& key) const +{ + Object::AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return 0; + Value::Ptr val = iter->second; + if (!val->isUint()) + return 0; + return val->asUint(); +} + +int32_t Event::attrInt(const string& key) const +{ + Object::AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return 0; + Value::Ptr val = iter->second; + if (!val->isInt()) + return 0; + return val->asInt(); +} + +uint64_t Event::attrUint64(const string& key) const +{ + Object::AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return 0; + Value::Ptr val = iter->second; + if (!val->isUint64()) + return 0; + return val->asUint64(); +} + +int64_t Event::attrInt64(const string& key) const +{ + Object::AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return 0; + Value::Ptr val = iter->second; + if (!val->isInt64()) + return 0; + return val->asInt64(); +} + +string Event::attrString(const string& key) const +{ + Object::AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return string(); + Value::Ptr val = iter->second; + if (!val->isString()) + return string(); + return val->asString(); +} + +bool Event::attrBool(const string& key) const +{ + Object::AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return false; + Value::Ptr val = iter->second; + if (!val->isBool()) + return false; + return val->asBool(); +} + +float Event::attrFloat(const string& key) const +{ + Object::AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return 0.0; + Value::Ptr val = iter->second; + if (!val->isFloat()) + return 0.0; + return val->asFloat(); +} + +double Event::attrDouble(const string& key) const +{ + Object::AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return 0.0; + Value::Ptr val = iter->second; + if (!val->isDouble()) + return 0.0; + return val->asDouble(); +} + +Uuid Event::attrUuid(const string& key) const +{ + Object::AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return Uuid(); + Value::Ptr val = iter->second; + if (!val->isUuid()) + return Uuid(); + return val->asUuid(); +} + +FieldTable Event::attrMap(const string& key) const +{ + Object::AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return FieldTable(); + Value::Ptr val = iter->second; + if (!val->isMap()) + return FieldTable(); + return val->asMap(); +} + + +std::ostream& qpid::console::operator<<(std::ostream& o, const Event& event) +{ + const ClassKey& key = event.getClassKey(); + sys::AbsTime aTime(sys::AbsTime(), sys::Duration(event.getTimestamp())); + o << aTime << " " << event.getSeverityString() << " " << + key.getPackageName() << ":" << key.getClassName() << + " broker=" << event.getBroker()->getUrl(); + + const Object::AttributeMap& attributes = event.getAttributes(); + for (Object::AttributeMap::const_iterator iter = attributes.begin(); + iter != attributes.end(); iter++) { + o << " " << iter->first << "=" << iter->second->str(); + } + return o; +} + + diff --git a/RC9/qpid/cpp/src/qpid/console/Event.h b/RC9/qpid/cpp/src/qpid/console/Event.h new file mode 100644 index 0000000000..c212b72889 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/Event.h @@ -0,0 +1,83 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _QPID_CONSOLE_EVENT_H_ +#define _QPID_CONSOLE_EVENT_H_ + +#include "Object.h" +#include "qpid/framing/Uuid.h" +#include "qpid/framing/FieldTable.h" + +namespace qpid { +namespace framing { + class Buffer; +} +namespace console { + + class Broker; + class SchemaClass; + class ClassKey; + + /** + * + * \ingroup qmfconsoleapi + */ + class Event { + public: + typedef enum { + EMERGENCY = 0, ALERT = 1, CRITICAL = 2, ERROR = 3, WARNING = 4, + NOTICE = 5, INFO = 6, DEBUG = 7 + } Severity; + + Event(Broker* broker, SchemaClass* schemaClass, framing::Buffer& buffer); + Broker* getBroker() const { return broker; } + const ClassKey& getClassKey() const; + SchemaClass* getSchema() const { return schema; } + const Object::AttributeMap& getAttributes() const { return attributes; } + uint64_t getTimestamp() const { return timestamp; } + uint8_t getSeverity() const { return severity; } + std::string getSeverityString() const; + + ObjectId attrRef(const std::string& key) const; + uint32_t attrUint(const std::string& key) const; + int32_t attrInt(const std::string& key) const; + uint64_t attrUint64(const std::string& key) const; + int64_t attrInt64(const std::string& key) const; + std::string attrString(const std::string& key) const; + bool attrBool(const std::string& key) const; + float attrFloat(const std::string& key) const; + double attrDouble(const std::string& key) const; + framing::Uuid attrUuid(const std::string& key) const; + framing::FieldTable attrMap(const std::string& key) const; + + private: + Broker* broker; + SchemaClass* schema; + uint64_t timestamp; + Severity severity; + Object::AttributeMap attributes; + }; + + std::ostream& operator<<(std::ostream& o, const Event& event); +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/console/Object.cpp b/RC9/qpid/cpp/src/qpid/console/Object.cpp new file mode 100644 index 0000000000..da8ab962e0 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/Object.cpp @@ -0,0 +1,383 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SessionManager.h" +#include "Broker.h" +#include "Object.h" +#include "Schema.h" +#include "ClassKey.h" +#include "Value.h" +#include "qpid/framing/Buffer.h" +#include "qpid/sys/Mutex.h" + +using namespace qpid::console; +using namespace qpid::sys; +using namespace std; +using qpid::framing::Uuid; +using qpid::framing::FieldTable; + +void Object::AttributeMap::addRef(const string& key, const ObjectId& val) +{ + (*this)[key] = Value::Ptr(new RefValue(val)); +} + +void Object::AttributeMap::addUint(const string& key, uint32_t val) +{ + (*this)[key] = Value::Ptr(new UintValue(val)); +} + +void Object::AttributeMap::addInt(const string& key, int32_t val) +{ + (*this)[key] = Value::Ptr(new IntValue(val)); +} + +void Object::AttributeMap::addUint64(const string& key, uint64_t val) +{ + (*this)[key] = Value::Ptr(new Uint64Value(val)); +} + +void Object::AttributeMap::addInt64(const string& key, int64_t val) +{ + (*this)[key] = Value::Ptr(new Int64Value(val)); +} + +void Object::AttributeMap::addString(const string& key, const string& val) +{ + (*this)[key] = Value::Ptr(new StringValue(val)); +} + +void Object::AttributeMap::addBool(const string& key, bool val) +{ + (*this)[key] = Value::Ptr(new BoolValue(val)); +} + +void Object::AttributeMap::addFloat(const string& key, float val) +{ + (*this)[key] = Value::Ptr(new FloatValue(val)); +} + +void Object::AttributeMap::addDouble(const string& key, double val) +{ + (*this)[key] = Value::Ptr(new DoubleValue(val)); +} + +void Object::AttributeMap::addUuid(const string& key, const framing::Uuid& val) +{ + (*this)[key] = Value::Ptr(new UuidValue(val)); +} + +void Object::AttributeMap::addMap(const string& key, const framing::FieldTable& val) +{ + (*this)[key] = Value::Ptr(new MapValue(val)); +} + +Object::Object(Broker* b, SchemaClass* s, framing::Buffer& buffer, bool prop, bool stat) : + broker(b), schema(s), pendingMethod(0) +{ + currentTime = buffer.getLongLong(); + createTime = buffer.getLongLong(); + deleteTime = buffer.getLongLong(); + objectId.decode(buffer); + + if (prop) { + set<string> excludes; + parsePresenceMasks(buffer, excludes); + for (vector<SchemaProperty*>::const_iterator pIter = schema->properties.begin(); + pIter != schema->properties.end(); pIter++) { + SchemaProperty* property = *pIter; + if (excludes.count(property->name) != 0) { + attributes[property->name] = Value::Ptr(new NullValue()); + } else { + attributes[property->name] = property->decodeValue(buffer); + } + } + } + + if (stat) { + for (vector<SchemaStatistic*>::const_iterator sIter = schema->statistics.begin(); + sIter != schema->statistics.end(); sIter++) { + SchemaStatistic* statistic = *sIter; + attributes[statistic->name] = statistic->decodeValue(buffer); + } + } +} + +Object::~Object() {} + +const ClassKey& Object::getClassKey() const +{ + return schema->getClassKey(); +} + +string Object::getIndex() const +{ + string result; + + for (vector<SchemaProperty*>::const_iterator pIter = schema->properties.begin(); + pIter != schema->properties.end(); pIter++) { + SchemaProperty* property = *pIter; + if (property->isIndex) { + AttributeMap::const_iterator vIter = attributes.find(property->name); + if (vIter != attributes.end()) { + if (!result.empty()) + result += ":"; + result += vIter->second->str(); + } + } + } + return result; +} + +void Object::mergeUpdate(const Object& /*updated*/) +{ + // TODO +} + +void Object::invokeMethod(const string name, const AttributeMap& args, MethodResponse& result) +{ + for (vector<SchemaMethod*>::const_iterator iter = schema->methods.begin(); + iter != schema->methods.end(); iter++) { + if ((*iter)->name == name) { + SchemaMethod* method = *iter; + char rawbuffer[65536]; + framing::Buffer buffer(rawbuffer, 65536); + uint32_t sequence = broker->sessionManager.sequenceManager.reserve("method"); + pendingMethod = method; + broker->methodObject = this; + broker->encodeHeader(buffer, 'M', sequence); + objectId.encode(buffer); + schema->key.encode(buffer); + buffer.putShortString(name); + + for (vector<SchemaArgument*>::const_iterator aIter = method->arguments.begin(); + aIter != method->arguments.end(); aIter++) { + SchemaArgument* arg = *aIter; + if (arg->dirInput) { + AttributeMap::const_iterator attr = args.find(arg->name); + if (attr != args.end()) { + ValueFactory::encodeValue(arg->typeCode, attr->second, buffer); + } else { + // TODO Use the default value instead of throwing + throw Exception("Missing arguments in method call"); + } + } + } + + uint32_t length = buffer.getPosition(); + buffer.reset(); + stringstream routingKey; + routingKey << "agent." << objectId.getBrokerBank() << "." << objectId.getAgentBank(); + broker->connThreadBody.sendBuffer(buffer, length, "qpid.management", routingKey.str()); + + { + Mutex::ScopedLock l(broker->lock); + bool ok = true; + while (pendingMethod != 0 && ok) { + ok = broker->cond.wait(broker->lock, AbsTime(now(), broker->sessionManager.settings.methodTimeout * TIME_SEC)); + } + + if (!ok) { + result.code = 0x1001; + result.text.assign("Method call timed out"); + result.arguments.clear(); + } else { + result = methodResponse; + } + } + } + } +} + +void Object::handleMethodResp(framing::Buffer& buffer, uint32_t sequence) +{ + broker->sessionManager.sequenceManager.release(sequence); + methodResponse.code = buffer.getLong(); + buffer.getMediumString(methodResponse.text); + methodResponse.arguments.clear(); + + for (vector<SchemaArgument*>::const_iterator aIter = pendingMethod->arguments.begin(); + aIter != pendingMethod->arguments.end(); aIter++) { + SchemaArgument* arg = *aIter; + if (arg->dirOutput) { + methodResponse.arguments[arg->name] = arg->decodeValue(buffer); + } + } + + { + Mutex::ScopedLock l(broker->lock); + pendingMethod = 0; + broker->cond.notify(); + } +} + +ObjectId Object::attrRef(const string& key) const +{ + AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return ObjectId(); + Value::Ptr val = iter->second; + if (!val->isObjectId()) + return ObjectId(); + return val->asObjectId(); +} + +uint32_t Object::attrUint(const string& key) const +{ + AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return 0; + Value::Ptr val = iter->second; + if (!val->isUint()) + return 0; + return val->asUint(); +} + +int32_t Object::attrInt(const string& key) const +{ + AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return 0; + Value::Ptr val = iter->second; + if (!val->isInt()) + return 0; + return val->asInt(); +} + +uint64_t Object::attrUint64(const string& key) const +{ + AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return 0; + Value::Ptr val = iter->second; + if (!val->isUint64()) + return 0; + return val->asUint64(); +} + +int64_t Object::attrInt64(const string& key) const +{ + AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return 0; + Value::Ptr val = iter->second; + if (!val->isInt64()) + return 0; + return val->asInt64(); +} + +string Object::attrString(const string& key) const +{ + AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return string(); + Value::Ptr val = iter->second; + if (!val->isString()) + return string(); + return val->asString(); +} + +bool Object::attrBool(const string& key) const +{ + AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return false; + Value::Ptr val = iter->second; + if (!val->isBool()) + return false; + return val->asBool(); +} + +float Object::attrFloat(const string& key) const +{ + AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return 0.0; + Value::Ptr val = iter->second; + if (!val->isFloat()) + return 0.0; + return val->asFloat(); +} + +double Object::attrDouble(const string& key) const +{ + AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return 0.0; + Value::Ptr val = iter->second; + if (!val->isDouble()) + return 0.0; + return val->asDouble(); +} + +Uuid Object::attrUuid(const string& key) const +{ + AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return Uuid(); + Value::Ptr val = iter->second; + if (!val->isUuid()) + return Uuid(); + return val->asUuid(); +} + +FieldTable Object::attrMap(const string& key) const +{ + AttributeMap::const_iterator iter = attributes.find(key); + if (iter == attributes.end()) + return FieldTable(); + Value::Ptr val = iter->second; + if (!val->isMap()) + return FieldTable(); + return val->asMap(); +} + +void Object::parsePresenceMasks(framing::Buffer& buffer, set<string>& excludeList) +{ + excludeList.clear(); + uint8_t bit = 0; + uint8_t mask = 0; + + for (vector<SchemaProperty*>::const_iterator pIter = schema->properties.begin(); + pIter != schema->properties.end(); pIter++) { + SchemaProperty* property = *pIter; + if (property->isOptional) { + if (bit == 0) { + mask = buffer.getOctet(); + bit = 1; + } + if ((mask & bit) == 0) + excludeList.insert(property->name); + if (bit == 0x80) + bit = 0; + else + bit = bit << 1; + } + } +} + +ostream& qpid::console::operator<<(ostream& o, const Object& object) +{ + const ClassKey& key = object.getClassKey(); + o << key.getPackageName() << ":" << key.getClassName() << "[" << object.getObjectId() << "] " << + object.getIndex(); + return o; +} + diff --git a/RC9/qpid/cpp/src/qpid/console/Object.h b/RC9/qpid/cpp/src/qpid/console/Object.h new file mode 100644 index 0000000000..54a3e0f6e8 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/Object.h @@ -0,0 +1,119 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _QPID_CONSOLE_OBJECT_H_ +#define _QPID_CONSOLE_OBJECT_H_ + +#include "ObjectId.h" +#include "qpid/framing/Uuid.h" +#include "qpid/framing/FieldTable.h" +#include <boost/shared_ptr.hpp> +#include <map> +#include <set> +#include <vector> + +namespace qpid { +namespace framing { + class Buffer; +} +namespace console { + + class Broker; + class SchemaClass; + class SchemaMethod; + class ObjectId; + class ClassKey; + class Value; + + /** + * \ingroup qmfconsoleapi + */ + struct MethodResponse { + uint32_t code; + std::string text; + std::map<std::string, boost::shared_ptr<Value> > arguments; + }; + + class Object { + public: + typedef std::vector<Object> Vector; + struct AttributeMap : public std::map<std::string, boost::shared_ptr<Value> > { + void addRef(const std::string& key, const ObjectId& val); + void addUint(const std::string& key, uint32_t val); + void addInt(const std::string& key, int32_t val); + void addUint64(const std::string& key, uint64_t val); + void addInt64(const std::string& key, int64_t val); + void addString(const std::string& key, const std::string& val); + void addBool(const std::string& key, bool val); + void addFloat(const std::string& key, float val); + void addDouble(const std::string& key, double val); + void addUuid(const std::string& key, const framing::Uuid& val); + void addMap(const std::string& key, const framing::FieldTable& val); + }; + + Object(Broker* broker, SchemaClass* schemaClass, framing::Buffer& buffer, bool prop, bool stat); + ~Object(); + + Broker* getBroker() const { return broker; } + const ObjectId& getObjectId() const { return objectId; } + const ClassKey& getClassKey() const; + SchemaClass* getSchema() const { return schema; } + uint64_t getCurrentTime() const { return currentTime; } + uint64_t getCreateTime() const { return createTime; } + uint64_t getDeleteTime() const { return deleteTime; } + bool isDeleted() const { return deleteTime != 0; } + std::string getIndex() const; + void mergeUpdate(const Object& updated); + const AttributeMap& getAttributes() const { return attributes; } + void invokeMethod(const std::string name, const AttributeMap& args, MethodResponse& result); + void handleMethodResp(framing::Buffer& buffer, uint32_t sequence); + + ObjectId attrRef(const std::string& key) const; + uint32_t attrUint(const std::string& key) const; + int32_t attrInt(const std::string& key) const; + uint64_t attrUint64(const std::string& key) const; + int64_t attrInt64(const std::string& key) const; + std::string attrString(const std::string& key) const; + bool attrBool(const std::string& key) const; + float attrFloat(const std::string& key) const; + double attrDouble(const std::string& key) const; + framing::Uuid attrUuid(const std::string& key) const; + framing::FieldTable attrMap(const std::string& key) const; + + private: + Broker* broker; + SchemaClass* schema; + ObjectId objectId; + uint64_t currentTime; + uint64_t createTime; + uint64_t deleteTime; + AttributeMap attributes; + SchemaMethod* pendingMethod; + MethodResponse methodResponse; + + void parsePresenceMasks(framing::Buffer& buffer, std::set<std::string>& excludeList); + }; + + std::ostream& operator<<(std::ostream& o, const Object& object); +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/console/ObjectId.cpp b/RC9/qpid/cpp/src/qpid/console/ObjectId.cpp new file mode 100644 index 0000000000..535e59e88d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/ObjectId.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 "ObjectId.h" +#include "qpid/framing/Buffer.h" + +using namespace qpid::console; +using namespace std; + +ObjectId::ObjectId(framing::Buffer& buffer) +{ + decode(buffer); +} + +void ObjectId::decode(framing::Buffer& buffer) +{ + first = buffer.getLongLong(); + second = buffer.getLongLong(); +} + +void ObjectId::encode(framing::Buffer& buffer) +{ + buffer.putLongLong(first); + buffer.putLongLong(second); +} + +ostream& qpid::console::operator<<(ostream& o, const ObjectId& id) +{ + o << (int) id.getFlags() << "-" << id.getSequence() << "-" << id.getBrokerBank() << "-" << + id.getAgentBank() << "-" << id.getObject(); + return o; +} + + diff --git a/RC9/qpid/cpp/src/qpid/console/ObjectId.h b/RC9/qpid/cpp/src/qpid/console/ObjectId.h new file mode 100644 index 0000000000..c9c2fc852a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/ObjectId.h @@ -0,0 +1,60 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _QPID_CONSOLE_OBJECTID_H +#define _QPID_CONSOLE_OBJECTID_H + +#include <iostream> + +namespace qpid { +namespace framing { + class Buffer; +} +namespace console { + + /** + * + * \ingroup qmfconsoleapi + */ + class ObjectId { + public: + ObjectId() : first(0), second(0) {} + ObjectId(framing::Buffer& buffer); + + uint8_t getFlags() const { return (first & 0xF000000000000000LL) >> 60; } + uint16_t getSequence() const { return (first & 0x0FFF000000000000LL) >> 48; } + uint32_t getBrokerBank() const { return (first & 0x0000FFFFF0000000LL) >> 28; } + uint32_t getAgentBank() const { return first & 0x000000000FFFFFFFLL; } + uint64_t getObject() const { return second; } + bool isDurable() const { return getSequence() == 0; } + void decode(framing::Buffer& buffer); + void encode(framing::Buffer& buffer); + void setValue(uint64_t f, uint64_t s) { first = f; second = s; } + + private: + uint64_t first; + uint64_t second; + }; + + std::ostream& operator<<(std::ostream& o, const ObjectId& id); +} +} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/console/Package.cpp b/RC9/qpid/cpp/src/qpid/console/Package.cpp new file mode 100644 index 0000000000..81a04445f2 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/Package.cpp @@ -0,0 +1,41 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Package.h" + +using namespace qpid::console; + +SchemaClass* Package::getClass(const std::string& className, uint8_t* hash) +{ + NameHash key(className, hash); + ClassMap::iterator iter = classes.find(key); + if (iter != classes.end()) + return iter->second; + return 0; +} + +void Package::addClass(const std::string& className, uint8_t* hash, SchemaClass* schemaClass) +{ + NameHash key(className, hash); + ClassMap::iterator iter = classes.find(key); + if (iter == classes.end()) + classes[key] = schemaClass; +} diff --git a/RC9/qpid/cpp/src/qpid/console/Package.h b/RC9/qpid/cpp/src/qpid/console/Package.h new file mode 100644 index 0000000000..a8679dff19 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/Package.h @@ -0,0 +1,76 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _QPID_CONSOLE_PACKAGE_H_ +#define _QPID_CONSOLE_PACKAGE_H_ + +#include <string> +#include <map> + +namespace qpid { +namespace console { + class SchemaClass; + + /** + * + * \ingroup qmfconsoleapi + */ + class Package { + public: + Package(const std::string& n) : name(n) {} + const std::string& getName() const { return name; } + + private: + friend class SessionManager; + struct NameHash { + std::string name; + uint8_t hash[16]; + NameHash(const std::string& n, const uint8_t* h) : name(n) { + for (int i = 0; i < 16; i++) + hash[i] = h[i]; + } + }; + + struct NameHashComp { + bool operator() (const NameHash& lhs, const NameHash& rhs) const + { + if (lhs.name != rhs.name) + return lhs.name < rhs.name; + else + for (int i = 0; i < 16; i++) + if (lhs.hash[i] != rhs.hash[i]) + return lhs.hash[i] < rhs.hash[i]; + return false; + } + }; + + typedef std::map<NameHash, SchemaClass*, NameHashComp> ClassMap; + + const std::string name; + ClassMap classes; + + SchemaClass* getClass(const std::string& className, uint8_t* hash); + void addClass(const std::string& className, uint8_t* hash, + SchemaClass* schemaClass); + }; +} +} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/console/Schema.cpp b/RC9/qpid/cpp/src/qpid/console/Schema.cpp new file mode 100644 index 0000000000..31d947cdd5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/Schema.cpp @@ -0,0 +1,155 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Schema.h" +#include "Value.h" +#include "qpid/framing/FieldTable.h" + +using namespace qpid::console; +using std::string; +using std::vector; + +SchemaArgument::SchemaArgument(framing::Buffer& buffer, bool forMethod) +{ + framing::FieldTable map; + map.decode(buffer); + + name = map.getAsString("name"); + typeCode = map.getAsInt("type"); + unit = map.getAsString("unit"); + min = map.getAsInt("min"); + max = map.getAsInt("max"); + maxLen = map.getAsInt("maxlen"); + desc = map.getAsString("desc"); + + dirInput = false; + dirOutput = false; + if (forMethod) { + string dir(map.getAsString("dir")); + if (dir.find('I') != dir.npos || dir.find('i') != dir.npos) + dirInput = true; + if (dir.find('O') != dir.npos || dir.find('o') != dir.npos) + dirOutput = true; + } +} + +Value::Ptr SchemaArgument::decodeValue(framing::Buffer& buffer) +{ + return ValueFactory::newValue(typeCode, buffer); +} + +SchemaProperty::SchemaProperty(framing::Buffer& buffer) +{ + framing::FieldTable map; + map.decode(buffer); + + name = map.getAsString("name"); + typeCode = map.getAsInt("type"); + accessCode = map.getAsInt("access"); + isIndex = map.getAsInt("index") != 0; + isOptional = map.getAsInt("optional") != 0; + unit = map.getAsString("unit"); + min = map.getAsInt("min"); + max = map.getAsInt("max"); + maxLen = map.getAsInt("maxlen"); + desc = map.getAsString("desc"); +} + +Value::Ptr SchemaProperty::decodeValue(framing::Buffer& buffer) +{ + return ValueFactory::newValue(typeCode, buffer); +} + +SchemaStatistic::SchemaStatistic(framing::Buffer& buffer) +{ + framing::FieldTable map; + map.decode(buffer); + + name = map.getAsString("name"); + typeCode = map.getAsInt("type"); + unit = map.getAsString("unit"); + desc = map.getAsString("desc"); +} + +Value::Ptr SchemaStatistic::decodeValue(framing::Buffer& buffer) +{ + return ValueFactory::newValue(typeCode, buffer); +} + +SchemaMethod::SchemaMethod(framing::Buffer& buffer) +{ + framing::FieldTable map; + map.decode(buffer); + + name = map.getAsString("name"); + desc = map.getAsString("desc"); + int argCount = map.getAsInt("argCount"); + + for (int i = 0; i < argCount; i++) + arguments.push_back(new SchemaArgument(buffer, true)); +} + +SchemaMethod::~SchemaMethod() +{ + for (vector<SchemaArgument*>::iterator iter = arguments.begin(); + iter != arguments.end(); iter++) + delete *iter; +} + +SchemaClass::SchemaClass(const uint8_t _kind, const ClassKey& _key, framing::Buffer& buffer) : + kind(_kind), key(_key) +{ + if (kind == KIND_TABLE) { + uint16_t propCount = buffer.getShort(); + uint16_t statCount = buffer.getShort(); + uint16_t methodCount = buffer.getShort(); + + for (uint16_t idx = 0; idx < propCount; idx++) + properties.push_back(new SchemaProperty(buffer)); + for (uint16_t idx = 0; idx < statCount; idx++) + statistics.push_back(new SchemaStatistic(buffer)); + for (uint16_t idx = 0; idx < methodCount; idx++) + methods.push_back(new SchemaMethod(buffer)); + + } else if (kind == KIND_EVENT) { + uint16_t argCount = buffer.getShort(); + + for (uint16_t idx = 0; idx < argCount; idx++) + arguments.push_back(new SchemaArgument(buffer)); + } +} + +SchemaClass::~SchemaClass() +{ + for (vector<SchemaProperty*>::iterator iter = properties.begin(); + iter != properties.end(); iter++) + delete *iter; + for (vector<SchemaStatistic*>::iterator iter = statistics.begin(); + iter != statistics.end(); iter++) + delete *iter; + for (vector<SchemaMethod*>::iterator iter = methods.begin(); + iter != methods.end(); iter++) + delete *iter; + for (vector<SchemaArgument*>::iterator iter = arguments.begin(); + iter != arguments.end(); iter++) + delete *iter; +} + diff --git a/RC9/qpid/cpp/src/qpid/console/Schema.h b/RC9/qpid/cpp/src/qpid/console/Schema.h new file mode 100644 index 0000000000..aacedfe23f --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/Schema.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. + * + */ +#ifndef _QPID_CONSOLE_SCHEMA_H_ +#define _QPID_CONSOLE_SCHEMA_H_ + +#include "ClassKey.h" +#include <boost/shared_ptr.hpp> +#include <vector> + +namespace qpid { +namespace framing { + class Buffer; +} +namespace console { + class Value; + + struct SchemaArgument { + SchemaArgument(framing::Buffer& buffer, bool forMethod = false); + boost::shared_ptr<Value> decodeValue(framing::Buffer& buffer); + + std::string name; + uint8_t typeCode; + bool dirInput; + bool dirOutput; + std::string unit; + int min; + int max; + int maxLen; + std::string desc; + std::string defaultVal; + }; + + struct SchemaProperty { + SchemaProperty(framing::Buffer& buffer); + boost::shared_ptr<Value> decodeValue(framing::Buffer& buffer); + + std::string name; + uint8_t typeCode; + uint8_t accessCode; + bool isIndex; + bool isOptional; + std::string unit; + int min; + int max; + int maxLen; + std::string desc; + }; + + struct SchemaStatistic { + SchemaStatistic(framing::Buffer& buffer); + boost::shared_ptr<Value> decodeValue(framing::Buffer& buffer); + + std::string name; + uint8_t typeCode; + std::string unit; + std::string desc; + }; + + struct SchemaMethod { + SchemaMethod(framing::Buffer& buffer); + ~SchemaMethod(); + + std::string name; + std::string desc; + std::vector<SchemaArgument*> arguments; + }; + + struct SchemaClass { + static const uint8_t KIND_TABLE = 1; + static const uint8_t KIND_EVENT = 2; + + SchemaClass(const uint8_t kind, const ClassKey& key, framing::Buffer& buffer); + ~SchemaClass(); + const ClassKey& getClassKey() const { return key; } + + const uint8_t kind; + const ClassKey key; + std::vector<SchemaProperty*> properties; + std::vector<SchemaStatistic*> statistics; + std::vector<SchemaMethod*> methods; + std::vector<SchemaArgument*> arguments; + }; +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/console/SequenceManager.cpp b/RC9/qpid/cpp/src/qpid/console/SequenceManager.cpp new file mode 100644 index 0000000000..ff777430c0 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/SequenceManager.cpp @@ -0,0 +1,48 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SequenceManager.h" + +using namespace qpid::console; +using namespace qpid::sys; +using std::string; +using std::cout; +using std::endl; + +uint32_t SequenceManager::reserve(const std::string& context) +{ + Mutex::ScopedLock l(lock); + uint32_t result = sequence++; + pending[result] = context; + return result; +} + +std::string SequenceManager::release(uint32_t seq) +{ + Mutex::ScopedLock l(lock); + std::map<uint32_t, string>::iterator iter = pending.find(seq); + if (iter == pending.end()) + return string(); + string result(iter->second); + pending.erase(iter); + return result; +} + diff --git a/RC9/qpid/cpp/src/qpid/console/SequenceManager.h b/RC9/qpid/cpp/src/qpid/console/SequenceManager.h new file mode 100644 index 0000000000..c7a8c20fe6 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/SequenceManager.h @@ -0,0 +1,53 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _QPID_CONSOLE_SEQUENCEMANAGER_H_ +#define _QPID_CONSOLE_SEQUENCEMANAGER_H_ + +#include "qpid/sys/Mutex.h" +#include <map> +#include <string> +#include <set> + +namespace qpid { +namespace console { + + /** + * + * \ingroup qpidconsoleapi + */ + class SequenceManager { + public: + typedef std::set<uint32_t> set; + + SequenceManager() : sequence(0) {} + uint32_t reserve(const std::string& context = ""); + std::string release(uint32_t seq); + + private: + sys::Mutex lock; + uint32_t sequence; + std::map<uint32_t, std::string> pending; + }; +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/console/SessionManager.cpp b/RC9/qpid/cpp/src/qpid/console/SessionManager.cpp new file mode 100644 index 0000000000..6aa347e051 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/SessionManager.cpp @@ -0,0 +1,462 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "SessionManager.h" +#include "Schema.h" +#include "Agent.h" +#include "qpid/console/ConsoleListener.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/Time.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/Uuid.h" +#include "qpid/framing/FieldTable.h" + +using namespace qpid::console; +using namespace qpid::sys; +using namespace std; +using qpid::framing::Buffer; +using qpid::framing::FieldTable; + +SessionManager::SessionManager(ConsoleListener* _listener, Settings _settings) : + listener(_listener), settings(_settings) +{ + bindingKeys(); +} + +Broker* SessionManager::addBroker(client::ConnectionSettings& settings) +{ + Broker* broker(new Broker(*this, settings)); + { + Mutex::ScopedLock l(brokerListLock); + brokers.push_back(broker); + } + return broker; +} + +void SessionManager::delBroker(Broker* broker) +{ + Mutex::ScopedLock l(brokerListLock); + for (vector<Broker*>::iterator iter = brokers.begin(); + iter != brokers.end(); iter++) + if (*iter == broker) { + brokers.erase(iter); + return; + } +} + +void SessionManager::getPackages(NameVector& packageNames) +{ + allBrokersStable(); + packageNames.clear(); + { + Mutex::ScopedLock l(lock); + for (map<string, Package*>::iterator iter = packages.begin(); + iter != packages.end(); iter++) + packageNames.push_back(iter->first); + } +} + +void SessionManager::getClasses(KeyVector& classKeys, const std::string& packageName) +{ + allBrokersStable(); + classKeys.clear(); + map<string, Package*>::iterator iter = packages.find(packageName); + if (iter == packages.end()) + return; + + Package& package = *(iter->second); + for (Package::ClassMap::const_iterator piter = package.classes.begin(); + piter != package.classes.end(); piter++) { + ClassKey key(piter->second->getClassKey()); + classKeys.push_back(key); + } +} + +SchemaClass& SessionManager::getSchema(const ClassKey& classKey) +{ + allBrokersStable(); + map<string, Package*>::iterator iter = packages.find(classKey.getPackageName()); + if (iter == packages.end()) + throw Exception("Unknown package"); + + Package& package = *(iter->second); + Package::NameHash key(classKey.getClassName(), classKey.getHash()); + Package::ClassMap::iterator cIter = package.classes.find(key); + if (cIter == package.classes.end()) + throw Exception("Unknown class"); + + return *(cIter->second); +} + +void SessionManager::bindPackage(const std::string& packageName) +{ + stringstream key; + key << "console.obj.*.*." << packageName << ".#"; + bindingKeyList.push_back(key.str()); + for (vector<Broker*>::iterator iter = brokers.begin(); iter != brokers.end(); iter++) + (*iter)->addBinding(key.str()); +} + +void SessionManager::bindClass(const ClassKey& classKey) +{ + bindClass(classKey.getPackageName(), classKey.getClassName()); +} + +void SessionManager::bindClass(const std::string& packageName, const std::string& className) +{ + stringstream key; + key << "console.obj.*.*." << packageName << "." << className << ".#"; + bindingKeyList.push_back(key.str()); + for (vector<Broker*>::iterator iter = brokers.begin(); + iter != brokers.end(); iter++) + (*iter)->addBinding(key.str()); +} + +void SessionManager::getAgents(Agent::Vector& agents, Broker* broker) +{ + agents.clear(); + if (broker != 0) { + broker->appendAgents(agents); + } else { + for (vector<Broker*>::iterator iter = brokers.begin(); iter != brokers.end(); iter++) { + (*iter)->appendAgents(agents); + } + } +} + +void SessionManager::getObjects(Object::Vector& objects, const std::string& className, + Broker* _broker, Agent* _agent) +{ + Agent::Vector agentList; + + if (_agent != 0) { + agentList.push_back(_agent); + _agent->getBroker()->waitForStable(); + } else { + if (_broker != 0) { + _broker->appendAgents(agentList); + _broker->waitForStable(); + } else { + allBrokersStable(); + Mutex::ScopedLock _lock(brokerListLock); + for (vector<Broker*>::iterator iter = brokers.begin(); iter != brokers.end(); iter++) { + (*iter)->appendAgents(agentList); + } + } + } + + FieldTable ft; + uint32_t sequence; + ft.setString("_class", className); + + getResult.clear(); + syncSequenceList.clear(); + error = string(); + + for (Agent::Vector::iterator iter = agentList.begin(); iter != agentList.end(); iter++) { + Agent* agent = *iter; + char rawbuffer[512]; + Buffer buffer(rawbuffer, 512); + stringstream routingKey; + routingKey << "agent." << agent->getBrokerBank() << "." << agent->getAgentBank(); + { + Mutex::ScopedLock _lock(lock); + sequence = sequenceManager.reserve("multiget"); + syncSequenceList.insert(sequence); + } + agent->getBroker()->encodeHeader(buffer, 'G', sequence); + ft.encode(buffer); + uint32_t length = buffer.getPosition(); + buffer.reset(); + agent->getBroker()->connThreadBody.sendBuffer(buffer, length, "qpid.management", routingKey.str()); + } + + { + Mutex::ScopedLock _lock(lock); + while (!syncSequenceList.empty() && error.empty()) { + cv.wait(lock, AbsTime(now(), settings.getTimeout * TIME_SEC)); + } + } + + objects = getResult; +} + +void SessionManager::bindingKeys() +{ + bindingKeyList.push_back("schema.#"); + if (settings.rcvObjects && settings.rcvEvents && settings.rcvHeartbeats && !settings.userBindings) { + bindingKeyList.push_back("console.#"); + } else { + if (settings.rcvObjects && !settings.userBindings) + bindingKeyList.push_back("console.obj.#"); + else + bindingKeyList.push_back("console.obj.*.*.org.apache.qpid.broker.agent"); + if (settings.rcvEvents) + bindingKeyList.push_back("console.event.#"); + if (settings.rcvHeartbeats) + bindingKeyList.push_back("console.heartbeat"); + } +} + +void SessionManager::allBrokersStable() +{ + Mutex::ScopedLock l(brokerListLock); + for (vector<Broker*>::iterator iter = brokers.begin(); + iter != brokers.end(); iter++) + (*iter)->waitForStable(); +} + +void SessionManager::startProtocol(Broker* broker) +{ + char rawbuffer[512]; + Buffer buffer(rawbuffer, 512); + + broker->encodeHeader(buffer, 'B'); + uint32_t length = 512 - buffer.available(); + buffer.reset(); + broker->connThreadBody.sendBuffer(buffer, length); +} + + +void SessionManager::handleBrokerResp(Broker* broker, Buffer& inBuffer, uint32_t) +{ + framing::Uuid brokerId; + + brokerId.decode(inBuffer); + broker->setBrokerId(brokerId); + + char rawbuffer[512]; + Buffer buffer(rawbuffer, 512); + + uint32_t sequence = sequenceManager.reserve("startup"); + broker->encodeHeader(buffer, 'P', sequence); + uint32_t length = 512 - buffer.available(); + buffer.reset(); + broker->connThreadBody.sendBuffer(buffer, length); + + if (listener != 0) { + listener->brokerInfo(*broker); + } +} + +void SessionManager::handlePackageInd(Broker* broker, Buffer& inBuffer, uint32_t) +{ + string packageName; + inBuffer.getShortString(packageName); + + { + Mutex::ScopedLock l(lock); + map<string, Package*>::iterator iter = packages.find(packageName); + if (iter == packages.end()) { + packages[packageName] = new Package(packageName); + if (listener != 0) + listener->newPackage(packageName); + } + } + + broker->incOutstanding(); + char rawbuffer[512]; + Buffer buffer(rawbuffer, 512); + + uint32_t sequence = sequenceManager.reserve("startup"); + broker->encodeHeader(buffer, 'Q', sequence); + buffer.putShortString(packageName); + uint32_t length = 512 - buffer.available(); + buffer.reset(); + broker->connThreadBody.sendBuffer(buffer, length); +} + +void SessionManager::handleCommandComplete(Broker* broker, Buffer& inBuffer, uint32_t sequence) +{ + Mutex::ScopedLock l(lock); + uint32_t resultCode = inBuffer.getLong(); + string resultText; + inBuffer.getShortString(resultText); + string context = sequenceManager.release(sequence); + if (resultCode != 0) + QPID_LOG(debug, "Received error in completion: " << resultCode << " " << resultText); + if (context == "startup") { + broker->decOutstanding(); + } else if (context == "multiget") { + if (syncSequenceList.count(sequence) == 1) { + syncSequenceList.erase(sequence); + if (syncSequenceList.empty()) { + cv.notify(); + } + } + } + // TODO: Other context cases +} + +void SessionManager::handleClassInd(Broker* broker, Buffer& inBuffer, uint32_t) +{ + uint8_t kind; + string packageName; + string className; + uint8_t hash[16]; + + kind = inBuffer.getOctet(); + inBuffer.getShortString(packageName); + inBuffer.getShortString(className); + inBuffer.getBin128(hash); + + { + Mutex::ScopedLock l(lock); + map<string, Package*>::iterator pIter = packages.find(packageName); + if (pIter == packages.end() || pIter->second->getClass(className, hash)) + return; + } + + broker->incOutstanding(); + char rawbuffer[512]; + Buffer buffer(rawbuffer, 512); + + uint32_t sequence = sequenceManager.reserve("startup"); + broker->encodeHeader(buffer, 'S', sequence); + buffer.putShortString(packageName); + buffer.putShortString(className); + buffer.putBin128(hash); + uint32_t length = 512 - buffer.available(); + buffer.reset(); + broker->connThreadBody.sendBuffer(buffer, length); +} + +void SessionManager::handleMethodResp(Broker* broker, Buffer& buffer, uint32_t sequence) +{ + if (broker->methodObject) { + broker->methodObject->handleMethodResp(buffer, sequence); + } +} + +void SessionManager::handleHeartbeatInd(Broker* /*broker*/, Buffer& /*inBuffer*/, uint32_t /*sequence*/) +{ +} + +void SessionManager::handleEventInd(Broker* broker, Buffer& buffer, uint32_t /*sequence*/) +{ + string packageName; + string className; + uint8_t hash[16]; + SchemaClass* schemaClass; + + buffer.getShortString(packageName); + buffer.getShortString(className); + buffer.getBin128(hash); + + { + Mutex::ScopedLock l(lock); + map<string, Package*>::iterator pIter = packages.find(packageName); + if (pIter == packages.end()) + return; + schemaClass = pIter->second->getClass(className, hash); + if (schemaClass == 0) + return; + } + + Event event(broker, schemaClass, buffer); + + if (listener) + listener->event(event); +} + +void SessionManager::handleSchemaResp(Broker* broker, Buffer& inBuffer, uint32_t sequence) +{ + uint8_t kind; + string packageName; + string className; + uint8_t hash[16]; + + kind = inBuffer.getOctet(); + inBuffer.getShortString(packageName); + inBuffer.getShortString(className); + inBuffer.getBin128(hash); + + { + Mutex::ScopedLock l(lock); + map<string, Package*>::iterator pIter = packages.find(packageName); + if (pIter != packages.end() && !pIter->second->getClass(className, hash)) { + ClassKey key(packageName, className, hash); + SchemaClass* schemaClass(new SchemaClass(kind, key, inBuffer)); + pIter->second->addClass(className, hash, schemaClass); + if (listener != 0) { + listener->newClass(schemaClass->getClassKey()); + } + } + } + + sequenceManager.release(sequence); + broker->decOutstanding(); +} + +void SessionManager::handleContentInd(Broker* broker, Buffer& buffer, uint32_t sequence, bool prop, bool stat) +{ + string packageName; + string className; + uint8_t hash[16]; + SchemaClass* schemaClass; + + buffer.getShortString(packageName); + buffer.getShortString(className); + buffer.getBin128(hash); + + { + Mutex::ScopedLock l(lock); + map<string, Package*>::iterator pIter = packages.find(packageName); + if (pIter == packages.end()) + return; + schemaClass = pIter->second->getClass(className, hash); + if (schemaClass == 0) + return; + } + + Object object(broker, schemaClass, buffer, prop, stat); + + if (prop && className == "agent" && packageName == "org.apache.qpid.broker") + broker->updateAgent(object); + + { + Mutex::ScopedLock l(lock); + if (syncSequenceList.count(sequence) == 1) { + if (!object.isDeleted()) + getResult.push_back(object); + } + return; + } + + if (listener) { + if (prop) + listener->objectProps(*broker, object); + if (stat) + listener->objectStats(*broker, object); + } +} + +void SessionManager::handleBrokerConnect(Broker* broker) +{ + if (listener != 0) + listener->brokerConnected(*broker); +} + +void SessionManager::handleBrokerDisconnect(Broker* broker) +{ + if (listener != 0) + listener->brokerDisconnected(*broker); +} + diff --git a/RC9/qpid/cpp/src/qpid/console/SessionManager.h b/RC9/qpid/cpp/src/qpid/console/SessionManager.h new file mode 100644 index 0000000000..27df00494c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/SessionManager.h @@ -0,0 +1,199 @@ +#ifndef _QPID_CONSOLE_SESSION_MANAGER_H +#define _QPID_CONSOLE_SESSION_MANAGER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Broker.h" +#include "Package.h" +#include "SequenceManager.h" +#include "ClassKey.h" +#include "Schema.h" +#include "Agent.h" +#include "Object.h" +#include "ObjectId.h" +#include "Value.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Condition.h" +#include "qpid/client/ConnectionSettings.h" +#include <string> +#include <vector> + +namespace qpid { +namespace console { + +class ConsoleListener; + +/** + * + * \ingroup qmfconsoleapi + */ +class SessionManager +{ + public: + typedef std::vector<std::string> NameVector; + typedef std::vector<ClassKey> KeyVector; + ~SessionManager() {} + + struct Settings { + bool rcvObjects; + bool rcvEvents; + bool rcvHeartbeats; + bool userBindings; + uint32_t methodTimeout; + uint32_t getTimeout; + + Settings() : rcvObjects(true), rcvEvents(true), rcvHeartbeats(true), userBindings(false), + methodTimeout(20), getTimeout(20) + {} + }; + + /** Create a new SessionManager + * + * Provide your own subclass of ConsoleListener to receive updates and indications + * asynchronously or leave it as its default and use only synchronous methods. + * + *@param listener Listener object to receive asynchronous indications. + *@param rcvObjects Listener wishes to receive managed object data. + *@param rcvEvents Listener wishes to receive events. + *@param rcvHeartbeats Listener wishes to receive agent heartbeats. + *@param userBindings If rcvObjects is true, userBindings allows the console client + * to control which object classes are received. See the bindPackage and bindClass + * methods. If userBindings is false, the listener will receive updates for all + * object classes. + */ + SessionManager(ConsoleListener* listener = 0, + Settings settings = Settings()); + + /** Connect a broker to the console session + * + *@param settings Connection settings for client access + *@return broker object if operation is successful + * an exception shall be thrown. + */ + Broker* addBroker(client::ConnectionSettings& settings); + + /** Disconnect a broker from the console session + * + *@param broker The broker object returned from an earlier call to addBroker. + */ + void delBroker(Broker* broker); + + /** Get a list of known management packages + * + *@param packages Vector of package names returned by the session manager. + */ + void getPackages(NameVector& packages); + + /** Get a list of class keys associated with a package + * + *@param classKeys List of class keys returned by the session manager. + *@param packageName Name of package being queried. + */ + void getClasses(KeyVector& classKeys, const std::string& packageName); + + /** Get the schema of a class given its class key + * + *@param classKey Class key of the desired schema. + */ + SchemaClass& getSchema(const ClassKey& classKey); + + /** Request that updates be received for all classes within a package + * + * Note that this method is only meaningful if a ConsoleListener was provided at session + * creation and if the 'userBindings' flag was set to true. + * + *@param packageName Name of the package to which to bind. + */ + void bindPackage(const std::string& packageName); + + /** Request update to be received for a particular class + * + * Note that this method is only meaningful if a ConsoleListener was provided at session + * creation and if the 'userBindings' flag was set to true. + * + *@param classKey Class key of class to which to bind. + */ + void bindClass(const ClassKey& classKey); + void bindClass(const std::string& packageName, const std::string& className); + + /** Get a list of qmf agents known to the session manager. + * + *@param agents Vector of Agent objects returned by the session manager. + *@param broker Return agents registered with this broker only. If NULL, return agents + * from all connected brokers. + */ + void getAgents(Agent::Vector& agents, Broker* broker = 0); + + /** Get objects from agents. There are four variants of this method with different ways of + * specifying from which class objects are being queried. + * + *@param objects List of objects received. + *@param classKey ClassKey object identifying class to be queried. + *@param className Class name identifying class to be queried. + *@param objectId Object Id of the single object to be queried. + *@param broker Restrict the query to this broker, or all brokers if NULL. + *@param agent Restrict the query to this agent, or all agents if NULL. + */ + void getObjects(Object::Vector& objects, const std::string& className, + Broker* broker = 0, Agent* agent = 0); + //void getObjects(Object::Vector& objects, const ClassKey& classKey, + // Broker* broker = 0, Agent* agent = 0); + //void getObjects(Object::Vector& objects, const ObjectId& objectId, + // Broker* broker = 0, Agent* agent = 0); + +private: + friend class Broker; + friend class Broker::ConnectionThread; + friend class Object; + sys::Mutex lock; + sys::Mutex brokerListLock; + ConsoleListener* listener; + std::vector<Broker*> brokers; + std::map<std::string, Package*> packages; + SequenceManager sequenceManager; + sys::Condition cv; + SequenceManager::set syncSequenceList; + Object::Vector getResult; + std::string error; + Settings settings; + NameVector bindingKeyList; + + void bindingKeys(); + void allBrokersStable(); + void startProtocol(Broker* broker); + void handleBrokerResp(Broker* broker, framing::Buffer& inBuffer, uint32_t sequence); + void handlePackageInd(Broker* broker, framing::Buffer& inBuffer, uint32_t sequence); + void handleCommandComplete(Broker* broker, framing::Buffer& inBuffer, uint32_t sequence); + void handleClassInd(Broker* broker, framing::Buffer& inBuffer, uint32_t sequence); + void handleMethodResp(Broker* broker, framing::Buffer& inBuffer, uint32_t sequence); + void handleHeartbeatInd(Broker* broker, framing::Buffer& inBuffer, uint32_t sequence); + void handleEventInd(Broker* broker, framing::Buffer& inBuffer, uint32_t sequence); + void handleSchemaResp(Broker* broker, framing::Buffer& inBuffer, uint32_t sequence); + void handleContentInd(Broker* broker, framing::Buffer& inBuffer, uint32_t sequence, bool prop, bool stat); + void handleBrokerConnect(Broker* broker); + void handleBrokerDisconnect(Broker* broker); + +}; + +}} // namespace qpid::console + +#endif /*!_QPID_CONSOLE_SESSION_MANAGER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/console/Value.cpp b/RC9/qpid/cpp/src/qpid/console/Value.cpp new file mode 100644 index 0000000000..532709ab05 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/Value.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 "Value.h" +#include "qpid/framing/Buffer.h" + +using namespace qpid::console; +using namespace std; + +string NullValue::str() const +{ + return "<Null>"; +} + +RefValue::RefValue(framing::Buffer& buffer) +{ + uint64_t first = buffer.getLongLong(); + uint64_t second = buffer.getLongLong(); + value.setValue(first, second); +} + +string RefValue::str() const +{ + stringstream s; + s << value; + return s.str(); +} + +string UintValue::str() const +{ + stringstream s; + s << value; + return s.str(); +} + +string IntValue::str() const +{ + stringstream s; + s << value; + return s.str(); +} + +string Uint64Value::str() const +{ + stringstream s; + s << value; + return s.str(); +} + +string Int64Value::str() const +{ + stringstream s; + s << value; + return s.str(); +} + +StringValue::StringValue(framing::Buffer& buffer, int tc) +{ + if (tc == 6) + buffer.getShortString(value); + else + buffer.getMediumString(value); +} + +string BoolValue::str() const +{ + return value ? "T" : "F"; +} + +string FloatValue::str() const +{ + stringstream s; + s << value; + return s.str(); +} + +string DoubleValue::str() const +{ + stringstream s; + s << value; + return s.str(); +} + +UuidValue::UuidValue(framing::Buffer& buffer) +{ + value.decode(buffer); +} + +string MapValue::str() const +{ + stringstream s; + s << value; + return s.str(); +} + +MapValue::MapValue(framing::Buffer& buffer) +{ + value.decode(buffer); +} + + +Value::Ptr ValueFactory::newValue(int typeCode, framing::Buffer& buffer) +{ + switch (typeCode) { + case 1: return Value::Ptr(new UintValue(buffer.getOctet())); // U8 + case 2: return Value::Ptr(new UintValue(buffer.getShort())); // U16 + case 3: return Value::Ptr(new UintValue(buffer.getLong())); // U32 + case 4: return Value::Ptr(new Uint64Value(buffer.getLongLong())); // U64 + case 6: return Value::Ptr(new StringValue(buffer, 6)); // SSTR + case 7: return Value::Ptr(new StringValue(buffer, 7)); // LSTR + case 8: return Value::Ptr(new Int64Value(buffer.getLongLong())); // ABSTIME + case 9: return Value::Ptr(new Uint64Value(buffer.getLongLong())); // DELTATIME + case 10: return Value::Ptr(new RefValue(buffer)); // REF + case 11: return Value::Ptr(new BoolValue(buffer.getOctet())); // BOOL + case 12: return Value::Ptr(new FloatValue(buffer.getFloat())); // FLOAT + case 13: return Value::Ptr(new DoubleValue(buffer.getDouble())); // DOUBLE + case 14: return Value::Ptr(new UuidValue(buffer)); // UUID + case 15: return Value::Ptr(new MapValue(buffer)); // MAP + case 16: return Value::Ptr(new IntValue(buffer.getOctet())); // S8 + case 17: return Value::Ptr(new IntValue(buffer.getShort())); // S16 + case 18: return Value::Ptr(new IntValue(buffer.getLong())); // S32 + case 19: return Value::Ptr(new Int64Value(buffer.getLongLong())); // S64 + } + + return Value::Ptr(); +} + +void ValueFactory::encodeValue(int typeCode, Value::Ptr value, framing::Buffer& buffer) +{ + switch (typeCode) { + case 1: buffer.putOctet(value->asUint()); return; // U8 + case 2: buffer.putShort(value->asUint()); return; // U16 + case 3: buffer.putLong(value->asUint()); return; // U32 + case 4: buffer.putLongLong(value->asUint64()); return; // U64 + case 6: buffer.putShortString(value->asString()); return; // SSTR + case 7: buffer.putMediumString(value->asString()); return; // LSTR + case 8: buffer.putLongLong(value->asInt64()); return; // ABSTIME + case 9: buffer.putLongLong(value->asUint64()); return; // DELTATIME + case 10: value->asObjectId().encode(buffer); return; // REF + case 11: buffer.putOctet(value->asBool() ? 1 : 0); return; // BOOL + case 12: buffer.putFloat(value->asFloat()); return; // FLOAT + case 13: buffer.putDouble(value->asDouble()); return; // DOUBLE + case 14: value->asUuid().encode(buffer); return; // UUID + case 15: value->asMap().encode(buffer); return; // MAP + case 16: buffer.putOctet(value->asInt()); return; // S8 + case 17: buffer.putShort(value->asInt()); return; // S16 + case 18: buffer.putLong(value->asInt()); return; // S32 + case 19: buffer.putLongLong(value->asInt64()); return; // S64 + } +} diff --git a/RC9/qpid/cpp/src/qpid/console/Value.h b/RC9/qpid/cpp/src/qpid/console/Value.h new file mode 100644 index 0000000000..5a0915c69b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/console/Value.h @@ -0,0 +1,207 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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_CONSOLE_VALUE_H_ +#define _QPID_CONSOLE_VALUE_H_ + +#include "qpid/Exception.h" +#include "qpid/framing/Uuid.h" +#include "qpid/framing/FieldTable.h" +#include "ObjectId.h" +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace framing { + class Buffer; +} +namespace console { + + /** + * \ingroup qmfconsoleapi + */ + class Value { + + public: + typedef boost::shared_ptr<Value> Ptr; + virtual ~Value() {} + virtual std::string str() const = 0; + + virtual bool isNull() const { return false; } + virtual bool isObjectId() const { return false; } + virtual bool isUint() const { return false; } + virtual bool isInt() const { return false; } + virtual bool isUint64() const { return false; } + virtual bool isInt64() const { return false; } + virtual bool isString() const { return false; } + virtual bool isBool() const { return false; } + virtual bool isFloat() const { return false; } + virtual bool isDouble() const { return false; } + virtual bool isUuid() const { return false; } + virtual bool isMap() const { return false; } + + virtual ObjectId asObjectId() const { incompatible(); return ObjectId(); } + virtual uint32_t asUint() const { incompatible(); return 0; } + virtual int32_t asInt() const { incompatible(); return 0; } + virtual uint64_t asUint64() const { incompatible(); return 0; } + virtual int64_t asInt64() const { incompatible(); return 0; } + virtual std::string asString() const { incompatible(); return std::string(); } + virtual bool asBool() const { incompatible(); return false; } + virtual float asFloat() const { incompatible(); return 0.0; } + virtual double asDouble() const { incompatible(); return 0.0; } + virtual framing::Uuid asUuid() const { incompatible(); return framing::Uuid(); } + virtual framing::FieldTable asMap() const { incompatible(); return framing::FieldTable(); } + + private: + void incompatible() const { + throw Exception("Incompatible Type"); + } + }; + + class NullValue : public Value { + public: + NullValue() {} + std::string str() const; + bool isNull() const { return true; } + }; + + class RefValue : public Value { + public: + RefValue(ObjectId v) : value(v) {} + RefValue(framing::Buffer& buffer); + std::string str() const; + bool isObjectId() const { return true; } + ObjectId asObjectId() const { return value; } + private: + ObjectId value; + }; + + class UintValue : public Value { + public: + UintValue(uint32_t v) : value(v) {} + std::string str() const; + bool isUint() const { return true; } + uint32_t asUint() const { return value; } + private: + uint32_t value; + }; + + class IntValue : public Value { + public: + IntValue(int32_t v) : value(v) {} + std::string str() const; + bool isInt() const { return true; } + int32_t asInt() const { return value; } + private: + int32_t value; + }; + + class Uint64Value : public Value { + public: + Uint64Value(uint64_t v) : value(v) {} + std::string str() const; + bool isUint64() const { return true; } + uint64_t asUint64() const { return value; } + private: + uint64_t value; + }; + + class Int64Value : public Value { + public: + Int64Value(int64_t v) : value(v) {} + std::string str() const; + bool isInt64() const { return true; } + int64_t asInt64() const { return value; } + private: + int64_t value; + }; + + class StringValue : public Value { + public: + StringValue(const std::string& v) : value(v) {} + StringValue(framing::Buffer& buffer, int tc); + std::string str() const { return value; } + bool isString() const { return true; } + std::string asString() const { return value; } + private: + std::string value; + }; + + class BoolValue : public Value { + public: + BoolValue(bool v) : value(v) {} + BoolValue(uint8_t v) : value(v != 0) {} + std::string str() const; + bool isBool() const { return true; } + bool asBool() const { return value; } + private: + bool value; + }; + + class FloatValue : public Value { + public: + FloatValue(float v) : value(v) {} + std::string str() const; + bool isFloat() const { return true; } + float asFloat() const { return value; } + private: + float value; + }; + + class DoubleValue : public Value { + public: + DoubleValue(double v) : value(v) {} + std::string str() const; + bool isDouble() const { return true; } + double asDouble() const { return value; } + private: + double value; + }; + + class UuidValue : public Value { + public: + UuidValue(const framing::Uuid& v) : value(v) {} + UuidValue(framing::Buffer& buffer); + std::string str() const { return value.str(); } + bool isUuid() const { return true; } + framing::Uuid asUuid() const { return value; } + private: + framing::Uuid value; + }; + + class MapValue : public Value { + public: + MapValue(const framing::FieldTable& v) : value(v) {} + MapValue(framing::Buffer& buffer); + std::string str() const; + bool isMap() const { return true; } + framing::FieldTable asMap() const { return value; } + private: + framing::FieldTable value; + }; + + class ValueFactory { + public: + static Value::Ptr newValue(int typeCode, framing::Buffer& buffer); + static void encodeValue(int typeCode, Value::Ptr value, framing::Buffer& buffer); + }; +} +} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/AMQBody.cpp b/RC9/qpid/cpp/src/qpid/framing/AMQBody.cpp new file mode 100644 index 0000000000..b3eeae0615 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AMQBody.cpp @@ -0,0 +1,64 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/framing/AMQBody.h" +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/framing/AMQHeaderBody.h" +#include "qpid/framing/AMQContentBody.h" +#include "qpid/framing/AMQHeartbeatBody.h" +#include <iostream> + +namespace qpid { +namespace framing { + +std::ostream& operator<<(std::ostream& out, const AMQBody& body) +{ + body.print(out); + return out; +} + +AMQBody::~AMQBody() {} + +namespace { +struct MatchBodies : public AMQBodyConstVisitor { + const AMQBody& body; + bool match; + + MatchBodies(const AMQBody& b) : body(b), match(false) {} + virtual ~MatchBodies() {} + + virtual void visit(const AMQHeaderBody&) { match=dynamic_cast<const AMQHeaderBody*>(&body); } + virtual void visit(const AMQContentBody&) { match=dynamic_cast<const AMQContentBody*>(&body); } + virtual void visit(const AMQHeartbeatBody&) { match=dynamic_cast<const AMQHeartbeatBody*>(&body); } + virtual void visit(const AMQMethodBody& x) { + const AMQMethodBody* y=dynamic_cast<const AMQMethodBody*>(&body); + match = (y && y->amqpMethodId() == x.amqpMethodId() && y->amqpClassId() == x.amqpClassId()); + } +}; + +} +bool AMQBody::match(const AMQBody& a, const AMQBody& b) { + MatchBodies matcher(a); + b.accept(matcher); + return matcher.match; +} + +}} // namespace diff --git a/RC9/qpid/cpp/src/qpid/framing/AMQBody.h b/RC9/qpid/cpp/src/qpid/framing/AMQBody.h new file mode 100644 index 0000000000..93f4319575 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AMQBody.h @@ -0,0 +1,78 @@ +#ifndef QPID_FRAMING_AMQBODY_H +#define QPID_FRAMING_AMQBODY_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/framing/amqp_types.h" + +#include <ostream> + +namespace qpid { +namespace framing { + +class Buffer; + +class AMQMethodBody; +class AMQHeaderBody; +class AMQContentBody; +class AMQHeartbeatBody; + +struct AMQBodyConstVisitor { + virtual ~AMQBodyConstVisitor() {} + virtual void visit(const AMQHeaderBody&) = 0; + virtual void visit(const AMQContentBody&) = 0; + virtual void visit(const AMQHeartbeatBody&) = 0; + virtual void visit(const AMQMethodBody&) = 0; +}; + +class AMQBody +{ + public: + virtual ~AMQBody(); + + virtual uint8_t type() const = 0; + + virtual void encode(Buffer& buffer) const = 0; + virtual void decode(Buffer& buffer, uint32_t=0) = 0; + virtual uint32_t encodedSize() const = 0; + + virtual void print(std::ostream& out) const = 0; + virtual void accept(AMQBodyConstVisitor&) const = 0; + + virtual AMQMethodBody* getMethod() { return 0; } + virtual const AMQMethodBody* getMethod() const { return 0; } + + /** Match if same type and same class/method ID for methods */ + static bool match(const AMQBody& , const AMQBody& ); +}; + +std::ostream& operator<<(std::ostream& out, const AMQBody& body) ; + +enum BodyTypes { + METHOD_BODY = 1, + HEADER_BODY = 2, + CONTENT_BODY = 3, + HEARTBEAT_BODY = 8 +}; + +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_AMQBODY_H*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/AMQCommandControlBody.h b/RC9/qpid/cpp/src/qpid/framing/AMQCommandControlBody.h new file mode 100644 index 0000000000..d12b70a168 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AMQCommandControlBody.h @@ -0,0 +1,70 @@ +#ifndef QPID_FRAMING_AMQCOMMANDCONTROLBODY_H +#define QPID_FRAMING_AMQCOMMANDCONTROLBODY_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/amqp_0_10/helpers.h" +#include "qpid/framing/AMQBody.h" + +namespace qpid { +namespace framing { + +/** + * AMQBody wrapper for Command and Control. + * Temporary measure to fit with old code. + */ +template <class T> class AMQCommandControlBody : public AMQBody, public T +{ + public: + virtual uint8_t type() const { return 100+T::SEGMENT_TYPE; } + + virtual void encode(Buffer& buffer) const { + Codec::encode(buffer.getIterator(), static_cast<const T&>(*this)); + } + virtual void decode(Buffer& buffer, uint32_t=0) { + Codec::decode(buffer.getIterator(), static_cast<T&>(*this)); + } + virtual uint32_t encodedSize() const { + Codec::size(buffer.getIterator(), static_cast<const T&>(*this)); + } + + virtual void print(std::ostream& out) const { + out << static_cast<const T&>(*this) << endl; + } + virtual void AMQBody::accept(AMQBodyConstVisitor&) const { assert(0); } +}; + +class CommandBody : public AMQCommandControlBody<amqp_0_10::Command> { + using Command::accept; // Hide AMQBody::accept + virtual Command* getCommand() { return this; } + virtual const Command* getCommand() const { return this; } +}; + +class ControlBody : public AMQCommandControlBody<amqp_0_10::Control> { + using Control::accept; // Hide AMQBody::accept + virtual Control* getControl() { return this; } + virtual const Control* getControl() const { return this; } +}; + +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_AMQCOMMANDCONTROLBODY_H*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/AMQContentBody.cpp b/RC9/qpid/cpp/src/qpid/framing/AMQContentBody.cpp new file mode 100644 index 0000000000..85fb95739b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AMQContentBody.cpp @@ -0,0 +1,44 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "AMQContentBody.h" +#include <iostream> + +qpid::framing::AMQContentBody::AMQContentBody(){ +} + +qpid::framing::AMQContentBody::AMQContentBody(const string& _data) : data(_data){ +} + +uint32_t qpid::framing::AMQContentBody::encodedSize() const{ + return data.size(); +} +void qpid::framing::AMQContentBody::encode(Buffer& buffer) const{ + buffer.putRawData(data); +} +void qpid::framing::AMQContentBody::decode(Buffer& buffer, uint32_t _size){ + buffer.getRawData(data, _size); +} + +void qpid::framing::AMQContentBody::print(std::ostream& out) const +{ + out << "content (" << encodedSize() << " bytes)"; + out << " " << data.substr(0,16) << "..."; +} diff --git a/RC9/qpid/cpp/src/qpid/framing/AMQContentBody.h b/RC9/qpid/cpp/src/qpid/framing/AMQContentBody.h new file mode 100644 index 0000000000..288f1549a9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AMQContentBody.h @@ -0,0 +1,53 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "amqp_types.h" +#include "AMQBody.h" +#include "Buffer.h" + +#ifndef _AMQContentBody_ +#define _AMQContentBody_ + +namespace qpid { +namespace framing { + +class AMQContentBody : public AMQBody +{ + string data; + +public: + AMQContentBody(); + AMQContentBody(const string& data); + inline virtual ~AMQContentBody(){} + inline uint8_t type() const { return CONTENT_BODY; }; + inline const string& getData() const { return data; } + inline string& getData() { return data; } + uint32_t encodedSize() const; + void encode(Buffer& buffer) const; + void decode(Buffer& buffer, uint32_t size); + void print(std::ostream& out) const; + void accept(AMQBodyConstVisitor& v) const { v.visit(*this); } +}; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/AMQDataBlock.h b/RC9/qpid/cpp/src/qpid/framing/AMQDataBlock.h new file mode 100644 index 0000000000..0b1f459627 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AMQDataBlock.h @@ -0,0 +1,42 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Buffer.h" + +#ifndef _AMQDataBlock_ +#define _AMQDataBlock_ + +namespace qpid { +namespace framing { + +class AMQDataBlock +{ +public: + virtual ~AMQDataBlock() {} + virtual void encode(Buffer& buffer) const = 0; + virtual bool decode(Buffer& buffer) = 0; + virtual uint32_t encodedSize() const = 0; +}; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/AMQFrame.cpp b/RC9/qpid/cpp/src/qpid/framing/AMQFrame.cpp new file mode 100644 index 0000000000..98a1354811 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AMQFrame.cpp @@ -0,0 +1,124 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "AMQFrame.h" + +#include "qpid/framing/variant.h" +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/framing/reply_exceptions.h" + +#include <boost/format.hpp> + +#include <iostream> + +namespace qpid { +namespace framing { + +AMQFrame::~AMQFrame() {} + +void AMQFrame::setBody(const AMQBody& b) { body = new BodyHolder(b); } + +void AMQFrame::setMethod(ClassId c, MethodId m) { body = new BodyHolder(c,m); } + +uint32_t AMQFrame::encodedSize() const { + return frameOverhead() + body->encodedSize(); +} + +uint32_t AMQFrame::frameOverhead() { + return 12 /*frame header*/; +} + +uint16_t AMQFrame::DECODE_SIZE_MIN=4; + +uint16_t AMQFrame::decodeSize(char* data) { + Buffer buf(data+2, DECODE_SIZE_MIN); + return buf.getShort(); +} + +void AMQFrame::encode(Buffer& buffer) const +{ + //set track first (controls on track 0, everything else on 1): + uint8_t track = getBody()->type() ? 1 : 0; + + uint8_t flags = (bof ? 0x08 : 0) | (eof ? 0x04 : 0) | (bos ? 0x02 : 0) | (eos ? 0x01 : 0); + buffer.putOctet(flags); + buffer.putOctet(getBody()->type()); + buffer.putShort(encodedSize()); + buffer.putOctet(0); + buffer.putOctet(0x0f & track); + buffer.putShort(channel); + buffer.putLong(0); + body->encode(buffer); +} + +bool AMQFrame::decode(Buffer& buffer) +{ + if(buffer.available() < frameOverhead()) + return false; + buffer.record(); + + uint8_t flags = buffer.getOctet(); + uint8_t framing_version = (flags & 0xc0) >> 6; + if (framing_version != 0) + throw FramingErrorException(QPID_MSG("Framing version unsupported")); + bof = flags & 0x08; + eof = flags & 0x04; + bos = flags & 0x02; + eos = flags & 0x01; + uint8_t type = buffer.getOctet(); + uint16_t frame_size = buffer.getShort(); + if (frame_size < frameOverhead()) + throw FramingErrorException(QPID_MSG("Frame size too small " << frame_size)); + uint8_t reserved1 = buffer.getOctet(); + uint8_t field1 = buffer.getOctet(); + subchannel = field1 & 0x0f; + channel = buffer.getShort(); + (void) buffer.getLong(); // reserved2 + + // Verify that the protocol header meets current spec + // TODO: should we check reserved2 against zero as well? - the + // spec isn't clear + if ((flags & 0x30) != 0 || reserved1 != 0 || (field1 & 0xf0) != 0) + throw FramingErrorException(QPID_MSG("Reserved bits not zero")); + + // TODO: should no longer care about body size and only pass up + // B,E,b,e flags + uint16_t body_size = frame_size - frameOverhead(); + if (buffer.available() < body_size){ + buffer.restore(); + return false; + } + body = new BodyHolder(); + body->decode(type,buffer, body_size); + return true; +} + +std::ostream& operator<<(std::ostream& out, const AMQFrame& f) +{ + return + out << "Frame[" + << (f.getBof() ? "B" : "") << (f.getEof() ? "E" : "") + << (f.getBos() ? "b" : "") << (f.getEos() ? "e" : "") << "; " + << "channel=" << f.getChannel() << "; " << *f.getBody() + << "]"; +} + + +}} // namespace qpid::framing diff --git a/RC9/qpid/cpp/src/qpid/framing/AMQFrame.h b/RC9/qpid/cpp/src/qpid/framing/AMQFrame.h new file mode 100644 index 0000000000..ddfe438806 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AMQFrame.h @@ -0,0 +1,128 @@ +#ifndef _AMQFrame_ +#define _AMQFrame_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "AMQDataBlock.h" +#include "AMQHeaderBody.h" +#include "AMQContentBody.h" +#include "AMQHeartbeatBody.h" +#include "ProtocolVersion.h" +#include "BodyHolder.h" + +#include <boost/intrusive_ptr.hpp> +#include <boost/cast.hpp> + +namespace qpid { +namespace framing { + +class BodyHolder; + +class AMQFrame : public AMQDataBlock +{ + public: + AMQFrame(boost::intrusive_ptr<BodyHolder> b=0) : body(b) { init(); } + AMQFrame(const AMQBody& b) { setBody(b); init(); } + ~AMQFrame(); + + template <class InPlace> + AMQFrame(const InPlace& ip, typename EnableInPlace<InPlace>::type* =0) { + init(); setBody(ip); + } + + ChannelId getChannel() const { return channel; } + void setChannel(ChannelId c) { channel = c; } + + boost::intrusive_ptr<BodyHolder> getHolder() { return body; } + + AMQBody* getBody() { return body ? body->get() : 0; } + const AMQBody* getBody() const { return body ? body->get() : 0; } + + AMQMethodBody* getMethod() { return getBody()->getMethod(); } + const AMQMethodBody* getMethod() const { return getBody()->getMethod(); } + + void setBody(const AMQBody& b); + + template <class InPlace> + typename EnableInPlace<InPlace>::type setBody(const InPlace& ip) { + body = new BodyHolder(ip); + } + + void setMethod(ClassId c, MethodId m); + + template <class T> T* castBody() { + return boost::polymorphic_downcast<T*>(getBody()); + } + + template <class T> const T* castBody() const { + return boost::polymorphic_downcast<const T*>(getBody()); + } + + void encode(Buffer& buffer) const; + bool decode(Buffer& buffer); + uint32_t encodedSize() const; + + // 0-10 terminology: first/last frame (in segment) first/last segment (in assembly) + + bool isFirstSegment() const { return bof; } + bool isLastSegment() const { return eof; } + bool isFirstFrame() const { return bos; } + bool isLastFrame() const { return eos; } + + void setFirstSegment(bool set=true) { bof = set; } + void setLastSegment(bool set=true) { eof = set; } + void setFirstFrame(bool set=true) { bos = set; } + void setLastFrame(bool set=true) { eos = set; } + + // 0-9 terminology: beginning/end of frameset, beginning/end of segment. + + bool getBof() const { return bof; } + void setBof(bool isBof) { bof = isBof; } + bool getEof() const { return eof; } + void setEof(bool isEof) { eof = isEof; } + + bool getBos() const { return bos; } + void setBos(bool isBos) { bos = isBos; } + bool getEos() const { return eos; } + void setEos(bool isEos) { eos = isEos; } + + static uint16_t DECODE_SIZE_MIN; + static uint32_t frameOverhead(); + /** Must point to at least DECODE_SIZE_MIN bytes of data */ + static uint16_t decodeSize(char* data); + private: + void init() { bof = eof = bos = eos = true; subchannel=0; channel=0; } + + boost::intrusive_ptr<BodyHolder> body; + uint16_t channel : 16; + uint8_t subchannel : 8; + bool bof : 1; + bool eof : 1; + bool bos : 1; + bool eos : 1; +}; + +std::ostream& operator<<(std::ostream&, const AMQFrame&); + +}} // namespace qpid::framing + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/AMQHeaderBody.cpp b/RC9/qpid/cpp/src/qpid/framing/AMQHeaderBody.cpp new file mode 100644 index 0000000000..276418b208 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AMQHeaderBody.cpp @@ -0,0 +1,63 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "AMQHeaderBody.h" +#include "qpid/Exception.h" +#include "qpid/log/Statement.h" + +uint32_t qpid::framing::AMQHeaderBody::encodedSize() const { + return properties.encodedSize(); +} + +void qpid::framing::AMQHeaderBody::encode(Buffer& buffer) const { + properties.encode(buffer); +} + +void qpid::framing::AMQHeaderBody::decode(Buffer& buffer, uint32_t size) { + uint32_t limit = buffer.available() - size; + while (buffer.available() > limit + 2) { + uint32_t len = buffer.getLong(); + uint16_t type = buffer.getShort(); + if (!properties.decode(buffer, len, type)) { + // TODO: should just skip & keep for later dispatch. + throw Exception(QPID_MSG("Unexpected property type: " << type)); + } + } +} + +uint64_t qpid::framing::AMQHeaderBody::getContentLength() const +{ + const MessageProperties* mProps = get<MessageProperties>(); + if (mProps) + return mProps->getContentLength(); + return 0; +} + +void qpid::framing::AMQHeaderBody::print(std::ostream& out) const +{ + out << "header (" << encodedSize() << " bytes)"; + out << "; properties={"; + properties.print(out); + out << "}"; +} + +void qpid::framing::AMQHeaderBody::accept(AMQBodyConstVisitor& v) const { + v.visit(*this); +} diff --git a/RC9/qpid/cpp/src/qpid/framing/AMQHeaderBody.h b/RC9/qpid/cpp/src/qpid/framing/AMQHeaderBody.h new file mode 100644 index 0000000000..7ddb7d89cf --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AMQHeaderBody.h @@ -0,0 +1,108 @@ +#ifndef QPID_FRAMING_AMQHEADERBODY_H +#define QPID_FRAMING_AMQHEADERBODY_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "amqp_types.h" +#include "AMQBody.h" +#include "Buffer.h" +#include "qpid/framing/DeliveryProperties.h" +#include "qpid/framing/MessageProperties.h" +#include <iostream> + +#include <boost/optional.hpp> + + +namespace qpid { +namespace framing { + +enum DeliveryMode { TRANSIENT = 1, PERSISTENT = 2}; + +class AMQHeaderBody : public AMQBody +{ + template <class T> struct OptProps { boost::optional<T> props; }; + template <class Base, class T> + struct PropSet : public Base, public OptProps<T> { + uint32_t encodedSize() const { + const boost::optional<T>& p=this->OptProps<T>::props; + return (p ? p->encodedSize() : 0) + Base::encodedSize(); + } + void encode(Buffer& buffer) const { + const boost::optional<T>& p=this->OptProps<T>::props; + if (p) p->encode(buffer); + Base::encode(buffer); + } + bool decode(Buffer& buffer, uint32_t size, uint16_t type) { + boost::optional<T>& p=this->OptProps<T>::props; + if (type == T::TYPE) { + p=T(); + p->decodeStructBody(buffer, size); + return true; + } + else + return Base::decode(buffer, size, type); + } + void print(std::ostream& out) const { + const boost::optional<T>& p=this->OptProps<T>::props; + if (p) out << *p; + Base::print(out); + } + }; + + struct Empty { + uint32_t encodedSize() const { return 0; } + void encode(Buffer&) const {}; + bool decode(Buffer&, uint32_t, uint16_t) const { return false; }; + void print(std::ostream&) const {} + }; + + // Could use boost::mpl::fold to construct a larger set. + typedef PropSet<PropSet<Empty, DeliveryProperties>, MessageProperties> Properties; + + Properties properties; + +public: + + inline uint8_t type() const { return HEADER_BODY; } + + uint32_t encodedSize() const; + void encode(Buffer& buffer) const; + void decode(Buffer& buffer, uint32_t size); + uint64_t getContentLength() const; + void print(std::ostream& out) const; + void accept(AMQBodyConstVisitor&) const; + + template <class T> T* get(bool create) { + boost::optional<T>& p=properties.OptProps<T>::props; + if (create && !p) p=T(); + return p.get_ptr(); + } + + template <class T> const T* get() const { + return properties.OptProps<T>::props.get_ptr(); + } +}; + +}} + + + +#endif /*!QPID_FRAMING_AMQHEADERBODY_H*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.cpp b/RC9/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.cpp new file mode 100644 index 0000000000..140ce2e794 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.cpp @@ -0,0 +1,29 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "AMQHeartbeatBody.h" +#include <iostream> + +qpid::framing::AMQHeartbeatBody::~AMQHeartbeatBody() {} + +void qpid::framing::AMQHeartbeatBody::print(std::ostream& out) const { + out << "heartbeat"; +} diff --git a/RC9/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.h b/RC9/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.h new file mode 100644 index 0000000000..7b42b46e5c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.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. + * + */ +#include "amqp_types.h" +#include "AMQBody.h" +#include "Buffer.h" + +#ifndef _AMQHeartbeatBody_ +#define _AMQHeartbeatBody_ + +namespace qpid { +namespace framing { + +class AMQHeartbeatBody : public AMQBody +{ +public: + virtual ~AMQHeartbeatBody(); + inline uint32_t encodedSize() const { return 0; } + inline uint8_t type() const { return HEARTBEAT_BODY; } + inline void encode(Buffer& ) const {} + inline void decode(Buffer& , uint32_t /*size*/) {} + virtual void print(std::ostream& out) const; + void accept(AMQBodyConstVisitor& v) const { v.visit(*this); } +}; + +} +} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/AMQMethodBody.cpp b/RC9/qpid/cpp/src/qpid/framing/AMQMethodBody.cpp new file mode 100644 index 0000000000..924d906d43 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AMQMethodBody.cpp @@ -0,0 +1,28 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "AMQMethodBody.h" + +namespace qpid { +namespace framing { + +AMQMethodBody::~AMQMethodBody() {} + +}} // namespace qpid::framing diff --git a/RC9/qpid/cpp/src/qpid/framing/AMQMethodBody.h b/RC9/qpid/cpp/src/qpid/framing/AMQMethodBody.h new file mode 100644 index 0000000000..cc7489ddd9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AMQMethodBody.h @@ -0,0 +1,72 @@ +#ifndef _AMQMethodBody_ +#define _AMQMethodBody_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "amqp_types.h" +#include "AMQBody.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/shared_ptr.h" + +#include <ostream> + +#include <assert.h> + +namespace qpid { +namespace framing { + +class Buffer; +class AMQP_ServerOperations; +class MethodBodyConstVisitor; + +class AMQMethodBody : public AMQBody { + public: + AMQMethodBody() {} + virtual ~AMQMethodBody(); + + virtual void accept(MethodBodyConstVisitor&) const = 0; + + virtual MethodId amqpMethodId() const = 0; + virtual ClassId amqpClassId() const = 0; + virtual bool isContentBearing() const = 0; + virtual bool resultExpected() const = 0; + virtual bool responseExpected() const = 0; + + template <class T> bool isA() const { + return amqpClassId()==T::CLASS_ID && amqpMethodId()==T::METHOD_ID; + } + + virtual uint32_t encodedSize() const = 0; + virtual uint8_t type() const { return METHOD_BODY; } + + virtual bool isSync() const { return false; /*only ModelMethods can have the sync flag set*/ } + virtual void setSync(bool) const { /*only ModelMethods can have the sync flag set*/ } + + AMQMethodBody* getMethod() { return this; } + const AMQMethodBody* getMethod() const { return this; } + void accept(AMQBodyConstVisitor& v) const { v.visit(*this); } +}; + + +}} // namespace qpid::framing + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/AMQP_HighestVersion.h b/RC9/qpid/cpp/src/qpid/framing/AMQP_HighestVersion.h new file mode 100644 index 0000000000..42139c7937 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AMQP_HighestVersion.h @@ -0,0 +1,40 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/* + * This file used to be auto-generated by Qpid Gentools v.0.1 + * its here temporarily until we get a full solution to multi-version support + */ +#ifndef qpid_framing_highestProtocolVersion__ +#define qpid_framing_highestProtocolVersion__ + +#include "qpid/framing/ProtocolVersion.h" + + +namespace qpid { +namespace framing { + +static ProtocolVersion highestProtocolVersion(0, 10); + +} /* namespace framing */ +} /* namespace qpid */ + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/AccumulatedAck.cpp b/RC9/qpid/cpp/src/qpid/framing/AccumulatedAck.cpp new file mode 100644 index 0000000000..2d3ecf3f6a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AccumulatedAck.cpp @@ -0,0 +1,164 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "AccumulatedAck.h" + +#include <assert.h> +#include <iostream> +#include <boost/bind.hpp> + +using std::list; +using std::max; +using std::min; +using namespace qpid::framing; + +AccumulatedAck::AccumulatedAck(SequenceNumber r) : mark(r) {} + +void AccumulatedAck::update(SequenceNumber first, SequenceNumber last){ + assert(first <= last); + if (last < mark) return; + + + Range r(first, last); + bool handled = false; + bool markMerged = false; + list<Range>::iterator merged = ranges.end(); + if (r.mergeable(mark)) { + mark = r.end; + markMerged = true; + handled = true; + } else { + for (list<Range>::iterator i = ranges.begin(); i != ranges.end() && !handled; i++) { + if (i->merge(r)) { + merged = i; + handled = true; + } else if (r.start < i->start) { + ranges.insert(i, r); + handled = true; + } + } + } + if (!handled) { + ranges.push_back(r); + } else { + while (!ranges.empty() && ranges.front().end <= mark) { + ranges.pop_front(); + } + if (markMerged) { + //new range is incorporated, but may be possible to consolidate + merged = ranges.begin(); + while (merged != ranges.end() && merged->mergeable(mark)) { + mark = merged->end; + merged = ranges.erase(merged); + } + } + if (merged != ranges.end()) { + //consolidate ranges + list<Range>::iterator i = merged; + list<Range>::iterator j = i++; + while (i != ranges.end() && j->merge(*i)) { + j = i++; + } + } + } +} + +void AccumulatedAck::consolidate(){} + +void AccumulatedAck::clear(){ + mark = SequenceNumber(0);//not sure that this is valid when wraparound is a possibility + ranges.clear(); +} + +bool AccumulatedAck::covers(SequenceNumber tag) const{ + if (tag <= mark) return true; + for (list<Range>::const_iterator i = ranges.begin(); i != ranges.end(); i++) { + if (i->contains(tag)) return true; + } + return false; +} + +void AccumulatedAck::collectRanges(SequenceNumberSet& set) const +{ + for (list<Range>::const_iterator i = ranges.begin(); i != ranges.end(); i++) { + set.push_back(i->start); + set.push_back(i->end); + } +} + +void AccumulatedAck::update(const SequenceNumber cumulative, const SequenceNumberSet& range) +{ + update(mark, cumulative); + range.processRanges(*this); +} + + +bool Range::contains(SequenceNumber i) const +{ + return i >= start && i <= end; +} + +bool Range::intersect(const Range& r) const +{ + return r.contains(start) || r.contains(end) || contains(r.start) || contains(r.end); +} + +bool Range::merge(const Range& r) +{ + if (intersect(r) || mergeable(r.end) || r.mergeable(end)) { + start = min(start, r.start); + end = max(end, r.end); + return true; + } else { + return false; + } +} + +bool Range::mergeable(const SequenceNumber& s) const +{ + if (contains(s) || start - s == 1) { + return true; + } else { + return false; + } +} + +Range::Range(SequenceNumber s, SequenceNumber e) : start(s), end(e) {} + + +namespace qpid{ +namespace framing{ + std::ostream& operator<<(std::ostream& out, const Range& r) + { + out << "[" << r.start.getValue() << "-" << r.end.getValue() << "]"; + return out; + } + + std::ostream& operator<<(std::ostream& out, const AccumulatedAck& a) + { + out << "{mark: " << a.mark.getValue() << ", ranges: ("; + for (list<Range>::const_iterator i = a.ranges.begin(); i != a.ranges.end(); i++) { + if (i != a.ranges.begin()) out << ", "; + out << *i; + } + out << ")]"; + return out; + } +}} diff --git a/RC9/qpid/cpp/src/qpid/framing/AccumulatedAck.h b/RC9/qpid/cpp/src/qpid/framing/AccumulatedAck.h new file mode 100644 index 0000000000..ea78b797e0 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/AccumulatedAck.h @@ -0,0 +1,76 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _AccumulatedAck_ +#define _AccumulatedAck_ + +#include <algorithm> +#include <functional> +#include <list> +#include <ostream> +#include "SequenceNumber.h" +#include "SequenceNumberSet.h" + +namespace qpid { + namespace framing { + + struct Range + { + SequenceNumber start; + SequenceNumber end; + + Range(SequenceNumber s, SequenceNumber e); + bool contains(SequenceNumber i) const; + bool intersect(const Range& r) const; + bool merge(const Range& r); + bool mergeable(const SequenceNumber& r) const; + }; + /** + * Keeps an accumulated record of acknowledged messages (by delivery + * tag). + */ + class AccumulatedAck { + public: + /** + * Everything up to this value has been acknowledged. + */ + SequenceNumber mark; + /** + * List of individually acknowledged messages greater than the + * 'mark'. + */ + std::list<Range> ranges; + + explicit AccumulatedAck(SequenceNumber r = SequenceNumber()); + void update(SequenceNumber firstTag, SequenceNumber lastTag); + void consolidate(); + void clear(); + bool covers(SequenceNumber tag) const; + void collectRanges(SequenceNumberSet& set) const; + void update(const SequenceNumber cumulative, const SequenceNumberSet& range); + void operator()(SequenceNumber first, SequenceNumber last) { update(first, last); } + }; + std::ostream& operator<<(std::ostream&, const Range&); + std::ostream& operator<<(std::ostream&, const AccumulatedAck&); + } +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/Array.cpp b/RC9/qpid/cpp/src/qpid/framing/Array.cpp new file mode 100644 index 0000000000..9f072f7b05 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/Array.cpp @@ -0,0 +1,129 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Array.h" +#include "Buffer.h" +#include "FieldValue.h" +#include "qpid/Exception.h" +#include "qpid/framing/reply_exceptions.h" +#include <assert.h> + +namespace qpid { +namespace framing { + +Array::Array() : type(TYPE_CODE_VOID) {} + +Array::Array(TypeCode t) : type(t) {} + +Array::Array(uint8_t t) : type(typeCode(t)) {} + +Array::Array(const std::vector<std::string>& in) +{ + type = TYPE_CODE_STR16; + for (std::vector<std::string>::const_iterator i = in.begin(); i != in.end(); ++i) { + ValuePtr value(new Str16Value(*i)); + values.push_back(value); + } +} + +uint32_t Array::encodedSize() const { + //note: size is only included when used as a 'top level' type + uint32_t len(4/*size*/ + 1/*type*/ + 4/*count*/); + for(ValueVector::const_iterator i = values.begin(); i != values.end(); ++i) { + len += (*i)->getData().encodedSize(); + } + return len; +} + +int Array::count() const { + return values.size(); +} + +std::ostream& operator<<(std::ostream& out, const Array& a) { + out << typeName(a.getType()) << "{"; + for(Array::ValueVector::const_iterator i = a.values.begin(); i != a.values.end(); ++i) { + if (i != a.values.begin()) out << ", "; + (*i)->print(out); + } + return out << "}"; +} + +void Array::encode(Buffer& buffer) const{ + buffer.putLong(encodedSize() - 4);//size added only when array is a top-level type + buffer.putOctet(type); + buffer.putLong(count()); + for (ValueVector::const_iterator i = values.begin(); i!=values.end(); ++i) { + (*i)->getData().encode(buffer); + } +} + +void Array::decode(Buffer& buffer){ + uint32_t size = buffer.getLong();//size added only when array is a top-level type + uint32_t available = buffer.available(); + if (available < size) { + throw IllegalArgumentException(QPID_MSG("Not enough data for array, expected " + << size << " bytes but only " << available << " available")); + } + if (size) { + type = TypeCode(buffer.getOctet()); + uint32_t count = buffer.getLong(); + + FieldValue dummy; + dummy.setType(type); + available = buffer.available(); + if (available < count * dummy.getData().encodedSize()) { + throw IllegalArgumentException(QPID_MSG("Not enough data for array, expected " + << count << " items of " << dummy.getData().encodedSize() + << " bytes each but only " << available << " bytes available")); + } + + for (uint32_t i = 0; i < count; i++) { + ValuePtr value(new FieldValue); + value->setType(type); + value->getData().decode(buffer); + values.push_back(ValuePtr(value)); + } + } +} + + +bool Array::operator==(const Array& x) const { + if (type != x.type) return false; + if (values.size() != x.values.size()) return false; + + for (ValueVector::const_iterator i = values.begin(), j = x.values.begin(); i != values.end(); ++i, ++j) { + if (*(i->get()) != *(j->get())) return false; + } + + return true; +} + +void Array::insert(iterator i, ValuePtr value) { + if (type != value->getType()) { + // FIXME aconway 2008-10-31: put meaningful strings in this message. + throw Exception(QPID_MSG("Wrong type of value in Array, expected " << type + << " but found " << TypeCode(value->getType()))); + } + values.insert(i, value); +} + + +} +} diff --git a/RC9/qpid/cpp/src/qpid/framing/Array.h b/RC9/qpid/cpp/src/qpid/framing/Array.h new file mode 100644 index 0000000000..183fcb6d5c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/Array.h @@ -0,0 +1,96 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "amqp_types.h" +#include "FieldValue.h" +#include "qpid/framing/TypeCode.h" +#include <boost/shared_ptr.hpp> +#include <iostream> +#include <vector> + +#ifndef _Array_ +#define _Array_ + +namespace qpid { +namespace framing { + +class Buffer; + +class Array +{ + public: + typedef boost::shared_ptr<FieldValue> ValuePtr; + typedef std::vector<ValuePtr> ValueVector; + typedef ValueVector::const_iterator const_iterator; + typedef ValueVector::iterator iterator; + + uint32_t encodedSize() const; + void encode(Buffer& buffer) const; + void decode(Buffer& buffer); + + int count() const; + bool operator==(const Array& other) const; + + Array(); + Array(TypeCode type); + Array(uint8_t type); + //creates a longstr array + Array(const std::vector<std::string>& in); + + TypeCode getType() const { return type; } + + // std collection interface. + const_iterator begin() const { return values.begin(); } + const_iterator end() const { return values.end(); } + iterator begin() { return values.begin(); } + iterator end(){ return values.end(); } + + ValuePtr front() const { return values.front(); } + ValuePtr back() const { return values.back(); } + size_t size() const { return values.size(); } + + void insert(iterator i, ValuePtr value); + void erase(iterator i) { values.erase(i); } + void push_back(ValuePtr value) { values.insert(end(), value); } + void pop_back() { values.pop_back(); } + + // Non-std interface + void add(ValuePtr value) { push_back(value); } + + template <class T> + void collect(std::vector<T>& out) const + { + for (ValueVector::const_iterator i = values.begin(); i != values.end(); ++i) { + out.push_back((*i)->get<T>()); + } + } + + private: + TypeCode type; + ValueVector values; + + friend std::ostream& operator<<(std::ostream& out, const Array& body); +}; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/Blob.cpp b/RC9/qpid/cpp/src/qpid/framing/Blob.cpp new file mode 100644 index 0000000000..388d4b64ef --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/Blob.cpp @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Blob.h" + + +namespace qpid { +namespace framing { + +void BlobHelper<void>::destroy(void*) {} + +void BlobHelper<void>::copy(void*, const void*) {} + +}} // namespace qpid::framing diff --git a/RC9/qpid/cpp/src/qpid/framing/Blob.h b/RC9/qpid/cpp/src/qpid/framing/Blob.h new file mode 100644 index 0000000000..5c84384ad7 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/Blob.h @@ -0,0 +1,197 @@ +#ifndef QPID_FRAMING_BLOB_H +#define QPID_FRAMING_BLOB_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/static_assert.hpp> +#include <boost/aligned_storage.hpp> +#include <boost/checked_delete.hpp> +#include <boost/utility/typed_in_place_factory.hpp> +#include <boost/type_traits/is_base_and_derived.hpp> +#include <boost/utility/enable_if.hpp> +#include <boost/version.hpp> + +#include <new> + +#include <assert.h> + + +namespace qpid { +namespace framing { + +using boost::in_place; +using boost::typed_in_place_factory_base; + +/** 0-arg typed_in_place_factory, missing in pre-1.35 boost. */ +#if (BOOST_VERSION < 103500) +template <class T> +struct typed_in_place_factory0 : public typed_in_place_factory_base { + typedef T value_type ; + void apply ( void* address ) const { new (address) T(); } +}; + +/** 0-arg in_place<T>() function, missing from boost. */ +template<class T> +typed_in_place_factory0<T> in_place() { return typed_in_place_factory0<T>(); } +#endif + +template <class T, class R=void> +struct EnableInPlace + : public boost::enable_if<boost::is_base_and_derived< + typed_in_place_factory_base, T>, + R> +{}; + +template <class T, class R=void> +struct DisableInPlace + : public boost::disable_if<boost::is_base_and_derived< + typed_in_place_factory_base, T>, + R> +{}; + +template <class T> struct BlobHelper { + static void destroy(void* ptr) { static_cast<T*>(ptr)->~T(); } + static void copy(void* dest, const void* src) { + new (dest) T(*static_cast<const T*>(src)); + } +}; + +template <> struct BlobHelper<void> { + static void destroy(void*); + static void copy(void* to, const void* from); +}; + +/** + * A Blob is a chunk of memory which can contain a single object at + * a time-arbitrary type, provided sizeof(T)<=blob.size(). Using Blobs + * ensures proper construction and destruction of its contents, + * and proper copying between Blobs, but nothing else. + * + * In particular you must ensure that the Blob is big enough for its + * contents and must know the type of object in the Blob to cast get(). + * + * If BaseType is specified then only an object that can be + * static_cast to BaseType may be stored in the Blob. + */ +template <size_t Size, class BaseType=void> +class Blob +{ + boost::aligned_storage<Size> store; + BaseType* basePtr; + + void (*destroy)(void*); + void (*copy)(void*, const void*); + + template <class T>void setType() { + BOOST_STATIC_ASSERT(sizeof(T) <= Size); + destroy=&BlobHelper<T>::destroy; + copy=&BlobHelper<T>::copy; + // Base pointer may be offeset from store.address() + basePtr = reinterpret_cast<T*>(store.address()); + } + + void initialize() { + destroy=&BlobHelper<void>::destroy; + copy=&BlobHelper<void>::copy; + basePtr=0; + } + + template<class Factory> + typename EnableInPlace<Factory>::type apply(const Factory& factory) + { + typedef typename Factory::value_type T; + assert(empty()); + factory.apply(store.address()); + setType<T>(); + } + + void assign(const Blob& b) { + assert(empty()); + if (b.empty()) return; + b.copy(this->store.address(), b.store.address()); + copy = b.copy; + destroy = b.destroy; + basePtr = reinterpret_cast<BaseType*>( + ((char*)this)+ ((const char*)(b.basePtr) - (const char*)(&b))); + } + + public: + /** Construct an empty Blob. */ + Blob() { initialize(); } + + /** Copy a Blob. */ + Blob(const Blob& b) { initialize(); assign(b); } + + /** Construct from in_place constructor. */ + template<class InPlace> + Blob(const InPlace & expr, typename EnableInPlace<InPlace>::type* =0) { + initialize(); apply(expr); + } + + /** Construct by copying an object constructor. */ + template<class T> + Blob(const T & t, typename DisableInPlace<T>::type* =0) { + initialize(); apply(in_place<T>(t)); + } + + ~Blob() { clear(); } + + /** Assign from another Blob. */ + Blob& operator=(const Blob& b) { + clear(); + assign(b); + return *this; + } + + /** Assign from an in_place constructor expression. */ + template<class InPlace> + typename EnableInPlace<InPlace,Blob&>::type operator=(const InPlace& expr) { + clear(); apply(expr); return *this; + } + + /** Assign from an object of type T. */ + template <class T> + typename DisableInPlace<T, Blob&>::type operator=(const T& x) { + clear(); apply(in_place<T>(x)); return *this; + } + + /** Get pointer to Blob contents, returns 0 if empty. */ + BaseType* get() { return basePtr; } + + /** Get pointer to Blob contents, returns 0 if empty. */ + const BaseType* get() const { return basePtr; } + + /** Destroy the object in the Blob making it empty. */ + void clear() { + void (*oldDestroy)(void*) = destroy; + initialize(); + oldDestroy(store.address()); + } + + bool empty() const { return destroy==BlobHelper<void>::destroy; } + + static size_t size() { return Size; } +}; + +}} // namespace qpid::framing + + +#endif /*!QPID_FRAMING_BLOB_H*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/BodyHandler.cpp b/RC9/qpid/cpp/src/qpid/framing/BodyHandler.cpp new file mode 100644 index 0000000000..ffbcf33a95 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/BodyHandler.cpp @@ -0,0 +1,55 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "BodyHandler.h" +#include "AMQMethodBody.h" +#include "AMQHeaderBody.h" +#include "AMQContentBody.h" +#include "AMQHeartbeatBody.h" +#include <boost/cast.hpp> +#include "qpid/framing/reply_exceptions.h" + +using namespace qpid::framing; +using namespace boost; + +BodyHandler::~BodyHandler() {} + +// TODO aconway 2007-08-13: Replace with visitor. +void BodyHandler::handleBody(AMQBody* body) { + switch(body->type()) + { + case METHOD_BODY: + handleMethod(polymorphic_downcast<AMQMethodBody*>(body)); + break; + case HEADER_BODY: + handleHeader(polymorphic_downcast<AMQHeaderBody*>(body)); + break; + case CONTENT_BODY: + handleContent(polymorphic_downcast<AMQContentBody*>(body)); + break; + case HEARTBEAT_BODY: + handleHeartbeat(polymorphic_downcast<AMQHeartbeatBody*>(body)); + break; + default: + throw FramingErrorException( + QPID_MSG("Invalid frame type " << body->type())); + } +} + diff --git a/RC9/qpid/cpp/src/qpid/framing/BodyHandler.h b/RC9/qpid/cpp/src/qpid/framing/BodyHandler.h new file mode 100644 index 0000000000..9ded737195 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/BodyHandler.h @@ -0,0 +1,56 @@ +#ifndef _BodyHandler_ +#define _BodyHandler_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace framing { +class AMQBody; +class AMQMethodBody; +class AMQHeaderBody; +class AMQContentBody; +class AMQHeartbeatBody; + +// TODO aconway 2007-08-10: rework using Visitor pattern? + +/** + * Interface to handle incoming frame bodies. + * Derived classes provide logic for each frame type. + */ +class BodyHandler { + public: + virtual ~BodyHandler(); + virtual void handleBody(AMQBody* body); + + protected: + virtual void handleMethod(AMQMethodBody*) = 0; + virtual void handleHeader(AMQHeaderBody*) = 0; + virtual void handleContent(AMQContentBody*) = 0; + virtual void handleHeartbeat(AMQHeartbeatBody*) = 0; +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/BodyHolder.cpp b/RC9/qpid/cpp/src/qpid/framing/BodyHolder.cpp new file mode 100644 index 0000000000..b30a52a38e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/BodyHolder.cpp @@ -0,0 +1,76 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "BodyHolder.h" +#include "AMQMethodBody.h" +#include "AMQHeaderBody.h" +#include "AMQContentBody.h" +#include "AMQHeartbeatBody.h" +#include "Buffer.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace framing { + + +// BodyHolder::operator=(const AMQBody&) is defined +// in generated file BodyHolder_gen.cpp + + +void BodyHolder::encode(Buffer& b) const { + const AMQMethodBody* method=getMethod(); + if (method) { + b.putOctet(method->amqpClassId()); + b.putOctet(method->amqpMethodId()); + method->encode(b); + } + else + get()->encode(b); +} + +void BodyHolder::decode(uint8_t type, Buffer& buffer, uint32_t size) { + switch(type) + { + case 0://CONTROL + case METHOD_BODY: { + ClassId c = buffer.getOctet(); + MethodId m = buffer.getOctet(); + setMethod(c, m); + break; + } + case HEADER_BODY: *this=in_place<AMQHeaderBody>(); break; + case CONTENT_BODY: *this=in_place<AMQContentBody>(); break; + case HEARTBEAT_BODY: *this=in_place<AMQHeartbeatBody>(); break; + default: + throw IllegalArgumentException(QPID_MSG("Invalid frame type " << type)); + } + get()->decode(buffer, size); +} + +uint32_t BodyHolder::encodedSize() const { + const AMQMethodBody* method=getMethod(); + if (method) + return sizeof(ClassId)+sizeof(MethodId)+method->encodedSize(); + else + return get()->encodedSize(); +} + +}} // namespace qpid::framing + diff --git a/RC9/qpid/cpp/src/qpid/framing/BodyHolder.h b/RC9/qpid/cpp/src/qpid/framing/BodyHolder.h new file mode 100644 index 0000000000..0a35bb5e99 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/BodyHolder.h @@ -0,0 +1,88 @@ +#ifndef QPID_FRAMING_BODYHOLDER_H +#define QPID_FRAMING_BODYHOLDER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/framing/AMQBody.h" +#include "qpid/framing/Blob.h" +#include "qpid/framing/MaxMethodBodySize.h" // Generated file. +#include "qpid/framing/amqp_types.h" +#include "qpid/RefCounted.h" + + +namespace qpid { +namespace framing { + +class AMQMethodBody; +class AMQBody; +class Buffer; + +/** + * Holder for arbitrary frame body. + */ +class BodyHolder : public RefCounted +{ + public: + // default copy, assign dtor ok. + BodyHolder() {} + BodyHolder(const AMQBody& b) { setBody(b); } + BodyHolder(ClassId c, MethodId m) { setMethod(c,m); } + + /** Construct from an in_place constructor expression. */ + template <class InPlace> + BodyHolder(const InPlace& ip, typename EnableInPlace<InPlace>::type* =0) + : blob(ip) {} + + void setBody(const AMQBody& b); + + /** Assign from an in_place constructor expression. */ + template <class InPlace> + typename EnableInPlace<InPlace,BodyHolder&>::type + operator=(const InPlace& ip) { blob=ip; return *this; } + + /** Assign by copying. */ + template <class T> + typename DisableInPlace<T,BodyHolder&>::type operator=(const T& x) + { blob=in_place<T>(x); return *this; } + + /** Set to method with ClassId c, MethodId m. */ + void setMethod(ClassId c, MethodId m); + + void encode(Buffer&) const; + void decode(uint8_t frameType, Buffer&, uint32_t=0); + uint32_t encodedSize() const; + + /** Return body pointer or 0 if empty. */ + AMQBody* get() { return blob.get(); } + const AMQBody* get() const { return blob.get(); } + + /** Return method pointer or 0 if not a method. */ + AMQMethodBody* getMethod() { return get()->getMethod(); } + const AMQMethodBody* getMethod() const { return get()->getMethod(); } + + private: + Blob<MAX_METHOD_BODY_SIZE, AMQBody> blob; +}; + +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_BODYHOLDER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/Buffer.cpp b/RC9/qpid/cpp/src/qpid/framing/Buffer.cpp new file mode 100644 index 0000000000..a90c3a2e64 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/Buffer.cpp @@ -0,0 +1,319 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Buffer.h" +#include "FieldTable.h" +#include <string.h> +#include <boost/format.hpp> +namespace qpid { + +namespace framing { + +Buffer::Buffer(char* _data, uint32_t _size) + : size(_size), data(_data), position(0) { +} + +void Buffer::record(){ + r_position = position; +} + +void Buffer::restore(bool reRecord){ + uint32_t savedPosition = position; + + position = r_position; + + if (reRecord) + r_position = savedPosition; +} + +void Buffer::reset(){ + position = 0; +} + +/////////////////////////////////////////////////// + +void Buffer::putOctet(uint8_t i){ + data[position++] = i; + assert(position <= size); +} + +void Buffer::putShort(uint16_t i){ + uint16_t b = i; + data[position++] = (uint8_t) (0xFF & (b >> 8)); + data[position++] = (uint8_t) (0xFF & b); + assert(position <= size); +} + +void Buffer::putLong(uint32_t i){ + uint32_t b = i; + data[position++] = (uint8_t) (0xFF & (b >> 24)); + data[position++] = (uint8_t) (0xFF & (b >> 16)); + data[position++] = (uint8_t) (0xFF & (b >> 8)); + data[position++] = (uint8_t) (0xFF & b); + assert(position <= size); +} + +void Buffer::putLongLong(uint64_t i){ + uint32_t hi = i >> 32; + uint32_t lo = i; + putLong(hi); + putLong(lo); +} + +void Buffer::putInt8(int8_t i){ + data[position++] = (uint8_t) i; + assert(position <= size); +} + +void Buffer::putInt16(int16_t i){ + putShort((uint16_t) i); +} + +void Buffer::putInt32(int32_t i){ + putLong((uint32_t) i); +} + +void Buffer::putInt64(int64_t i){ + putLongLong((uint64_t) i); +} + +void Buffer::putFloat(float f){ + union { + uint32_t i; + float f; + } val; + + val.f = f; + putLong (val.i); +} + +void Buffer::putDouble(double f){ + union { + uint64_t i; + double f; + } val; + + val.f = f; + putLongLong (val.i); +} + +void Buffer::putBin128(uint8_t* b){ + memcpy (data + position, b, 16); + position += 16; +} + +uint8_t Buffer::getOctet(){ + uint8_t octet = static_cast<uint8_t>(data[position++]); + assert(position <= size); + return octet; +} + +uint16_t Buffer::getShort(){ + uint16_t hi = (unsigned char) data[position++]; + hi = hi << 8; + hi |= (unsigned char) data[position++]; + assert(position <= size); + return hi; +} + +uint32_t Buffer::getLong(){ + uint32_t a = (unsigned char) data[position++]; + uint32_t b = (unsigned char) data[position++]; + uint32_t c = (unsigned char) data[position++]; + uint32_t d = (unsigned char) data[position++]; + assert(position <= size); + a = a << 24; + a |= b << 16; + a |= c << 8; + a |= d; + return a; +} + +uint64_t Buffer::getLongLong(){ + uint64_t hi = getLong(); + uint64_t lo = getLong(); + hi = hi << 32; + return hi | lo; +} + +int8_t Buffer::getInt8(){ + int8_t i = static_cast<int8_t>(data[position++]); + assert(position <= size); + return i; +} + +int16_t Buffer::getInt16(){ + return (int16_t) getShort(); +} + +int32_t Buffer::getInt32(){ + return (int32_t) getLong(); +} + +int64_t Buffer::getInt64(){ + return (int64_t) getLongLong(); +} + +float Buffer::getFloat(){ + union { + uint32_t i; + float f; + } val; + val.i = getLong(); + return val.f; +} + +double Buffer::getDouble(){ + union { + uint64_t i; + double f; + } val; + val.i = getLongLong(); + return val.f; +} + +template <> +uint64_t Buffer::getUInt<1>() { + return getOctet(); +} + +template <> +uint64_t Buffer::getUInt<2>() { + return getShort(); +} + +template <> +uint64_t Buffer::getUInt<4>() { + return getLong(); +} + +template <> +uint64_t Buffer::getUInt<8>() { + return getLongLong(); +} + +template <> +void Buffer::putUInt<1>(uint64_t i) { + putOctet(i); +} + +template <> +void Buffer::putUInt<2>(uint64_t i) { + putShort(i); +} + +template <> +void Buffer::putUInt<4>(uint64_t i) { + putLong(i); +} + +template <> +void Buffer::putUInt<8>(uint64_t i) { + putLongLong(i); +} + +void Buffer::putShortString(const string& s){ + size_t slen = s.length(); + uint8_t len = slen < 0x100 ? (uint8_t) slen : 0xFF; + putOctet(len); + s.copy(data + position, len); + position += len; +} + +void Buffer::putMediumString(const string& s){ + size_t slen = s.length(); + uint16_t len = slen < 0x10000 ? (uint16_t) slen : 0xFFFF; + putShort(len); + s.copy(data + position, len); + position += len; +} + +void Buffer::putLongString(const string& s){ + uint32_t len = s.length(); + putLong(len); + s.copy(data + position, len); + position += len; +} + +void Buffer::getShortString(string& s){ + uint8_t len = getOctet(); + checkAvailable(len); + s.assign(data + position, len); + position += len; +} + +void Buffer::getMediumString(string& s){ + uint16_t len = getShort(); + checkAvailable(len); + s.assign(data + position, len); + position += len; +} + +void Buffer::getLongString(string& s){ + uint32_t len = getLong(); + checkAvailable(len); + s.assign(data + position, len); + position += len; +} + +void Buffer::getBin128(uint8_t* b){ + memcpy (b, data + position, 16); + position += 16; +} + +void Buffer::putRawData(const string& s){ + uint32_t len = s.length(); + s.copy(data + position, len); + position += len; +} + +void Buffer::getRawData(string& s, uint32_t len){ + checkAvailable(len); + s.assign(data + position, len); + position += len; +} + +void Buffer::putRawData(const uint8_t* s, size_t len){ + memcpy(data + position, s, len); + position += len; +} + +void Buffer::getRawData(uint8_t* s, size_t len){ + checkAvailable(len); + memcpy(s, data + position, len); + position += len; +} + +void Buffer::dump(std::ostream& out) const { + for (uint32_t i = position; i < size; i++) + { + if (i != position) + out << " "; + out << boost::format("%02x") % ((unsigned) (uint8_t) data[i]); + } +} + +std::ostream& operator<<(std::ostream& out, const Buffer& b){ + out << "Buffer["; + b.dump(out); + return out << "]"; +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/framing/Buffer.h b/RC9/qpid/cpp/src/qpid/framing/Buffer.h new file mode 100644 index 0000000000..828e6e963a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/Buffer.h @@ -0,0 +1,135 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "amqp_types.h" +#include "qpid/Exception.h" +#include <boost/iterator/iterator_facade.hpp> + +#ifndef _Buffer_ +#define _Buffer_ + +namespace qpid { +namespace framing { + +struct OutOfBounds : qpid::Exception { + OutOfBounds() : qpid::Exception(std::string("Out of Bounds")) {} +}; + +class Content; +class FieldTable; + +class Buffer +{ + uint32_t size; + char* data; + uint32_t position; + uint32_t r_position; + + void checkAvailable(uint32_t count) { if (position + count > size) throw OutOfBounds(); } + + public: + + /** Buffer input/output iterator. + * Supports using an amqp_0_10::Codec with a framing::Buffer. + */ + class Iterator : public boost::iterator_facade< + Iterator, char, boost::random_access_traversal_tag> + { + public: + Iterator(Buffer& b) : buffer(&b) {} + + private: + friend class boost::iterator_core_access; + char& dereference() const { return buffer->data[buffer->position]; } + void increment() { ++buffer->position; } + bool equal(const Iterator& x) const { return buffer == x.buffer; } + + Buffer* buffer; + }; + + friend class Iterator; + + Buffer(char* data=0, uint32_t size=0); + + void record(); + void restore(bool reRecord = false); + void reset(); + + uint32_t available() { return size - position; } + uint32_t getSize() { return size; } + uint32_t getPosition() { return position; } + Iterator getIterator() { return Iterator(*this); } + char* getPointer() { return data; } + + void putOctet(uint8_t i); + void putShort(uint16_t i); + void putLong(uint32_t i); + void putLongLong(uint64_t i); + void putInt8(int8_t i); + void putInt16(int16_t i); + void putInt32(int32_t i); + void putInt64(int64_t i); + void putFloat(float f); + void putDouble(double f); + void putBin128(uint8_t* b); + + uint8_t getOctet(); + uint16_t getShort(); + uint32_t getLong(); + uint64_t getLongLong(); + int8_t getInt8(); + int16_t getInt16(); + int32_t getInt32(); + int64_t getInt64(); + float getFloat(); + double getDouble(); + + template <int n> + uint64_t getUInt(); + + template <int n> + void putUInt(uint64_t); + + void putShortString(const string& s); + void putMediumString(const string& s); + void putLongString(const string& s); + void getShortString(string& s); + void getMediumString(string& s); + void getLongString(string& s); + void getBin128(uint8_t* b); + + void putRawData(const string& s); + void getRawData(string& s, uint32_t size); + + void putRawData(const uint8_t* data, size_t size); + void getRawData(uint8_t* data, size_t size); + + template <class T> void put(const T& data) { data.encode(*this); } + template <class T> void get(T& data) { data.decode(*this); } + + void dump(std::ostream&) const; +}; + +std::ostream& operator<<(std::ostream&, const Buffer&); + +}} // namespace qpid::framing + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/ChannelHandler.h b/RC9/qpid/cpp/src/qpid/framing/ChannelHandler.h new file mode 100644 index 0000000000..69aaeac492 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/ChannelHandler.h @@ -0,0 +1,53 @@ +#ifndef QPID_FRAMING_CHANNELHANDLER_H +#define QPID_FRAMING_CHANNELHANDLER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "FrameHandler.h" +#include "AMQFrame.h" + +namespace qpid { +namespace framing { + +/** + * Sets the channel number on outgoing frames. + */ +class ChannelHandler : public FrameHandler +{ + public: + ChannelHandler(uint16_t channelId=0, FrameHandler* next=0) + : FrameHandler(next), channel(channelId) {} + void handle(AMQFrame& frame) { + frame.setChannel(channel); + next->handle(frame); + } + uint16_t get() const { return channel; } + ChannelHandler& set(uint16_t ch) { channel=ch; return *this; } + operator uint16_t() const { return get(); } + ChannelHandler& operator=(uint16_t ch) { return set(ch); } + + private: + uint16_t channel; +}; + +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_CHANNELHANDLER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/Endian.cpp b/RC9/qpid/cpp/src/qpid/framing/Endian.cpp new file mode 100644 index 0000000000..1948579b88 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/Endian.cpp @@ -0,0 +1,52 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Endian.h" + +namespace qpid { +namespace framing { + +Endian::Endian() : littleEndian(!testBigEndian()) {} + +bool Endian::testBigEndian() +{ + uint16_t a = 1; + uint16_t b; + uint8_t* p = (uint8_t*) &b; + p[0] = 0xFF & (a >> 8); + p[1] = 0xFF & (a); + return a == b; +} + +uint8_t* Endian::convertIfRequired(uint8_t* const octets, int width) +{ + if (instance.littleEndian) { + for (int i = 0; i < (width/2); i++) { + uint8_t temp = octets[i]; + octets[i] = octets[width - (1 + i)]; + octets[width - (1 + i)] = temp; + } + } + return octets; +} + +const Endian Endian::instance; + +}} // namespace qpid::framing diff --git a/RC9/qpid/cpp/src/qpid/framing/Endian.h b/RC9/qpid/cpp/src/qpid/framing/Endian.h new file mode 100644 index 0000000000..077d5a3e9b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/Endian.h @@ -0,0 +1,46 @@ +#ifndef QPID_FRAMING_ENDIAN_H +#define QPID_FRAMING_ENDIAN_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/IntegerTypes.h" + +namespace qpid { +namespace framing { + +/** + * Conversion utility for little-endian platforms that need to convert + * to and from network ordered octet sequences + */ +class Endian +{ + public: + static uint8_t* convertIfRequired(uint8_t* const octets, int width); + private: + const bool littleEndian; + Endian(); + static const Endian instance; + static bool testBigEndian(); +}; +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_ENDIAN_H*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/FieldTable.cpp b/RC9/qpid/cpp/src/qpid/framing/FieldTable.cpp new file mode 100644 index 0000000000..7260261970 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/FieldTable.cpp @@ -0,0 +1,240 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "FieldTable.h" +#include "Array.h" +#include "Buffer.h" +#include "Endian.h" +#include "FieldValue.h" +#include "qpid/Exception.h" +#include "qpid/framing/reply_exceptions.h" +#include <assert.h> + +namespace qpid { +namespace framing { + +FieldTable::FieldTable(const FieldTable& ft) +{ + *this = ft; +} + +FieldTable& FieldTable::operator=(const FieldTable& ft) +{ + clear(); + values = ft.values; + return *this; +} + +FieldTable::~FieldTable() {} + +uint32_t FieldTable::encodedSize() const { + uint32_t len(4/*size field*/ + 4/*count field*/); + for(ValueMap::const_iterator i = values.begin(); i != values.end(); ++i) { + // shortstr_len_byte + key size + value size + len += 1 + (i->first).size() + (i->second)->encodedSize(); + } + return len; +} + +int FieldTable::count() const { + return values.size(); +} + +namespace +{ +std::ostream& operator<<(std::ostream& out, const FieldTable::ValueMap::value_type& i) { + return out << i.first << ":" << *i.second; +} +} + +std::ostream& operator<<(std::ostream& out, const FieldTable& t) { + out << "{"; + FieldTable::ValueMap::const_iterator i = t.begin(); + if (i != t.end()) out << *i++; + while (i != t.end()) + { + out << "," << *i++; + } + return out << "}"; +} + +void FieldTable::set(const std::string& name, const ValuePtr& value){ + values[name] = value; +} + +void FieldTable::setString(const std::string& name, const std::string& value){ + values[name] = ValuePtr(new Str16Value(value)); +} + +void FieldTable::setInt(const std::string& name, const int value){ + values[name] = ValuePtr(new IntegerValue(value)); +} + +void FieldTable::setInt64(const std::string& name, const int64_t value){ + values[name] = ValuePtr(new Integer64Value(value)); +} + +void FieldTable::setTimestamp(const std::string& name, const uint64_t value){ + values[name] = ValuePtr(new TimeValue(value)); +} + +void FieldTable::setUInt64(const std::string& name, const uint64_t value){ + values[name] = ValuePtr(new Unsigned64Value(value)); +} + +void FieldTable::setTable(const std::string& name, const FieldTable& value) +{ + values[name] = ValuePtr(new FieldTableValue(value)); +} +void FieldTable::setArray(const std::string& name, const Array& value) +{ + values[name] = ValuePtr(new ArrayValue(value)); +} + +void FieldTable::setFloat(const std::string& name, const float value){ + values[name] = ValuePtr(new FloatValue(value)); +} + +void FieldTable::setDouble(const std::string& name, double value){ + values[name] = ValuePtr(new DoubleValue(value)); +} + +FieldTable::ValuePtr FieldTable::get(const std::string& name) const +{ + ValuePtr value; + ValueMap::const_iterator i = values.find(name); + if ( i!=values.end() ) + value = i->second; + return value; +} + +namespace { + template <class T> T default_value() { return T(); } + template <> int default_value<int>() { return 0; } + template <> uint64_t default_value<uint64_t>() { return 0; } +} + +template <class T> +T getValue(const FieldTable::ValuePtr value) +{ + if (!value || !value->convertsTo<T>()) + return default_value<T>(); + + return value->get<T>(); +} + +std::string FieldTable::getAsString(const std::string& name) const { + return getValue<std::string>(get(name)); +} + +int FieldTable::getAsInt(const std::string& name) const { + return getValue<int>(get(name)); +} + +uint64_t FieldTable::getAsUInt64(const std::string& name) const { + return static_cast<uint64_t>( getValue<int64_t>(get(name))); +} + +int64_t FieldTable::getAsInt64(const std::string& name) const { + return getValue<int64_t>(get(name)); +} + +bool FieldTable::getTable(const std::string& name, FieldTable& value) const { + return getEncodedValue<FieldTable>(get(name), value); +} + +bool FieldTable::getArray(const std::string& name, Array& value) const { + return getEncodedValue<Array>(get(name), value); +} + +template <class T, int width, uint8_t typecode> +bool getRawFixedWidthValue(FieldTable::ValuePtr vptr, T& value) +{ + if (vptr && vptr->getType() == typecode) { + FixedWidthValue<width>* fwv = dynamic_cast< FixedWidthValue<width>* >(&vptr->getData()); + if (fwv) { + uint8_t* const octets = Endian::convertIfRequired(fwv->rawOctets(), width); + uint8_t* const target = reinterpret_cast<uint8_t*>(&value); + for (uint i = 0; i < width; ++i) target[i] = octets[i]; + return true; + } + } + return false; +} + +bool FieldTable::getFloat(const std::string& name, float& value) const { + return getRawFixedWidthValue<float, 4, 0x23>(get(name), value); +} + +bool FieldTable::getDouble(const std::string& name, double& value) const { + return getRawFixedWidthValue<double, 8, 0x33>(get(name), value); +} + +//uint64_t FieldTable::getTimestamp(const std::string& name) const { +// return getValue<uint64_t>(name); +//} + +void FieldTable::encode(Buffer& buffer) const{ + buffer.putLong(encodedSize() - 4); + buffer.putLong(values.size()); + for (ValueMap::const_iterator i = values.begin(); i!=values.end(); ++i) { + buffer.putShortString(i->first); + i->second->encode(buffer); + } +} + +void FieldTable::decode(Buffer& buffer){ + uint32_t len = buffer.getLong(); + if (len) { + uint32_t available = buffer.available(); + if (available < len) + throw IllegalArgumentException(QPID_MSG("Not enough data for field table.")); + uint32_t count = buffer.getLong(); + uint32_t leftover = available - len; + while(buffer.available() > leftover && count--){ + std::string name; + ValuePtr value(new FieldValue); + + buffer.getShortString(name); + value->decode(buffer); + values[name] = ValuePtr(value); + } + } +} + + +bool FieldTable::operator==(const FieldTable& x) const { + if (values.size() != x.values.size()) return false; + for (ValueMap::const_iterator i = values.begin(); i != values.end(); ++i) { + ValueMap::const_iterator j = x.values.find(i->first); + if (j == x.values.end()) return false; + if (*(i->second) != *(j->second)) return false; + } + return true; +} + +void FieldTable::erase(const std::string& name) +{ + if (values.find(name) != values.end()) + values.erase(name); +} + +} +} diff --git a/RC9/qpid/cpp/src/qpid/framing/FieldTable.h b/RC9/qpid/cpp/src/qpid/framing/FieldTable.h new file mode 100644 index 0000000000..9e1214a28c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/FieldTable.h @@ -0,0 +1,120 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include <vector> +#include <boost/shared_ptr.hpp> +#include <map> +#include "amqp_types.h" + +#ifndef _FieldTable_ +#define _FieldTable_ + +namespace qpid { + /** + * The framing namespace contains classes that are used to create, + * send and receive the basic packets from which AMQP is built. + */ +namespace framing { + +class Array; +class FieldValue; +class Buffer; + +/** + * A set of name-value pairs. (See the AMQP spec for more details on + * AMQP field tables). + * + * \ingroup clientapi + */ +class FieldTable +{ + public: + typedef boost::shared_ptr<FieldValue> ValuePtr; + typedef std::map<std::string, ValuePtr> ValueMap; + typedef ValueMap::iterator iterator; + + FieldTable() {}; + FieldTable(const FieldTable& ft); + ~FieldTable(); + FieldTable& operator=(const FieldTable& ft); + uint32_t encodedSize() const; + void encode(Buffer& buffer) const; + void decode(Buffer& buffer); + + int count() const; + void set(const std::string& name, const ValuePtr& value); + ValuePtr get(const std::string& name) const; + bool isSet(const std::string& name) const { return get(name).get() != 0; } + + void setString(const std::string& name, const std::string& value); + void setInt(const std::string& name, const int value); + void setInt64(const std::string& name, const int64_t value); + void setTimestamp(const std::string& name, const uint64_t value); + void setUInt64(const std::string& name, const uint64_t value); + void setTable(const std::string& name, const FieldTable& value); + void setArray(const std::string& name, const Array& value); + void setFloat(const std::string& name, const float value); + void setDouble(const std::string& name, const double value); + //void setDecimal(string& name, xxx& value); + + int getAsInt(const std::string& name) const; + uint64_t getAsUInt64(const std::string& name) const; + int64_t getAsInt64(const std::string& name) const; + std::string getAsString(const std::string& name) const; + + bool getTable(const std::string& name, FieldTable& value) const; + bool getArray(const std::string& name, Array& value) const; + bool getFloat(const std::string& name, float& value) const; + bool getDouble(const std::string& name, double& value) const; + //bool getTimestamp(const std::string& name, uint64_t& value) const; + //bool getDecimal(string& name, xxx& value); + void erase(const std::string& name); + + + bool operator==(const FieldTable& other) const; + + // Map-like interface. + // TODO: may need to duplicate into versions that return mutable iterator + ValueMap::const_iterator begin() const { return values.begin(); } + ValueMap::const_iterator end() const { return values.end(); } + ValueMap::const_iterator find(const std::string& s) const { return values.find(s); } + + std::pair <ValueMap::iterator, bool> insert(const ValueMap::value_type&); + void clear() { values.clear(); } + + // ### Hack Alert + + ValueMap::iterator getValues() { return values.begin(); } + + private: + ValueMap values; + + friend std::ostream& operator<<(std::ostream& out, const FieldTable& body); +}; + +//class FieldNotFoundException{}; +//class UnknownFieldName : public FieldNotFoundException{}; +//class IncorrectFieldType : public FieldNotFoundException{}; +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/FieldValue.cpp b/RC9/qpid/cpp/src/qpid/framing/FieldValue.cpp new file mode 100644 index 0000000000..ecf469236d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/FieldValue.cpp @@ -0,0 +1,180 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "FieldValue.h" +#include "Array.h" +#include "Buffer.h" +#include "Endian.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace framing { + +uint8_t FieldValue::getType() +{ + return typeOctet; +} + +void FieldValue::setType(uint8_t type) +{ + typeOctet = type; + if (typeOctet == 0xA8) { + data.reset(new EncodedValue<FieldTable>()); + } else if (typeOctet == 0xAA) { + data.reset(new EncodedValue<Array>()); + } else { + uint8_t lenType = typeOctet >> 4; + switch(lenType){ + case 0: + data.reset(new FixedWidthValue<1>()); + break; + case 1: + data.reset(new FixedWidthValue<2>()); + break; + case 2: + data.reset(new FixedWidthValue<4>()); + break; + case 3: + data.reset(new FixedWidthValue<8>()); + break; + case 4: + data.reset(new FixedWidthValue<16>()); + break; + case 5: + data.reset(new FixedWidthValue<32>()); + break; + case 6: + data.reset(new FixedWidthValue<64>()); + break; + case 7: + data.reset(new FixedWidthValue<128>()); + break; + case 8: + data.reset(new VariableWidthValue<1>()); + break; + case 9: + data.reset(new VariableWidthValue<2>()); + break; + case 0xA: + data.reset(new VariableWidthValue<4>()); + break; + case 0xC: + data.reset(new FixedWidthValue<5>()); + break; + case 0xD: + data.reset(new FixedWidthValue<9>()); + break; + case 0xF: + data.reset(new FixedWidthValue<0>()); + break; + default: + throw IllegalArgumentException(QPID_MSG("Unknown field table value type: " << (int)typeOctet)); + } + } +} + +void FieldValue::decode(Buffer& buffer) +{ + setType(buffer.getOctet()); + data->decode(buffer); +} + +void FieldValue::encode(Buffer& buffer) +{ + buffer.putOctet(typeOctet); + data->encode(buffer); +} + +bool FieldValue::operator==(const FieldValue& v) const +{ + return + typeOctet == v.typeOctet && + *data == *v.data; +} + +Str8Value::Str8Value(const std::string& v) : + FieldValue( + TYPE_CODE_STR8, + new VariableWidthValue<1>( + reinterpret_cast<const uint8_t*>(v.data()), + reinterpret_cast<const uint8_t*>(v.data()+v.size()))) +{ +} + +Str16Value::Str16Value(const std::string& v) : + FieldValue( + 0x95, + new VariableWidthValue<2>( + reinterpret_cast<const uint8_t*>(v.data()), + reinterpret_cast<const uint8_t*>(v.data()+v.size()))) +{} + +Struct32Value::Struct32Value(const std::string& v) : + FieldValue( + 0xAB, + new VariableWidthValue<4>( + reinterpret_cast<const uint8_t*>(v.data()), + reinterpret_cast<const uint8_t*>(v.data()+v.size()))) +{} + +IntegerValue::IntegerValue(int v) : + FieldValue(0x21, new FixedWidthValue<4>(v)) +{} + +FloatValue::FloatValue(float v) : + FieldValue(0x23, new FixedWidthValue<4>(Endian::convertIfRequired(reinterpret_cast<uint8_t*>(&v), 4))) +{} + +DoubleValue::DoubleValue(double v) : + FieldValue(0x33, new FixedWidthValue<8>(Endian::convertIfRequired(reinterpret_cast<uint8_t*>(&v), 8))) +{} + +Integer64Value::Integer64Value(int64_t v) : + FieldValue(0x31, new FixedWidthValue<8>(v)) +{} + +Unsigned64Value::Unsigned64Value(uint64_t v) : + FieldValue(0x32, new FixedWidthValue<8>(v)) +{} + + +TimeValue::TimeValue(uint64_t v) : + FieldValue(0x38, new FixedWidthValue<8>(v)) +{ +} + +FieldTableValue::FieldTableValue(const FieldTable& f) : FieldValue(0xa8, new EncodedValue<FieldTable>(f)) +{ +} + +ArrayValue::ArrayValue(const Array& a) : FieldValue(0xaa, new EncodedValue<Array>(a)) +{ +} + +void FieldValue::print(std::ostream& out) const { + data->print(out); + out << TypeCode(typeOctet) << '('; + if (data->convertsToString()) out << data->getString(); + else if (data->convertsToInt()) out << data->getInt(); + else data->print(out); + out << ')'; +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/framing/FieldValue.h b/RC9/qpid/cpp/src/qpid/framing/FieldValue.h new file mode 100644 index 0000000000..29760619e5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/FieldValue.h @@ -0,0 +1,320 @@ +#ifndef _framing_FieldValue_h +#define _framing_FieldValue_h +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Exception.h" +#include "amqp_types.h" +#include "Buffer.h" +#include "FieldTable.h" + +#include "assert.h" + +#include <iostream> +#include <memory> +#include <vector> + +namespace qpid { +namespace framing { + +//class Array; +/** + * Exception that is the base exception for all field table errors. + * + * \ingroup clientapi + */ +class FieldValueException : public qpid::Exception {}; + +/** + * Exception thrown when we can't perform requested conversion + * + * \ingroup clientapi + */ +struct InvalidConversionException : public FieldValueException { + InvalidConversionException() {} +}; + +/** + * Value that can appear in an AMQP field table + * + * \ingroup clientapi + */ +class FieldValue { + public: + /* + * Abstract type for content of different types + */ + class Data { + public: + virtual ~Data() {}; + virtual uint32_t encodedSize() const = 0; + virtual void encode(Buffer& buffer) = 0; + virtual void decode(Buffer& buffer) = 0; + virtual bool operator==(const Data&) const = 0; + + virtual bool convertsToInt() const { return false; } + virtual bool convertsToString() const { return false; } + virtual int64_t getInt() const { throw InvalidConversionException();} + virtual std::string getString() const { throw InvalidConversionException(); } + + virtual void print(std::ostream& out) const = 0; + }; + + FieldValue(): data(0) {}; + // Default assignment operator is fine + void setType(uint8_t type); + uint8_t getType(); + Data& getData() { return *data; } + uint32_t encodedSize() const { return 1 + data->encodedSize(); }; + bool empty() const { return data.get() == 0; } + void encode(Buffer& buffer); + void decode(Buffer& buffer); + bool operator==(const FieldValue&) const; + bool operator!=(const FieldValue& v) const { return !(*this == v); } + + void print(std::ostream& out) const; + + template <typename T> bool convertsTo() const { return false; } + template <typename T> T get() const { throw InvalidConversionException(); } + + protected: + FieldValue(uint8_t t, Data* d): typeOctet(t), data(d) {} + + private: + uint8_t typeOctet; + std::auto_ptr<Data> data; +}; + +template <> +inline bool FieldValue::convertsTo<int>() const { return data->convertsToInt(); } + +template <> +inline bool FieldValue::convertsTo<int64_t>() const { return data->convertsToInt(); } + +template <> +inline bool FieldValue::convertsTo<std::string>() const { return data->convertsToString(); } + +template <> +inline int FieldValue::get<int>() const { return data->getInt(); } + +template <> +inline int64_t FieldValue::get<int64_t>() const { return data->getInt(); } + +template <> +inline std::string FieldValue::get<std::string>() const { return data->getString(); } + +inline std::ostream& operator<<(std::ostream& out, const FieldValue& v) { + v.print(out); + return out; +} + +template <int width> +class FixedWidthValue : public FieldValue::Data { + uint8_t octets[width]; + + public: + FixedWidthValue() {} + FixedWidthValue(const uint8_t (&data)[width]) : octets(data) {} + FixedWidthValue(const uint8_t* const data) + { + for (int i = 0; i < width; i++) octets[i] = data[i]; + } + FixedWidthValue(uint64_t v) + { + for (int i = width; i > 1; --i) { + octets[i-1] = (uint8_t) (0xFF & v); v >>= 8; + } + octets[0] = (uint8_t) (0xFF & v); + } + uint32_t encodedSize() const { return width; } + void encode(Buffer& buffer) { buffer.putRawData(octets, width); } + void decode(Buffer& buffer) { buffer.getRawData(octets, width); } + bool operator==(const Data& d) const { + const FixedWidthValue<width>* rhs = dynamic_cast< const FixedWidthValue<width>* >(&d); + if (rhs == 0) return false; + else return std::equal(&octets[0], &octets[width], &rhs->octets[0]); + } + + bool convertsToInt() const { return true; } + int64_t getInt() const + { + int64_t v = 0; + for (int i = 0; i < width-1; ++i) { + v |= octets[i]; v <<= 8; + } + v |= octets[width-1]; + return v; + } + uint8_t* rawOctets() { return octets; } + + void print(std::ostream& o) const { o << "F" << width << ":"; }; +}; + +template <> +class FixedWidthValue<0> : public FieldValue::Data { + public: + // Implicit default constructor is fine + uint32_t encodedSize() const { return 0; } + void encode(Buffer&) {}; + void decode(Buffer&) {}; + bool operator==(const Data& d) const { + const FixedWidthValue<0>* rhs = dynamic_cast< const FixedWidthValue<0>* >(&d); + return rhs != 0; + } + void print(std::ostream& o) const { o << "F0"; }; +}; + +template <int lenwidth> +class VariableWidthValue : public FieldValue::Data { + std::vector<uint8_t> octets; + + public: + VariableWidthValue() {} + VariableWidthValue(const std::vector<uint8_t>& data) : octets(data) {} + VariableWidthValue(const uint8_t* start, const uint8_t* end) : octets(start, end) {} + uint32_t encodedSize() const { return lenwidth + octets.size(); } + void encode(Buffer& buffer) { + buffer.putUInt<lenwidth>(octets.size()); + if (octets.size() > 0) + buffer.putRawData(&octets[0], octets.size()); + }; + void decode(Buffer& buffer) { + uint32_t len = buffer.getUInt<lenwidth>(); + octets.resize(len); + if (len > 0) + buffer.getRawData(&octets[0], len); + } + bool operator==(const Data& d) const { + const VariableWidthValue<lenwidth>* rhs = dynamic_cast< const VariableWidthValue<lenwidth>* >(&d); + if (rhs == 0) return false; + else return octets==rhs->octets; + } + + bool convertsToString() const { return true; } + std::string getString() const { return std::string(octets.begin(), octets.end()); } + + void print(std::ostream& o) const { o << "V" << lenwidth << ":" << octets.size() << ":"; }; +}; + +template <class T> +class EncodedValue : public FieldValue::Data { + T value; + public: + + EncodedValue() {} + EncodedValue(const T& v) : value(v) {} + + T& getValue() { return value; } + const T& getValue() const { return value; } + + uint32_t encodedSize() const { return value.encodedSize(); } + + void encode(Buffer& buffer) { + value.encode(buffer); + }; + void decode(Buffer& buffer) { + value.decode(buffer); + } + bool operator==(const Data& d) const { + const EncodedValue<T>* rhs = dynamic_cast< const EncodedValue<T>* >(&d); + if (rhs == 0) return false; + else return value==rhs->value; + } + + void print(std::ostream& o) const { o << "[" << value << "]"; }; +}; + +class Str8Value : public FieldValue { + public: + Str8Value(const std::string& v); +}; + +class Str16Value : public FieldValue { + public: + Str16Value(const std::string& v); +}; + +class Struct32Value : public FieldValue { + public: + Struct32Value(const std::string& v); +}; + +class FloatValue : public FieldValue +{ + public: + FloatValue(float f); +}; +class DoubleValue : public FieldValue +{ + public: + DoubleValue(double f); +}; + +/* + * Basic integer value encodes as signed 32 bit + */ +class IntegerValue : public FieldValue { + public: + IntegerValue(int v); +}; + +class TimeValue : public FieldValue { + public: + TimeValue(uint64_t v); +}; + +class Integer64Value : public FieldValue { + public: + Integer64Value(int64_t v); +}; + +class Unsigned64Value : public FieldValue { + public: + Unsigned64Value(uint64_t v); +}; + +class FieldTableValue : public FieldValue { + public: + FieldTableValue(const FieldTable&); +}; + +class ArrayValue : public FieldValue { + public: + ArrayValue(const Array&); +}; + +template <class T> +bool getEncodedValue(FieldTable::ValuePtr vptr, T& value) +{ + if (vptr) { + const EncodedValue<T>* ev = dynamic_cast< EncodedValue<T>* >(&(vptr->getData())); + if (ev != 0) { + value = ev->getValue(); + return true; + } + } + return false; +} + + +}} // qpid::framing + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/FrameDecoder.cpp b/RC9/qpid/cpp/src/qpid/framing/FrameDecoder.cpp new file mode 100644 index 0000000000..07e2f7513d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/FrameDecoder.cpp @@ -0,0 +1,68 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "FrameDecoder.h" +#include "Buffer.h" +#include "qpid/log/Statement.h" +#include <algorithm> + +namespace qpid { +namespace framing { + +namespace { +/** Move up to n bytes from start of buf to end of bytes. */ +void move(std::vector<char>& bytes, Buffer& buffer, size_t n) { + size_t oldSize = bytes.size(); + n = std::min(n, size_t(buffer.available())); + bytes.resize(oldSize+n); + char* p = &bytes[oldSize]; + buffer.getRawData(reinterpret_cast<uint8_t*>(p), n); +} +} + +bool FrameDecoder::decode(Buffer& buffer) { + if (buffer.available() == 0) return false; + if (fragment.empty()) { + if (frame.decode(buffer)) // Decode from buffer + return true; + else // Store fragment + move(fragment, buffer, buffer.available()); + } + else { // Already have a fragment + // Get enough data to decode the frame size. + if (fragment.size() < AMQFrame::DECODE_SIZE_MIN) { + move(fragment, buffer, AMQFrame::DECODE_SIZE_MIN - fragment.size()); + } + if (fragment.size() >= AMQFrame::DECODE_SIZE_MIN) { + uint16_t size = AMQFrame::decodeSize(&fragment[0]); + assert(size > fragment.size()); + move(fragment, buffer, size-fragment.size()); + Buffer b(&fragment[0], fragment.size()); + if (frame.decode(b)) { + assert(b.available() == 0); + fragment.clear(); + return true; + } + } + } + return false; +} + +}} // namespace qpid::framing diff --git a/RC9/qpid/cpp/src/qpid/framing/FrameDecoder.h b/RC9/qpid/cpp/src/qpid/framing/FrameDecoder.h new file mode 100644 index 0000000000..7f974dadc3 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/FrameDecoder.h @@ -0,0 +1,44 @@ +#ifndef QPID_FRAMING_FRAMEDECODER_H +#define QPID_FRAMING_FRAMEDECODER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "AMQFrame.h" + +namespace qpid { +namespace framing { + +/** + * Decode a frame from buffer. If buffer does not contain a complete + * frame, caches the fragment for the next call to decode. + */ +class FrameDecoder +{ + public: + bool decode(Buffer& buffer); + AMQFrame frame; + private: + std::vector<char> fragment; +}; +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_FRAMEDECODER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/FrameDefaultVisitor.h b/RC9/qpid/cpp/src/qpid/framing/FrameDefaultVisitor.h new file mode 100644 index 0000000000..bd676960bf --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/FrameDefaultVisitor.h @@ -0,0 +1,60 @@ +#ifndef QPID_FRAMING_FRAMEVISITOR_H +#define QPID_FRAMING_FRAMEVISITOR_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/framing/MethodBodyDefaultVisitor.h" +#include "qpid/framing/AMQBody.h" +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/framing/AMQHeaderBody.h" +#include "qpid/framing/AMQContentBody.h" +#include "qpid/framing/AMQHeartbeatBody.h" + +namespace qpid { +namespace framing { +/** + * Visitor for all concrete frame body types, which combines + * AMQBodyConstVisitor and MethodBodyDefaultVisitor. + * + * Derived classes can override visit methods to specify actions. + * Derived classes must override defaultVisit(), which is called + * for any non-overridden visit functions. + * + */ +struct FrameDefaultVisitor : public AMQBodyConstVisitor, + protected MethodBodyDefaultVisitor +{ + virtual void defaultVisit(const AMQBody&) = 0; + void defaultVisit(const AMQMethodBody& method) { defaultVisit(static_cast<const AMQBody&>(method)); } + + void visit(const AMQHeaderBody& b) { defaultVisit(b); } + void visit(const AMQContentBody& b) { defaultVisit(b); } + void visit(const AMQHeartbeatBody& b) { defaultVisit(b); } + void visit(const AMQMethodBody& b) { b.accept(static_cast<MethodBodyDefaultVisitor&>(*this)); } + + using AMQBodyConstVisitor::visit; + using MethodBodyDefaultVisitor::visit; +}; + +}} // namespace qpid::framing + + +#endif /*!QPID_FRAMING_FRAMEVISITOR_H*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/FrameHandler.h b/RC9/qpid/cpp/src/qpid/framing/FrameHandler.h new file mode 100644 index 0000000000..457968c35e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/FrameHandler.h @@ -0,0 +1,33 @@ +#ifndef QPID_FRAMING_FRAMEHANDLER_H +#define QPID_FRAMING_FRAMEHANDLER_H +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Handler.h" + +namespace qpid { +namespace framing { + +class AMQFrame; +typedef Handler<AMQFrame&> FrameHandler; + + +}} +#endif /*!QPID_FRAMING_FRAMEHANDLER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/FrameSet.cpp b/RC9/qpid/cpp/src/qpid/framing/FrameSet.cpp new file mode 100644 index 0000000000..9846f1d42b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/FrameSet.cpp @@ -0,0 +1,90 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "FrameSet.h" +#include "qpid/framing/all_method_bodies.h" +#include "qpid/framing/frame_functors.h" +#include "qpid/framing/MessageProperties.h" +#include "qpid/framing/TypeFilter.h" + +using namespace qpid::framing; +using namespace boost; + +FrameSet::FrameSet(const SequenceNumber& _id) : id(_id),contentSize(0),recalculateSize(true) { } + +void FrameSet::append(const AMQFrame& part) +{ + parts.push_back(part); + recalculateSize = true; +} + +bool FrameSet::isComplete() const +{ + return !parts.empty() && parts.back().getEof() && parts.back().getEos(); +} + +bool FrameSet::isContentBearing() const +{ + const AMQMethodBody* method = getMethod(); + return method && method->isContentBearing(); +} + +const AMQMethodBody* FrameSet::getMethod() const +{ + return parts.empty() ? 0 : parts[0].getMethod(); +} + +const AMQHeaderBody* FrameSet::getHeaders() const +{ + return parts.size() < 2 ? 0 : parts[1].castBody<AMQHeaderBody>(); +} + +AMQHeaderBody* FrameSet::getHeaders() +{ + return parts.size() < 2 ? 0 : parts[1].castBody<AMQHeaderBody>(); +} + +uint64_t FrameSet::getContentSize() const +{ + if (recalculateSize) + { + SumBodySize sum; + map_if(sum, TypeFilter<CONTENT_BODY>()); + contentSize = sum.getSize(); + recalculateSize = false; + } + return contentSize; +} + +void FrameSet::getContent(std::string& out) const { + out.clear(); + out.reserve(getContentSize()); + for(Frames::const_iterator i = parts.begin(); i != parts.end(); i++) { + if (i->getBody()->type() == CONTENT_BODY) + out += i->castBody<AMQContentBody>()->getData(); + } +} + +std::string FrameSet::getContent() const { + std::string out; + getContent(out); + return out; +} diff --git a/RC9/qpid/cpp/src/qpid/framing/FrameSet.h b/RC9/qpid/cpp/src/qpid/framing/FrameSet.h new file mode 100644 index 0000000000..82987910a7 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/FrameSet.h @@ -0,0 +1,107 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <string> +#include "qpid/InlineVector.h" +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/SequenceNumber.h" + +#ifndef _FrameSet_ +#define _FrameSet_ + +namespace qpid { +namespace framing { + +/** + * Collects the frames representing a message. + */ +class FrameSet +{ + typedef InlineVector<AMQFrame, 4> Frames; + const SequenceNumber id; + Frames parts; + mutable uint64_t contentSize; + mutable bool recalculateSize; + +public: + typedef boost::shared_ptr<FrameSet> shared_ptr; + + FrameSet(const SequenceNumber& id); + void append(const AMQFrame& part); + bool isComplete() const; + + uint64_t getContentSize() const; + void getContent(std::string&) const; + std::string getContent() const; + + bool isContentBearing() const; + + const AMQMethodBody* getMethod() const; + const AMQHeaderBody* getHeaders() const; + AMQHeaderBody* getHeaders(); + + template <class T> bool isA() const { + const AMQMethodBody* method = getMethod(); + return method && method->isA<T>(); + } + + template <class T> const T* as() const { + const AMQMethodBody* method = getMethod(); + return (method && method->isA<T>()) ? dynamic_cast<const T*>(method) : 0; + } + + template <class T> const T* getHeaderProperties() const { + const AMQHeaderBody* header = getHeaders(); + return header ? header->get<T>() : 0; + } + + const SequenceNumber& getId() const { return id; } + + template <class P> void remove(P predicate) { + parts.erase(std::remove_if(parts.begin(), parts.end(), predicate), parts.end()); + } + + template <class F> void map(F& functor) { + std::for_each(parts.begin(), parts.end(), functor); + } + + template <class F> void map(F& functor) const { + std::for_each(parts.begin(), parts.end(), functor); + } + + template <class F, class P> void map_if(F& functor, P predicate) { + for(Frames::iterator i = parts.begin(); i != parts.end(); i++) { + if (predicate(*i)) functor(*i); + } + } + + template <class F, class P> void map_if(F& functor, P predicate) const { + for(Frames::const_iterator i = parts.begin(); i != parts.end(); i++) { + if (predicate(*i)) functor(*i); + } + } +}; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/Handler.h b/RC9/qpid/cpp/src/qpid/framing/Handler.h new file mode 100644 index 0000000000..e07a803e17 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/Handler.h @@ -0,0 +1,101 @@ +#ifndef QPID_FRAMING_HANDLER_H +#define QPID_FRAMING_HANDLER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/shared_ptr.h" +#include <boost/type_traits/remove_reference.hpp> +#include <assert.h> + +namespace qpid { +namespace framing { + +template <class T> +struct Handler { + typedef T HandledType; + typedef void handleFptr(T); + typedef void result_type; // Compatible with std/boost functors. + + Handler(Handler<T>* next_=0) : next(next_) {} + virtual ~Handler() {} + virtual void handle(T) = 0; + + /** Allow functor syntax for calling handle */ + void operator()(T t) { handle(t); } + + + /** Pointer to next handler in a linked list. */ + Handler<T>* next; + + /** Adapt any void(T) functor as a Handler. + * Functor<F>(f) will copy f. + * Functor<F&>(f) will only take a reference to x. + */ + template <class F> class Functor : public Handler<T> { + public: + Functor(F f, Handler<T>* next=0) : Handler<T>(next), functor(f) {} + void handle(T t) { functor(t); } + private: + F functor; + }; + + /** Adapt a member function of X as a Handler. + * Only holds a reference to its target, not a copy. + */ + template <class X, void (X::*F)(T)> + class MemFunRef : public Handler<T> { + public: + MemFunRef(X& x, Handler<T>* next=0) : Handler(next), target(&x) {} + void handle(T t) { (target->*F)(t); } + + /** Allow calling with -> syntax */ + MemFunRef* operator->() { return this; } + + private: + X* target; + }; + + /** Interface for a handler that implements a + * pair of in/out handle operations. + * @see InOutHandler + */ + class InOutHandlerInterface { + public: + virtual ~InOutHandlerInterface() {} + virtual void handleIn(T) = 0; + virtual void handleOut(T) = 0; + }; + + /** Support for implementing an in-out handler pair as a single class. + * Overrides handleIn, handleOut functions in a single class. + */ + struct InOutHandler : protected InOutHandlerInterface { + InOutHandler(Handler<T>* nextIn=0, Handler<T>* nextOut=0) : in(*this, nextIn), out(*this, nextOut) {} + MemFunRef<InOutHandlerInterface, &InOutHandlerInterface::handleIn> in; + MemFunRef<InOutHandlerInterface, &InOutHandlerInterface::handleOut> out; + }; +}; + + + +}} +#endif /*!QPID_FRAMING_HANDLER_H*/ +// diff --git a/RC9/qpid/cpp/src/qpid/framing/HeaderProperties.h b/RC9/qpid/cpp/src/qpid/framing/HeaderProperties.h new file mode 100644 index 0000000000..39c448fc3c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/HeaderProperties.h @@ -0,0 +1,44 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "amqp_types.h" +#include "Buffer.h" + +#ifndef _HeaderProperties_ +#define _HeaderProperties_ + +namespace qpid { +namespace framing { + + class HeaderProperties + { + + public: + inline virtual ~HeaderProperties(){} + virtual uint8_t classId() const = 0; + virtual uint32_t encodedSize() const = 0; + virtual void encode(Buffer& buffer) const = 0; + virtual void decode(Buffer& buffer, uint32_t size) = 0; + }; +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/InitiationHandler.cpp b/RC9/qpid/cpp/src/qpid/framing/InitiationHandler.cpp new file mode 100644 index 0000000000..eceeaf4abc --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/InitiationHandler.cpp @@ -0,0 +1,24 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "InitiationHandler.h" + +qpid::framing::InitiationHandler::~InitiationHandler() {} diff --git a/RC9/qpid/cpp/src/qpid/framing/InitiationHandler.h b/RC9/qpid/cpp/src/qpid/framing/InitiationHandler.h new file mode 100644 index 0000000000..16a6b502e8 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/InitiationHandler.h @@ -0,0 +1,41 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <string> + +#ifndef _InitiationHandler_ +#define _InitiationHandler_ + +#include "ProtocolInitiation.h" + +namespace qpid { +namespace framing { + + class InitiationHandler{ + public: + virtual ~InitiationHandler(); + virtual void initiated(const ProtocolInitiation&) = 0; + }; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/InputHandler.h b/RC9/qpid/cpp/src/qpid/framing/InputHandler.h new file mode 100644 index 0000000000..3a6d786a24 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/InputHandler.h @@ -0,0 +1,41 @@ +#ifndef _InputHandler_ +#define _InputHandler_ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "FrameHandler.h" +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace framing { + +// TODO aconway 2007-08-29: Eliminate, replace with FrameHandler. +class InputHandler : public FrameHandler { + public: + virtual ~InputHandler() {} + virtual void received(AMQFrame&) = 0; + void handle(AMQFrame& f) { received(f); } +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/Invoker.h b/RC9/qpid/cpp/src/qpid/framing/Invoker.h new file mode 100644 index 0000000000..4f1cf7c331 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/Invoker.h @@ -0,0 +1,86 @@ +#ifndef QPID_FRAMING_INVOKER_H +#define QPID_FRAMING_INVOKER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/framing/MethodBodyDefaultVisitor.h" +#include "qpid/framing/StructHelper.h" + +#include <boost/optional.hpp> + +namespace qpid { +namespace framing { + +class AMQMethodBody; + +/** + * Base class for invoker visitors. + */ +class Invoker: public MethodBodyDefaultVisitor, protected StructHelper +{ + public: + struct Result { + public: + Result() : handled(false) {} + const std::string& getResult() const { return result; } + bool hasResult() const { return !result.empty(); } + bool wasHandled() const { return handled; } + operator bool() const { return handled; } + + std::string result; + bool handled; + }; + + void defaultVisit(const AMQMethodBody&) {} + Result getResult() const { return result; } + + protected: + Result result; +}; + +/** + * Invoke an invocable object. + * Invocable classes must provide a nested type Invoker. + */ +template <class Invocable> +Invoker::Result invoke(Invocable& target, const AMQMethodBody& body) { + typename Invocable::Invoker invoker(target); + body.accept(invoker); + return invoker.getResult(); +} + +/** + * Invoke an invocable object. + * Invocable classes must provide a nested type Invoker. + */ +template <class Invocable> +Invoker::Result invoke(Invocable& target, const AMQBody& body) { + typename Invocable::Invoker invoker(target); + const AMQMethodBody* method = body.getMethod(); + if (method) + method->accept(invoker); + return invoker.getResult(); +} + +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_INVOKER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/MethodContent.h b/RC9/qpid/cpp/src/qpid/framing/MethodContent.h new file mode 100644 index 0000000000..737c0d6b7b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/MethodContent.h @@ -0,0 +1,40 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _MethodContent_ +#define _MethodContent_ + +#include <string> +#include "AMQHeaderBody.h" + +namespace qpid { +namespace framing { + +class MethodContent +{ +public: + virtual ~MethodContent() {} + //TODO: rethink this interface + virtual AMQHeaderBody getHeader() const = 0; + virtual const std::string& getData() const = 0; +}; + +}} +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/ModelMethod.h b/RC9/qpid/cpp/src/qpid/framing/ModelMethod.h new file mode 100644 index 0000000000..afbdf7b6e2 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/ModelMethod.h @@ -0,0 +1,49 @@ +#ifndef _ModelMethod_ +#define _ModelMethod_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "AMQMethodBody.h" +#include "qpid/framing/Header.h" + +namespace qpid { +namespace framing { + + +class ModelMethod : public AMQMethodBody +{ + mutable Header header; +public: + virtual ~ModelMethod() {} + virtual void encodeHeader(Buffer& buffer) const { header.encode(buffer); } + virtual void decodeHeader(Buffer& buffer, uint32_t size=0) { header.decode(buffer, size); } + virtual uint32_t headerSize() const { return header.encodedSize(); } + virtual bool isSync() const { return header.getSync(); } + virtual void setSync(bool on) const { header.setSync(on); } + Header& getHeader() { return header; } + const Header& getHeader() const { return header; } +}; + + +}} // namespace qpid::framing + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/OutputHandler.h b/RC9/qpid/cpp/src/qpid/framing/OutputHandler.h new file mode 100644 index 0000000000..6f4b27fb72 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/OutputHandler.h @@ -0,0 +1,42 @@ +#ifndef _OutputHandler_ +#define _OutputHandler_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <boost/noncopyable.hpp> +#include "FrameHandler.h" + +namespace qpid { +namespace framing { + +// TODO aconway 2007-08-29: Replace with FrameHandler. +class OutputHandler : public FrameHandler { + public: + virtual ~OutputHandler() {} + virtual void send(AMQFrame&) = 0; + void handle(AMQFrame& f) { send(f); } +}; + + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp b/RC9/qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp new file mode 100644 index 0000000000..50617de017 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp @@ -0,0 +1,66 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "ProtocolInitiation.h" + +namespace qpid { +namespace framing { + +ProtocolInitiation::ProtocolInitiation(){} + +ProtocolInitiation::ProtocolInitiation(uint8_t _major, uint8_t _minor) : version(_major, _minor) {} + +ProtocolInitiation::ProtocolInitiation(ProtocolVersion p) : version(p) {} + +ProtocolInitiation::~ProtocolInitiation(){} + +void ProtocolInitiation::encode(Buffer& buffer) const { + buffer.putOctet('A'); + buffer.putOctet('M'); + buffer.putOctet('Q'); + buffer.putOctet('P'); + buffer.putOctet(1);//class + buffer.putOctet(1);//instance + buffer.putOctet(version.getMajor()); + buffer.putOctet(version.getMinor()); +} + +bool ProtocolInitiation::decode(Buffer& buffer){ + if(buffer.available() >= 8){ + buffer.getOctet();//A + buffer.getOctet();//M + buffer.getOctet();//Q + buffer.getOctet();//P + buffer.getOctet();//class + buffer.getOctet();//instance + version.setMajor(buffer.getOctet()); + version.setMinor(buffer.getOctet()); + return true; + }else{ + return false; + } +} + + +std::ostream& operator<<(std::ostream& o, const framing::ProtocolInitiation& pi) { + return o << int(pi.getMajor()) << "-" << int(pi.getMinor()); +} + +}} // namespace qpid::framing diff --git a/RC9/qpid/cpp/src/qpid/framing/ProtocolInitiation.h b/RC9/qpid/cpp/src/qpid/framing/ProtocolInitiation.h new file mode 100644 index 0000000000..6584fee55c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/ProtocolInitiation.h @@ -0,0 +1,58 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "amqp_types.h" +#include "Buffer.h" +#include "AMQDataBlock.h" +#include "ProtocolVersion.h" + +#ifndef _ProtocolInitiation_ +#define _ProtocolInitiation_ + +namespace qpid { +namespace framing { + +class ProtocolInitiation : public AMQDataBlock +{ +private: + ProtocolVersion version; + +public: + ProtocolInitiation(); + ProtocolInitiation(uint8_t major, uint8_t minor); + ProtocolInitiation(ProtocolVersion p); + virtual ~ProtocolInitiation(); + virtual void encode(Buffer& buffer) const; + virtual bool decode(Buffer& buffer); + inline virtual uint32_t encodedSize() const { return 8; } + inline uint8_t getMajor() const { return version.getMajor(); } + inline uint8_t getMinor() const { return version.getMinor(); } + inline ProtocolVersion getVersion() const { return version; } + bool operator==(ProtocolVersion v) const { return v == getVersion(); } +}; + +std::ostream& operator<<(std::ostream& o, const framing::ProtocolInitiation& pi); + + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/ProtocolVersion.cpp b/RC9/qpid/cpp/src/qpid/framing/ProtocolVersion.cpp new file mode 100644 index 0000000000..7a96bfa925 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/ProtocolVersion.cpp @@ -0,0 +1,44 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "ProtocolVersion.h" +#include <sstream> + +using namespace qpid::framing; + +const std::string ProtocolVersion::toString() const +{ + std::stringstream ss; + ss << major_ << "-" << minor_; + return ss.str(); +} + +ProtocolVersion& ProtocolVersion::operator=(ProtocolVersion p) +{ + major_ = p.major_; + minor_ = p.minor_; + return *this; +} + +bool ProtocolVersion::operator==(ProtocolVersion p) const +{ + return major_ == p.major_ && minor_ == p.minor_; +} + diff --git a/RC9/qpid/cpp/src/qpid/framing/ProtocolVersion.h b/RC9/qpid/cpp/src/qpid/framing/ProtocolVersion.h new file mode 100644 index 0000000000..9a7ebec491 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/ProtocolVersion.h @@ -0,0 +1,57 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ProtocolVersion_ +#define _ProtocolVersion_ + +#include "amqp_types.h" + +namespace qpid +{ +namespace framing +{ + +class ProtocolVersion +{ +private: + uint8_t major_; + uint8_t minor_; + +public: + explicit ProtocolVersion(uint8_t _major=0, uint8_t _minor=0) + : major_(_major), minor_(_minor) {} + + uint8_t getMajor() const { return major_; } + void setMajor(uint8_t major) { major_ = major; } + uint8_t getMinor() const { return minor_; } + void setMinor(uint8_t minor) { minor_ = minor; } + const std::string toString() const; + + ProtocolVersion& operator=(ProtocolVersion p); + + bool operator==(ProtocolVersion p) const; + bool operator!=(ProtocolVersion p) const { return ! (*this == p); } +}; + +} // namespace framing +} // namespace qpid + + +#endif // ifndef _ProtocolVersion_ diff --git a/RC9/qpid/cpp/src/qpid/framing/Proxy.cpp b/RC9/qpid/cpp/src/qpid/framing/Proxy.cpp new file mode 100644 index 0000000000..6b37fb368d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/Proxy.cpp @@ -0,0 +1,42 @@ +/* + * + * 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 "Proxy.h" +#include "AMQFrame.h" + +namespace qpid { +namespace framing { + +Proxy::Proxy(FrameHandler& h) : out(&h) {} + +Proxy::~Proxy() {} + +void Proxy::send(const AMQBody& b) { + AMQFrame f(b); + out->handle(f); +} + +ProtocolVersion Proxy::getVersion() const { + return ProtocolVersion(); +} + +FrameHandler& Proxy::getHandler() { return *out; } + +void Proxy::setHandler(FrameHandler& f) { out=&f; } + +}} // namespace qpid::framing diff --git a/RC9/qpid/cpp/src/qpid/framing/Proxy.h b/RC9/qpid/cpp/src/qpid/framing/Proxy.h new file mode 100644 index 0000000000..3dc082097a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/Proxy.h @@ -0,0 +1,54 @@ +#ifndef _framing_Proxy_h +#define _framing_Proxy_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "FrameHandler.h" +#include "ProtocolVersion.h" + +namespace qpid { +namespace framing { + +class AMQBody; + +/** + * Base class for proxies. + */ +class Proxy +{ + public: + Proxy(FrameHandler& h); + virtual ~Proxy(); + + void send(const AMQBody&); + + ProtocolVersion getVersion() const; + + FrameHandler& getHandler(); + void setHandler(FrameHandler&); + + private: + FrameHandler* out; +}; + +}} // namespace qpid::framing + + + +#endif /*!_framing_Proxy_h*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/SendContent.cpp b/RC9/qpid/cpp/src/qpid/framing/SendContent.cpp new file mode 100644 index 0000000000..f390106dee --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/SendContent.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 "SendContent.h" + +qpid::framing::SendContent::SendContent(FrameHandler& h, uint16_t mfs, uint efc) : handler(h), + maxFrameSize(mfs), + expectedFrameCount(efc), frameCount(0) {} + +void qpid::framing::SendContent::operator()(const AMQFrame& f) +{ + bool first = frameCount == 0; + bool last = ++frameCount == expectedFrameCount; + + /*end of frame marker is included in frameOverhead() but not in + real frame size, hence substract -1 from frameOverhead()*/ + uint16_t maxContentSize = maxFrameSize - (AMQFrame::frameOverhead() - 1); + const AMQContentBody* body(f.castBody<AMQContentBody>()); + if (body->encodedSize() > maxContentSize) { + uint32_t offset = 0; + for (int chunk = body->encodedSize() / maxContentSize; chunk > 0; chunk--) { + sendFragment(*body, offset, maxContentSize, first && offset == 0, last && offset + maxContentSize == body->encodedSize()); + offset += maxContentSize; + } + uint32_t remainder = body->encodedSize() % maxContentSize; + if (remainder) { + sendFragment(*body, offset, remainder, first && offset == 0, last); + } + } else { + AMQFrame copy(f); + setFlags(copy, first, last); + handler.handle(copy); + } +} + +void qpid::framing::SendContent::sendFragment(const AMQContentBody& body, uint32_t offset, uint16_t size, bool first, bool last) const +{ + AMQFrame fragment(in_place<AMQContentBody>( + body.getData().substr(offset, size))); + setFlags(fragment, first, last); + handler.handle(fragment); +} + +void qpid::framing::SendContent::setFlags(AMQFrame& f, bool first, bool last) const +{ + f.setBof(false); + f.setBos(first); + f.setEof(true);//content is always the last segment + f.setEos(last); +} + diff --git a/RC9/qpid/cpp/src/qpid/framing/SendContent.h b/RC9/qpid/cpp/src/qpid/framing/SendContent.h new file mode 100644 index 0000000000..dcd5202b3e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/SendContent.h @@ -0,0 +1,55 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <string> +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/FrameHandler.h" + +#ifndef _SendContent_ +#define _SendContent_ + +namespace qpid { +namespace framing { + +/** + * Functor that sends frame to handler, refragmenting if + * necessary. Currently only works on content frames but this could be + * changed once we support multi-frame segments in general. + */ +class SendContent +{ + mutable FrameHandler& handler; + const uint16_t maxFrameSize; + uint expectedFrameCount; + uint frameCount; + + void sendFragment(const AMQContentBody& body, uint32_t offset, uint16_t size, bool first, bool last) const; + void setFlags(AMQFrame& f, bool first, bool last) const; +public: + SendContent(FrameHandler& _handler, uint16_t _maxFrameSize, uint frameCount); + void operator()(const AMQFrame& f); +}; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/SequenceNumber.cpp b/RC9/qpid/cpp/src/qpid/framing/SequenceNumber.cpp new file mode 100644 index 0000000000..cac4e6681e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/SequenceNumber.cpp @@ -0,0 +1,110 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SequenceNumber.h" +#include "Buffer.h" +#include <ostream> + +using qpid::framing::SequenceNumber; +using qpid::framing::Buffer; + +SequenceNumber::SequenceNumber() : value(0) {} + +SequenceNumber::SequenceNumber(uint32_t v) : value((int32_t) v) {} + +bool SequenceNumber::operator==(const SequenceNumber& other) const +{ + return value == other.value; +} + +bool SequenceNumber::operator!=(const SequenceNumber& other) const +{ + return !(value == other.value); +} + + +SequenceNumber& SequenceNumber::operator++() +{ + value = value + 1; + return *this; +} + +const SequenceNumber SequenceNumber::operator++(int) +{ + SequenceNumber old(value); + value = value + 1; + return old; +} + +SequenceNumber& SequenceNumber::operator--() +{ + value = value - 1; + return *this; +} + +bool SequenceNumber::operator<(const SequenceNumber& other) const +{ + return (value - other.value) < 0; +} + +bool SequenceNumber::operator>(const SequenceNumber& other) const +{ + return other < *this; +} + +bool SequenceNumber::operator<=(const SequenceNumber& other) const +{ + return *this == other || *this < other; +} + +bool SequenceNumber::operator>=(const SequenceNumber& other) const +{ + return *this == other || *this > other; +} + +void SequenceNumber::encode(Buffer& buffer) const +{ + buffer.putLong(value); +} + +void SequenceNumber::decode(Buffer& buffer) +{ + value = buffer.getLong(); +} + +uint32_t SequenceNumber::encodedSize() const { + return 4; +} + +namespace qpid { +namespace framing { + +int32_t operator-(const SequenceNumber& a, const SequenceNumber& b) +{ + int32_t result = a.value - b.value; + return result; +} + +std::ostream& operator<<(std::ostream& o, const SequenceNumber& n) { + return o << n.getValue(); +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/framing/SequenceNumber.h b/RC9/qpid/cpp/src/qpid/framing/SequenceNumber.h new file mode 100644 index 0000000000..930e146863 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/SequenceNumber.h @@ -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. + * + */ +#ifndef _framing_SequenceNumber_h +#define _framing_SequenceNumber_h + +#include "amqp_types.h" +#include <iosfwd> + +namespace qpid { +namespace framing { + +class Buffer; + +/** + * 4-byte sequence number that 'wraps around'. + */ +class SequenceNumber +{ + int32_t value; + + public: + SequenceNumber(); + SequenceNumber(uint32_t v); + + SequenceNumber& operator++();//prefix ++ + const SequenceNumber operator++(int);//postfix ++ + SequenceNumber& operator--();//prefix ++ + bool operator==(const SequenceNumber& other) const; + bool operator!=(const SequenceNumber& other) const; + bool operator<(const SequenceNumber& other) const; + bool operator>(const SequenceNumber& other) const; + bool operator<=(const SequenceNumber& other) const; + bool operator>=(const SequenceNumber& other) const; + uint32_t getValue() const { return (uint32_t) value; } + operator uint32_t() const { return (uint32_t) value; } + + friend int32_t operator-(const SequenceNumber& a, const SequenceNumber& b); + + void encode(Buffer& buffer) const; + void decode(Buffer& buffer); + uint32_t encodedSize() const; + + template <class S> void serialize(S& s) { s(value); } +}; + +struct Window +{ + SequenceNumber hwm; + SequenceNumber lwm; +}; + +std::ostream& operator<<(std::ostream& o, const SequenceNumber& n); + +}} // namespace qpid::framing + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/SequenceNumberSet.cpp b/RC9/qpid/cpp/src/qpid/framing/SequenceNumberSet.cpp new file mode 100644 index 0000000000..afab9033e5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/SequenceNumberSet.cpp @@ -0,0 +1,89 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SequenceNumberSet.h" + +using namespace qpid::framing; + +void SequenceNumberSet::encode(Buffer& buffer) const +{ + buffer.putShort(size() * 4); + for (const_iterator i = begin(); i != end(); i++) { + buffer.putLong(i->getValue()); + } +} + +void SequenceNumberSet::decode(Buffer& buffer) +{ + uint16_t count = (buffer.getShort() / 4); + for (uint16_t i = 0; i < count; i++) { + push_back(SequenceNumber(buffer.getLong())); + } +} + +uint32_t SequenceNumberSet::encodedSize() const +{ + return 2 /*count*/ + (size() * 4); +} + +SequenceNumberSet SequenceNumberSet::condense() const +{ + SequenceNumberSet result; + const_iterator last = end(); + const_iterator start = end(); + for (const_iterator i = begin(); i != end(); i++) { + if (start == end()) { + start = i; + } else if (*i - *last > 1) { + result.push_back(*start); + result.push_back(*last); + start = i; + } + last = i; + } + if (start != end()) { + result.push_back(*start); + result.push_back(*last); + } + return result; +} + +void SequenceNumberSet::addRange(const SequenceNumber& start, const SequenceNumber& end) +{ + push_back(start); + push_back(end); +} + +namespace qpid{ +namespace framing{ + +std::ostream& operator<<(std::ostream& out, const SequenceNumberSet& set) { + out << "{"; + for (SequenceNumberSet::const_iterator i = set.begin(); i != set.end(); i++) { + if (i != set.begin()) out << ", "; + out << (i->getValue()); + } + out << "}"; + return out; +} + +} +} diff --git a/RC9/qpid/cpp/src/qpid/framing/SequenceNumberSet.h b/RC9/qpid/cpp/src/qpid/framing/SequenceNumberSet.h new file mode 100644 index 0000000000..666307f9d9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/SequenceNumberSet.h @@ -0,0 +1,68 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _framing_SequenceNumberSet_h +#define _framing_SequenceNumberSet_h + +#include <ostream> +#include "amqp_types.h" +#include "Buffer.h" +#include "SequenceNumber.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/InlineVector.h" + +namespace qpid { +namespace framing { + +class SequenceNumberSet : public InlineVector<SequenceNumber, 2> +{ + typedef InlineVector<SequenceNumber, 2> Base; +public: + typedef Base::const_iterator const_iterator; + typedef Base::iterator iterator; + + void encode(Buffer& buffer) const; + void decode(Buffer& buffer); + uint32_t encodedSize() const; + SequenceNumberSet condense() const; + void addRange(const SequenceNumber& start, const SequenceNumber& end); + + template <class T> + void processRanges(T& t) const + { + if (size() % 2) { //must be even number + throw InvalidArgumentException("SequenceNumberSet contains odd number of elements"); + } + + for (SequenceNumberSet::const_iterator i = begin(); i != end(); i++) { + SequenceNumber first = *(i); + SequenceNumber last = *(++i); + t(first, last); + } + } + + friend std::ostream& operator<<(std::ostream&, const SequenceNumberSet&); +}; + + +}} // namespace qpid::framing + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/SequenceSet.cpp b/RC9/qpid/cpp/src/qpid/framing/SequenceSet.cpp new file mode 100644 index 0000000000..2046fac3e1 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/SequenceSet.cpp @@ -0,0 +1,100 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SequenceSet.h" +#include "Buffer.h" +#include "qpid/framing/reply_exceptions.h" + +using namespace qpid::framing; +using std::max; +using std::min; + +namespace qpid { +namespace framing { + +namespace { +//each range contains 2 numbers, 4 bytes each +uint16_t RANGE_SIZE = 2 * 4; +} + +void SequenceSet::encode(Buffer& buffer) const +{ + buffer.putShort(rangesSize() * RANGE_SIZE); + for (RangeIterator i = rangesBegin(); i != rangesEnd(); i++) { + buffer.putLong(i->first().getValue()); + buffer.putLong(i->last().getValue()); + } +} + +void SequenceSet::decode(Buffer& buffer) +{ + uint16_t size = buffer.getShort(); + uint16_t count = size / RANGE_SIZE;//number of ranges + if (size % RANGE_SIZE) + throw IllegalArgumentException(QPID_MSG("Invalid size for sequence set: " << size)); + + for (uint16_t i = 0; i < count; i++) { + add(SequenceNumber(buffer.getLong()), SequenceNumber(buffer.getLong())); + } +} + +uint32_t SequenceSet::encodedSize() const { + return 2 /*size field*/ + (rangesSize() * RANGE_SIZE); +} + +bool SequenceSet::contains(const SequenceNumber& s) const { + return RangeSet<SequenceNumber>::contains(s); +} + +void SequenceSet::add(const SequenceNumber& s) { *this += s; } + +void SequenceSet::add(const SequenceNumber& start, const SequenceNumber& finish) { + *this += Range<SequenceNumber>::makeClosed(std::min(start,finish), std::max(start, finish)); +} + +void SequenceSet::add(const SequenceSet& set) { *this += set; } + +void SequenceSet::remove(const SequenceSet& set) { *this -= set; } + +void SequenceSet::remove(const SequenceNumber& start, const SequenceNumber& finish) { + *this -= Range<SequenceNumber>::makeClosed(std::min(start,finish), std::max(start, finish)); +} + +void SequenceSet::remove(const SequenceNumber& s) { *this -= s; } + + +struct RangePrinter { + std::ostream& out; + RangePrinter(std::ostream& o) : out(o) {} + void operator()(SequenceNumber i, SequenceNumber j) const { + out << "[" << i.getValue() << "," << j.getValue() << "] "; + } +}; + +std::ostream& operator<<(std::ostream& o, const SequenceSet& s) { + RangePrinter print(o); + o << "{ "; + s.for_each(print); + return o << "}"; +} + +}} // namespace qpid::framing + diff --git a/RC9/qpid/cpp/src/qpid/framing/SequenceSet.h b/RC9/qpid/cpp/src/qpid/framing/SequenceSet.h new file mode 100644 index 0000000000..57b9c2c8e1 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/SequenceSet.h @@ -0,0 +1,68 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _framing_SequenceSet_h +#define _framing_SequenceSet_h + +#include "SequenceNumber.h" +#include "qpid/RangeSet.h" + +namespace qpid { +namespace framing { +class Buffer; + +class SequenceSet : public RangeSet<SequenceNumber> { + public: + SequenceSet() {} + SequenceSet(const RangeSet<SequenceNumber>& r) + : RangeSet<SequenceNumber>(r) {} + SequenceSet(const SequenceNumber& s) { add(s); } + SequenceSet(const SequenceNumber& start, const SequenceNumber finish) { add(start,finish); } + + + void encode(Buffer& buffer) const; + void decode(Buffer& buffer); + uint32_t encodedSize() const; + + bool contains(const SequenceNumber& s) const; + void add(const SequenceNumber& s); + void add(const SequenceNumber& start, const SequenceNumber& finish); // Closed range + void add(const SequenceSet& set); + void remove(const SequenceNumber& s); + void remove(const SequenceNumber& start, const SequenceNumber& finish); // Closed range + void remove(const SequenceSet& set); + + template <class T> void for_each(T& t) const { + for (RangeIterator i = rangesBegin(); i != rangesEnd(); i++) + t(i->first(), i->last()); + } + + template <class T> void for_each(const T& t) const { + for (RangeIterator i = rangesBegin(); i != rangesEnd(); i++) + t(i->first(), i->last()); + } + + friend std::ostream& operator<<(std::ostream&, const SequenceSet&); +}; + +}} // namespace qpid::framing + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/StructHelper.h b/RC9/qpid/cpp/src/qpid/framing/StructHelper.h new file mode 100644 index 0000000000..e3dce4f5ec --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/StructHelper.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 _StructHelper_ +#define _StructHelper_ + +#include "qpid/Exception.h" +#include "Buffer.h" + +#include <stdlib.h> // For alloca + +namespace qpid { +namespace framing { + +class StructHelper +{ +public: + + template <class T> void encode(const T t, std::string& data) { + uint32_t size = t.bodySize() + 2/*type*/; + data.resize(size); + Buffer wbuffer(const_cast<char*>(data.data()), size); + wbuffer.putShort(T::TYPE); + t.encodeStructBody(wbuffer); + } + + template <class T> void decode(T& t, const std::string& data) { + Buffer rbuffer(const_cast<char*>(data.data()), data.length()); + uint16_t type = rbuffer.getShort(); + if (type == T::TYPE) { + t.decodeStructBody(rbuffer); + } else { + throw Exception("Type code does not match"); + } + } +}; + +}} +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/TemplateVisitor.h b/RC9/qpid/cpp/src/qpid/framing/TemplateVisitor.h new file mode 100644 index 0000000000..d6d59603f7 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/TemplateVisitor.h @@ -0,0 +1,89 @@ +#ifndef QPID_FRAMING_TEMPLATEVISITOR_H +#define QPID_FRAMING_TEMPLATEVISITOR_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <boost/mpl/fold.hpp> +#include <boost/utility/value_init.hpp> + +namespace qpid { +namespace framing { + +/** + * Metafunction to generate a visitor class derived from Base, with a + * visit for each type in TypeList calling functor F. TypeList may be + * any boost::mpl type collection e.g. mpl::list. + * + * Generated class is: TemplateVisitor<Base, F, TypeList>::type + * + * @see make_visitor + */ +template <class VisitTemplate, class TypeList, class F> +class TemplateVisitor +{ + struct Base : public VisitorBase { + F action; + Base(F f) : action(f) {} + using VisitorBase::visit; + }; + + template <class B, class T> struct Visit : public B { + Visit(F action) : B(action) {} + using B::visit; + void visit(const T& body) { action(body); } + }; + + typedef typename boost::mpl::fold< + TypeList, Base, Visit<boost::mpl::placeholders::_1, + boost::mpl::placeholders::_2> + >::type type; +}; + +/** + * Construct a TemplateVisitor to perform the given action, + * for example: + * @code + */ +template <class VisitorBase, class TypeList, class F> +TemplateVisitor<VisitorBase,TypeList,F>::type make_visitor(F action) { + return TemplateVisitor<VisitorBase,TypeList,F>::type(action); +}; + +/** + * For method body classes in TypeList, invoke the corresponding function + * on Target and return true. For other body types return false. + */ +template <class TypeList, class Target> +bool invoke(const AMQBody& body, Target& target) { + typename InvokeVisitor<TypeList, Target>::type v(target); + body.accept(v); + return v.target; +} + +}} // namespace qpid::framing + + +#endif /*!QPID_FRAMING_INVOKEVISITOR_H*/ + +}} // namespace qpid::framing + + + +#endif /*!QPID_FRAMING_TEMPLATEVISITOR_H*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/TransferContent.cpp b/RC9/qpid/cpp/src/qpid/framing/TransferContent.cpp new file mode 100644 index 0000000000..3fc54296fa --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/TransferContent.cpp @@ -0,0 +1,102 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "TransferContent.h" + +namespace qpid { +namespace framing { + +TransferContent::TransferContent(const std::string& data, const std::string& key) { + setData(data); + if (!key.empty()) getDeliveryProperties().setRoutingKey(key); +} + + +AMQHeaderBody TransferContent::getHeader() const +{ + return header; +} + +const std::string& TransferContent::getData() const { + return data; +} + +std::string& TransferContent::getData() { + return data; +} + +void TransferContent::setData(const std::string& _data) +{ + data = _data; + header.get<MessageProperties>(true)->setContentLength(data.size()); +} + +void TransferContent::appendData(const std::string& _data) +{ + data += _data; + header.get<MessageProperties>(true)->setContentLength(data.size()); +} + +MessageProperties& TransferContent::getMessageProperties() +{ + return *header.get<MessageProperties>(true); +} + +DeliveryProperties& TransferContent::getDeliveryProperties() +{ + return *header.get<DeliveryProperties>(true); +} + +void TransferContent::populate(const FrameSet& frameset) +{ + const AMQHeaderBody* h = frameset.getHeaders(); + if (h) { + header = *h; + } + frameset.getContent(data); +} + +const MessageProperties& TransferContent::getMessageProperties() const +{ + const MessageProperties* props = header.get<MessageProperties>(); + if (!props) throw Exception("No message properties."); + return *props; +} + +const DeliveryProperties& TransferContent::getDeliveryProperties() const +{ + const DeliveryProperties* props = header.get<DeliveryProperties>(); + if (!props) throw Exception("No message properties."); + return *props; +} + +bool TransferContent::hasMessageProperties() const +{ + return header.get<MessageProperties>(); +} + +bool TransferContent::hasDeliveryProperties() const +{ + return header.get<DeliveryProperties>(); +} + + +}} diff --git a/RC9/qpid/cpp/src/qpid/framing/TransferContent.h b/RC9/qpid/cpp/src/qpid/framing/TransferContent.h new file mode 100644 index 0000000000..e3f6666fa4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/TransferContent.h @@ -0,0 +1,63 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TransferContent_ +#define _TransferContent_ + +#include "FrameSet.h" +#include "MethodContent.h" +#include "qpid/Exception.h" +#include "qpid/framing/MessageProperties.h" +#include "qpid/framing/DeliveryProperties.h" + +namespace qpid { +namespace framing { + +/** Message content */ +class TransferContent : public MethodContent +{ + AMQHeaderBody header; + std::string data; +public: + TransferContent(const std::string& data = std::string(), const std::string& key=std::string()); + + ///@internal + AMQHeaderBody getHeader() const; + + void setData(const std::string&); + const std::string& getData() const; + std::string& getData(); + + void appendData(const std::string&); + + bool hasMessageProperties() const; + MessageProperties& getMessageProperties(); + const MessageProperties& getMessageProperties() const; + + bool hasDeliveryProperties() const; + DeliveryProperties& getDeliveryProperties(); + const DeliveryProperties& getDeliveryProperties() const; + + ///@internal + void populate(const FrameSet& frameset); +}; + +}} +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/TypeFilter.h b/RC9/qpid/cpp/src/qpid/framing/TypeFilter.h new file mode 100644 index 0000000000..d1c42de583 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/TypeFilter.h @@ -0,0 +1,51 @@ +#ifndef QPID_FRAMING_TYPEFILTER_H +#define QPID_FRAMING_TYPEFILTER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <string> +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/FrameHandler.h" + +namespace qpid { +namespace framing { + +/** + * Predicate that selects frames by type + */ +template <uint8_t Type> +struct TypeFilter { + bool operator()(const AMQFrame& f) const { + return f.getBody()->type() == Type; + } +}; + +template <uint8_t T1, uint8_t T2> +struct TypeFilter2 { + bool operator()(const AMQFrame& f) const { + return f.getBody()->type() == T1 || f.getBody()->type() == T2; + } +}; + +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_TYPEFILTER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/Uuid.cpp b/RC9/qpid/cpp/src/qpid/framing/Uuid.cpp new file mode 100644 index 0000000000..fbbaac1cf5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/Uuid.cpp @@ -0,0 +1,61 @@ +/* + * + * 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 "Uuid.h" +#include "qpid/Exception.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace framing { + +using namespace std; + +static const size_t UNPARSED_SIZE=36; + +void Uuid::encode(Buffer& buf) const { + buf.putRawData(data(), size()); +} + +void Uuid::decode(Buffer& buf) { + if (buf.available() < size()) + throw IllegalArgumentException(QPID_MSG("Not enough data for UUID.")); + buf.getRawData(c_array(), size()); +} + +ostream& operator<<(ostream& out, Uuid uuid) { + char unparsed[UNPARSED_SIZE + 1]; + uuid_unparse(uuid.data(), unparsed); + return out << unparsed; +} + +istream& operator>>(istream& in, Uuid& uuid) { + char unparsed[UNPARSED_SIZE + 1] = {0}; + in.get(unparsed, sizeof(unparsed)); + if (uuid_parse(unparsed, uuid.c_array()) != 0) + in.setstate(ios::failbit); + return in; +} + +std::string Uuid::str() const { + std::ostringstream os; + os << *this; + return os.str(); +} + +}} // namespace qpid::framing diff --git a/RC9/qpid/cpp/src/qpid/framing/Uuid.h b/RC9/qpid/cpp/src/qpid/framing/Uuid.h new file mode 100644 index 0000000000..2fcbb5a261 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/Uuid.h @@ -0,0 +1,88 @@ +#ifndef QPID_FRAMING_UUID_H +#define QPID_FRAMING_UUID_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/sys/uuid.h" + +#include <boost/array.hpp> + +#include <ostream> +#include <istream> + +namespace qpid { +namespace framing { + +class Buffer; + +/** + * A UUID is represented as a boost::array of 16 bytes. + * + * Full value semantics, operators ==, < etc. are provided by + * boost::array so Uuid can be the key type in a map etc. + */ +struct Uuid : public boost::array<uint8_t, 16> { + /** If unique is true, generate a unique ID else a null ID. */ + Uuid(bool unique=false) { if (unique) generate(); else clear(); } + + /** Copy from 16 bytes of data. */ + Uuid(uint8_t* data) { assign(data); } + + /** Copy from 16 bytes of data. */ + void assign(uint8_t* data) { + uuid_copy(c_array(), data); + } + + /** Set to a new unique identifier. */ + void generate() { uuid_generate(c_array()); } + + /** Set to all zeros. */ + void clear() { uuid_clear(c_array()); } + + /** Test for null (all zeros). */ + bool isNull() { + return uuid_is_null(data()); + } + + // Default op= and copy ctor are fine. + // boost::array gives us ==, < etc. + + void encode(framing::Buffer& buf) const; + void decode(framing::Buffer& buf); + uint32_t encodedSize() const { return size(); } + + /** String value in format 1b4e28ba-2fa1-11d2-883f-b9a761bde3fb. */ + std::string str() const; + + template <class S> void serialize(S& s) { + s.raw(begin(), size()); + } +}; + +/** Print in format 1b4e28ba-2fa1-11d2-883f-b9a761bde3fb. */ +std::ostream& operator<<(std::ostream&, Uuid); + +/** Read from format 1b4e28ba-2fa1-11d2-883f-b9a761bde3fb. */ +std::istream& operator>>(std::istream&, Uuid&); + +}} // namespace qpid::framing + + + +#endif /*!QPID_FRAMING_UUID_H*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/Visitor.h b/RC9/qpid/cpp/src/qpid/framing/Visitor.h new file mode 100644 index 0000000000..759ee65914 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/Visitor.h @@ -0,0 +1,92 @@ +#ifndef QPID_FRAMING_VISITOR_H +#define QPID_FRAMING_VISITOR_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/mpl/vector.hpp> +#include <boost/type_traits/remove_reference.hpp> +#include <boost/preprocessor/seq/for_each.hpp> + +namespace qpid { +namespace framing { + +/** @file Generic visitor pattern. */ + +/** visit() interface for type T (optional return type R, default is void.) + * To create a visitor for a set of types T1, T2 ... do this: + * struct MyVisitor : public Visit<T1>, public Visit<T2> ... {}; + *@param T Type to visit. This must be forward declared, and need not be defined. + */ +template <class T, class R=void> struct Visit { + typedef R ReturnType; + typedef T VisitType; + + virtual ~Visit() {} + virtual R visit(T&) = 0; +}; + + +#define QPID_VISITOR_DECL(_1,_2,T) class T; + +#define QPID_VISITOR_BASE(_1,_2,T) , public ::qpid::framing::Visit<T> + +/** Convenience macro to generate a visitor interface. + * QPID_VISITOR(MyVisitor,(A)(B)(C)); is equivalent to: + * @code + * class A; class B; class C; + * class MyVisitor : public Visit<A> , public Visit<B> , public Visit<C> {}; + * @endcode + * @param visitor name of the generated visitor class. + * @param bases a sequence of visitable types in the form (T1)(T2)... + * Any parenthesized notations are due to quirks of the preprocesser. + */ +#define QPID_VISITOR(visitor,types) \ + BOOST_PP_SEQ_FOR_EACH(QPID_VISITOR_DECL, _, types) \ + class visitor : public ::qpid::framing::Visit<BOOST_PP_SEQ_HEAD(types)> \ + BOOST_PP_SEQ_FOR_EACH(QPID_VISITOR_BASE, _, BOOST_PP_SEQ_TAIL(types)) \ + {} + +/** The root class for the hierarchy of objects visitable by Visitor V. + * Defines virtual accept(). + */ +template <class V, class R=void> +struct VisitableRoot { + typedef V VisitorType; + typedef R ReturnType; + virtual ~VisitableRoot() {} + virtual R accept(V& v) = 0; +}; + +/** The base class for concrete visitable classes. + * Implements accept(). + * @param T type of visitable class (CRTP). + * @param Base base class to inherit from. + */ +template <class T, class Base> +struct Visitable : public Base { + void accept(typename Base::VisitorType& v) { + static_cast<Visit<T>& >(v).visit(static_cast<T&>(*this)); + } +}; + +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_VISITOR_H*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/amqp_framing.h b/RC9/qpid/cpp/src/qpid/framing/amqp_framing.h new file mode 100644 index 0000000000..4e4747c3f4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/amqp_framing.h @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "amqp_types.h" +#include "AMQFrame.h" +#include "AMQBody.h" +#include "BodyHandler.h" +#include "AMQMethodBody.h" +#include "AMQHeaderBody.h" +#include "AMQContentBody.h" +#include "AMQHeartbeatBody.h" +#include "InputHandler.h" +#include "OutputHandler.h" +#include "ProtocolInitiation.h" +#include "ProtocolVersion.h" diff --git a/RC9/qpid/cpp/src/qpid/framing/amqp_types.h b/RC9/qpid/cpp/src/qpid/framing/amqp_types.h new file mode 100644 index 0000000000..97b889a7ca --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/amqp_types.h @@ -0,0 +1,63 @@ +#ifndef AMQP_TYPES_H +#define AMQP_TYPES_H +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/** \file + * Definitions and forward declarations of all types used + * in AMQP messages. + */ + +#include "qpid/sys/IntegerTypes.h" +#include <string> + +namespace qpid { +namespace framing { + +using std::string; +typedef uint8_t FrameType; +typedef uint16_t ChannelId; +typedef uint32_t BatchOffset; +typedef uint8_t ClassId; +typedef uint8_t MethodId; +typedef uint16_t ReplyCode; + +// Types represented by classes. +class Content; +class FieldTable; +class SequenceNumberSet; +struct Uuid; + +// Useful constants + +/** Maximum channel ID used by broker. Reserve high bit for internal use.*/ +const ChannelId CHANNEL_MAX=(ChannelId(~1))>>1; +const ChannelId CHANNEL_HIGH_BIT= ChannelId(~CHANNEL_MAX); + +// Forward declare class types +class FramingContent; +class FieldTable; +class SequenceNumberSet; +class SequenceSet; +struct Uuid; + +}} // namespace qpid::framing +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/amqp_types_full.h b/RC9/qpid/cpp/src/qpid/framing/amqp_types_full.h new file mode 100644 index 0000000000..d0aaf28cb4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/amqp_types_full.h @@ -0,0 +1,38 @@ +#ifndef _framing_amqp_types_decl_h +#define _framing_amqp_types_decl_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. + * + */ + +/** \file + * Definitions and full declarations of all types used + * in AMQP messages. + * + * It's better to include amqp_types.h in another header instead of this file + * unless the header actually needs the full declarations. Including + * full declarations when forward declarations would increase compile + * times. + */ + +#include "amqp_types.h" +#include "Array.h" +#include "FieldTable.h" +#include "SequenceSet.h" +#include "Uuid.h" + +#endif /*!_framing_amqp_types_decl_h*/ diff --git a/RC9/qpid/cpp/src/qpid/framing/frame_functors.h b/RC9/qpid/cpp/src/qpid/framing/frame_functors.h new file mode 100644 index 0000000000..d2064d6a57 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/frame_functors.h @@ -0,0 +1,116 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <string> +#include <ostream> +#include <iostream> +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/Buffer.h" + +#ifndef _frame_functors_ +#define _frame_functors_ + +namespace qpid { +namespace framing { + +class SumFrameSize +{ + uint64_t size; +public: + SumFrameSize() : size(0) {} + void operator()(const AMQFrame& f) { size += f.encodedSize(); } + uint64_t getSize() { return size; } +}; + +class SumBodySize +{ + uint64_t size; +public: + SumBodySize() : size(0) {} + void operator()(const AMQFrame& f) { size += f.getBody()->encodedSize(); } + uint64_t getSize() { return size; } +}; + +class Count +{ + uint count; +public: + Count() : count(0) {} + void operator()(const AMQFrame&) { count++; } + uint getCount() { return count; } +}; + +class EncodeFrame +{ + Buffer& buffer; +public: + EncodeFrame(Buffer& b) : buffer(b) {} + void operator()(const AMQFrame& f) { f.encode(buffer); } +}; + +class EncodeBody +{ + Buffer& buffer; +public: + EncodeBody(Buffer& b) : buffer(b) {} + void operator()(const AMQFrame& f) { f.getBody()->encode(buffer); } +}; + +/** + * Sends to the specified handler a copy of the frame it is applied to. + */ +class Relay +{ + FrameHandler& handler; + +public: + Relay(FrameHandler& h) : handler(h) {} + + void operator()(const AMQFrame& f) + { + AMQFrame copy(f); + handler.handle(copy); + } +}; + +class Print +{ + std::ostream& out; +public: + Print(std::ostream& o) : out(o) {} + + void operator()(const AMQFrame& f) + { + out << f << std::endl; + } +}; + +class MarkLastSegment +{ +public: + void operator()(AMQFrame& f) const { f.setEof(true); } +}; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/framing/variant.h b/RC9/qpid/cpp/src/qpid/framing/variant.h new file mode 100644 index 0000000000..8e13063385 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/framing/variant.h @@ -0,0 +1,91 @@ +#ifndef QPID_FRAMING_VARIANT_H +#define QPID_FRAMING_VARIANT_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/**@file Tools for using boost::variant. */ + + +#include <boost/variant.hpp> + +namespace qpid { +namespace framing { +class Buffer; + +/** boost::static_visitor that throws an exception if variant contains a blank. + * Subclasses need to have a using() declaration, which can be generated + * with QPID_USING_NOBLANK(R) + */ +template <class R=void> +struct NoBlankVisitor : public boost::static_visitor<R> { + R foundBlank() const { + assert(0); + throw Exception(QPID_MSG("Invalid variant value.")); + } + R operator()(const boost::blank&) const { return foundBlank(); } + R operator()(boost::blank&) const { return foundBlank(); } +}; + + +}} // qpid::framing + + +/** Generate a using statement, needed in visitors inheriting NoBlankVisitor + * @param R return type. + */ +#define QPID_USING_NOBLANK(R) using ::qpid::framing::NoBlankVisitor<R>::operator() + +namespace qpid { +namespace framing { + +/** Convert the variant value to type R. */ +template <class R> struct ConvertVisitor : public NoBlankVisitor<R> { + QPID_USING_NOBLANK(R); + template <class T> R operator()(T& t) const { return t; } +}; + +/** Convert the address of variant value to type R. */ +template <class R> struct AddressVisitor : public NoBlankVisitor<R> { + QPID_USING_NOBLANK(R); + template <class T> R operator()(T& t) const { return &t; } +}; + +/** Apply a visitor to the nested variant.*/ +template<class V> +struct ApplyVisitor : public NoBlankVisitor<typename V::result_type> { + QPID_USING_NOBLANK(typename V::result_type); + const V& visitor; + ApplyVisitor(const V& v) : visitor(v) {} + template <class T> typename V::result_type operator()(T& t) const { + return boost::apply_visitor(visitor, t); + } +}; + +/** Convenience function to construct and apply an ApplyVisitor */ +template <class Visitor, class Visitable> +typename Visitor::result_type applyApplyVisitor(const Visitor& visitor, Visitable& visitable) { + return boost::apply_visitor(ApplyVisitor<Visitor>(visitor), visitable); +} + +}} // namespace qpid::framing + + +#endif /*!QPID_FRAMING_VARIANT_H*/ diff --git a/RC9/qpid/cpp/src/qpid/log/Helpers.h b/RC9/qpid/cpp/src/qpid/log/Helpers.h new file mode 100644 index 0000000000..82ef8244be --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/Helpers.h @@ -0,0 +1,79 @@ +#ifndef QPID_LOG_HELPERS_H +#define QPID_LOG_HELPERS_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/range.hpp> + +#include <ostream> + +namespace qpid { +namespace log { + +/** @file Helper classes for logging complex types */ + +/// @internal +template <class Range> +struct ListFormatter { + typedef typename boost::range_const_iterator<Range>::type Iterator; + boost::iterator_range<Iterator> range; + const char* separator; + + ListFormatter(const Range& r, const char* s=", ") : range(r), separator(s) {} +}; + +/// @internal +template <class Range> +std::ostream& operator<<(std::ostream& out, const ListFormatter<Range>& sl) { + typename ListFormatter<Range>::Iterator i = sl.range.begin(); + if (i != sl.range.end()) out << *(i++); + while (i != sl.range.end()) out << sl.separator << *(i++); + return out; +} + +/** Return a formatting object with operator << + * to stream range as a separated list. + *@param range: a range - all standard containers are ranges, + * as is a pair of iterators. + *@param separator: printed between elements, default ", " + */ +template <class Range> +ListFormatter<Range> formatList(const Range& range, const char* separator=", ") { + return ListFormatter<Range>(range, separator); +} + +/** Return a formatting object with operator << + * to stream the range defined by iterators [begin, end) + * as a separated list. + *@param begin, end: Beginning and end of range. + *@param separator: printed between elements, default ", " + */ +template <class U, class V> +ListFormatter<std::pair<U,V> > formatList(U begin, V end, const char* separator=", ") { + return formatList(std::make_pair(begin,end), separator); +} + + +}} // namespace qpid::log + + + +#endif /*!QPID_LOG_HELPERS_H*/ diff --git a/RC9/qpid/cpp/src/qpid/log/Logger.cpp b/RC9/qpid/cpp/src/qpid/log/Logger.cpp new file mode 100644 index 0000000000..07e4245399 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/Logger.cpp @@ -0,0 +1,168 @@ +/* + * + * 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 "Logger.h" +#include "Options.h" +#include "SinkOptions.h" +#include "qpid/memory.h" +#include "qpid/sys/Thread.h" +#include <boost/pool/detail/singleton.hpp> +#include <boost/bind.hpp> +#include <boost/function.hpp> +#include <algorithm> +#include <sstream> +#include <iomanip> +#include <stdexcept> +#include <time.h> + + +namespace qpid { +namespace log { + +using namespace std; + +typedef sys::Mutex::ScopedLock ScopedLock; + +inline void Logger::enable_unlocked(Statement* s) { + s->enabled=selector.isEnabled(s->level, s->function); +} + +Logger& Logger::instance() { + return boost::details::pool::singleton_default<Logger>::instance(); +} + +Logger::Logger() : flags(0) { + // Initialize myself from env variables so all programs + // (e.g. tests) can use logging even if they don't parse + // command line args. + Options opts(""); + opts.parse(0, 0); + configure(opts); +} + +Logger::~Logger() {} + +void Logger::select(const Selector& s) { + ScopedLock l(lock); + selector=s; + std::for_each(statements.begin(), statements.end(), + boost::bind(&Logger::enable_unlocked, this, _1)); +} + +Logger::Output::Output() {} +Logger::Output::~Output() {} + +void Logger::log(const Statement& s, const std::string& msg) { + // Format the message outside the lock. + std::ostringstream os; + if (!prefix.empty()) + os << prefix << ": "; + if (flags&TIME) + { + const char * month_abbrevs[] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; + time_t rawtime; + struct tm * timeinfo; + + time ( & rawtime ); + timeinfo = localtime ( &rawtime ); + char time_string[100]; + sprintf ( time_string, + "%d-%s-%02d %02d:%02d:%02d", + 1900 + timeinfo->tm_year, + month_abbrevs[timeinfo->tm_mon], + timeinfo->tm_mday, + timeinfo->tm_hour, + timeinfo->tm_min, + timeinfo->tm_sec + ); + os << time_string << " "; + } + if (flags&LEVEL) + os << LevelTraits::name(s.level) << " "; + if (flags&THREAD) + os << "[0x" << hex << qpid::sys::Thread::logId() << "] "; + if (flags&FILE) + os << s.file << ":"; + if (flags&LINE) + os << dec << s.line << ":"; + if (flags&FUNCTION) + os << s.function << ":"; + if (flags & (FILE|LINE|FUNCTION)) + os << " "; + os << msg << endl; + std::string formatted=os.str(); + { + ScopedLock l(lock); + std::for_each(outputs.begin(), outputs.end(), + boost::bind(&Output::log, _1, s, formatted)); + } +} + +void Logger::output(std::auto_ptr<Output> out) { + ScopedLock l(lock); + outputs.push_back(out.release()); +} + +void Logger::clear() { + select(Selector()); // locked + format(0); // locked + ScopedLock l(lock); + outputs.clear(); +} + +void Logger::format(int formatFlags) { + ScopedLock l(lock); + flags=formatFlags; +} + +static int bitIf(bool test, int bit) { + return test ? bit : 0; +} + +int Logger::format(const Options& opts) { + int flags= + bitIf(opts.level, LEVEL) | + bitIf(opts.time, TIME) | + bitIf(opts.source, (FILE|LINE)) | + bitIf(opts.function, FUNCTION) | + bitIf(opts.thread, THREAD); + format(flags); + return flags; +} + +void Logger::add(Statement& s) { + ScopedLock l(lock); + enable_unlocked(&s); + statements.insert(&s); +} + +void Logger::configure(const Options& opts) { + options = opts; + clear(); + Options o(opts); + if (o.trace) + o.selectors.push_back("trace+"); + format(o); + select(Selector(o)); + setPrefix(opts.prefix); + options.sinkOptions->setup(this); +} + +void Logger::setPrefix(const std::string& p) { prefix = p; } + +}} // namespace qpid::log diff --git a/RC9/qpid/cpp/src/qpid/log/Logger.h b/RC9/qpid/cpp/src/qpid/log/Logger.h new file mode 100644 index 0000000000..539c1c851b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/Logger.h @@ -0,0 +1,113 @@ +#ifndef QPID_LOG_LOGGER_H +#define QPID_LOG_LOGGER_H + +/* + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "Selector.h" +#include "Options.h" +#include "qpid/sys/Mutex.h" +#include <boost/ptr_container/ptr_vector.hpp> +#include <boost/noncopyable.hpp> +#include <set> + +namespace qpid { +namespace log { + +/** + * Central logging agent. + * + * Thread safe, singleton. + * + * The Logger provides all needed functionality for selecting and + * formatting logging output. The actual outputting of log records + * is handled by Logger::Output-derived classes instantiated by the + * platform's sink-related options. + */ +class Logger : private boost::noncopyable { + public: + /** Flags indicating what to include in the log output */ + enum FormatFlag { FILE=1, LINE=2, FUNCTION=4, LEVEL=8, TIME=16, THREAD=32}; + + /** + * Logging output sink. + * + * The Output sink provides an interface to direct logging output to. + * Logging sinks are primarily platform-specific as provided for on + * each platform. + * + * Implementations of Output must be thread safe. + */ + class Output { + public: + Output(); + virtual ~Output(); + /** Receives the statemnt of origin and formatted message to log. */ + virtual void log(const Statement&, const std::string&) =0; + }; + + static Logger& instance(); + + Logger(); + ~Logger(); + + /** Select the messages to be logged. */ + void select(const Selector& s); + + /** Set the formatting flags, bitwise OR of FormatFlag values. */ + void format(int formatFlags); + + /** Set format flags from options object. + *@returns computed flags. + */ + int format(const Options&); + + /** Configure logger from Options */ + void configure(const Options& o); + + /** Add a statement. */ + void add(Statement& s); + + /** Log a message. */ + void log(const Statement&, const std::string&); + + /** Add an output destination for messages */ + void output(std::auto_ptr<Output> out); + + /** Set a prefix for all messages */ + void setPrefix(const std::string& prefix); + + /** Reset the logger. */ + void clear(); + + /** Get the options used to configure the logger. */ + const Options& getOptions() const { return options; } + + + private: + typedef boost::ptr_vector<Output> Outputs; + typedef std::set<Statement*> Statements; + + sys::Mutex lock; + inline void enable_unlocked(Statement* s); + + Statements statements; + Outputs outputs; + Selector selector; + int flags; + std::string prefix; + Options options; +}; + +}} // namespace qpid::log + + +#endif /*!QPID_LOG_LOGGER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/log/Options.cpp b/RC9/qpid/cpp/src/qpid/log/Options.cpp new file mode 100644 index 0000000000..a33da6fd8f --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/Options.cpp @@ -0,0 +1,107 @@ +/* + * + * 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 "Options.h" +#include "SinkOptions.h" +#include "Statement.h" +#include "qpid/Options.h" +#include <map> +#include <string> +#include <algorithm> + +namespace qpid { +namespace log { + +using namespace std; + +Options::Options(const std::string& argv0_, const std::string& name_) : + qpid::Options(name_), + argv0(argv0_), + name(name_), + time(true), + level(true), + thread(false), + source(false), + function(false), + trace(false), + sinkOptions (SinkOptions::create(argv0_)) +{ + selectors.push_back("notice+"); + + ostringstream levels; + levels << LevelTraits::name(Level(0)); + for (int i = 1; i < LevelTraits::COUNT; ++i) + levels << " " << LevelTraits::name(Level(i)); + + addOptions() + ("trace,t", optValue(trace), "Enables all logging" ) + ("log-enable", optValue(selectors, "RULE"), + ("Enables logging for selected levels and components. " + "RULE is in the form 'LEVEL[+][:PATTERN]' " + "Levels are one of: \n\t "+levels.str()+"\n" + "For example:\n" + "\t'--log-enable warning+' " + "logs all warning, error and critical messages.\n" + "\t'--log-enable debug:framing' " + "logs debug messages from the framing namespace. " + "This option can be used multiple times").c_str()) + ("log-time", optValue(time, "yes|no"), "Include time in log messages") + ("log-level", optValue(level,"yes|no"), "Include severity level in log messages") + ("log-source", optValue(source,"yes|no"), "Include source file:line in log messages") + ("log-thread", optValue(thread,"yes|no"), "Include thread ID in log messages") + ("log-function", optValue(function,"yes|no"), "Include function signature in log messages") + ("log-prefix", optValue(prefix,"STRING"), "Prefix to append to all log messages") + ; + add(*sinkOptions); +} + +Options::Options(const Options &o) : + qpid::Options(o.name), + argv0(o.argv0), + name(o.name), + selectors(o.selectors), + time(o.time), + level(o.level), + thread(o.thread), + source(o.source), + function(o.function), + trace(o.trace), + prefix(o.prefix), + sinkOptions (SinkOptions::create(o.argv0)) +{ + *sinkOptions = *o.sinkOptions; +} + +Options& Options::operator=(const Options& x) { + if (this != &x) { + argv0 = x.argv0; + name = x.name; + selectors = x.selectors; + time = x.time; + level= x.level; + thread = x.thread; + source = x.source; + function = x.function; + trace = x.trace; + prefix = x.prefix; + *sinkOptions = *x.sinkOptions; + } + return *this; +} + +}} // namespace qpid::log diff --git a/RC9/qpid/cpp/src/qpid/log/Options.h b/RC9/qpid/cpp/src/qpid/log/Options.h new file mode 100644 index 0000000000..8a3c352d14 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/Options.h @@ -0,0 +1,49 @@ +#ifndef QPID_LOG_OPTIONS_H +#define QPID_LOG_OPTIONS_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "qpid/Options.h" +#include "SinkOptions.h" +#include <iosfwd> +#include <memory> + +namespace qpid { +namespace log { + +/** Logging options for config parser. */ +struct Options : public qpid::Options { + /** Pass argv[0] for use in syslog output */ + Options(const std::string& argv0_=std::string(), + const std::string& name_="Logging options"); + Options(const Options &); + + Options& operator=(const Options&); + + std::string argv0; + std::string name; + std::vector<std::string> selectors; + bool time, level, thread, source, function; + bool trace; + std::string prefix; + std::auto_ptr<SinkOptions> sinkOptions; +}; + +}} // namespace qpid::log + +#endif /*!QPID_LOG_OPTIONS_H*/ diff --git a/RC9/qpid/cpp/src/qpid/log/OstreamOutput.cpp b/RC9/qpid/cpp/src/qpid/log/OstreamOutput.cpp new file mode 100644 index 0000000000..c96a311a4e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/OstreamOutput.cpp @@ -0,0 +1,41 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "OstreamOutput.h" +#include <stdexcept> + +using namespace std; + +namespace qpid { +namespace log { + +OstreamOutput::OstreamOutput(std::ostream& o) : out(&o) {} + +OstreamOutput::OstreamOutput(const std::string& file) + : out(new ofstream(file.c_str(), ios_base::out | ios_base::app)), + mine(out) +{ + if (!out->good()) + throw std::runtime_error("Can't open log file: "+file); +} + +void OstreamOutput::log(const Statement&, const std::string& m) { + *out << m << flush; +} + +}} // namespace qpid::log diff --git a/RC9/qpid/cpp/src/qpid/log/OstreamOutput.h b/RC9/qpid/cpp/src/qpid/log/OstreamOutput.h new file mode 100644 index 0000000000..8bbfc8c38b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/OstreamOutput.h @@ -0,0 +1,41 @@ +#ifndef QPID_LOG_OSTREAMOUTPUT_H +#define QPID_LOG_OSTREAMOUTPUT_H + +/* + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "Logger.h" +#include <boost/scoped_ptr.hpp> +#include <fstream> +#include <ostream> + +namespace qpid { +namespace log { + +/** + * OstreamOutput is a reusable logging sink that directs logging to a C++ + * ostream. + */ +class OstreamOutput : public qpid::log::Logger::Output { +public: + OstreamOutput(std::ostream& o); + OstreamOutput(const std::string& file); + + virtual void log(const Statement&, const std::string& m); + +private: + std::ostream* out; + boost::scoped_ptr<std::ostream> mine; +}; + +}} // namespace qpid::log + +#endif /*!QPID_LOG_OSTREAMOUTPUT_H*/ diff --git a/RC9/qpid/cpp/src/qpid/log/Selector.cpp b/RC9/qpid/cpp/src/qpid/log/Selector.cpp new file mode 100644 index 0000000000..4d1c5b6e0c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/Selector.cpp @@ -0,0 +1,67 @@ +/* + * + * 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 "Selector.h" +#include "Options.h" +#include <boost/bind.hpp> +#include <algorithm> + +namespace qpid { +namespace log { + +using namespace std; + +void Selector::enable(const string& enableStr) { + string level, pattern; + size_t c=enableStr.find(':'); + if (c==string::npos) { + level=enableStr; + } + else { + level=enableStr.substr(0,c); + pattern=enableStr.substr(c+1); + } + if (!level.empty() && level[level.size()-1]=='+') { + for (int i = LevelTraits::level(level.substr(0,level.size()-1)); + i < LevelTraits::COUNT; + ++i) + enable(Level(i), pattern); + } + else { + enable(LevelTraits::level(level), pattern); + } +} + +Selector::Selector(const Options& opt){ + for_each(opt.selectors.begin(), opt.selectors.end(), + boost::bind(&Selector::enable, this, _1)); +} + +bool Selector::isEnabled(Level level, const char* function) { + const char* functionEnd = function+::strlen(function); + for (std::vector<std::string>::iterator i=substrings[level].begin(); + i != substrings[level].end(); + ++i) + { + if (std::search(function, functionEnd, i->begin(), i->end()) != functionEnd) + return true; + } + return false; +} + +}} // namespace qpid::log diff --git a/RC9/qpid/cpp/src/qpid/log/Selector.h b/RC9/qpid/cpp/src/qpid/log/Selector.h new file mode 100644 index 0000000000..705abfeb5d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/Selector.h @@ -0,0 +1,70 @@ +#ifndef SELECTOR_H +#define SELECTOR_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "Statement.h" +#include <vector> + +namespace qpid { +namespace log { +struct Options; + +/** + * A selector identifies the set of log messages to enable. + * + * Thread object unsafe, pass-by-value type. + */ +class Selector { + public: + /** Empty selector selects nothing */ + Selector() {} + + /** Set selector from Options */ + Selector(const Options&); + + /** Equavlient to: Selector s; s.enable(l, s) */ + Selector(Level l, const std::string& s=std::string()) { + enable(l,s); + } + + Selector(const std::string& enableStr) { enable(enableStr); } + /** + * Enable messages with level in levels where the file + * name contains substring. Empty string matches all. + */ + void enable(Level level, const std::string& substring=std::string()) { + substrings[level].push_back(substring); + } + + /** Enable based on a 'level[+]:file' string */ + void enable(const std::string& enableStr); + + /** True if level is enabled for file. */ + bool isEnabled(Level level, const char* function); + + private: + std::vector<std::string> substrings[LevelTraits::COUNT]; +}; + + +}} // namespace qpid::log + + +#endif /*!SELECTOR_H*/ diff --git a/RC9/qpid/cpp/src/qpid/log/SinkOptions.h b/RC9/qpid/cpp/src/qpid/log/SinkOptions.h new file mode 100644 index 0000000000..7ec2cfbc17 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/SinkOptions.h @@ -0,0 +1,64 @@ +#ifndef QPID_LOG_SINKOPTIONS_H +#define QPID_LOG_SINKOPTIONS_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/Options.h" +#include <string> + +namespace qpid { +namespace log { + +class Logger; + +/** + * Logging sink options. + * + * Most logging sink options will be platform-specific, even if some are + * duplicated. The range of platforms to which this code may be ported + * can't be assumed to all have C++ iostreams or files. Thus, this class + * is primarily for implementing in a platform-specific way. + */ +struct SinkOptions : public qpid::Options { + + // Create a platform's SinkOptions. Pass argv0 as the program name, + // useful for syslog-type logging. + static SinkOptions *create(const std::string& argv0=std::string()); + + SinkOptions(const std::string& name="Logging sink options") + : qpid::Options(name) + {} + virtual ~SinkOptions() {} + + virtual SinkOptions& operator=(const SinkOptions&) = 0; + + // This allows the caller to indicate that there's no normal outputs + // available. For example, when running as a daemon. In these cases, the + // platform's "syslog"-type output should replace the default stderr + // unless some other sink has been selected. + virtual void detached(void) = 0; + + // The Logger acting on these options calls setup() to request any + // Sinks be set up and fed back to the logger. + virtual void setup(Logger *logger) = 0; +}; + +}} // namespace qpid::log + +#endif /*!QPID_LOG_OPTIONS_H*/ diff --git a/RC9/qpid/cpp/src/qpid/log/Statement.cpp b/RC9/qpid/cpp/src/qpid/log/Statement.cpp new file mode 100644 index 0000000000..090a993e78 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/Statement.cpp @@ -0,0 +1,83 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "Statement.h" +#include "Logger.h" +#include <boost/bind.hpp> +#include <stdexcept> +#include <algorithm> +#include <ctype.h> + +namespace qpid { +namespace log { + +namespace { +using namespace std; + +struct NonPrint { bool operator()(unsigned char c) { return !isprint(c) && !isspace(c); } }; + +const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + +std::string quote(const std::string& str) { + NonPrint nonPrint; + size_t n = std::count_if(str.begin(), str.end(), nonPrint); + if (n==0) return str; + std::string ret; + ret.reserve(str.size()+2*n); // Avoid extra allocations. + for (string::const_iterator i = str.begin(); i != str.end(); ++i) { + if (nonPrint(*i)) { + ret.push_back('\\'); + ret.push_back('x'); + ret.push_back(hex[((*i) >> 4)&0xf]); + ret.push_back(hex[(*i) & 0xf]); + } + else ret.push_back(*i); + } + return ret; +} + +} + +void Statement::log(const std::string& message) { + Logger::instance().log(*this, quote(message)); +} + +Statement::Initializer::Initializer(Statement& s) : statement(s) { + Logger::instance().add(s); +} + +namespace { +const char* names[LevelTraits::COUNT] = { + "trace", "debug", "info", "notice", "warning", "error", "critical" +}; + +} // namespace + +Level LevelTraits::level(const char* name) { + for (int i =0; i < LevelTraits::COUNT; ++i) { + if (strcmp(names[i], name)==0) + return Level(i); + } + throw std::runtime_error(std::string("Invalid log level name: ")+name); +} + +const char* LevelTraits::name(Level l) { + return names[l]; +} + +}} // namespace qpid::log diff --git a/RC9/qpid/cpp/src/qpid/log/Statement.h b/RC9/qpid/cpp/src/qpid/log/Statement.h new file mode 100644 index 0000000000..3c67b04b20 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/Statement.h @@ -0,0 +1,121 @@ +#ifndef STATEMENT_H +#define STATEMENT_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/Msg.h" + +#include <boost/current_function.hpp> + +namespace qpid { +namespace log { + +/** Debugging severity levels + * - trace: High-volume debugging messages. + * - debug: Debugging messages. + * - info: Informational messages. + * - notice: Normal but significant condition. + * - warning: Warn of a possible problem. + * - error: A definite error has occured. + * - critical: System in danger of severe failure. + */ +enum Level { trace, debug, info, notice, warning, error, critical }; +struct LevelTraits { + static const int COUNT=critical+1; + + /** Get level from string name. + *@exception if name invalid. + */ + static Level level(const char* name); + + /** Get level from string name. + *@exception if name invalid. + */ + static Level level(const std::string& name) { + return level(name.c_str()); + } + + /** String name of level */ + static const char* name(Level); +}; + +/** POD struct representing a logging statement in source code. */ +struct Statement { + bool enabled; + const char* file; + int line; + const char* function; + Level level; + + void log(const std::string& message); + + struct Initializer { + Initializer(Statement& s); + Statement& statement; + }; +}; + +///@internal static initializer for a Statement. +#define QPID_LOG_STATEMENT_INIT(level) \ + { 0, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, (::qpid::log::level) } + +/** + * Like QPID_LOG but computes an additional boolean test expression + * to determine if the message should be logged. Evaluation of both + * the test and message expressions occurs only if the requested log level + * is enabled. + *@param LEVEL severity Level for message, should be one of: + * debug, info, notice, warning, error, critical. NB no qpid::log:: prefix. + *@param TEST message is logged only if expression TEST evaluates to true. + *@param MESSAGE any object with an @eostream operator<<, or a sequence + * like of ostreamable objects separated by @e<<. + */ +#define QPID_LOG_IF(LEVEL, TEST, MESSAGE) \ + do { \ + using ::qpid::log::Statement; \ + static Statement stmt_= QPID_LOG_STATEMENT_INIT(LEVEL); \ + static Statement::Initializer init_(stmt_); \ + if (stmt_.enabled && (TEST)) \ + stmt_.log(::qpid::Msg() << MESSAGE); \ + } while(0) + +/** + * Macro for log statements. Example of use: + * @code + * QPID_LOG(debug, "There are " << foocount << " foos in the bar."); + * QPID_LOG(error, boost::format("Dohickey %s exploded") % dohicky.name()); + * @endcode + * + * You can subscribe to log messages by level, by component, by filename + * or a combination @see Configuration. + + *@param LEVEL severity Level for message, should be one of: + * debug, info, notice, warning, error, critical. NB no qpid::log:: prefix. + *@param MESSAGE any object with an @eostream operator<<, or a sequence + * like of ostreamable objects separated by @e<<. + */ +#define QPID_LOG(LEVEL, MESSAGE) QPID_LOG_IF(LEVEL, true, MESSAGE); + +}} // namespace qpid::log + + + + +#endif /*!STATEMENT_H*/ + diff --git a/RC9/qpid/cpp/src/qpid/log/posix/SinkOptions.cpp b/RC9/qpid/cpp/src/qpid/log/posix/SinkOptions.cpp new file mode 100644 index 0000000000..9d51358e2e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/posix/SinkOptions.cpp @@ -0,0 +1,211 @@ +/* + * + * 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 "SinkOptions.h" +#include "qpid/log/SinkOptions.h" +#include "qpid/log/Logger.h" +#include "qpid/log/OstreamOutput.h" +#include "qpid/memory.h" +#include "qpid/Exception.h" +#include <iostream> +#include <map> +#include <string> +#include <syslog.h> + +using std::string; +using qpid::Exception; + +namespace { + +// SyslogFacilities maps from syslog values to the text equivalents. +class SyslogFacilities { +public: + typedef std::map<string, int> ByName; + typedef std::map<int, string> ByValue; + + SyslogFacilities() { + struct NameValue { const char* name; int value; }; + NameValue nameValue[] = { + { "AUTH", LOG_AUTH }, + { "AUTHPRIV", LOG_AUTHPRIV }, + { "CRON", LOG_CRON }, + { "DAEMON", LOG_DAEMON }, + { "FTP", LOG_FTP }, + { "KERN", LOG_KERN }, + { "LOCAL0", LOG_LOCAL0 }, + { "LOCAL1", LOG_LOCAL1 }, + { "LOCAL2", LOG_LOCAL2 }, + { "LOCAL3", LOG_LOCAL3 }, + { "LOCAL4", LOG_LOCAL4 }, + { "LOCAL5", LOG_LOCAL5 }, + { "LOCAL6", LOG_LOCAL6 }, + { "LOCAL7", LOG_LOCAL7 }, + { "LPR", LOG_LPR }, + { "MAIL", LOG_MAIL }, + { "NEWS", LOG_NEWS }, + { "SYSLOG", LOG_SYSLOG }, + { "USER", LOG_USER }, + { "UUCP", LOG_UUCP } + }; + for (size_t i = 0; i < sizeof(nameValue)/sizeof(nameValue[0]); ++i) { + byName.insert(ByName::value_type(nameValue[i].name, nameValue[i].value)); + // Recognise with and without LOG_ prefix e.g.: AUTH and LOG_AUTH + byName.insert(ByName::value_type(string("LOG_")+nameValue[i].name, nameValue[i].value)); + byValue.insert(ByValue::value_type(nameValue[i].value, string("LOG_")+nameValue[i].name)); + } + } + + int value(const string& name) const { + string key(name); + transform(key.begin(), key.end(), key.begin(), ::toupper); + ByName::const_iterator i = byName.find(key); + if (i == byName.end()) + throw Exception("Not a valid syslog facility: " + name); + return i->second; + } + + string name(int value) const { + ByValue::const_iterator i = byValue.find(value); + if (i == byValue.end()) + throw Exception("Not a valid syslog value: " + value); + return i->second; + } + + private: + ByName byName; + ByValue byValue; +}; + +// 'priorities' maps qpid log levels to syslog priorities. They are in +// order of qpid log levels and must map to: +// "trace", "debug", "info", "notice", "warning", "error", "critical" +static int priorities[qpid::log::LevelTraits::COUNT] = { + LOG_DEBUG, LOG_DEBUG, LOG_INFO, LOG_NOTICE, + LOG_WARNING, LOG_ERR, LOG_CRIT +}; + +std::string basename(const std::string path) { + size_t i = path.find_last_of('/'); + return path.substr((i == std::string::npos) ? 0 : i+1); +} + +} // namespace + +namespace qpid { +namespace log { +namespace posix { + +std::ostream& operator<<(std::ostream& o, const SyslogFacility& f) { + return o << SyslogFacilities().name(f.value); +} + +std::istream& operator>>(std::istream& i, SyslogFacility& f) { + std::string name; + i >> name; + f.value = SyslogFacilities().value(name); + return i; +} + +class SyslogOutput : public qpid::log::Logger::Output { +public: + SyslogOutput(const std::string& logName, const SyslogFacility& logFacility) + : name(logName), facility(logFacility.value) + { + ::openlog(name.c_str(), LOG_PID, facility); + } + + virtual ~SyslogOutput() { + ::closelog(); + } + + virtual void log(const Statement& s, const std::string& m) + { + syslog(priorities[s.level], "%s", m.c_str()); + } + +private: + std::string name; + int facility; +}; + +SinkOptions::SinkOptions(const std::string& argv0) + : qpid::log::SinkOptions(), + logToStderr(true), + logToStdout(false), + logToSyslog(false), + syslogName(basename(argv0)), + syslogFacility(LOG_DAEMON) { + + addOptions() + ("log-to-stderr", optValue(logToStderr, "yes|no"), "Send logging output to stderr") + ("log-to-stdout", optValue(logToStdout, "yes|no"), "Send logging output to stdout") + ("log-to-file", optValue(logFile, "FILE"), "Send log output to FILE.") + ("log-to-syslog", optValue(logToSyslog, "yes|no"), "Send logging output to syslog;\n\tcustomize using --syslog-name and --syslog-facility") + ("syslog-name", optValue(syslogName, "NAME"), "Name to use in syslog messages") + ("syslog-facility", optValue(syslogFacility,"LOG_XXX"), "Facility to use in syslog messages") + ; + +} + +qpid::log::SinkOptions& SinkOptions::operator=(const qpid::log::SinkOptions& rhs) { + const SinkOptions *prhs = dynamic_cast<const SinkOptions*>(&rhs); + if (this != prhs) { + logToStderr = prhs->logToStderr; + logToStdout = prhs->logToStdout; + logToSyslog = prhs->logToSyslog; + logFile = prhs->logFile; + syslogName = prhs->syslogName; + syslogFacility.value = prhs->syslogFacility.value; + } + return *this; +} + +void SinkOptions::detached(void) { + if (logToStderr && !logToStdout && !logToSyslog) { + logToStderr = false; + logToSyslog = true; + } +} + +// The Logger acting on these options calls setup() to request any +// Sinks be set up and fed back to the logger. +void SinkOptions::setup(qpid::log::Logger *logger) { + if (logToStderr) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new qpid::log::OstreamOutput(std::clog))); + if (logToStdout) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new qpid::log::OstreamOutput(std::cout))); + + if (logFile.length() > 0) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new qpid::log::OstreamOutput(logFile))); + + if (logToSyslog) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new SyslogOutput(syslogName, syslogFacility))); + +} + +} // namespace qpid::log::posix + +SinkOptions* SinkOptions::create(const std::string& argv0) { + return new qpid::log::posix::SinkOptions (argv0); +} + +}} // namespace qpid::log diff --git a/RC9/qpid/cpp/src/qpid/log/posix/SinkOptions.h b/RC9/qpid/cpp/src/qpid/log/posix/SinkOptions.h new file mode 100644 index 0000000000..d929c29025 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/posix/SinkOptions.h @@ -0,0 +1,64 @@ +#ifndef QPID_LOG_POSIX_SINKOPTIONS_H +#define QPID_LOG_POSIX_SINKOPTIONS_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/log/SinkOptions.h" +#include <string> + +namespace qpid { +namespace log { +namespace posix { + +/** + * Provides a type that can be passed to << and >> operators to convert + * syslog facility values to/from strings. + */ +struct SyslogFacility { + int value; + SyslogFacility(int i=0) : value(i) {} +}; + +struct SinkOptions : public qpid::log::SinkOptions { + SinkOptions(const std::string& argv0); + virtual ~SinkOptions() {} + + virtual qpid::log::SinkOptions& operator=(const qpid::log::SinkOptions& rhs); + + // This allows the caller to indicate that there's no normal outputs + // available. For example, when running as a daemon. In these cases, the + // platform's "syslog"-type output should replace the default stderr + // unless some other sink has been selected. + virtual void detached(void); + + // The Logger acting on these options calls setup() to request any + // Sinks be set up and fed back to the logger. + virtual void setup(qpid::log::Logger *logger); + + bool logToStderr; + bool logToStdout; + bool logToSyslog; + std::string logFile; + std::string syslogName; + SyslogFacility syslogFacility; +}; + +}}} // namespace qpid::log::posix + +#endif /*!QPID_LOG_POSIX_SINKOPTIONS_H*/ diff --git a/RC9/qpid/cpp/src/qpid/log/windows/SinkOptions.cpp b/RC9/qpid/cpp/src/qpid/log/windows/SinkOptions.cpp new file mode 100644 index 0000000000..cfcb965464 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/windows/SinkOptions.cpp @@ -0,0 +1,148 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "SinkOptions.h" +#include "qpid/log/SinkOptions.h" +#include "qpid/log/Logger.h" +#include "qpid/log/OstreamOutput.h" +#include "qpid/memory.h" +#include "qpid/Exception.h" +#include <iostream> +#include <map> +#include <string> + +#include <windows.h> + +using qpid::Exception; + +namespace qpid { +namespace log { +namespace windows { + +namespace { + +// 'eventTypes' maps qpid log levels to Windows event types. They are in +// order of qpid log levels and must map to: +// "trace", "debug", "info", "notice", "warning", "error", "critical" +static int eventTypes[qpid::log::LevelTraits::COUNT] = { + EVENTLOG_INFORMATION_TYPE, /* trace */ + EVENTLOG_INFORMATION_TYPE, /* debug */ + EVENTLOG_INFORMATION_TYPE, /* info */ + EVENTLOG_INFORMATION_TYPE, /* notice */ + EVENTLOG_WARNING_TYPE, /* warning */ + EVENTLOG_ERROR_TYPE, /* error */ + EVENTLOG_ERROR_TYPE /* critical */ +}; + +} // namespace + +class EventLogOutput : public qpid::log::Logger::Output { +public: + EventLogOutput(const std::string& sourceName) : logHandle(0) + { + logHandle = OpenEventLog(0, "Application"); + } + + virtual ~EventLogOutput() { + if (logHandle) + CloseEventLog(logHandle); + } + + virtual void log(const Statement& s, const std::string& m) + { + if (logHandle) { + const char *msg = m.c_str(); + ReportEvent(logHandle, + eventTypes[s.level], + 0, /* category unused */ + 0, /* event id */ + 0, /* user security id */ + 1, /* number of strings */ + 0, /* no event-specific data */ + &msg, + 0); + } + } + +private: + HANDLE logHandle; +}; + +SinkOptions::SinkOptions(const std::string& argv0) + : qpid::log::SinkOptions(), + logToStderr(true), + logToStdout(false), + logToEventLog(false), + eventSource("Application") +{ + addOptions() + ("log-to-stderr", optValue(logToStderr, "yes|no"), "Send logging output to stderr") + ("log-to-stdout", optValue(logToStdout, "yes|no"), "Send logging output to stdout") + ("log-to-file", optValue(logFile, "FILE"), "Send log output to FILE.") + ("log-to-eventlog", optValue(logToEventLog, "yes|no"), "Send logging output to event log;\n\tcustomize using --syslog-name and --syslog-facility") + ("eventlog-source-name", optValue(eventSource, "Application"), "Event source to log to") + ; + +} + +qpid::log::SinkOptions& SinkOptions::operator=(const qpid::log::SinkOptions& rhs) { + const SinkOptions *prhs = dynamic_cast<const SinkOptions*>(&rhs); + if (this != prhs) { + logToStderr = prhs->logToStderr; + logToStdout = prhs->logToStdout; + logToEventLog = prhs->logToEventLog; + eventSource = prhs->eventSource; + logFile = prhs->logFile; + } + return *this; +} + +void SinkOptions::detached(void) { + if (logToStderr && !logToStdout && !logToEventLog) { + logToStderr = false; + logToEventLog = true; + } +} + +// The Logger acting on these options calls setup() to request any +// Sinks be set up and fed back to the logger. +void SinkOptions::setup(qpid::log::Logger *logger) { + if (logToStderr) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new qpid::log::OstreamOutput(std::clog))); + if (logToStdout) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new qpid::log::OstreamOutput(std::cout))); + + if (logFile.length() > 0) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new qpid::log::OstreamOutput(logFile))); + + if (logToEventLog) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new EventLogOutput(eventSource))); + +} + +} // namespace windows + +SinkOptions* SinkOptions::create(const std::string& argv0) { + return new qpid::log::windows::SinkOptions (argv0); +} + +}} // namespace qpid::log diff --git a/RC9/qpid/cpp/src/qpid/log/windows/SinkOptions.h b/RC9/qpid/cpp/src/qpid/log/windows/SinkOptions.h new file mode 100644 index 0000000000..d14e9352be --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/log/windows/SinkOptions.h @@ -0,0 +1,54 @@ +#ifndef QPID_LOG_WINDOWS_SINKOPTIONS_H +#define QPID_LOG_WINDOWS_SINKOPTIONS_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/log/SinkOptions.h" +#include <string> + +namespace qpid { +namespace log { +namespace windows { + +struct SinkOptions : public qpid::log::SinkOptions { + SinkOptions(const std::string& argv0); + virtual ~SinkOptions() {} + + virtual qpid::log::SinkOptions& operator=(const qpid::log::SinkOptions& rhs); + + // This allows the caller to indicate that there's no normal outputs + // available. For example, when running as a service. In these cases, the + // platform's "syslog"-type output should replace the default stderr + // unless some other sink has been selected. + virtual void detached(void); + + // The Logger acting on these options calls setup() to request any + // Sinks be set up and fed back to the logger. + virtual void setup(qpid::log::Logger *logger); + + bool logToStderr; + bool logToStdout; + bool logToEventLog; + std::string eventSource; + std::string logFile; +}; + +}}} // namespace qpid::log::windows + +#endif /*!QPID_LOG_WINDOWS_SINKOPTIONS_H*/ diff --git a/RC9/qpid/cpp/src/qpid/management/Args.h b/RC9/qpid/cpp/src/qpid/management/Args.h new file mode 100644 index 0000000000..da1fb033b9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/management/Args.h @@ -0,0 +1,44 @@ +#ifndef _Args_ +#define _Args_ + +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + + +namespace qpid { +namespace management { + +class Args +{ + public: + + virtual ~Args (void) = 0; + +}; + +inline Args::~Args (void) {} + +class ArgsNone : public Args +{ +}; + +}} + + +#endif /*!_Args_*/ diff --git a/RC9/qpid/cpp/src/qpid/management/Manageable.cpp b/RC9/qpid/cpp/src/qpid/management/Manageable.cpp new file mode 100644 index 0000000000..08bb541fc7 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/management/Manageable.cpp @@ -0,0 +1,48 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include "Manageable.h" + +using namespace qpid::management; +using std::string; + +string Manageable::StatusText (status_t status, string text) +{ + if ((status & STATUS_USER) == STATUS_USER) + return text; + + switch (status) + { + case STATUS_OK : return "OK"; + case STATUS_UNKNOWN_OBJECT : return "UnknownObject"; + case STATUS_UNKNOWN_METHOD : return "UnknownMethod"; + case STATUS_NOT_IMPLEMENTED : return "NotImplemented"; + case STATUS_INVALID_PARAMETER : return "InvalidParameter"; + case STATUS_FEATURE_NOT_IMPLEMENTED : return "FeatureNotImplemented"; + case STATUS_FORBIDDEN : return "Forbidden"; + } + + return "??"; +} + +Manageable::status_t Manageable::ManagementMethod (uint32_t, Args&, std::string&) +{ + return STATUS_UNKNOWN_METHOD; +} + diff --git a/RC9/qpid/cpp/src/qpid/management/Manageable.h b/RC9/qpid/cpp/src/qpid/management/Manageable.h new file mode 100644 index 0000000000..b4d80d8fad --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/management/Manageable.h @@ -0,0 +1,71 @@ +#ifndef _Manageable_ +#define _Manageable_ + +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include "ManagementObject.h" +#include "Args.h" +#include <string> + +namespace qpid { +namespace management { + +class Manageable +{ + public: + + virtual ~Manageable(void) = 0; + + // status_t is a type used to pass completion status from the method handler. + // + typedef uint32_t status_t; + static std::string StatusText(status_t status, std::string text = std::string()); + + static const status_t STATUS_OK = 0; + static const status_t STATUS_UNKNOWN_OBJECT = 1; + static const status_t STATUS_UNKNOWN_METHOD = 2; + static const status_t STATUS_NOT_IMPLEMENTED = 3; + static const status_t STATUS_INVALID_PARAMETER = 4; + static const status_t STATUS_FEATURE_NOT_IMPLEMENTED = 5; + static const status_t STATUS_FORBIDDEN = 6; + static const status_t STATUS_EXCEPTION = 7; + static const status_t STATUS_USER = 0x00010000; + + // Every "Manageable" object must hold a reference to exactly one + // management object. This object is always of a class derived from + // the pure-virtual "ManagementObject". + // + // This accessor function returns a pointer to the management object. + // + virtual ManagementObject* GetManagementObject(void) const = 0; + + // Every "Manageable" object must implement ManagementMethod. This + // function is called when a remote management client invokes a method + // on this object. The input and output arguments are specific to the + // method being called and must be down-cast to the appropriate sub class + // before use. + virtual status_t ManagementMethod(uint32_t methodId, Args& args, std::string& text); +}; + +inline Manageable::~Manageable(void) {} + +}} + +#endif /*!_Manageable_*/ diff --git a/RC9/qpid/cpp/src/qpid/management/ManagementBroker.cpp b/RC9/qpid/cpp/src/qpid/management/ManagementBroker.cpp new file mode 100644 index 0000000000..cc7a2dc4f3 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/management/ManagementBroker.cpp @@ -0,0 +1,1138 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ManagementBroker.h" +#include "qpid/broker/DeliverableMessage.h" +#include "qpid/log/Statement.h" +#include <qpid/broker/Message.h> +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/sys/Time.h" +#include "qpid/broker/ConnectionState.h" +#include "qpid/broker/AclModule.h" +#include <list> +#include <iostream> +#include <fstream> + +using boost::intrusive_ptr; +using qpid::framing::Uuid; +using namespace qpid::framing; +using namespace qpid::management; +using namespace qpid::broker; +using namespace qpid::sys; +using namespace std; +namespace _qmf = qmf::org::apache::qpid::broker; + +Mutex ManagementAgent::Singleton::lock; +bool ManagementAgent::Singleton::disabled = false; +ManagementAgent* ManagementAgent::Singleton::agent = 0; +int ManagementAgent::Singleton::refCount = 0; + +ManagementAgent::Singleton::Singleton(bool disableManagement) +{ + Mutex::ScopedLock _lock(lock); + if (disableManagement && !disabled) { + disabled = true; + assert(refCount == 0); // can't disable after agent has been allocated + } + if (refCount == 0 && !disabled) + agent = new ManagementBroker(); + refCount++; +} + +ManagementAgent::Singleton::~Singleton() +{ + Mutex::ScopedLock _lock(lock); + refCount--; + if (refCount == 0 && !disabled) { + delete agent; + agent = 0; + } +} + +ManagementAgent* ManagementAgent::Singleton::getInstance() +{ + return agent; +} + +ManagementBroker::RemoteAgent::~RemoteAgent () +{ + if (mgmtObject != 0) + mgmtObject->resourceDestroy(); +} + +ManagementBroker::ManagementBroker () : + threadPoolSize(1), interval(10), broker(0) +{ + nextObjectId = 1; + brokerBank = 1; + bootSequence = 1; + nextRemoteBank = 10; + nextRequestSequence = 1; + clientWasAdded = false; +} + +ManagementBroker::~ManagementBroker () +{ + timer.stop(); + { + Mutex::ScopedLock lock (userLock); + + // Reset the shared pointers to exchanges. If this is not done now, the exchanges + // will stick around until dExchange and mExchange are implicitely destroyed (long + // after this destructor completes). Those exchanges hold references to management + // objects that will be invalid. + dExchange.reset(); + mExchange.reset(); + + moveNewObjectsLH(); + for (ManagementObjectMap::iterator iter = managementObjects.begin (); + iter != managementObjects.end (); + iter++) { + ManagementObject* object = iter->second; + delete object; + } + managementObjects.clear(); + } +} + +void ManagementBroker::configure(const string& _dataDir, uint16_t _interval, + qpid::broker::Broker* _broker, int _threads) +{ + dataDir = _dataDir; + interval = _interval; + broker = _broker; + threadPoolSize = _threads; + timer.add (intrusive_ptr<TimerTask> (new Periodic(*this, interval))); + + // Get from file or generate and save to file. + if (dataDir.empty()) + { + uuid.generate(); + QPID_LOG (info, "ManagementBroker has no data directory, generated new broker ID: " + << uuid); + } + else + { + string filename(dataDir + "/.mbrokerdata"); + ifstream inFile(filename.c_str ()); + + if (inFile.good()) + { + inFile >> uuid; + inFile >> bootSequence; + inFile >> nextRemoteBank; + inFile.close(); + QPID_LOG (debug, "ManagementBroker restored broker ID: " << uuid); + + // if sequence goes beyond a 12-bit field, skip zero and wrap to 1. + bootSequence++; + if (bootSequence & 0xF000) + bootSequence = 1; + writeData(); + } + else + { + uuid.generate(); + QPID_LOG (info, "ManagementBroker generated broker ID: " << uuid); + writeData(); + } + + QPID_LOG (debug, "ManagementBroker boot sequence: " << bootSequence); + } +} + +void ManagementBroker::writeData () +{ + string filename (dataDir + "/.mbrokerdata"); + ofstream outFile (filename.c_str ()); + + if (outFile.good()) + { + outFile << uuid << " " << bootSequence << " " << nextRemoteBank << endl; + outFile.close(); + } +} + +void ManagementBroker::setExchange (qpid::broker::Exchange::shared_ptr _mexchange, + qpid::broker::Exchange::shared_ptr _dexchange) +{ + mExchange = _mexchange; + dExchange = _dexchange; +} + +void ManagementBroker::registerClass (const string& packageName, + const string& className, + uint8_t* md5Sum, + ManagementObject::writeSchemaCall_t schemaCall) +{ + Mutex::ScopedLock lock(userLock); + PackageMap::iterator pIter = findOrAddPackageLH(packageName); + addClassLH(ManagementItem::CLASS_KIND_TABLE, pIter, className, md5Sum, schemaCall); +} + +void ManagementBroker::registerEvent (const string& packageName, + const string& eventName, + uint8_t* md5Sum, + ManagementObject::writeSchemaCall_t schemaCall) +{ + Mutex::ScopedLock lock(userLock); + PackageMap::iterator pIter = findOrAddPackageLH(packageName); + addClassLH(ManagementItem::CLASS_KIND_EVENT, pIter, eventName, md5Sum, schemaCall); +} + +ObjectId ManagementBroker::addObject (ManagementObject* object, + uint64_t persistId) +{ + Mutex::ScopedLock lock (addLock); + uint16_t sequence; + uint64_t objectNum; + + if (persistId == 0) { + sequence = bootSequence; + objectNum = nextObjectId++; + } else { + sequence = 0; + objectNum = persistId; + } + + ObjectId objId(0 /*flags*/ , sequence, brokerBank, 0, objectNum); + + object->setObjectId(objId); + newManagementObjects[objId] = object; + return objId; +} + +void ManagementBroker::raiseEvent(const ManagementEvent& event, severity_t severity) +{ + Mutex::ScopedLock lock (userLock); + Buffer outBuffer(eventBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + uint8_t sev = (severity == SEV_DEFAULT) ? event.getSeverity() : (uint8_t) severity; + + encodeHeader(outBuffer, 'e'); + outBuffer.putShortString(event.getPackageName()); + outBuffer.putShortString(event.getEventName()); + outBuffer.putBin128(event.getMd5Sum()); + outBuffer.putLongLong(uint64_t(Duration(now()))); + outBuffer.putOctet(sev); + event.encode(outBuffer); + outLen = MA_BUFFER_SIZE - outBuffer.available(); + outBuffer.reset(); + sendBuffer(outBuffer, outLen, mExchange, + "console.event.1.0." + event.getPackageName() + "." + event.getEventName()); +} + +ManagementBroker::Periodic::Periodic (ManagementBroker& _broker, uint32_t _seconds) + : TimerTask (qpid::sys::Duration ((_seconds ? _seconds : 1) * qpid::sys::TIME_SEC)), broker(_broker) {} + +ManagementBroker::Periodic::~Periodic () {} + +void ManagementBroker::Periodic::fire () +{ + broker.timer.add (intrusive_ptr<TimerTask> (new Periodic (broker, broker.interval))); + broker.periodicProcessing (); +} + +void ManagementBroker::clientAdded (const std::string& routingKey) +{ + if (routingKey.find("console") != 0) + return; + + clientWasAdded = true; + for (RemoteAgentMap::iterator aIter = remoteAgents.begin(); + aIter != remoteAgents.end(); + aIter++) { + char localBuffer[16]; + Buffer outBuffer(localBuffer, 16); + uint32_t outLen; + + encodeHeader(outBuffer, 'x'); + outLen = outBuffer.getPosition(); + outBuffer.reset(); + sendBuffer(outBuffer, outLen, dExchange, aIter->second->routingKey); + } +} + +void ManagementBroker::encodeHeader (Buffer& buf, uint8_t opcode, uint32_t seq) +{ + buf.putOctet ('A'); + buf.putOctet ('M'); + buf.putOctet ('2'); + buf.putOctet (opcode); + buf.putLong (seq); +} + +bool ManagementBroker::checkHeader (Buffer& buf, uint8_t *opcode, uint32_t *seq) +{ + uint8_t h1 = buf.getOctet(); + uint8_t h2 = buf.getOctet(); + uint8_t h3 = buf.getOctet(); + + *opcode = buf.getOctet(); + *seq = buf.getLong(); + + return h1 == 'A' && h2 == 'M' && h3 == '2'; +} + +void ManagementBroker::sendBuffer(Buffer& buf, + uint32_t length, + qpid::broker::Exchange::shared_ptr exchange, + string routingKey) +{ + if (exchange.get() == 0) + return; + + intrusive_ptr<Message> msg(new Message()); + AMQFrame method(in_place<MessageTransferBody>( + ProtocolVersion(), exchange->getName (), 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + AMQFrame content(in_place<AMQContentBody>()); + + content.castBody<AMQContentBody>()->decode(buf, length); + + method.setEof(false); + header.setBof(false); + header.setEof(false); + content.setBof(false); + + msg->getFrames().append(method); + msg->getFrames().append(header); + + MessageProperties* props = + msg->getFrames().getHeaders()->get<MessageProperties>(true); + props->setContentLength(length); + msg->getFrames().append(content); + + DeliverableMessage deliverable (msg); + try { + exchange->route(deliverable, routingKey, 0); + } catch(exception&) {} +} + +void ManagementBroker::moveNewObjectsLH() +{ + Mutex::ScopedLock lock (addLock); + for (ManagementObjectMap::iterator iter = newManagementObjects.begin (); + iter != newManagementObjects.end (); + iter++) + managementObjects[iter->first] = iter->second; + newManagementObjects.clear(); +} + +void ManagementBroker::periodicProcessing (void) +{ +#define BUFSIZE 65536 + Mutex::ScopedLock lock (userLock); + char msgChars[BUFSIZE]; + uint32_t contentSize; + string routingKey; + list<pair<ObjectId, ManagementObject*> > deleteList; + + moveNewObjectsLH(); + + if (clientWasAdded) { + clientWasAdded = false; + for (ManagementObjectMap::iterator iter = managementObjects.begin (); + iter != managementObjects.end (); + iter++) { + ManagementObject* object = iter->second; + object->setForcePublish(true); + } + } + + for (ManagementObjectMap::iterator iter = managementObjects.begin (); + iter != managementObjects.end (); + iter++) { + ManagementObject* object = iter->second; + + if (object->getConfigChanged() || object->getInstChanged()) + object->setUpdateTime(); + + if (object->getConfigChanged() || object->getForcePublish() || object->isDeleted()) { + Buffer msgBuffer (msgChars, BUFSIZE); + encodeHeader (msgBuffer, 'c'); + object->writeProperties(msgBuffer); + + contentSize = BUFSIZE - msgBuffer.available (); + msgBuffer.reset (); + routingKey = "console.obj.1.0." + object->getPackageName() + "." + object->getClassName(); + sendBuffer (msgBuffer, contentSize, mExchange, routingKey); + } + + if (object->hasInst() && (object->getInstChanged() || object->getForcePublish())) { + Buffer msgBuffer (msgChars, BUFSIZE); + encodeHeader (msgBuffer, 'i'); + object->writeStatistics(msgBuffer); + + contentSize = BUFSIZE - msgBuffer.available (); + msgBuffer.reset (); + routingKey = "console.obj.1.0." + object->getPackageName() + "." + object->getClassName(); + sendBuffer (msgBuffer, contentSize, mExchange, routingKey); + } + + if (object->isDeleted()) + deleteList.push_back(pair<ObjectId, ManagementObject*>(iter->first, object)); + object->setForcePublish(false); + } + + // Delete flagged objects + for (list<pair<ObjectId, ManagementObject*> >::reverse_iterator iter = deleteList.rbegin(); + iter != deleteList.rend(); + iter++) { + delete iter->second; + managementObjects.erase(iter->first); + } + + if (!deleteList.empty()) { + deleteList.clear(); + deleteOrphanedAgentsLH(); + } + + { + Buffer msgBuffer(msgChars, BUFSIZE); + encodeHeader(msgBuffer, 'h'); + msgBuffer.putLongLong(uint64_t(Duration(now()))); + + contentSize = BUFSIZE - msgBuffer.available (); + msgBuffer.reset (); + routingKey = "console.heartbeat.1.0"; + sendBuffer (msgBuffer, contentSize, mExchange, routingKey); + } +} + +void ManagementBroker::sendCommandComplete (string replyToKey, uint32_t sequence, + uint32_t code, string text) +{ + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader (outBuffer, 'z', sequence); + outBuffer.putLong (code); + outBuffer.putShortString (text); + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + sendBuffer (outBuffer, outLen, dExchange, replyToKey); +} + +bool ManagementBroker::dispatchCommand (Deliverable& deliverable, + const string& routingKey, + const FieldTable* /*args*/) +{ + Mutex::ScopedLock lock (userLock); + Message& msg = ((DeliverableMessage&) deliverable).getMessage (); + + // Parse the routing key. This management broker should act as though it + // is bound to the exchange to match the following keys: + // + // agent.1.0.# + // broker + // schema.# + + if (routingKey == "broker") { + dispatchAgentCommandLH(msg); + return false; + } + + else if (routingKey.compare(0, 9, "agent.1.0") == 0) { + dispatchAgentCommandLH(msg); + return false; + } + + else if (routingKey.compare(0, 8, "agent.1.") == 0) { + return authorizeAgentMessageLH(msg); + } + + else if (routingKey.compare(0, 7, "schema.") == 0) { + dispatchAgentCommandLH(msg); + return true; + } + + return true; +} + +void ManagementBroker::handleMethodRequestLH (Buffer& inBuffer, string replyToKey, + uint32_t sequence, const ConnectionToken* connToken) +{ + string methodName; + string packageName; + string className; + uint8_t hash[16]; + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + AclModule* acl = broker->getAcl(); + + ObjectId objId(inBuffer); + inBuffer.getShortString(packageName); + inBuffer.getShortString(className); + inBuffer.getBin128(hash); + inBuffer.getShortString(methodName); + encodeHeader(outBuffer, 'm', sequence); + + if (acl != 0) { + string userId = ((const qpid::broker::ConnectionState*) connToken)->getUserId(); + map<acl::Property, string> params; + params[acl::PROP_SCHEMAPACKAGE] = packageName; + params[acl::PROP_SCHEMACLASS] = className; + + if (!acl->authorise(userId, acl::ACT_ACCESS, acl::OBJ_METHOD, methodName, ¶ms)) { + outBuffer.putLong(Manageable::STATUS_FORBIDDEN); + outBuffer.putMediumString(Manageable::StatusText(Manageable::STATUS_FORBIDDEN)); + outLen = MA_BUFFER_SIZE - outBuffer.available(); + outBuffer.reset(); + sendBuffer(outBuffer, outLen, dExchange, replyToKey); + return; + } + } + + ManagementObjectMap::iterator iter = managementObjects.find(objId); + if (iter == managementObjects.end() || iter->second->isDeleted()) { + outBuffer.putLong (Manageable::STATUS_UNKNOWN_OBJECT); + outBuffer.putMediumString(Manageable::StatusText (Manageable::STATUS_UNKNOWN_OBJECT)); + } else { + if ((iter->second->getPackageName() != packageName) || + (iter->second->getClassName() != className)) { + outBuffer.putLong (Manageable::STATUS_INVALID_PARAMETER); + outBuffer.putMediumString(Manageable::StatusText (Manageable::STATUS_INVALID_PARAMETER)); + } + else + try { + outBuffer.record(); + iter->second->doMethod(methodName, inBuffer, outBuffer); + } catch(exception& e) { + outBuffer.restore(); + outBuffer.putLong(Manageable::STATUS_EXCEPTION); + outBuffer.putMediumString(e.what()); + } + } + + outLen = MA_BUFFER_SIZE - outBuffer.available(); + outBuffer.reset(); + sendBuffer(outBuffer, outLen, dExchange, replyToKey); +} + +void ManagementBroker::handleBrokerRequestLH (Buffer&, string replyToKey, uint32_t sequence) +{ + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader (outBuffer, 'b', sequence); + uuid.encode (outBuffer); + + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + sendBuffer (outBuffer, outLen, dExchange, replyToKey); +} + +void ManagementBroker::handlePackageQueryLH (Buffer&, string replyToKey, uint32_t sequence) +{ + for (PackageMap::iterator pIter = packages.begin (); + pIter != packages.end (); + pIter++) + { + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader (outBuffer, 'p', sequence); + encodePackageIndication (outBuffer, pIter); + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + sendBuffer (outBuffer, outLen, dExchange, replyToKey); + } + + sendCommandComplete (replyToKey, sequence); +} + +void ManagementBroker::handlePackageIndLH (Buffer& inBuffer, string /*replyToKey*/, uint32_t /*sequence*/) +{ + string packageName; + + inBuffer.getShortString(packageName); + findOrAddPackageLH(packageName); +} + +void ManagementBroker::handleClassQueryLH(Buffer& inBuffer, string replyToKey, uint32_t sequence) +{ + string packageName; + + inBuffer.getShortString(packageName); + PackageMap::iterator pIter = packages.find(packageName); + if (pIter != packages.end()) + { + ClassMap cMap = pIter->second; + for (ClassMap::iterator cIter = cMap.begin(); + cIter != cMap.end(); + cIter++) + { + if (cIter->second.hasSchema()) + { + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader(outBuffer, 'q', sequence); + encodeClassIndication(outBuffer, pIter, cIter); + outLen = MA_BUFFER_SIZE - outBuffer.available(); + outBuffer.reset(); + sendBuffer(outBuffer, outLen, dExchange, replyToKey); + } + } + } + sendCommandComplete(replyToKey, sequence); +} + +void ManagementBroker::handleClassIndLH (Buffer& inBuffer, string replyToKey, uint32_t) +{ + string packageName; + SchemaClassKey key; + + uint8_t kind = inBuffer.getOctet(); + inBuffer.getShortString(packageName); + inBuffer.getShortString(key.name); + inBuffer.getBin128(key.hash); + + PackageMap::iterator pIter = findOrAddPackageLH(packageName); + ClassMap::iterator cIter = pIter->second.find(key); + if (cIter == pIter->second.end() || !cIter->second.hasSchema()) { + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + uint32_t sequence = nextRequestSequence++; + + encodeHeader (outBuffer, 'S', sequence); + outBuffer.putShortString(packageName); + outBuffer.putShortString(key.name); + outBuffer.putBin128(key.hash); + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + sendBuffer (outBuffer, outLen, dExchange, replyToKey); + + if (cIter != pIter->second.end()) + pIter->second.erase(key); + + pIter->second.insert(pair<SchemaClassKey, SchemaClass>(key, SchemaClass(kind, sequence))); + } +} + +void ManagementBroker::SchemaClass::appendSchema(Buffer& buf) +{ + // If the management package is attached locally (embedded in the broker or + // linked in via plug-in), call the schema handler directly. If the package + // is from a remote management agent, send the stored schema information. + + if (writeSchemaCall != 0) + writeSchemaCall(buf); + else + buf.putRawData(buffer, bufferLen); +} + +void ManagementBroker::handleSchemaRequestLH(Buffer& inBuffer, string replyToKey, uint32_t sequence) +{ + string packageName; + SchemaClassKey key; + + inBuffer.getShortString (packageName); + inBuffer.getShortString (key.name); + inBuffer.getBin128 (key.hash); + + PackageMap::iterator pIter = packages.find(packageName); + if (pIter != packages.end()) { + ClassMap& cMap = pIter->second; + ClassMap::iterator cIter = cMap.find(key); + if (cIter != cMap.end()) { + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + SchemaClass& classInfo = cIter->second; + + if (classInfo.hasSchema()) { + encodeHeader(outBuffer, 's', sequence); + classInfo.appendSchema(outBuffer); + outLen = MA_BUFFER_SIZE - outBuffer.available(); + outBuffer.reset(); + sendBuffer(outBuffer, outLen, dExchange, replyToKey); + } + else + sendCommandComplete(replyToKey, sequence, 1, "Schema not available"); + } + else + sendCommandComplete(replyToKey, sequence, 1, "Class key not found"); + } + else + sendCommandComplete(replyToKey, sequence, 1, "Package not found"); +} + +void ManagementBroker::handleSchemaResponseLH(Buffer& inBuffer, string /*replyToKey*/, uint32_t sequence) +{ + string packageName; + SchemaClassKey key; + + inBuffer.record(); + inBuffer.getOctet(); + inBuffer.getShortString(packageName); + inBuffer.getShortString(key.name); + inBuffer.getBin128(key.hash); + inBuffer.restore(); + + PackageMap::iterator pIter = packages.find(packageName); + if (pIter != packages.end()) { + ClassMap& cMap = pIter->second; + ClassMap::iterator cIter = cMap.find(key); + if (cIter != cMap.end() && cIter->second.pendingSequence == sequence) { + size_t length = validateSchema(inBuffer, cIter->second.kind); + if (length == 0) { + QPID_LOG(warning, "Management Broker received invalid schema response: " << packageName << "." << key.name); + cMap.erase(key); + } else { + cIter->second.buffer = (uint8_t*) malloc(length); + cIter->second.bufferLen = length; + inBuffer.getRawData(cIter->second.buffer, cIter->second.bufferLen); + + // Publish a class-indication message + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader(outBuffer, 'q'); + encodeClassIndication(outBuffer, pIter, cIter); + outLen = MA_BUFFER_SIZE - outBuffer.available(); + outBuffer.reset(); + sendBuffer(outBuffer, outLen, mExchange, "schema.class"); + } + } + } +} + +bool ManagementBroker::bankInUse (uint32_t bank) +{ + for (RemoteAgentMap::iterator aIter = remoteAgents.begin(); + aIter != remoteAgents.end(); + aIter++) + if (aIter->second->agentBank == bank) + return true; + return false; +} + +uint32_t ManagementBroker::allocateNewBank () +{ + while (bankInUse (nextRemoteBank)) + nextRemoteBank++; + + uint32_t allocated = nextRemoteBank++; + writeData (); + return allocated; +} + +uint32_t ManagementBroker::assignBankLH (uint32_t requestedBank) +{ + if (requestedBank == 0 || bankInUse (requestedBank)) + return allocateNewBank (); + return requestedBank; +} + +void ManagementBroker::deleteOrphanedAgentsLH() +{ + vector<ObjectId> deleteList; + + for (RemoteAgentMap::iterator aIter = remoteAgents.begin(); aIter != remoteAgents.end(); aIter++) { + ObjectId connectionRef = aIter->first; + bool found = false; + + for (ManagementObjectMap::iterator iter = managementObjects.begin(); + iter != managementObjects.end(); + iter++) { + if (iter->first == connectionRef && !iter->second->isDeleted()) { + found = true; + break; + } + } + + if (!found) { + deleteList.push_back(connectionRef); + delete aIter->second; + } + } + + for (vector<ObjectId>::iterator dIter = deleteList.begin(); dIter != deleteList.end(); dIter++) + remoteAgents.erase(*dIter); + + deleteList.clear(); +} + +void ManagementBroker::handleAttachRequestLH (Buffer& inBuffer, string replyToKey, uint32_t sequence, const ConnectionToken* connToken) +{ + string label; + uint32_t requestedBrokerBank, requestedAgentBank; + uint32_t assignedBank; + ObjectId connectionRef = ((const ConnectionState*) connToken)->GetManagementObject()->getObjectId(); + Uuid systemId; + + moveNewObjectsLH(); + deleteOrphanedAgentsLH(); + RemoteAgentMap::iterator aIter = remoteAgents.find(connectionRef); + if (aIter != remoteAgents.end()) { + // There already exists an agent on this session. Reject the request. + sendCommandComplete(replyToKey, sequence, 1, "Connection already has remote agent"); + return; + } + + inBuffer.getShortString(label); + systemId.decode(inBuffer); + requestedBrokerBank = inBuffer.getLong(); + requestedAgentBank = inBuffer.getLong(); + assignedBank = assignBankLH(requestedAgentBank); + + RemoteAgent* agent = new RemoteAgent; + agent->brokerBank = brokerBank; + agent->agentBank = assignedBank; + agent->routingKey = replyToKey; + agent->connectionRef = connectionRef; + agent->mgmtObject = new _qmf::Agent (this, agent); + agent->mgmtObject->set_connectionRef(agent->connectionRef); + agent->mgmtObject->set_label (label); + agent->mgmtObject->set_registeredTo (broker->GetManagementObject()->getObjectId()); + agent->mgmtObject->set_systemId (systemId); + agent->mgmtObject->set_brokerBank (brokerBank); + agent->mgmtObject->set_agentBank (assignedBank); + addObject (agent->mgmtObject); + + remoteAgents[connectionRef] = agent; + + // Send an Attach Response + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader (outBuffer, 'a', sequence); + outBuffer.putLong (brokerBank); + outBuffer.putLong (assignedBank); + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + sendBuffer (outBuffer, outLen, dExchange, replyToKey); +} + +void ManagementBroker::handleGetQueryLH (Buffer& inBuffer, string replyToKey, uint32_t sequence) +{ + FieldTable ft; + FieldTable::ValuePtr value; + + moveNewObjectsLH(); + + ft.decode(inBuffer); + value = ft.get("_class"); + if (value.get() == 0 || !value->convertsTo<string>()) { + value = ft.get("_objectid"); + if (value.get() == 0 || !value->convertsTo<string>()) + return; + + ObjectId selector(value->get<string>()); + ManagementObjectMap::iterator iter = managementObjects.find(selector); + if (iter != managementObjects.end()) { + ManagementObject* object = iter->second; + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader(outBuffer, 'g', sequence); + object->writeProperties(outBuffer); + object->writeStatistics(outBuffer, true); + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + sendBuffer(outBuffer, outLen, dExchange, replyToKey); + } + sendCommandComplete(replyToKey, sequence); + return; + } + + string className (value->get<string>()); + + for (ManagementObjectMap::iterator iter = managementObjects.begin(); + iter != managementObjects.end(); + iter++) { + ManagementObject* object = iter->second; + if (object->getClassName () == className) { + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader(outBuffer, 'g', sequence); + object->writeProperties(outBuffer); + object->writeStatistics(outBuffer, true); + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + sendBuffer(outBuffer, outLen, dExchange, replyToKey); + } + } + + sendCommandComplete(replyToKey, sequence); +} + +bool ManagementBroker::authorizeAgentMessageLH(Message& msg) +{ + Buffer inBuffer (inputBuffer, MA_BUFFER_SIZE); + uint8_t opcode; + uint32_t sequence; + string replyToKey; + + if (msg.encodedSize() > MA_BUFFER_SIZE) + return false; + + msg.encodeContent(inBuffer); + inBuffer.reset(); + + if (!checkHeader(inBuffer, &opcode, &sequence)) + return false; + + if (opcode == 'M') { + // TODO: check method call against ACL list. + AclModule* acl = broker->getAcl(); + if (acl == 0) + return true; + + string userId = ((const qpid::broker::ConnectionState*) msg.getPublisher())->getUserId(); + string packageName; + string className; + uint8_t hash[16]; + string methodName; + + map<acl::Property, string> params; + ObjectId objId(inBuffer); + inBuffer.getShortString(packageName); + inBuffer.getShortString(className); + inBuffer.getBin128(hash); + inBuffer.getShortString(methodName); + + params[acl::PROP_SCHEMAPACKAGE] = packageName; + params[acl::PROP_SCHEMACLASS] = className; + + if (acl->authorise(userId, acl::ACT_ACCESS, acl::OBJ_METHOD, methodName, ¶ms)) + return true; + + const framing::MessageProperties* p = + msg.getFrames().getHeaders()->get<framing::MessageProperties>(); + if (p && p->hasReplyTo()) { + const framing::ReplyTo& rt = p->getReplyTo(); + replyToKey = rt.getRoutingKey(); + + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader(outBuffer, 'm', sequence); + outBuffer.putLong(Manageable::STATUS_FORBIDDEN); + outBuffer.putMediumString(Manageable::StatusText(Manageable::STATUS_FORBIDDEN)); + outLen = MA_BUFFER_SIZE - outBuffer.available(); + outBuffer.reset(); + sendBuffer(outBuffer, outLen, dExchange, replyToKey); + } + + return false; + } + + return true; +} + +void ManagementBroker::dispatchAgentCommandLH(Message& msg) +{ + Buffer inBuffer(inputBuffer, MA_BUFFER_SIZE); + uint8_t opcode; + uint32_t sequence; + string replyToKey; + + const framing::MessageProperties* p = + msg.getFrames().getHeaders()->get<framing::MessageProperties>(); + if (p && p->hasReplyTo()) { + const framing::ReplyTo& rt = p->getReplyTo(); + replyToKey = rt.getRoutingKey(); + } + else + return; + + if (msg.encodedSize() > MA_BUFFER_SIZE) { + QPID_LOG(debug, "ManagementBroker::dispatchAgentCommandLH: Message too large: " << + msg.encodedSize()); + return; + } + + msg.encodeContent(inBuffer); + uint32_t bufferLen = inBuffer.getPosition(); + inBuffer.reset(); + + while (inBuffer.getPosition() < bufferLen) { + if (!checkHeader(inBuffer, &opcode, &sequence)) + return; + + if (opcode == 'B') handleBrokerRequestLH (inBuffer, replyToKey, sequence); + else if (opcode == 'P') handlePackageQueryLH (inBuffer, replyToKey, sequence); + else if (opcode == 'p') handlePackageIndLH (inBuffer, replyToKey, sequence); + else if (opcode == 'Q') handleClassQueryLH (inBuffer, replyToKey, sequence); + else if (opcode == 'q') handleClassIndLH (inBuffer, replyToKey, sequence); + else if (opcode == 'S') handleSchemaRequestLH (inBuffer, replyToKey, sequence); + else if (opcode == 's') handleSchemaResponseLH (inBuffer, replyToKey, sequence); + else if (opcode == 'A') handleAttachRequestLH (inBuffer, replyToKey, sequence, msg.getPublisher()); + else if (opcode == 'G') handleGetQueryLH (inBuffer, replyToKey, sequence); + else if (opcode == 'M') handleMethodRequestLH (inBuffer, replyToKey, sequence, msg.getPublisher()); + } +} + +ManagementBroker::PackageMap::iterator ManagementBroker::findOrAddPackageLH(string name) +{ + PackageMap::iterator pIter = packages.find (name); + if (pIter != packages.end ()) + return pIter; + + // No such package found, create a new map entry. + pair<PackageMap::iterator, bool> result = + packages.insert(pair<string, ClassMap>(name, ClassMap())); + QPID_LOG (debug, "ManagementBroker added package " << name); + + // Publish a package-indication message + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + encodeHeader (outBuffer, 'p'); + encodePackageIndication (outBuffer, result.first); + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + sendBuffer (outBuffer, outLen, mExchange, "schema.package"); + + return result.first; +} + +void ManagementBroker::addClassLH(uint8_t kind, + PackageMap::iterator pIter, + const string& className, + uint8_t* md5Sum, + ManagementObject::writeSchemaCall_t schemaCall) +{ + SchemaClassKey key; + ClassMap& cMap = pIter->second; + + key.name = className; + memcpy(&key.hash, md5Sum, 16); + + ClassMap::iterator cIter = cMap.find(key); + if (cIter != cMap.end()) + return; + + // No such class found, create a new class with local information. + QPID_LOG (debug, "ManagementBroker added class " << pIter->first << ":" << + key.name); + + cMap.insert(pair<SchemaClassKey, SchemaClass>(key, SchemaClass(kind, schemaCall))); + cIter = cMap.find(key); +} + +void ManagementBroker::encodePackageIndication(Buffer& buf, + PackageMap::iterator pIter) +{ + buf.putShortString((*pIter).first); +} + +void ManagementBroker::encodeClassIndication(Buffer& buf, + PackageMap::iterator pIter, + ClassMap::iterator cIter) +{ + SchemaClassKey key = (*cIter).first; + + buf.putOctet((*cIter).second.kind); + buf.putShortString((*pIter).first); + buf.putShortString(key.name); + buf.putBin128(key.hash); +} + +size_t ManagementBroker::validateSchema(Buffer& inBuffer, uint8_t kind) +{ + if (kind == ManagementItem::CLASS_KIND_TABLE) + return validateTableSchema(inBuffer); + else if (kind == ManagementItem::CLASS_KIND_EVENT) + return validateEventSchema(inBuffer); + return 0; +} + +size_t ManagementBroker::validateTableSchema(Buffer& inBuffer) +{ + uint32_t start = inBuffer.getPosition(); + uint32_t end; + string text; + uint8_t hash[16]; + + try { + inBuffer.record(); + uint8_t kind = inBuffer.getOctet(); + if (kind != ManagementItem::CLASS_KIND_TABLE) + return 0; + + inBuffer.getShortString(text); + inBuffer.getShortString(text); + inBuffer.getBin128(hash); + + uint16_t propCount = inBuffer.getShort(); + uint16_t statCount = inBuffer.getShort(); + uint16_t methCount = inBuffer.getShort(); + + for (uint16_t idx = 0; idx < propCount + statCount; idx++) { + FieldTable ft; + ft.decode(inBuffer); + } + + for (uint16_t idx = 0; idx < methCount; idx++) { + FieldTable ft; + ft.decode(inBuffer); + if (!ft.isSet("argCount")) + return 0; + int argCount = ft.getAsInt("argCount"); + for (int mIdx = 0; mIdx < argCount; mIdx++) { + FieldTable aft; + aft.decode(inBuffer); + } + } + } catch (exception& /*e*/) { + return 0; + } + + end = inBuffer.getPosition(); + inBuffer.restore(); // restore original position + return end - start; +} + +size_t ManagementBroker::validateEventSchema(Buffer& inBuffer) +{ + uint32_t start = inBuffer.getPosition(); + uint32_t end; + string text; + uint8_t hash[16]; + + try { + inBuffer.record(); + uint8_t kind = inBuffer.getOctet(); + if (kind != ManagementItem::CLASS_KIND_EVENT) + return 0; + + inBuffer.getShortString(text); + inBuffer.getShortString(text); + inBuffer.getBin128(hash); + + uint16_t argCount = inBuffer.getShort(); + + for (uint16_t idx = 0; idx < argCount; idx++) { + FieldTable ft; + ft.decode(inBuffer); + } + } catch (exception& /*e*/) { + return 0; + } + + end = inBuffer.getPosition(); + inBuffer.restore(); // restore original position + return end - start; +} diff --git a/RC9/qpid/cpp/src/qpid/management/ManagementBroker.h b/RC9/qpid/cpp/src/qpid/management/ManagementBroker.h new file mode 100644 index 0000000000..59dfb98596 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/management/ManagementBroker.h @@ -0,0 +1,235 @@ +#ifndef _ManagementBroker_ +#define _ManagementBroker_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/Options.h" +#include "qpid/broker/Exchange.h" +#include "qpid/broker/Timer.h" +#include "qpid/framing/Uuid.h" +#include "qpid/sys/Mutex.h" +#include "qpid/broker/ConnectionToken.h" +#include "qpid/agent/ManagementAgent.h" +#include "ManagementObject.h" +#include "Manageable.h" +#include "qmf/org/apache/qpid/broker/Agent.h" +#include <qpid/framing/AMQFrame.h> + +namespace qpid { +namespace management { + +class ManagementBroker : public ManagementAgent +{ +private: + + int threadPoolSize; + +public: + + ManagementBroker (); + virtual ~ManagementBroker (); + + void configure (const std::string& dataDir, uint16_t interval, + qpid::broker::Broker* broker, int threadPoolSize); + void setInterval (uint16_t _interval) { interval = _interval; } + void setExchange (qpid::broker::Exchange::shared_ptr mgmtExchange, + qpid::broker::Exchange::shared_ptr directExchange); + int getMaxThreads () { return threadPoolSize; } + void registerClass (const std::string& packageName, + const std::string& className, + uint8_t* md5Sum, + ManagementObject::writeSchemaCall_t schemaCall); + void registerEvent (const std::string& packageName, + const std::string& eventName, + uint8_t* md5Sum, + ManagementObject::writeSchemaCall_t schemaCall); + ObjectId addObject (ManagementObject* object, + uint64_t persistId = 0); + void raiseEvent(const ManagementEvent& event, severity_t severity = SEV_DEFAULT); + void clientAdded (const std::string& routingKey); + bool dispatchCommand (qpid::broker::Deliverable& msg, + const std::string& routingKey, + const framing::FieldTable* args); + const framing::Uuid& getUuid() const { return uuid; } + + // Stubs for remote management agent calls + void init(const std::string&, uint16_t, uint16_t, bool, + const std::string&, const std::string&, const std::string&, + const std::string&, const std::string&) { assert(0); } + void init(const client::ConnectionSettings&, uint16_t, bool, const std::string&) { assert(0); } + uint32_t pollCallbacks (uint32_t) { assert(0); return 0; } + int getSignalFd () { assert(0); return -1; } + +private: + friend class ManagementAgent; + + struct Periodic : public qpid::broker::TimerTask + { + ManagementBroker& broker; + + Periodic (ManagementBroker& broker, uint32_t seconds); + virtual ~Periodic (); + void fire (); + }; + + // Storage for tracking remote management agents, attached via the client + // management agent API. + // + struct RemoteAgent : public Manageable + { + uint32_t brokerBank; + uint32_t agentBank; + std::string routingKey; + ObjectId connectionRef; + qmf::org::apache::qpid::broker::Agent* mgmtObject; + ManagementObject* GetManagementObject (void) const { return mgmtObject; } + virtual ~RemoteAgent (); + }; + + // TODO: Eventually replace string with entire reply-to structure. reply-to + // currently assumes that the exchange is "amq.direct" even though it could + // in theory be specified differently. + typedef std::map<ObjectId, RemoteAgent*> RemoteAgentMap; + typedef std::vector<std::string> ReplyToVector; + + // Storage for known schema classes: + // + // SchemaClassKey -- Key elements for map lookups + // SchemaClassKeyComp -- Comparison class for SchemaClassKey + // SchemaClass -- Non-key elements for classes + // + struct SchemaClassKey + { + std::string name; + uint8_t hash[16]; + }; + + struct SchemaClassKeyComp + { + bool operator() (const SchemaClassKey& lhs, const SchemaClassKey& rhs) const + { + if (lhs.name != rhs.name) + return lhs.name < rhs.name; + else + for (int i = 0; i < 16; i++) + if (lhs.hash[i] != rhs.hash[i]) + return lhs.hash[i] < rhs.hash[i]; + return false; + } + }; + + struct SchemaClass + { + uint8_t kind; + ManagementObject::writeSchemaCall_t writeSchemaCall; + uint32_t pendingSequence; + size_t bufferLen; + uint8_t* buffer; + + SchemaClass(uint8_t _kind, uint32_t seq) : + kind(_kind), writeSchemaCall(0), pendingSequence(seq), bufferLen(0), buffer(0) {} + SchemaClass(uint8_t _kind, ManagementObject::writeSchemaCall_t call) : + kind(_kind), writeSchemaCall(call), pendingSequence(0), bufferLen(0), buffer(0) {} + bool hasSchema () { return (writeSchemaCall != 0) || (buffer != 0); } + void appendSchema (framing::Buffer& buf); + }; + + typedef std::map<SchemaClassKey, SchemaClass, SchemaClassKeyComp> ClassMap; + typedef std::map<std::string, ClassMap> PackageMap; + + RemoteAgentMap remoteAgents; + PackageMap packages; + ManagementObjectMap managementObjects; + ManagementObjectMap newManagementObjects; + + static ManagementAgent* agent; + static bool enabled; + + framing::Uuid uuid; + sys::Mutex addLock; + sys::Mutex userLock; + qpid::broker::Timer timer; + qpid::broker::Exchange::shared_ptr mExchange; + qpid::broker::Exchange::shared_ptr dExchange; + std::string dataDir; + uint16_t interval; + qpid::broker::Broker* broker; + uint16_t bootSequence; + uint32_t nextObjectId; + uint32_t brokerBank; + uint32_t nextRemoteBank; + uint32_t nextRequestSequence; + bool clientWasAdded; + +# define MA_BUFFER_SIZE 65536 + char inputBuffer[MA_BUFFER_SIZE]; + char outputBuffer[MA_BUFFER_SIZE]; + char eventBuffer[MA_BUFFER_SIZE]; + + void writeData (); + void periodicProcessing (void); + void encodeHeader (framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0); + bool checkHeader (framing::Buffer& buf, uint8_t *opcode, uint32_t *seq); + void sendBuffer (framing::Buffer& buf, + uint32_t length, + qpid::broker::Exchange::shared_ptr exchange, + std::string routingKey); + void moveNewObjectsLH(); + + bool authorizeAgentMessageLH(qpid::broker::Message& msg); + void dispatchAgentCommandLH(qpid::broker::Message& msg); + + PackageMap::iterator findOrAddPackageLH(std::string name); + void addClassLH(uint8_t kind, + PackageMap::iterator pIter, + const std::string& className, + uint8_t* md5Sum, + ManagementObject::writeSchemaCall_t schemaCall); + void encodePackageIndication (framing::Buffer& buf, + PackageMap::iterator pIter); + void encodeClassIndication (framing::Buffer& buf, + PackageMap::iterator pIter, + ClassMap::iterator cIter); + bool bankInUse (uint32_t bank); + uint32_t allocateNewBank (); + uint32_t assignBankLH (uint32_t requestedPrefix); + void deleteOrphanedAgentsLH(); + void sendCommandComplete (std::string replyToKey, uint32_t sequence, + uint32_t code = 0, std::string text = std::string("OK")); + void handleBrokerRequestLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence); + void handlePackageQueryLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence); + void handlePackageIndLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence); + void handleClassQueryLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence); + void handleClassIndLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence); + void handleSchemaRequestLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence); + void handleSchemaResponseLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence); + void handleAttachRequestLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence, const qpid::broker::ConnectionToken* connToken); + void handleGetQueryLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence); + void handleMethodRequestLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence, const qpid::broker::ConnectionToken* connToken); + + size_t validateSchema(framing::Buffer&, uint8_t kind); + size_t validateTableSchema(framing::Buffer&); + size_t validateEventSchema(framing::Buffer&); +}; + +}} + +#endif /*!_ManagementBroker_*/ diff --git a/RC9/qpid/cpp/src/qpid/management/ManagementEvent.h b/RC9/qpid/cpp/src/qpid/management/ManagementEvent.h new file mode 100644 index 0000000000..8566f31c47 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/management/ManagementEvent.h @@ -0,0 +1,49 @@ +#ifndef _ManagementEvent_ +#define _ManagementEvent_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ManagementObject.h" +#include <qpid/framing/Buffer.h> +#include <string> + +namespace qpid { +namespace management { + +class ManagementAgent; + +class ManagementEvent : public ManagementItem { +public: + typedef void (*writeSchemaCall_t)(qpid::framing::Buffer&); + virtual ~ManagementEvent() {} + + virtual writeSchemaCall_t getWriteSchemaCall(void) = 0; + virtual std::string& getEventName() const = 0; + virtual std::string& getPackageName() const = 0; + virtual uint8_t* getMd5Sum() const = 0; + virtual uint8_t getSeverity() const = 0; + virtual void encode(qpid::framing::Buffer&) const = 0; +}; + +}} + +#endif /*!_ManagementEvent_*/ diff --git a/RC9/qpid/cpp/src/qpid/management/ManagementExchange.cpp b/RC9/qpid/cpp/src/qpid/management/ManagementExchange.cpp new file mode 100644 index 0000000000..4dcafbfcdd --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/management/ManagementExchange.cpp @@ -0,0 +1,72 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ManagementExchange.h" +#include "qpid/log/Statement.h" + +using namespace qpid::management; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +ManagementExchange::ManagementExchange (const string& _name, Manageable* _parent) : + Exchange (_name, _parent), TopicExchange(_name, _parent) {} +ManagementExchange::ManagementExchange (const std::string& _name, + bool _durable, + const FieldTable& _args, + Manageable* _parent) : + Exchange (_name, _durable, _args, _parent), + TopicExchange(_name, _durable, _args, _parent) {} + +void ManagementExchange::route (Deliverable& msg, + const string& routingKey, + const FieldTable* args) +{ + bool routeIt = true; + + // Intercept management agent commands + if ((routingKey.length() > 6 && + routingKey.substr(0, 6).compare("agent.") == 0) || + (routingKey == "broker")) + routeIt = managementAgent->dispatchCommand(msg, routingKey, args); + + if (routeIt) + TopicExchange::route(msg, routingKey, args); +} + +bool ManagementExchange::bind (Queue::shared_ptr queue, + const string& routingKey, + const qpid::framing::FieldTable* args) +{ + managementAgent->clientAdded(routingKey); + return TopicExchange::bind(queue, routingKey, args); +} + +void ManagementExchange::setManagmentAgent (ManagementBroker* agent) +{ + managementAgent = agent; +} + + +ManagementExchange::~ManagementExchange() {} + +const std::string ManagementExchange::typeName("management"); + diff --git a/RC9/qpid/cpp/src/qpid/management/ManagementExchange.h b/RC9/qpid/cpp/src/qpid/management/ManagementExchange.h new file mode 100644 index 0000000000..d54db1a74e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/management/ManagementExchange.h @@ -0,0 +1,62 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ManagementExchange_ +#define _ManagementExchange_ + +#include "qpid/broker/TopicExchange.h" +#include "ManagementBroker.h" + +namespace qpid { +namespace broker { + +class ManagementExchange : public virtual TopicExchange +{ + private: + management::ManagementBroker* managementAgent; + + public: + static const std::string typeName; + + ManagementExchange (const string& name, Manageable* _parent = 0); + ManagementExchange (const string& _name, bool _durable, + const qpid::framing::FieldTable& _args, + Manageable* _parent = 0); + + virtual std::string getType() const { return typeName; } + + virtual void route (Deliverable& msg, + const string& routingKey, + const qpid::framing::FieldTable* args); + + virtual bool bind (Queue::shared_ptr queue, + const string& routingKey, + const qpid::framing::FieldTable* args); + + void setManagmentAgent (management::ManagementBroker* agent); + + virtual ~ManagementExchange(); +}; + + +} +} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/management/ManagementObject.cpp b/RC9/qpid/cpp/src/qpid/management/ManagementObject.cpp new file mode 100644 index 0000000000..f4c45de126 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/management/ManagementObject.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 "Manageable.h" +#include "ManagementObject.h" +#include "qpid/agent/ManagementAgent.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/Thread.h" + +#include <stdlib.h> + +using namespace qpid; +using namespace qpid::management; + +void AgentAttachment::setBanks(uint32_t broker, uint32_t bank) +{ + first = + ((uint64_t) (broker & 0x000fffff)) << 28 | + ((uint64_t) (bank & 0x0fffffff)); +} + +ObjectId::ObjectId(uint8_t flags, uint16_t seq, uint32_t broker, uint32_t bank, uint64_t object) + : agent(0) +{ + first = + ((uint64_t) (flags & 0x0f)) << 60 | + ((uint64_t) (seq & 0x0fff)) << 48 | + ((uint64_t) (broker & 0x000fffff)) << 28 | + ((uint64_t) (bank & 0x0fffffff)); + second = object; +} + +ObjectId::ObjectId(AgentAttachment* _agent, uint8_t flags, uint16_t seq, uint64_t object) + : agent(_agent) +{ + first = + ((uint64_t) (flags & 0x0f)) << 60 | + ((uint64_t) (seq & 0x0fff)) << 48; + second = object; +} + +ObjectId::ObjectId(std::istream& in) : agent(0) +{ + std::string text; + in >> text; + fromString(text); +} + +ObjectId::ObjectId(const std::string& text) : agent(0) +{ + fromString(text); +} + +void ObjectId::fromString(const std::string& text) +{ +#define FIELDS 5 +#if defined (_WIN32) && !defined (atoll) +# define atoll(X) _atoi64(X) +#endif + + std::string copy(text.c_str()); + char* cText; + char* field[FIELDS]; + bool atFieldStart = true; + int idx = 0; + + cText = const_cast<char*>(copy.c_str()); + for (char* cursor = cText; *cursor; cursor++) { + if (atFieldStart) { + if (idx >= FIELDS) + throw Exception("Invalid ObjectId format"); + field[idx++] = cursor; + atFieldStart = false; + } else { + if (*cursor == '-') { + *cursor = '\0'; + atFieldStart = true; + } + } + } + + if (idx != FIELDS) + throw Exception("Invalid ObjectId format"); + + first = (atoll(field[0]) << 60) + + (atoll(field[1]) << 48) + + (atoll(field[2]) << 28) + + atoll(field[3]); + second = atoll(field[4]); +} + + +bool ObjectId::operator==(const ObjectId &other) const +{ + uint64_t otherFirst = agent == 0 ? other.first : other.first & 0xffff000000000000LL; + + return first == otherFirst && second == other.second; +} + +bool ObjectId::operator<(const ObjectId &other) const +{ + uint64_t otherFirst = agent == 0 ? other.first : other.first & 0xffff000000000000LL; + + return (first < otherFirst) || ((first == otherFirst) && (second < other.second)); +} + +void ObjectId::encode(framing::Buffer& buffer) +{ + if (agent == 0) + buffer.putLongLong(first); + else + buffer.putLongLong(first | agent->first); + buffer.putLongLong(second); +} + +void ObjectId::decode(framing::Buffer& buffer) +{ + first = buffer.getLongLong(); + second = buffer.getLongLong(); +} + +namespace qpid { +namespace management { + +std::ostream& operator<<(std::ostream& out, const ObjectId& i) +{ + uint64_t virtFirst = i.first; + if (i.agent) + virtFirst |= i.agent->getFirst(); + + out << ((virtFirst & 0xF000000000000000LL) >> 60) << + "-" << ((virtFirst & 0x0FFF000000000000LL) >> 48) << + "-" << ((virtFirst & 0x0000FFFFF0000000LL) >> 28) << + "-" << (virtFirst & 0x000000000FFFFFFFLL) << + "-" << i.second; + return out; +} + +}} + +int ManagementObject::nextThreadIndex = 0; + +void ManagementObject::writeTimestamps (framing::Buffer& buf) +{ + buf.putShortString (getPackageName ()); + buf.putShortString (getClassName ()); + buf.putBin128 (getMd5Sum ()); + buf.putLongLong (updateTime); + buf.putLongLong (createTime); + buf.putLongLong (destroyTime); + objectId.encode(buf); +} + +void ManagementObject::setReference(ObjectId) {} + +int ManagementObject::getThreadIndex() { + static QPID_TSS int thisIndex = -1; + if (thisIndex == -1) { + sys::Mutex::ScopedLock mutex(accessLock); + thisIndex = nextThreadIndex; + if (nextThreadIndex < agent->getMaxThreads() - 1) + nextThreadIndex++; + } + return thisIndex; +} diff --git a/RC9/qpid/cpp/src/qpid/management/ManagementObject.h b/RC9/qpid/cpp/src/qpid/management/ManagementObject.h new file mode 100644 index 0000000000..fbdad347b8 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/management/ManagementObject.h @@ -0,0 +1,188 @@ +#ifndef _ManagementObject_ +#define _ManagementObject_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Time.h" +#include "qpid/sys/Mutex.h" +#include <qpid/framing/Buffer.h> +#include <map> + +namespace qpid { +namespace management { + +class Manageable; +class ManagementAgent; +class ObjectId; + + +class AgentAttachment { + friend class ObjectId; +private: + uint64_t first; +public: + AgentAttachment() : first(0) {} + void setBanks(uint32_t broker, uint32_t bank); + uint64_t getFirst() const { return first; } +}; + + +class ObjectId { +protected: + const AgentAttachment* agent; + uint64_t first; + uint64_t second; + void fromString(const std::string&); +public: + ObjectId() : agent(0), first(0), second(0) {} + ObjectId(framing::Buffer& buf) : agent(0) { decode(buf); } + ObjectId(uint8_t flags, uint16_t seq, uint32_t broker, uint32_t bank, uint64_t object); + ObjectId(AgentAttachment* _agent, uint8_t flags, uint16_t seq, uint64_t object); + ObjectId(std::istream&); + ObjectId(const std::string&); + bool operator==(const ObjectId &other) const; + bool operator<(const ObjectId &other) const; + void encode(framing::Buffer& buffer); + void decode(framing::Buffer& buffer); + friend std::ostream& operator<<(std::ostream&, const ObjectId&); +}; + +class ManagementItem { +public: + static const uint8_t TYPE_U8 = 1; + static const uint8_t TYPE_U16 = 2; + static const uint8_t TYPE_U32 = 3; + static const uint8_t TYPE_U64 = 4; + static const uint8_t TYPE_SSTR = 6; + static const uint8_t TYPE_LSTR = 7; + static const uint8_t TYPE_ABSTIME = 8; + static const uint8_t TYPE_DELTATIME = 9; + static const uint8_t TYPE_REF = 10; + static const uint8_t TYPE_BOOL = 11; + static const uint8_t TYPE_FLOAT = 12; + static const uint8_t TYPE_DOUBLE = 13; + static const uint8_t TYPE_UUID = 14; + static const uint8_t TYPE_FTABLE = 15; + static const uint8_t TYPE_S8 = 16; + static const uint8_t TYPE_S16 = 17; + static const uint8_t TYPE_S32 = 18; + static const uint8_t TYPE_S64 = 19; + + static const uint8_t ACCESS_RC = 1; + static const uint8_t ACCESS_RW = 2; + static const uint8_t ACCESS_RO = 3; + + static const uint8_t DIR_I = 1; + static const uint8_t DIR_O = 2; + static const uint8_t DIR_IO = 3; + + static const uint8_t FLAG_CONFIG = 0x01; + static const uint8_t FLAG_INDEX = 0x02; + static const uint8_t FLAG_END = 0x80; + + const static uint8_t CLASS_KIND_TABLE = 1; + const static uint8_t CLASS_KIND_EVENT = 2; + + + +public: + virtual ~ManagementItem() {} +}; + +class ManagementObject : public ManagementItem +{ + protected: + + uint64_t createTime; + uint64_t destroyTime; + uint64_t updateTime; + ObjectId objectId; + bool configChanged; + bool instChanged; + bool deleted; + Manageable* coreObject; + sys::Mutex accessLock; + ManagementAgent* agent; + int maxThreads; + uint32_t flags; + + static int nextThreadIndex; + bool forcePublish; + + int getThreadIndex(); + void writeTimestamps(qpid::framing::Buffer& buf); + + public: + typedef void (*writeSchemaCall_t) (qpid::framing::Buffer&); + + ManagementObject(ManagementAgent* _agent, Manageable* _core) : + createTime(uint64_t(qpid::sys::Duration(qpid::sys::now()))), + destroyTime(0), updateTime(createTime), configChanged(true), + instChanged(true), deleted(false), + coreObject(_core), agent(_agent), forcePublish(false) {} + virtual ~ManagementObject() {} + + virtual writeSchemaCall_t getWriteSchemaCall() = 0; + virtual void writeProperties(qpid::framing::Buffer& buf) = 0; + virtual void writeStatistics(qpid::framing::Buffer& buf, + bool skipHeaders = false) = 0; + virtual void doMethod(std::string& methodName, + qpid::framing::Buffer& inBuf, + qpid::framing::Buffer& outBuf) = 0; + virtual void setReference(ObjectId objectId); + + virtual std::string& getClassName() const = 0; + virtual std::string& getPackageName() const = 0; + virtual uint8_t* getMd5Sum() const = 0; + + void setObjectId(ObjectId oid) { objectId = oid; } + ObjectId getObjectId() { return objectId; } + inline bool getConfigChanged() { return configChanged; } + virtual bool getInstChanged() { return instChanged; } + virtual bool hasInst() { return true; } + inline void setForcePublish(bool f) { forcePublish = f; } + inline bool getForcePublish() { return forcePublish; } + inline void setUpdateTime() { updateTime = (uint64_t(sys::Duration(sys::now()))); } + + inline void resourceDestroy() { + destroyTime = uint64_t (qpid::sys::Duration(qpid::sys::now())); + deleted = true; + } + inline bool isDeleted() { return deleted; } + inline void setFlags(uint32_t f) { flags = f; } + inline uint32_t getFlags() { return flags; } + bool isSameClass(ManagementObject& other) { + for (int idx = 0; idx < 16; idx++) + if (other.getMd5Sum()[idx] != getMd5Sum()[idx]) + return false; + return other.getClassName() == getClassName() && + other.getPackageName() == getPackageName(); + } +}; + +typedef std::map<ObjectId, ManagementObject*> ManagementObjectMap; + +}} + + + +#endif /*!_ManagementObject_*/ diff --git a/RC9/qpid/cpp/src/qpid/memory.h b/RC9/qpid/cpp/src/qpid/memory.h new file mode 100644 index 0000000000..99d7a71e7b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/memory.h @@ -0,0 +1,32 @@ +#ifndef QPID_AUTO_PTR_H +#define QPID_AUTO_PTR_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <memory> +namespace qpid { +/** Convenient template for creating auto_ptr in-place in an argument list. */ +template <class T> +std::auto_ptr<T> make_auto_ptr(T* ptr) { return std::auto_ptr<T>(ptr); } + +} // namespace qpid + + + +#endif /*!QPID_AUTO_PTR_H*/ diff --git a/RC9/qpid/cpp/src/qpid/pointer_to_other.h b/RC9/qpid/cpp/src/qpid/pointer_to_other.h new file mode 100644 index 0000000000..a99dc89658 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/pointer_to_other.h @@ -0,0 +1,62 @@ +#ifndef QPID_POINTERTOOTHER_H +#define QPID_POINTERTOOTHER_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +namespace qpid { + +// Defines the same pointer type (raw or smart) to another pointee type + +template<class T, class U> +struct pointer_to_other; + +template<class T, class U, + template<class> class Sp> +struct pointer_to_other< Sp<T>, U > + { + typedef Sp<U> type; + }; + +template<class T, class T2, class U, + template<class, class> class Sp> +struct pointer_to_other< Sp<T, T2>, U > + { + typedef Sp<U, T2> type; + }; + +template<class T, class T2, class T3, class U, + template<class, class, class> class Sp> +struct pointer_to_other< Sp<T, T2, T3>, U > + { + typedef Sp<U, T2, T3> type; + }; + +template<class T, class U> +struct pointer_to_other< T*, U > +{ + typedef U* type; +}; + +} // namespace qpid + + + +#endif /*!QPID_POINTERTOOTHER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/ptr_map.h b/RC9/qpid/cpp/src/qpid/ptr_map.h new file mode 100644 index 0000000000..6ffcd48e89 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/ptr_map.h @@ -0,0 +1,57 @@ +#ifndef QPID_PTR_MAP +#define QPID_PTR_MAP + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/ptr_container/ptr_map.hpp> +#include <boost/utility/enable_if.hpp> +#include <boost/type_traits/is_same.hpp> +#include <boost/type_traits/remove_const.hpp> + +namespace qpid { + +/** @file + * Workaround for API change between boost 1.33 and 1.34. + * + * To be portable across these versions, code using boost::ptr_map + * iterators should use ptr_map_ptr(i) to get the pointer from + * boost::ptr_map::iterator i. + * + * @see http://www.boost.org/libs/ptr_container/doc/ptr_container.html#upgrading-from-boost-v-1-33 + */ + + +typedef boost::is_same<boost::ptr_map<int, int>::iterator::value_type, int> IsOldPtrMap; + +template <class Iter> +typename boost::enable_if<IsOldPtrMap, typename Iter::value_type*>::type +ptr_map_ptr(const Iter& i) { return &*i; } + +template <class Iter> +typename boost::disable_if<IsOldPtrMap, + typename boost::remove_const<typename Iter::value_type::second_type>::type + >::type +ptr_map_ptr(const Iter& i) { return i->second; } + +} // namespace qpid + +#endif /*!QPID_PTR_MAP*/ diff --git a/RC9/qpid/cpp/src/qpid/shared_ptr.h b/RC9/qpid/cpp/src/qpid/shared_ptr.h new file mode 100644 index 0000000000..0c933ea6a6 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/shared_ptr.h @@ -0,0 +1,51 @@ +#ifndef _common_shared_ptr_h +#define _common_shared_ptr_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/shared_ptr.hpp> +#include <boost/cast.hpp> + +namespace qpid { + +// Import shared_ptr definitions into qpid namespace and define some +// useful shared_ptr templates for convenience. + +using boost::shared_ptr; +using boost::dynamic_pointer_cast; +using boost::static_pointer_cast; +using boost::const_pointer_cast; +using boost::shared_polymorphic_downcast; + +template <class T> shared_ptr<T> make_shared_ptr(T* ptr) { + return shared_ptr<T>(ptr); +} + +template <class T, class D> +shared_ptr<T> make_shared_ptr(T* ptr, D deleter) { + return shared_ptr<T>(ptr, deleter); +} + +inline void nullDeleter(void const *) {} + +} // namespace qpid + + + +#endif /*!_common_shared_ptr_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/ActivityTimer.h b/RC9/qpid/cpp/src/qpid/sys/ActivityTimer.h new file mode 100644 index 0000000000..d49e16bc4f --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ActivityTimer.h @@ -0,0 +1,106 @@ +#ifndef QPID_SYS_ACTIVITYTIMER_H +#define QPID_SYS_ACTIVITYTIMER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Time.h" +#include "qpid/sys/Thread.h" +#include <boost/current_function.hpp> +#include <stdio.h> + +namespace qpid { +namespace sys { + +/** + * Measures and reports time spent in a particular segment of code. + * This is real time so it includes time blocked/sleeping as well as time on CPU. + * + * Intended to be used via the QPID_ACTIVITY_TIMER macro for profiling + * during development & debugging + */ +class ActivityTimer +{ + public: + + struct Stat { // Must be a POD + uint64_t total, count; + void sample(uint64_t value) { total += value; ++count; } + uint64_t mean() { return count ? total/count : 0; } + void reset() { total = count = 0; } + }; + + struct Data { // Must be a POD + uint64_t start, entered; + Stat active; + + void reset() { + start = entered = 0; + active.reset(); + } + + void enter(uint64_t now) { + entered=now; + if (!start) start = Duration(now); + } + + void exit(uint64_t now) { + active.sample(now - entered); + } + }; + + ActivityTimer(Data& d, const char* fn, const char* file, int line, uint64_t reportInterval) : data(d) { + uint64_t now = Duration(qpid::sys::now()); + if (data.start) { + interval = now-data.start; + if (interval > reportInterval) + report(fn, file, line); + } + data.enter(now); + } + + ~ActivityTimer() { + data.exit(Duration(now())); + } + + private: + Data& data; + uint64_t interval; + + void report(const char* fn, const char* file, int line) { + long rate = (data.active.count*TIME_SEC)/interval; + double percent = (data.active.total*100.0)/interval; + printf("%s:%d: TIMER %ld/sec %f%% [%lu] %s\n", + file, line, rate, percent, Thread::current().id(), fn); + data.reset(); + } +}; + +}} // namespace qpid::sys + +/** Measures time between the point of declaration and the end of the innermost enclosing scope. + * Can only have one in a given scope. + */ +#define ACTIVITY_TIMER(REPORT_INTERVAL_SECS) \ + static __thread ::qpid::sys::ActivityTimer::Data qpid__ActivityTimerData__ = { 0, 0, { 0,0 }}; \ + ::qpid::sys::ActivityTimer qpid__ActivityTimerInstance__(qpid__ActivityTimerData__, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__, 2*::qpid::sys::TIME_SEC) + +#endif /*!QPID_SYS_ACTIVITYTIMER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/AggregateOutput.cpp b/RC9/qpid/cpp/src/qpid/sys/AggregateOutput.cpp new file mode 100644 index 0000000000..fa6901d3e4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/AggregateOutput.cpp @@ -0,0 +1,73 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/AggregateOutput.h" +#include "qpid/log/Statement.h" +#include <algorithm> + +namespace qpid { +namespace sys { + +void AggregateOutput::activateOutput() { control.activateOutput(); } + +void AggregateOutput::giveReadCredit(int32_t credit) { control.giveReadCredit(credit); } + +bool AggregateOutput::hasOutput() { + for (TaskList::const_iterator i = tasks.begin(); i != tasks.end(); ++i) + if ((*i)->hasOutput()) return true; + return false; +} + +bool AggregateOutput::doOutput() +{ + bool result = false; + if (!tasks.empty()) { + if (next >= tasks.size()) next = next % tasks.size(); + + size_t start = next; + //loop until a task generated some output + while (!result) { + result = tasks[next++]->doOutput(); + if (tasks.empty()) break; + if (next >= tasks.size()) next = next % tasks.size(); + if (start == next) break; + } + } + return result; +} + +void AggregateOutput::addOutputTask(OutputTask* t) +{ + tasks.push_back(t); +} + +void AggregateOutput::removeOutputTask(OutputTask* t) +{ + TaskList::iterator i = std::find(tasks.begin(), tasks.end(), t); + if (i != tasks.end()) tasks.erase(i); +} + +void AggregateOutput::removeAll() +{ + tasks.clear(); +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/AggregateOutput.h b/RC9/qpid/cpp/src/qpid/sys/AggregateOutput.h new file mode 100644 index 0000000000..1cda4456b4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/AggregateOutput.h @@ -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. + * + */ +#ifndef _AggregateOutput_ +#define _AggregateOutput_ + +#include "Mutex.h" +#include "OutputControl.h" +#include "OutputTask.h" + +#include <algorithm> +#include <vector> + +namespace qpid { +namespace sys { + + class AggregateOutput : public OutputTask, public OutputControl + { + typedef std::vector<OutputTask*> TaskList; + + TaskList tasks; + size_t next; + OutputControl& control; + + public: + AggregateOutput(OutputControl& c) : next(0), control(c) {}; + //this may be called on any thread + void activateOutput(); + void giveReadCredit(int32_t); + + //all the following will be called on the same thread + bool doOutput(); + bool hasOutput(); + void addOutputTask(OutputTask* t); + void removeOutputTask(OutputTask* t); + void removeAll(); + + /** Apply f to each OutputTask* in the tasks list */ + template <class F> void eachOutput(F f) { + std::for_each(tasks.begin(), tasks.end(), f); + } + }; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/sys/AsynchIO.h b/RC9/qpid/cpp/src/qpid/sys/AsynchIO.h new file mode 100644 index 0000000000..68e441349a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/AsynchIO.h @@ -0,0 +1,152 @@ +#ifndef _sys_AsynchIO +#define _sys_AsynchIO +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +// @@TODO: TAKE THIS OUT... SHould be in posix version. +#include "DispatchHandle.h" + +#include <boost/function.hpp> +#include <deque> + +namespace qpid { +namespace sys { + +class Socket; + +/* + * Asynchronous acceptor: accepts connections then does a callback with the + * accepted fd + */ +class AsynchAcceptorPrivate; +class AsynchAcceptor { +public: + typedef boost::function1<void, const Socket&> Callback; + +private: + AsynchAcceptorPrivate* impl; + +public: + AsynchAcceptor(const Socket& s, Callback callback); + ~AsynchAcceptor(); + void start(Poller::shared_ptr poller); +}; + +/* + * Asynchronous connector: starts the process of initiating a connection and + * invokes a callback when completed or failed. + */ +class AsynchConnector { +public: + typedef boost::function1<void, const Socket&> ConnectedCallback; + typedef boost::function2<void, int, std::string> FailedCallback; + + // Call create() to allocate a new AsynchConnector object with the + // specified poller, addressing, and callbacks. + // This method is implemented in platform-specific code to + // create a correctly typed object. The platform code also manages + // deletes. To correctly manage heaps when needed, the allocate and + // delete should both be done from the same class/library. + static AsynchConnector* create(const Socket& s, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb = 0); + +protected: + AsynchConnector() {} + virtual ~AsynchConnector() {} +}; + +struct AsynchIOBufferBase { + char* const bytes; + const int32_t byteCount; + int32_t dataStart; + int32_t dataCount; + + AsynchIOBufferBase(char* const b, const int32_t s) : + bytes(b), + byteCount(s), + dataStart(0), + dataCount(0) + {} + + virtual ~AsynchIOBufferBase() + {} +}; + +/* + * Asychronous reader/writer: + * Reader accepts buffers to read into; reads into the provided buffers + * and then does a callback with the buffer and amount read. Optionally it + * can callback when there is something to read but no buffer to read it into. + * + * Writer accepts a buffer and queues it for writing; can also be given + * a callback for when writing is "idle" (ie fd is writable, but nothing + * to write). + */ +class AsynchIO { +public: + typedef AsynchIOBufferBase BufferBase; + + typedef boost::function2<bool, AsynchIO&, BufferBase*> ReadCallback; + typedef boost::function1<void, AsynchIO&> EofCallback; + typedef boost::function1<void, AsynchIO&> DisconnectCallback; + typedef boost::function2<void, AsynchIO&, const Socket&> ClosedCallback; + typedef boost::function1<void, AsynchIO&> BuffersEmptyCallback; + typedef boost::function1<void, AsynchIO&> IdleCallback; + + // Call create() to allocate a new AsynchIO object with the specified + // callbacks. This method is implemented in platform-specific code to + // create a correctly typed object. The platform code also manages + // deletes. To correctly manage heaps when needed, the allocate and + // delete should both be done from the same class/library. + static AsynchIO* create(const Socket& s, + ReadCallback rCb, + EofCallback eofCb, + DisconnectCallback disCb, + ClosedCallback cCb = 0, + BuffersEmptyCallback eCb = 0, + IdleCallback iCb = 0); +public: + virtual void queueForDeletion() = 0; + + virtual void start(Poller::shared_ptr poller) = 0; + virtual void queueReadBuffer(BufferBase* buff) = 0; + virtual void unread(BufferBase* buff) = 0; + virtual void queueWrite(BufferBase* buff) = 0; + virtual void notifyPendingWrite() = 0; + virtual void queueWriteClose() = 0; + virtual bool writeQueueEmpty() = 0; + virtual void startReading() = 0; + virtual BufferBase* getQueuedBuffer() = 0; + +protected: + // Derived class manages lifetime; must be constructed using the + // static create() method. Deletes not allowed from outside. + AsynchIO() {} + virtual ~AsynchIO() {} +}; + +}} + +#endif // _sys_AsynchIO diff --git a/RC9/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp b/RC9/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp new file mode 100644 index 0000000000..83b6329889 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp @@ -0,0 +1,199 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "AsynchIOHandler.h" +#include "qpid/sys/AsynchIO.h" +#include "qpid/sys/Socket.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace sys { + +// Buffer definition +struct Buff : public AsynchIO::BufferBase { + Buff() : + AsynchIO::BufferBase(new char[65536], 65536) + {} + ~Buff() + { delete [] bytes;} +}; + +AsynchIOHandler::AsynchIOHandler(std::string id, ConnectionCodec::Factory* f) : + identifier(id), + aio(0), + factory(f), + codec(0), + readError(false), + isClient(false), + readCredit(InfiniteCredit) +{} + +AsynchIOHandler::~AsynchIOHandler() { + if (codec) + codec->closed(); + delete codec; +} + +void AsynchIOHandler::init(AsynchIO* a, int numBuffs) { + aio = a; + + // Give connection some buffers to use + for (int i = 0; i < numBuffs; i++) { + aio->queueReadBuffer(new Buff); + } +} + +void AsynchIOHandler::write(const framing::ProtocolInitiation& data) +{ + QPID_LOG(debug, "SENT [" << identifier << "] INIT(" << data << ")"); + AsynchIO::BufferBase* buff = aio->getQueuedBuffer(); + if (!buff) + buff = new Buff; + framing::Buffer out(buff->bytes, buff->byteCount); + data.encode(out); + buff->dataCount = data.encodedSize(); + aio->queueWrite(buff); +} + +void AsynchIOHandler::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; + else if (readCredit.fetchAndAdd(credit) != 0) + return; + // Lock and retest credit to make sure we don't race with decreasing credit + ScopedLock<Mutex> l(creditLock); + assert(readCredit.get() >= 0); + if (readCredit.get() != 0) + aio->startReading(); +} + +bool AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) { + if (readError) { + return false; + } + size_t decoded = 0; + if (codec) { // Already initiated + try { + decoded = codec->decode(buff->bytes+buff->dataStart, buff->dataCount); + }catch(const std::exception& e){ + QPID_LOG(error, e.what()); + readError = true; + aio->queueWriteClose(); + } + }else{ + framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount); + framing::ProtocolInitiation protocolInit; + if (protocolInit.decode(in)) { + decoded = in.getPosition(); + QPID_LOG(debug, "RECV [" << identifier << "] INIT(" << protocolInit << ")"); + try { + codec = factory->create(protocolInit.getVersion(), *this, identifier); + 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); + } + // Check here for read credit + if (readCredit.get() != InfiniteCredit) { + if (--readCredit == 0) { + // Lock and retest credit to make sure we don't race with increasing credit + ScopedLock<Mutex> l(creditLock); + assert(readCredit.get() >= 0); + if (readCredit.get() == 0) + return false; + } + } + return true; +} + +void AsynchIOHandler::eof(AsynchIO&) { + QPID_LOG(debug, "DISCONNECTED [" << identifier << "]"); + if (codec) codec->closed(); + aio->queueWriteClose(); +} + +void AsynchIOHandler::closedSocket(AsynchIO&, const Socket& s) { + // If we closed with data still to send log a warning + if (!aio->writeQueueEmpty()) { + QPID_LOG(warning, "CLOSING [" << identifier << "] unsent data (probably due to client disconnect)"); + } + delete &s; + aio->queueForDeletion(); + delete this; +} + +void AsynchIOHandler::disconnect(AsynchIO& a) { + // treat the same as eof + eof(a); +} + +// Notifications +void AsynchIOHandler::nobuffs(AsynchIO&) { +} + +void AsynchIOHandler::idle(AsynchIO&){ + if (isClient && codec == 0) { + codec = factory->create(*this, identifier); + write(framing::ProtocolInitiation(codec->getVersion())); + return; + } + if (codec == 0) return; + if (codec->canEncode()) { + // Try and get a queued buffer if not then construct new one + AsynchIO::BufferBase* buff = aio->getQueuedBuffer(); + if (!buff) buff = new Buff; + size_t encoded=codec->encode(buff->bytes, buff->byteCount); + buff->dataCount = encoded; + aio->queueWrite(buff); + } + if (codec->isClosed()) + aio->queueWriteClose(); +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/AsynchIOHandler.h b/RC9/qpid/cpp/src/qpid/sys/AsynchIOHandler.h new file mode 100644 index 0000000000..fa020fbce4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/AsynchIOHandler.h @@ -0,0 +1,79 @@ +#ifndef _sys_AsynchIOHandler_h +#define _sys_AsynchIOHandler_h +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "OutputControl.h" +#include "ConnectionCodec.h" +#include "AtomicValue.h" +#include "Mutex.h" + +namespace qpid { + +namespace framing { + class ProtocolInitiation; +} + +namespace sys { + +class AsynchIO; +struct AsynchIOBufferBase; +class Socket; + +class AsynchIOHandler : public OutputControl { + std::string identifier; + AsynchIO* aio; + ConnectionCodec::Factory* factory; + ConnectionCodec* codec; + bool readError; + bool isClient; + AtomicValue<int32_t> readCredit; + static const int32_t InfiniteCredit = -1; + Mutex creditLock; + + void write(const framing::ProtocolInitiation&); + + public: + AsynchIOHandler(std::string id, ConnectionCodec::Factory* f); + ~AsynchIOHandler(); + void init(AsynchIO* a, int numBuffs); + + void setClient() { isClient = true; } + + // Output side + void close(); + void activateOutput(); + void giveReadCredit(int32_t credit); + + // Input side + bool readbuff(AsynchIO& aio, AsynchIOBufferBase* buff); + void eof(AsynchIO& aio); + void disconnect(AsynchIO& aio); + + // Notifications + void nobuffs(AsynchIO& aio); + void idle(AsynchIO& aio); + void closedSocket(AsynchIO& aio, const Socket& s); +}; + +}} // namespace qpid::sys + +#endif // _sys_AsynchIOHandler_h diff --git a/RC9/qpid/cpp/src/qpid/sys/AtomicCount.h b/RC9/qpid/cpp/src/qpid/sys/AtomicCount.h new file mode 100644 index 0000000000..d598b49427 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/AtomicCount.h @@ -0,0 +1,52 @@ +#ifndef _posix_AtomicCount_h +#define _posix_AtomicCount_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/detail/atomic_count.hpp> +#include "ScopedIncrement.h" + +namespace qpid { +namespace sys { + +/** + * Atomic counter. + */ +class AtomicCount { + public: + typedef ::qpid::sys::ScopedDecrement<AtomicCount> ScopedDecrement; + typedef ::qpid::sys::ScopedIncrement<AtomicCount> ScopedIncrement; + + AtomicCount(long value = 0) : count(value) {} + + void operator++() { ++count ; } + + long operator--() { return --count; } + + operator long() const { return count; } + + private: + boost::detail::atomic_count count; +}; + + +}} + + +#endif // _posix_AtomicCount_h diff --git a/RC9/qpid/cpp/src/qpid/sys/AtomicValue.h b/RC9/qpid/cpp/src/qpid/sys/AtomicValue.h new file mode 100644 index 0000000000..6e90eafead --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/AtomicValue.h @@ -0,0 +1,34 @@ +#ifndef QPID_SYS_ATOMICVALUE_H +#define QPID_SYS_ATOMICVALUE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#if defined( __GNUC__ ) && __GNUC__ >= 4 && ( defined( __i686__ ) || defined( __x86_64__ ) ) +// Use the Gnu C built-in atomic operations if compiling with gcc on a suitable platform. +#include "qpid/sys/AtomicValue_gcc.h" + +#else +// Fall-back to mutex locked operations if we don't have atomic ops. +#include "qpid/sys/AtomicValue_mutex.h" +#endif + +#endif /*!QPID_SYS_ATOMICVALUE_GCC_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/AtomicValue_gcc.h b/RC9/qpid/cpp/src/qpid/sys/AtomicValue_gcc.h new file mode 100644 index 0000000000..d022b07c1d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/AtomicValue_gcc.h @@ -0,0 +1,68 @@ +#ifndef QPID_SYS_ATOMICVALUE_GCC_H +#define QPID_SYS_ATOMICVALUE_GCC_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#if !defined(QPID_SYS_ATOMICVALUE_H) +#error "This file should only be included via AtomicValue.h." +#endif + +namespace qpid { +namespace sys { + +/** + * Atomic value of type T. T must be an integral type of size 1,2,4 or 8 bytes. + * All operations are atomic and preform a full memory barrier unless otherwise noted. + */ +template <class T> +class AtomicValue +{ + public: + AtomicValue(T init=0) : value(init) {} + + // Update and return new value. + inline T operator+=(T n) { return __sync_add_and_fetch(&value, n); } + inline T operator-=(T n) { return __sync_sub_and_fetch(&value, n); } + inline T operator++() { return *this += 1; } + inline T operator--() { return *this -= 1; } + + // Update and return old value. + inline T fetchAndAdd(T n) { return __sync_fetch_and_add(&value, n); } + inline T fetchAndSub(T n) { return __sync_fetch_and_sub(&value, n); } + inline T operator++(int) { return fetchAndAdd(1); } + inline T operator--(int) { return fetchAndSub(1); } + + /** If current value == testval then set to newval. Returns the old value. */ + T valueCompareAndSwap(T testval, T newval) { return __sync_val_compare_and_swap(&value, testval, newval); } + + /** If current value == testval then set to newval. Returns true if the swap was performed. */ + bool boolCompareAndSwap(T testval, T newval) { return __sync_bool_compare_and_swap(&value, testval, newval); } + + T get() const { return const_cast<AtomicValue<T>*>(this)->fetchAndAdd(static_cast<T>(0)); } + + private: + T value; +}; + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_ATOMICVALUE_GCC_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/AtomicValue_mutex.h b/RC9/qpid/cpp/src/qpid/sys/AtomicValue_mutex.h new file mode 100644 index 0000000000..e4d433e7f5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/AtomicValue_mutex.h @@ -0,0 +1,83 @@ +#ifndef QPID_SYS_ATOMICVALUE_MUTEX_H +#define QPID_SYS_ATOMICVALUE_MUTEX_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#if !defined(QPID_SYS_ATOMICVALUE_H) +#error "This file should only be included via AtomicValue.h." +#endif + +#include "qpid/sys/Mutex.h" + +namespace qpid { +namespace sys { + +/** + * Atomic value of type T. T must be an integral type of size 1,2,4 or 8 bytes. + * All operations are atomic and preform a full memory barrier unless otherwise noted. + */ +template <class T> +class AtomicValue +{ + public: + AtomicValue(T init=0) : value(init) {} + + // Update and return new value. + inline T operator+=(T n) { Lock l(lock); return value += n; } + inline T operator-=(T n) { Lock l(lock); return value -= n; } + inline T operator++() { return *this += 1; } + inline T operator--() { return *this -= 1; } + + // Update and return old value. + inline T fetchAndAdd(T n) { Lock l(lock); T old=value; value += n; return old; } + inline T fetchAndSub(T n) { Lock l(lock); T old=value; value -= n; return old; } + inline T operator++(int) { return fetchAndAdd(1); } + inline T operator--(int) { return fetchAndSub(1); } + + AtomicValue& operator=(T newval) { Lock l(lock); value = newval; return *this; } + + /** If current value == testval then set to newval. Returns the old value. */ + T valueCompareAndSwap(T testval, T newval) { + Lock l(lock); + T old=value; + if (value == testval) value = newval; + return old; + } + + /** If current value == testval then set to newval. Returns true if the swap was performed. */ + bool boolCompareAndSwap(T testval, T newval) { + Lock l(lock); + if (value == testval) { value = newval; return true; } + return false; + } + + T get() const { Lock l(lock); return value; } + + private: + typedef Mutex::ScopedLock Lock; + T value; + mutable Mutex lock; +}; + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_ATOMICVALUE_MUTEX_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/BlockingQueue.h b/RC9/qpid/cpp/src/qpid/sys/BlockingQueue.h new file mode 100644 index 0000000000..a05a10d811 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/BlockingQueue.h @@ -0,0 +1,125 @@ +#ifndef QPID_SYS_BLOCKINGQUEUE_H +#define QPID_SYS_BLOCKINGQUEUE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Waitable.h" + +#include <queue> + +namespace qpid { +namespace sys { + +/** + * A simple blocking queue template + */ +template <class T> +class BlockingQueue +{ + mutable sys::Waitable waitable; + std::queue<T> queue; + +public: + BlockingQueue() {} + ~BlockingQueue() { close(); } + + /** Pop from the queue, block up to timeout if empty. + *@param result Set to value popped from queue. + *@param timeout Defaults to infinite. + *@return true if result was set, false if queue empty after timeout. + */ + bool pop(T& result, Duration timeout=TIME_INFINITE) { + Mutex::ScopedLock l(waitable); + { + Waitable::ScopedWait w(waitable); + if (timeout == TIME_INFINITE) { + while (queue.empty()) waitable.wait(); + } else { + AbsTime deadline(now(),timeout); + while (queue.empty() && deadline > now()) waitable.wait(deadline); + } + } + if (queue.empty()) return false; + result = queue.front(); + queue.pop(); + if (!queue.empty()) + waitable.notify(); // Notify another waiter. + return true; + } + + T pop(Duration timeout=TIME_INFINITE) { + T result; + bool ok = pop(result, timeout); + if (!ok) + throw Exception("Timed out waiting on a blocking queue"); + return result; + } + + /** Push a value onto the queue. + * Note it is not an error to push onto a closed queue. + */ + void push(const T& t) { + Mutex::ScopedLock l(waitable); + queue.push(t); + waitable.notify(); // Notify a waiter. + } + + /** + * Close the queue. + *@ex exception to throw to waiting threads. ClosedException by default. + */ + void close(const ExceptionHolder& ex=ExceptionHolder(new ClosedException())) + { + Mutex::ScopedLock l(waitable); + if (!waitable.hasException()) { + waitable.setException(ex); + waitable.notifyAll(); + waitable.waitWaiters(); // Ensure no threads are still waiting. + } + } + + /** Open a closed queue. */ + void open() { + Mutex::ScopedLock l(waitable); + waitable.resetException(); + } + + bool isClosed() const { + Mutex::ScopedLock l(waitable); + return waitable.hasException(); + } + + bool empty() const { + Mutex::ScopedLock l(waitable); + return queue.empty(); + } + size_t size() const { + Mutex::ScopedLock l(waitable); + return queue.size(); + } +}; + +}} + + + +#endif /*!QPID_SYS_BLOCKINGQUEUE_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/Condition.h b/RC9/qpid/cpp/src/qpid/sys/Condition.h new file mode 100644 index 0000000000..fe0e3a1c71 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Condition.h @@ -0,0 +1,33 @@ +#ifndef _sys_Condition_h +#define _sys_Condition_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifdef USE_APR_PLATFORM +#include "apr/Condition.h" +#elif defined (_WIN32) +#include "windows/Condition.h" +#else +#include "posix/Condition.h" +#endif + +#endif /*!_sys_Condition_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/ConnectionCodec.h b/RC9/qpid/cpp/src/qpid/sys/ConnectionCodec.h new file mode 100644 index 0000000000..b1b047d2cc --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ConnectionCodec.h @@ -0,0 +1,78 @@ +#ifndef QPID_SYS_CONNECTION_CODEC_H +#define QPID_SYS_CONNECTION_CODEC_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/framing/ProtocolVersion.h" + +namespace qpid { + +namespace sys { + +class InputHandlerFactory; +class OutputControl; + +/** + * Interface of coder/decoder for a connection of a specific protocol + * version. + */ +class ConnectionCodec { + public: + virtual ~ConnectionCodec() {} + + /** Decode from buffer, return number of bytes decoded. + * @return may be less than size if there was incomplete + * data at the end of the buffer. + */ + virtual size_t decode(const char* buffer, size_t size) = 0; + + + /** Encode into buffer, return number of bytes encoded */ + virtual size_t encode(const char* buffer, size_t size) = 0; + + /** Return true if we have data to encode */ + virtual bool canEncode() = 0; + + /** Network connection was closed from other end. */ + virtual void closed() = 0; + + virtual bool isClosed() const = 0; + + virtual framing::ProtocolVersion getVersion() const = 0; + + struct Factory { + virtual ~Factory() {} + + /** Return 0 if version unknown */ + virtual ConnectionCodec* create( + framing::ProtocolVersion, OutputControl&, const std::string& id + ) = 0; + + /** Return "preferred" codec for outbound connections. */ + virtual ConnectionCodec* create( + OutputControl&, const std::string& id + ) = 0; + }; +}; + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_CONNECTION_CODEC_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/ConnectionInputHandler.h b/RC9/qpid/cpp/src/qpid/sys/ConnectionInputHandler.h new file mode 100644 index 0000000000..9a5b9f75a5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ConnectionInputHandler.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 _ConnectionInputHandler_ +#define _ConnectionInputHandler_ + +#include "qpid/framing/InputHandler.h" +#include "OutputTask.h" +#include "TimeoutHandler.h" + +namespace qpid { +namespace sys { + + class ConnectionInputHandler : + public qpid::framing::InputHandler, + public TimeoutHandler, public OutputTask + { + public: + + virtual void closed() = 0; + }; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h b/RC9/qpid/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h new file mode 100644 index 0000000000..9bb7e13686 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h @@ -0,0 +1,54 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ConnectionInputHandlerFactory_ +#define _ConnectionInputHandlerFactory_ + +#include <boost/noncopyable.hpp> +#include <string> + +namespace qpid { +namespace sys { + +class ConnectionOutputHandler; +class ConnectionInputHandler; + +/** + * Callback interface used by the Acceptor to + * create a ConnectionInputHandler for each new connection. + */ +class ConnectionInputHandlerFactory : private boost::noncopyable +{ + public: + /** + *@param out handler for connection output. + *@param id identify the connection for management purposes. + */ + virtual ConnectionInputHandler* create(ConnectionOutputHandler* out, + const std::string& id, + bool isClient) = 0; + + virtual ~ConnectionInputHandlerFactory(){} +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/sys/ConnectionOutputHandler.h b/RC9/qpid/cpp/src/qpid/sys/ConnectionOutputHandler.h new file mode 100644 index 0000000000..de0bef3630 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ConnectionOutputHandler.h @@ -0,0 +1,43 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ConnectionOutputHandler_ +#define _ConnectionOutputHandler_ + +#include "qpid/framing/OutputHandler.h" +#include "OutputControl.h" + +namespace qpid { +namespace sys { + +/** + * Provides the output handler associated with a connection. + */ +class ConnectionOutputHandler : public virtual qpid::framing::OutputHandler, public OutputControl +{ + public: + virtual void close() = 0; + virtual size_t getBuffered() const { return 0; } +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h b/RC9/qpid/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h new file mode 100644 index 0000000000..df6de89982 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h @@ -0,0 +1,55 @@ +#ifndef QPID_SYS_CONNECTIONOUTPUTHANDLERPTR_H +#define QPID_SYS_CONNECTIONOUTPUTHANDLERPTR_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ConnectionOutputHandler.h" + +namespace qpid { +namespace sys { + +/** + * A ConnectionOutputHandler that delegates to another + * ConnectionOutputHandler. Allows the "real" ConnectionOutputHandler + * to be changed modified without updating all the pointers/references + * using the ConnectionOutputHandlerPtr + */ +class ConnectionOutputHandlerPtr : public ConnectionOutputHandler +{ + public: + ConnectionOutputHandlerPtr(ConnectionOutputHandler* p) : next(p) { assert(next); } + void set(ConnectionOutputHandler* p) { next = p; assert(next); } + ConnectionOutputHandler* get() { return next; } + const ConnectionOutputHandler* get() const { return next; } + + void close() { next->close(); } + size_t getBuffered() const { return next->getBuffered(); } + void activateOutput() { next->activateOutput(); } + void giveReadCredit(int32_t credit) { next->giveReadCredit(credit); } + void send(framing::AMQFrame& f) { next->send(f); } + + private: + ConnectionOutputHandler* next; +}; +}} // namespace qpid::sys + +#endif /*!QPID_SYS_CONNECTIONOUTPUTHANDLERPTR_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/CopyOnWriteArray.h b/RC9/qpid/cpp/src/qpid/sys/CopyOnWriteArray.h new file mode 100644 index 0000000000..c5bdcc0942 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/CopyOnWriteArray.h @@ -0,0 +1,126 @@ +#ifndef QPID_SYS_COPYONWRITEARRAY_H +#define QPID_SYS_COPYONWRITEARRAY_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Mutex.h" +#include <algorithm> +#include <vector> +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace sys { + +/** + * An array that copies on adding/removing element allowing lock-free + * iteration. + */ +template <class T> +class CopyOnWriteArray +{ +public: + typedef boost::shared_ptr<const std::vector<T> > ConstPtr; + + CopyOnWriteArray() {} + CopyOnWriteArray(const CopyOnWriteArray& c) : array(c.array) {} + + void add(T& t) + { + Mutex::ScopedLock l(lock); + ArrayPtr copy(array ? new std::vector<T>(*array) : new std::vector<T>()); + copy->push_back(t); + array = copy; + } + + bool remove(T& t) + { + Mutex::ScopedLock l(lock); + if (array && std::find(array->begin(), array->end(), t) != array->end()) { + ArrayPtr copy(new std::vector<T>(*array)); + copy->erase(std::find(copy->begin(), copy->end(), t)); + array = copy; + return true; + } else { + return false; + } + } + + template <class F> + bool add_unless(T& t, F f) + { + Mutex::ScopedLock l(lock); + if (array && find_if(array->begin(), array->end(), f) != array->end()) { + return false; + } else { + ArrayPtr copy(array ? new std::vector<T>(*array) : new std::vector<T>()); + copy->push_back(t); + array = copy; + return true; + } + } + + template <class F> + bool remove_if(F f) + { + Mutex::ScopedLock l(lock); + if (array && std::find_if(array->begin(), array->end(), f) != array->end()) { + ArrayPtr copy(new std::vector<T>(*array)); + copy->erase(std::remove_if(copy->begin(), copy->end(), f), copy->end()); + array = copy; + return true; + } + return false; + } + + template <class F> + F for_each(F f) + { + ArrayPtr a; + { + Mutex::ScopedLock l(lock); + a = array; + } + if (!a) return f; + return std::for_each(a->begin(), a->end(), f); + } + + ConstPtr snapshot() + { + ConstPtr a; + { + Mutex::ScopedLock l(lock); + a = array; + } + return a; + } + +private: + typedef boost::shared_ptr< std::vector<T> > ArrayPtr; + Mutex lock; + ArrayPtr array; +}; + +}} + + + +#endif /*!QPID_SYS_COPYONWRITEARRAY_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/DeletionManager.h b/RC9/qpid/cpp/src/qpid/sys/DeletionManager.h new file mode 100644 index 0000000000..43154eb98e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/DeletionManager.h @@ -0,0 +1,138 @@ +#ifndef _sys_DeletionManager_h +#define _sys_DeletionManager_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <vector> +#include <algorithm> +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace sys { + +struct deleter +{ + template <typename T> + void operator()(T* ptr){ delete ptr;} +}; + +/** + * DeletionManager keeps track of handles that need to be deleted but may still be + * in use by one of the threads concurrently. + * + * The mode of operation is like this: + * - When we want to delete but we might still be using the handle we + * * Transfer ownership of the handle to this class + * * Mark the handle as (potentially) in use by every thread + * - Then subsequently at points where the thread code knows it isn't + * using any handles it declares that it is using no handles + * - When the last thread declares no use of a handle it automatically + * gets deleted by the shared_ptr implementation + * + * The class only has static members and data and so can only be used once for + * any particular handle type + */ +template <typename H> +class DeletionManager +{ +public: + // Mark every thread as using the handle - it will be deleted + // below after every thread marks the handle as unused + static void markForDeletion(H* handle) { + allThreadsStatuses.addHandle(shared_ptr(handle)); + } + + // Mark this thread is not using any handle - + // handles get deleted here when no one else + // is using them either + static void markAllUnusedInThisThread() { + static __thread ThreadStatus* threadStatus = 0; + + // Thread local vars can't be dynamically constructed so we need + // to check whether we've made it yet and construct it if not + // (no locking necessary for the check as it's thread local!) + if (!threadStatus) { + threadStatus = new ThreadStatus; + allThreadsStatuses.addThreadStatus(threadStatus); + } + + ScopedLock<Mutex> l(threadStatus->lock); + + // The actual deletions will happen here when all the shared_ptr + // ref counts hit 0 (that is when every thread marks the handle unused) + threadStatus->handles.clear(); + } + +private: + typedef boost::shared_ptr<H> shared_ptr; + + // In theory we know that we never need more handles than the number of + // threads runnning so we could use a fixed size array. However at this point + // in the code we don't have easy access to this information. + struct ThreadStatus + { + Mutex lock; + std::vector<shared_ptr> handles; + }; + + class AllThreadsStatuses + { + Mutex lock; + std::vector<ThreadStatus*> statuses; + + struct handleAdder + { + shared_ptr handle; + + handleAdder(shared_ptr h): handle(h) {} + + void operator()(ThreadStatus* ptr) { + ScopedLock<Mutex> l(ptr->lock); + ptr->handles.push_back(handle); + } + }; + + public: + // Need this to be able to do static initialisation + explicit AllThreadsStatuses(int) {} + + ~AllThreadsStatuses() { + ScopedLock<Mutex> l(lock); + std::for_each(statuses.begin(), statuses.end(), deleter()); + } + + void addThreadStatus(ThreadStatus* t) { + ScopedLock<Mutex> l(lock); + statuses.push_back(t); + } + + void addHandle(shared_ptr h) { + ScopedLock<Mutex> l(lock); + std::for_each(statuses.begin(), statuses.end(), handleAdder(h)); + } + }; + + static AllThreadsStatuses allThreadsStatuses; +}; + +}} +#endif // _sys_DeletionManager_h diff --git a/RC9/qpid/cpp/src/qpid/sys/DispatchHandle.cpp b/RC9/qpid/cpp/src/qpid/sys/DispatchHandle.cpp new file mode 100644 index 0000000000..4722fc0b8b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/DispatchHandle.cpp @@ -0,0 +1,409 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "DispatchHandle.h" + +#include <boost/cast.hpp> + +#include <assert.h> + +namespace qpid { +namespace sys { + +DispatchHandle::~DispatchHandle() { + stopWatch(); +} + +void DispatchHandle::startWatch(Poller::shared_ptr poller0) { + bool r = readableCallback; + bool w = writableCallback; + + ScopedLock<Mutex> lock(stateLock); + assert(state == IDLE); + + // If no callbacks set then do nothing (that is what we were asked to do!) + // TODO: Maybe this should be an assert instead + if (!r && !w) { + state = INACTIVE; + return; + } + + Poller::Direction d = r ? + (w ? Poller::INOUT : Poller::INPUT) : + Poller::OUTPUT; + + poller = poller0; + poller->addFd(*this, d); + + state = r ? + (w ? ACTIVE_RW : ACTIVE_R) : + ACTIVE_W; +} + +void DispatchHandle::rewatch() { + bool r = readableCallback; + bool w = writableCallback; + + ScopedLock<Mutex> lock(stateLock); + switch(state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_R: + case DELAYED_W: + case DELAYED_INACTIVE: + state = r ? + (w ? DELAYED_RW : DELAYED_R) : + DELAYED_W; + break; + case DELAYED_DELETE: + break; + case INACTIVE: + case ACTIVE_R: + case ACTIVE_W: { + assert(poller); + Poller::Direction d = r ? + (w ? Poller::INOUT : Poller::INPUT) : + Poller::OUTPUT; + poller->modFd(*this, d); + state = r ? + (w ? ACTIVE_RW : ACTIVE_R) : + ACTIVE_W; + break; + } + case DELAYED_RW: + case ACTIVE_RW: + // Don't need to do anything already waiting for readable/writable + break; + } +} + +void DispatchHandle::rewatchRead() { + if (!readableCallback) { + return; + } + + ScopedLock<Mutex> lock(stateLock); + switch(state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_R: + case DELAYED_RW: + case DELAYED_DELETE: + break; + case DELAYED_W: + state = DELAYED_RW; + break; + case DELAYED_INACTIVE: + state = DELAYED_R; + break; + case ACTIVE_R: + case ACTIVE_RW: + // Nothing to do: already waiting for readable + break; + case INACTIVE: + assert(poller); + poller->modFd(*this, Poller::INPUT); + state = ACTIVE_R; + break; + case ACTIVE_W: + assert(poller); + poller->modFd(*this, Poller::INOUT); + state = ACTIVE_RW; + break; + } +} + +void DispatchHandle::rewatchWrite() { + if (!writableCallback) { + return; + } + + ScopedLock<Mutex> lock(stateLock); + switch(state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_W: + case DELAYED_RW: + case DELAYED_DELETE: + break; + case DELAYED_R: + state = DELAYED_RW; + break; + case DELAYED_INACTIVE: + state = DELAYED_W; + break; + case INACTIVE: + assert(poller); + poller->modFd(*this, Poller::OUTPUT); + state = ACTIVE_W; + break; + case ACTIVE_R: + assert(poller); + poller->modFd(*this, Poller::INOUT); + state = ACTIVE_RW; + break; + case ACTIVE_W: + case ACTIVE_RW: + // Nothing to do: already waiting for writable + break; + } +} + +void DispatchHandle::unwatchRead() { + if (!readableCallback) { + return; + } + + ScopedLock<Mutex> lock(stateLock); + switch(state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_R: + state = DELAYED_INACTIVE; + break; + case DELAYED_RW: + state = DELAYED_W; + break; + case DELAYED_W: + case DELAYED_INACTIVE: + case DELAYED_DELETE: + break; + case ACTIVE_R: + assert(poller); + poller->modFd(*this, Poller::NONE); + state = INACTIVE; + break; + case ACTIVE_RW: + assert(poller); + poller->modFd(*this, Poller::OUTPUT); + state = ACTIVE_W; + break; + case ACTIVE_W: + case INACTIVE: + break; + } +} + +void DispatchHandle::unwatchWrite() { + if (!writableCallback) { + return; + } + + ScopedLock<Mutex> lock(stateLock); + switch(state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_W: + state = DELAYED_INACTIVE; + break; + case DELAYED_RW: + state = DELAYED_R; + break; + case DELAYED_R: + case DELAYED_INACTIVE: + case DELAYED_DELETE: + break; + case ACTIVE_W: + assert(poller); + poller->modFd(*this, Poller::NONE); + state = INACTIVE; + break; + case ACTIVE_RW: + assert(poller); + poller->modFd(*this, Poller::INPUT); + state = ACTIVE_R; + break; + case ACTIVE_R: + case INACTIVE: + break; + } +} + +void DispatchHandle::unwatch() { + ScopedLock<Mutex> lock(stateLock); + switch (state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_R: + case DELAYED_W: + case DELAYED_RW: + case DELAYED_INACTIVE: + state = DELAYED_INACTIVE; + break; + case DELAYED_DELETE: + break; + default: + assert(poller); + poller->modFd(*this, Poller::NONE); + state = INACTIVE; + break; + } +} + +void DispatchHandle::stopWatch() { + ScopedLock<Mutex> lock(stateLock); + switch (state) { + case IDLE: + case DELAYED_IDLE: + case DELAYED_DELETE: + return; + case DELAYED_R: + case DELAYED_W: + case DELAYED_RW: + case DELAYED_INACTIVE: + state = DELAYED_IDLE; + break; + default: + state = IDLE; + break; + } + assert(poller); + poller->delFd(*this); + poller.reset(); +} + +// The slightly strange switch structure +// is to ensure that the lock is released before +// we do the delete +void DispatchHandle::doDelete() { + // Ensure that we're no longer watching anything + stopWatch(); + + // If we're in the middle of a callback defer the delete + { + ScopedLock<Mutex> lock(stateLock); + switch (state) { + case DELAYED_IDLE: + case DELAYED_DELETE: + state = DELAYED_DELETE; + return; + case IDLE: + break; + default: + // Can only get out of stopWatch() in DELAYED_IDLE/DELAYED_DELETE/IDLE states + assert(false); + } + } + // If we're not then do it right away + delete this; +} + +void DispatchHandle::processEvent(Poller::EventType type) { + // Note that we are now doing the callbacks + { + ScopedLock<Mutex> lock(stateLock); + + // Set up to wait for same events next time unless reset + switch(state) { + case ACTIVE_R: + state = DELAYED_R; + break; + case ACTIVE_W: + state = DELAYED_W; + break; + case ACTIVE_RW: + state = DELAYED_RW; + break; + // Can only get here in a DELAYED_* state in the rare case + // that we're already here for reading and we get activated for + // writing and we can write (it might be possible the other way + // round too). In this case we're already processing the handle + // in a different thread in this function so return right away + case DELAYED_R: + case DELAYED_W: + case DELAYED_RW: + case DELAYED_INACTIVE: + case DELAYED_IDLE: + case DELAYED_DELETE: + return; + default: + assert(false); + } + } + + // Do callbacks - whilst we are doing the callbacks we are prevented from processing + // the same handle until we re-enable it. To avoid rentering the callbacks for a single + // handle re-enabling in the callbacks is actually deferred until they are complete. + switch (type) { + case Poller::READABLE: + readableCallback(*this); + break; + case Poller::WRITABLE: + writableCallback(*this); + break; + case Poller::READ_WRITABLE: + readableCallback(*this); + writableCallback(*this); + break; + case Poller::DISCONNECTED: + { + ScopedLock<Mutex> lock(stateLock); + state = DELAYED_INACTIVE; + } + if (disconnectedCallback) { + disconnectedCallback(*this); + } + break; + default: + assert(false); + } + + // If any of the callbacks re-enabled reading/writing then actually + // do it now + { + ScopedLock<Mutex> lock(stateLock); + switch (state) { + case DELAYED_R: + poller->modFd(*this, Poller::INPUT); + state = ACTIVE_R; + return; + case DELAYED_W: + poller->modFd(*this, Poller::OUTPUT); + state = ACTIVE_W; + return; + case DELAYED_RW: + poller->modFd(*this, Poller::INOUT); + state = ACTIVE_RW; + return; + case DELAYED_INACTIVE: + state = INACTIVE; + return; + case DELAYED_IDLE: + state = IDLE; + return; + default: + // This should be impossible + assert(false); + return; + case DELAYED_DELETE: + break; + } + } + delete this; +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/sys/DispatchHandle.h b/RC9/qpid/cpp/src/qpid/sys/DispatchHandle.h new file mode 100644 index 0000000000..219f2c53d6 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/DispatchHandle.h @@ -0,0 +1,146 @@ +#ifndef _sys_DispatchHandle_h +#define _sys_DispatchHandle_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Poller.h" +#include "Mutex.h" + +#include <boost/function.hpp> + + +namespace qpid { +namespace sys { + +class DispatchHandleRef; +/** + * In order to have your own handle (file descriptor on Unix) watched by the poller + * you need to: + * + * - Subclass IOHandle, in the constructor supply an appropriate + * IOHandlerPrivate object for the platform. + * + * - Construct a DispatchHandle passing it your IOHandle and + * callback functions for read, write and disconnect events. + * + * - Ensure the DispatchHandle is not deleted until the poller is no longer using it. + * TODO: astitcher document DispatchHandleRef to simplify this. + * + * When an event occurs on the handle, the poller calls the relevant callback and + * stops watching that handle. Your callback can call rewatch() or related functions + * to re-enable polling. + */ +class DispatchHandle : public PollerHandle { + friend class DispatchHandleRef; +public: + typedef boost::function1<void, DispatchHandle&> Callback; + +private: + Callback readableCallback; + Callback writableCallback; + Callback disconnectedCallback; + Poller::shared_ptr poller; + Mutex stateLock; + enum { + IDLE, INACTIVE, ACTIVE_R, ACTIVE_W, ACTIVE_RW, + DELAYED_IDLE, DELAYED_INACTIVE, DELAYED_R, DELAYED_W, DELAYED_RW, + DELAYED_DELETE + } state; + +public: + /** + * Provide a handle to poll and a set of callbacks. Note + * callbacks can be 0, meaning you are not interested in that + * event. + * + *@param h: the handle to watch. The IOHandle encapsulates a + * platfrom-specific handle to an IO object (e.g. a file descriptor + * on Unix.) + *@param rCb Callback called when the handle is readable. + *@param wCb Callback called when the handle is writable. + *@param dCb Callback called when the handle is disconnected. + */ + DispatchHandle(const IOHandle& h, Callback rCb, Callback wCb, Callback dCb) : + PollerHandle(h), + readableCallback(rCb), + writableCallback(wCb), + disconnectedCallback(dCb), + state(IDLE) + {} + + ~DispatchHandle(); + + /** Add this DispatchHandle to the poller to be watched. */ + void startWatch(Poller::shared_ptr poller); + + /** Resume watchingn for all non-0 callbacks. */ + void rewatch(); + /** Resume watchingn for read only. */ + void rewatchRead(); + + /** Resume watchingn for write only. */ + void rewatchWrite(); + + /** Stop watching temporarily. The DispatchHandle remains + associated with the poller and can be re-activated using + rewatch. */ + void unwatch(); + /** Stop watching for read */ + void unwatchRead(); + /** Stop watching for write */ + void unwatchWrite(); + + /** Stop watching permanently. Disassociates from the poller. */ + void stopWatch(); + +protected: + /** Override to get extra processing done when the DispatchHandle is deleted. */ + void doDelete(); + +private: + void processEvent(Poller::EventType dir); +}; + +class DispatchHandleRef { + DispatchHandle* ref; + +public: + typedef boost::function1<void, DispatchHandle&> Callback; + DispatchHandleRef(const IOHandle& h, Callback rCb, Callback wCb, Callback dCb) : + ref(new DispatchHandle(h, rCb, wCb, dCb)) + {} + + ~DispatchHandleRef() { ref->doDelete(); } + + void startWatch(Poller::shared_ptr poller) { ref->startWatch(poller); } + void rewatch() { ref->rewatch(); } + void rewatchRead() { ref->rewatchRead(); } + void rewatchWrite() { ref->rewatchWrite(); } + void unwatch() { ref->unwatch(); } + void unwatchRead() { ref->unwatchRead(); } + void unwatchWrite() { ref->unwatchWrite(); } + void stopWatch() { ref->stopWatch(); } +}; + +}} + +#endif // _sys_DispatchHandle_h diff --git a/RC9/qpid/cpp/src/qpid/sys/Dispatcher.cpp b/RC9/qpid/cpp/src/qpid/sys/Dispatcher.cpp new file mode 100644 index 0000000000..8d1d1b79f5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Dispatcher.cpp @@ -0,0 +1,59 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Dispatcher.h" + +#include <assert.h> + +namespace qpid { +namespace sys { + +Dispatcher::Dispatcher(Poller::shared_ptr poller0) : + poller(poller0) { +} + +Dispatcher::~Dispatcher() { +} + +void Dispatcher::run() { + do { + Poller::Event event = poller->wait(); + + // If can read/write then dispatch appropriate callbacks + if (event.handle) { + event.process(); + } else { + // Handle shutdown + switch (event.type) { + case Poller::SHUTDOWN: + goto dispatcher_shutdown; + default: + // This should be impossible + assert(false); + } + } + } while (true); + +dispatcher_shutdown: + ; +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/sys/Dispatcher.h b/RC9/qpid/cpp/src/qpid/sys/Dispatcher.h new file mode 100644 index 0000000000..f7c9e8d731 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Dispatcher.h @@ -0,0 +1,43 @@ +#ifndef _sys_Dispatcher_h +#define _sys_Dispatcher_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Poller.h" +#include "Runnable.h" + +namespace qpid { +namespace sys { + +class Dispatcher : public Runnable { + const Poller::shared_ptr poller; + +public: + Dispatcher(Poller::shared_ptr poller); + ~Dispatcher(); + + void run(); +}; + +}} + +#endif // _sys_Dispatcher_h diff --git a/RC9/qpid/cpp/src/qpid/sys/ExceptionHolder.h b/RC9/qpid/cpp/src/qpid/sys/ExceptionHolder.h new file mode 100644 index 0000000000..fecaa73eea --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ExceptionHolder.h @@ -0,0 +1,75 @@ +#ifndef QPID_EXCEPTIONHOLDER_H +#define QPID_EXCEPTIONHOLDER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/memory.h" +#include <boost/shared_ptr.hpp> + + +namespace qpid { +namespace sys { + +struct Raisable { + virtual ~Raisable() {}; + virtual void raise() const=0; + virtual std::string what() const=0; +}; + +/** + * Holder for exceptions. Allows the thread that notices an error condition to + * create an exception and store it to be thrown by another thread. + */ +class ExceptionHolder : public Raisable { + public: + ExceptionHolder() {} + // Use default copy & assign. + + /** Take ownership of ex */ + template <class Ex> ExceptionHolder(Ex* ex) { wrap(ex); } + template <class Ex> ExceptionHolder(const boost::shared_ptr<Ex>& ex) { wrap(ex.release()); } + + template <class Ex> ExceptionHolder& operator=(Ex* ex) { wrap(ex); return *this; } + template <class Ex> ExceptionHolder& operator=(boost::shared_ptr<Ex> ex) { wrap(ex.release()); return *this; } + + void raise() const { if (wrapper.get()) wrapper->raise() ; } + std::string what() const { return wrapper.get() ? wrapper->what() : std::string(); } + bool empty() const { return !wrapper.get(); } + operator bool() const { return !empty(); } + void reset() { wrapper.reset(); } + + private: + template <class Ex> struct Wrapper : public Raisable { + Wrapper(Ex* ptr) : exception(ptr) {} + void raise() const { throw *exception; } + std::string what() const { return exception->what(); } + boost::shared_ptr<Ex> exception; + }; + template <class Ex> void wrap(Ex* ex) { wrapper.reset(new Wrapper<Ex>(ex)); } + boost::shared_ptr<Raisable> wrapper; +}; + + +}} // namespace qpid::sys + + +#endif /*!QPID_EXCEPTIONHOLDER_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/FileSysDir.h b/RC9/qpid/cpp/src/qpid/sys/FileSysDir.h new file mode 100755 index 0000000000..ffe7823f0a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/FileSysDir.h @@ -0,0 +1,62 @@ +#ifndef QPID_SYS_FILESYSDIR_H +#define QPID_SYS_FILESYSDIR_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <string> + +namespace qpid { +namespace sys { + +/** + * @class FileSysDir + * + * Represents a filesystem directory accessible from the local host. + * This class simply checks existence of, and creates, a directory. It could + * be added to later to list contents, etc. + */ +class FileSysDir +{ + const std::string dirPath; + + public: + + FileSysDir (std::string path) : dirPath(path) {} + ~FileSysDir () {} + + /** + * Check to see if the directory exists and is a directory. Throws an + * exception if there is an error checking existence or if the path + * exists but is not a directory. + * + * @retval true if the path exists and is a directory. + * @retval false if the path does not exist. + */ + bool exists (void) const; + + void mkdir(void); + + std::string getPath () { return dirPath; } +}; + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_FILESYSDIR_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/Fork.h b/RC9/qpid/cpp/src/qpid/sys/Fork.h new file mode 100644 index 0000000000..4ec061f7bc --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Fork.h @@ -0,0 +1,24 @@ +#ifndef QPID_SYS_FORK_H +#define QPID_SYS_FORK_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "posix/Fork.h" + +#endif /*!QPID_SYS_FORK_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/IOHandle.h b/RC9/qpid/cpp/src/qpid/sys/IOHandle.h new file mode 100644 index 0000000000..0bf2abbafa --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/IOHandle.h @@ -0,0 +1,59 @@ +#ifndef _sys_IOHandle_h +#define _sys_IOHandle_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 { + +/** + * This is a class intended to abstract the Unix concept of file descriptor + * or the Windows concept of HANDLE + */ +// Windows-related classes +class AsynchAcceptorPrivate; +class AsynchAcceptResult; +namespace windows { + class AsynchIO; +} + +// General classes +class PollerHandle; +class IOHandlePrivate; +class IOHandle { + + friend class AsynchAcceptorPrivate; + friend class AsynchAcceptResult; + friend class windows::AsynchIO; + + friend class PollerHandle; + +protected: + IOHandlePrivate* const impl; + + IOHandle(IOHandlePrivate*); + virtual ~IOHandle(); +}; + +}} + +#endif // _sys_IOHandle_h diff --git a/RC9/qpid/cpp/src/qpid/sys/IntegerTypes.h b/RC9/qpid/cpp/src/qpid/sys/IntegerTypes.h new file mode 100755 index 0000000000..89635f033e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/IntegerTypes.h @@ -0,0 +1,31 @@ +#ifndef QPID_SYS_INTEGERTYPES_H +#define QPID_SYS_INTEGERTYPES_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#if (defined(_WINDOWS) || defined (WIN32)) && defined(_MSC_VER) +#include "qpid/sys/windows/IntegerTypes.h" +#endif +#if !defined _WINDOWS && !defined WIN32 +#include "qpid/sys/posix/IntegerTypes.h" +#endif + +#endif /*!QPID_SYS_INTEGERTYPES_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/LockFile.h b/RC9/qpid/cpp/src/qpid/sys/LockFile.h new file mode 100644 index 0000000000..2ff8c2f6d4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/LockFile.h @@ -0,0 +1,80 @@ +#ifndef _sys_LockFile_h +#define _sys_LockFile_h + +/* + * + * Copyright (c) 2008 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <string> + +#include "IntegerTypes.h" + +namespace qpid { +namespace sys { + +class LockFilePrivate; + +/** + * @class LockFile + * + * LockFile represents a locked file suitable for a coarse-grain system + * lock. For example, the broker uses this to ensure that only one broker + * runs. A common usage idiom is to store the current "owner" process ID + * in the lock file - if the lock file exists, but the stored process ID + * doesn't, the old owner has probably died without cleaning up the lock + * file. + */ +class LockFile : private boost::noncopyable +{ + boost::shared_ptr<LockFilePrivate> impl; + + std::string path; + bool created; + +public: + LockFile(const std::string& path_, bool create); + ~LockFile(); + + /** + * Read the process ID from the lock file. This method assumes that + * if there is a process ID in the file, it was written there by + * writePid(); thus, it's at the start of the file. + * + * Throws an exception if there is an error reading the file. + * + * @returns The stored process ID. No validity check is done on it. + */ + pid_t readPid(void) const; + + /** + * Write the current process's ID to the lock file. It's written at + * the start of the file and will overwrite any other content that + * may be in the file. + * + * Throws an exception if the write fails. + */ + void writePid(void); +}; + +}} /* namespace qpid::sys */ + +#endif /*!_sys_LockFile_h*/ + + + diff --git a/RC9/qpid/cpp/src/qpid/sys/LockPtr.h b/RC9/qpid/cpp/src/qpid/sys/LockPtr.h new file mode 100644 index 0000000000..738a864317 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/LockPtr.h @@ -0,0 +1,89 @@ +#ifndef QPID_SYS_LOCKPTR_H +#define QPID_SYS_LOCKPTR_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Mutex.h" +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace sys { + +class Mutex; + +/** + * LockPtr is a smart pointer to T. It is constructed from a volatile + * T* and a Lock (by default a Mutex). It const_casts away the + * volatile qualifier and locks the Lock for the duration of its + * + * Used in conjuntion with the "volatile" keyword to get the compiler + * to help enforce correct concurrent use of mutli-threaded objects. + * See ochttp://www.ddj.com/cpp/184403766 for a detailed discussion. + * + * To summarize the convention: + * - Declare thread-safe member functions as volatile. + * - Declare instances of the class that may be called concurrently as volatile. + * - Use LockPtr to cast away the volatile qualifier while taking a lock. + * + * This means that code calling on a concurrently-used object + * (declared volatile) can only call thread-safe (volatile) member + * functions. Code that needs to use thread-unsafe members must use a + * LockPtr, thereby acquiring the lock and making it safe to do so. + * + * A good type-safe pattern is the internally-locked object: + * - It has it's own private lock member. + * - All public functions are thread safe and declared volatile. + * - Any thread-unsafe, non-volatile functions are private. + * - Only member function implementations use LockPtr to access private functions. + * + * This encapsulates all the locking logic inside the class. + * + * One nice feature of this convention is the common case where you + * need a public, locked version of some function foo() and also a + * private unlocked version to avoid recursive locks. They can be declared as + * volatile and non-volatile overloads of the same function: + * + * // public + * void Thing::foo() volatile { LockPtr<Thing>(this, myLock)->foo(); } + * // private + * void Thing::foo() { ... do stuff ...} + */ + +template <class T, class Lock> class LockPtr : public boost::noncopyable { + public: + LockPtr(volatile T* p, Lock& l) : ptr(const_cast<T*>(p)), lock(l) { lock.lock(); } + LockPtr(volatile T* p, volatile Lock& l) : ptr(const_cast<T*>(p)), lock(const_cast<Lock&>(l)) { lock.lock(); } + ~LockPtr() { lock.unlock(); } + + T& operator*() { return *ptr; } + T* operator->() { return ptr; } + + private: + T* ptr; + Lock& lock; +}; + + +}} // namespace qpid::sys + + +#endif /*!QPID_SYS_LOCKPTR_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/Monitor.h b/RC9/qpid/cpp/src/qpid/sys/Monitor.h new file mode 100644 index 0000000000..2dd405efaf --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Monitor.h @@ -0,0 +1,49 @@ +#ifndef _sys_Monitor_h +#define _sys_Monitor_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Condition.h" + +namespace qpid { +namespace sys { + +/** + * A monitor is a condition variable and a mutex + */ +class Monitor : public Mutex, public Condition { + public: + inline void wait(); + inline bool wait(const AbsTime& absoluteTime); +}; + + +void Monitor::wait() { + Condition::wait(*this); +} + +bool Monitor::wait(const AbsTime& absoluteTime) { + return Condition::wait(*this, absoluteTime); +} + +}} +#endif /*!_sys_Monitor_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/Mutex.h b/RC9/qpid/cpp/src/qpid/sys/Mutex.h new file mode 100644 index 0000000000..00bf392604 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Mutex.h @@ -0,0 +1,91 @@ +#ifndef _sys_Mutex_h +#define _sys_Mutex_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +namespace qpid { +namespace sys { + +/** + * Scoped lock template: calls lock() in ctor, unlock() in dtor. + * L can be any class with lock() and unlock() functions. + */ +template <class L> +class ScopedLock +{ + public: + ScopedLock(L& l) : mutex(l) { l.lock(); } + ~ScopedLock() { mutex.unlock(); } + private: + L& mutex; +}; + +template <class L> +class ScopedUnlock +{ + public: + ScopedUnlock(L& l) : mutex(l) { l.unlock(); } + ~ScopedUnlock() { mutex.lock(); } + private: + L& mutex; +}; + +template <class L> +class ScopedRlock +{ + public: + ScopedRlock(L& l) : mutex(l) { l.rlock(); } + ~ScopedRlock() { mutex.unlock(); } + private: + L& mutex; +}; + +template <class L> +class ScopedWlock +{ + public: + ScopedWlock(L& l) : mutex(l) { l.wlock(); } + ~ScopedWlock() { mutex.unlock(); } + private: + L& mutex; +}; + +template <class L> +class ConditionalScopedLock +{ + public: + ConditionalScopedLock(L& l) : mutex(l) { acquired = l.trylock(); } + ~ConditionalScopedLock() { if (acquired) mutex.unlock(); } + bool lockAcquired() { return acquired; } + private: + L& mutex; + bool acquired; +}; + +}} + +#ifdef USE_APR_PLATFORM +#include "apr/Mutex.h" +#elif defined (_WIN32) +#include "windows/Mutex.h" +#else +#include "posix/Mutex.h" +#endif + +#endif /*!_sys_Mutex_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/OutputControl.h b/RC9/qpid/cpp/src/qpid/sys/OutputControl.h new file mode 100644 index 0000000000..e9e6c57a9b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/OutputControl.h @@ -0,0 +1,42 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "IntegerTypes.h" + +#ifndef _OutputControl_ +#define _OutputControl_ + +namespace qpid { +namespace sys { + + class OutputControl + { + public: + virtual ~OutputControl() {} + virtual void activateOutput() = 0; + virtual void giveReadCredit(int32_t credit) = 0; + }; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/sys/OutputTask.h b/RC9/qpid/cpp/src/qpid/sys/OutputTask.h new file mode 100644 index 0000000000..005ae7dbc4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/OutputTask.h @@ -0,0 +1,48 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _OutputTask_ +#define _OutputTask_ + +namespace qpid { +namespace sys { + + class OutputTask + { + public: + virtual ~OutputTask() {} + /** Generate some output. + *@return true if output was generated, false if there is no work to do. + */ + virtual bool doOutput() = 0; + + /** Check if there may be work to do, but don't do it. + * @return True if there may be work to do, false if there is none. + * Can to return a false positive, to allow implementations to do a + * faster check than doOutput(). Must never return a false negative. + */ + virtual bool hasOutput() = 0; + }; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/sys/PollableCondition.h b/RC9/qpid/cpp/src/qpid/sys/PollableCondition.h new file mode 100644 index 0000000000..56d38f90da --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/PollableCondition.h @@ -0,0 +1,28 @@ +#ifndef QPID_SYS_POLLABLECONDITION_H +#define QPID_SYS_POLLABLECONDITION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +// Currently only has a posix implementation, add #ifdefs for other platforms as needed. +#include "posix/PollableCondition.h" + +#endif /*!QPID_SYS_POLLABLECONDITION_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/PollableQueue.h b/RC9/qpid/cpp/src/qpid/sys/PollableQueue.h new file mode 100644 index 0000000000..7f11cc35a9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/PollableQueue.h @@ -0,0 +1,152 @@ +#ifndef QPID_SYS_POLLABLEQUEUE_H +#define QPID_SYS_POLLABLEQUEUE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/PollableCondition.h" +#include "qpid/sys/Dispatcher.h" +#include "qpid/sys/DispatchHandle.h" +#include "qpid/sys/Monitor.h" +#include <boost/function.hpp> +#include <boost/bind.hpp> +#include <algorithm> +#include <deque> + +namespace qpid { +namespace sys { + +class Poller; + +/** + * A queue that can be polled by sys::Poller. Any thread can push to + * the queue, on wakeup the poller thread processes all items on the + * queue by passing them to a callback in a batch. + */ +template <class T> +class PollableQueue { + public: + typedef std::deque<T> Queue; + + /** + * Callback to process a batch of items from the queue. + * @param values to process, any items remaining after call are put back on the queue. + */ + typedef boost::function<void (Queue& values)> Callback; + + /** When the queue is selected by the poller, values are passed to callback cb. */ + PollableQueue(const Callback& cb, const boost::shared_ptr<sys::Poller>& poller); + + ~PollableQueue(); + + /** Push a value onto the queue. Thread safe */ + void push(const T& t); + + /** Start polling. */ + void start(); + + /** Stop polling and wait for the current callback, if any, to complete. */ + void stop(); + + /** Are we currently stopped?*/ + bool isStopped() const { ScopedLock l(lock); return stopped; } + + size_t size() { ScopedLock l(lock); return queue.size(); } + bool empty() { ScopedLock l(lock); return queue.empty(); } + + private: + typedef sys::Monitor::ScopedLock ScopedLock; + typedef sys::Monitor::ScopedUnlock ScopedUnlock; + + void dispatch(sys::DispatchHandle&); + + mutable sys::Monitor lock; + Callback callback; + boost::shared_ptr<sys::Poller> poller; + PollableCondition condition; + DispatchHandle handle; + Queue queue, batch; + Thread dispatcher; + bool stopped; +}; + +template <class T> PollableQueue<T>::PollableQueue( + const Callback& cb, const boost::shared_ptr<sys::Poller>& p) + : callback(cb), poller(p), + handle(condition, boost::bind(&PollableQueue<T>::dispatch, this, _1), 0, 0), stopped(true) +{ + handle.startWatch(poller); + handle.unwatch(); +} + +template <class T> void PollableQueue<T>::start() { + ScopedLock l(lock); + if (!stopped) return; + stopped = false; + if (!queue.empty()) condition.set(); + handle.rewatch(); +} + +template <class T> PollableQueue<T>::~PollableQueue() { + handle.stopWatch(); +} + +template <class T> void PollableQueue<T>::push(const T& t) { + ScopedLock l(lock); + if (queue.empty()) condition.set(); + queue.push_back(t); +} + +template <class T> void PollableQueue<T>::dispatch(sys::DispatchHandle& h) { + ScopedLock l(lock); + assert(dispatcher.id() == 0); + dispatcher = Thread::current(); + while (!stopped && !queue.empty()) { + assert(batch.empty()); + batch.swap(queue); + { + ScopedUnlock u(lock); // Allow concurrent push to queue. + callback(batch); + } + if (!batch.empty()) { + queue.insert(queue.begin(), batch.begin(), batch.end()); // put back unprocessed items. + batch.clear(); + } + } + dispatcher = Thread(); + if (queue.empty()) condition.clear(); + if (stopped) lock.notifyAll(); + else h.rewatch(); +} + +template <class T> void PollableQueue<T>::stop() { + ScopedLock l(lock); + if (stopped) return; + handle.unwatch(); + stopped = true; + // Avoid deadlock if stop is called from the dispatch thread + while (dispatcher.id() && dispatcher.id() != Thread::current().id()) + lock.wait(); +} + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_POLLABLEQUEUE_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/Poller.h b/RC9/qpid/cpp/src/qpid/sys/Poller.h new file mode 100644 index 0000000000..6b7f4d818e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Poller.h @@ -0,0 +1,109 @@ +#ifndef _sys_Poller_h +#define _sys_Poller_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Time.h" + +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace sys { + +/** + * Poller: abstract class to encapsulate a file descriptor poll to be used + * by a reactor. + * + * @see DispatchHandler for more details of normal use. + */ +class PollerHandle; +class PollerPrivate; +class Poller { + PollerPrivate* const impl; + +public: + typedef boost::shared_ptr<Poller> shared_ptr; + + enum Direction { + NONE = 0, + INPUT, + OUTPUT, + INOUT + }; + + enum EventType { + INVALID = 0, + READABLE, + WRITABLE, + READ_WRITABLE, + DISCONNECTED, + SHUTDOWN, + TIMEOUT + }; + + struct Event { + PollerHandle* handle; + EventType type; + + Event(PollerHandle* handle0, EventType type0) : + handle(handle0), + type(type0) { + } + + void process(); + }; + + Poller(); + ~Poller(); + /** Note: this function is async-signal safe */ + void shutdown(); + + void addFd(PollerHandle& handle, Direction dir); + void delFd(PollerHandle& handle); + void modFd(PollerHandle& handle, Direction dir); + void rearmFd(PollerHandle& handle); + Event wait(Duration timeout = TIME_INFINITE); +}; + +/** + * Handle class to use for polling + */ +class IOHandle; +class PollerHandlePrivate; +class PollerHandle { + friend class Poller; + friend struct Poller::Event; + + PollerHandlePrivate* const impl; + virtual void processEvent(Poller::EventType) {}; + +public: + PollerHandle(const IOHandle& h); + virtual ~PollerHandle(); +}; + +inline void Poller::Event::process() { + handle->processEvent(type); +} + +}} +#endif // _sys_Poller_h diff --git a/RC9/qpid/cpp/src/qpid/sys/ProtocolFactory.h b/RC9/qpid/cpp/src/qpid/sys/ProtocolFactory.h new file mode 100644 index 0000000000..56ab404d82 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ProtocolFactory.h @@ -0,0 +1,58 @@ +#ifndef _sys_ProtocolFactory_h +#define _sys_ProtocolFactory_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/IntegerTypes.h" +#include "qpid/SharedObject.h" +#include "ConnectionCodec.h" +#include <boost/function.hpp> + +namespace qpid { +namespace sys { + +class Poller; + +class ProtocolFactory : public qpid::SharedObject<ProtocolFactory> +{ + public: + typedef boost::function2<void, int, std::string> ConnectFailedCallback; + + virtual ~ProtocolFactory() = 0; + virtual uint16_t getPort() const = 0; + virtual std::string getHost() const = 0; + virtual void accept(boost::shared_ptr<Poller>, ConnectionCodec::Factory*) = 0; + virtual void connect( + boost::shared_ptr<Poller>, + const std::string& host, int16_t port, + ConnectionCodec::Factory* codec, + ConnectFailedCallback failed) = 0; + virtual bool supports(const std::string& /*capability*/) { return false; } +}; + +inline ProtocolFactory::~ProtocolFactory() {} + +}} + + + +#endif //!_sys_ProtocolFactory_h diff --git a/RC9/qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp b/RC9/qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp new file mode 100644 index 0000000000..8afe8ba5ef --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp @@ -0,0 +1,356 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ProtocolFactory.h" + +#include "qpid/Plugin.h" +#include "qpid/broker/Broker.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/rdma/RdmaIO.h" +#include "qpid/sys/OutputControl.h" + +#include <boost/bind.hpp> +#include <memory> + +#include <netdb.h> + +using std::auto_ptr; +using std::string; +using std::stringstream; + +namespace qpid { +namespace sys { + +class RdmaIOHandler : public OutputControl { + Rdma::Connection::intrusive_ptr connection; + std::string identifier; + Rdma::AsynchIO* aio; + ConnectionCodec::Factory* factory; + ConnectionCodec* codec; + bool readError; + + void write(const framing::ProtocolInitiation&); + + public: + RdmaIOHandler(Rdma::Connection::intrusive_ptr& c, ConnectionCodec::Factory* f); + ~RdmaIOHandler(); + void init(Rdma::AsynchIO* a); + void start(Poller::shared_ptr poller) {aio->start(poller);} + + // Output side + void close(); + void activateOutput(); + void giveReadCredit(int32_t credit); + void initProtocolOut(); + + // Input side + void readbuff(Rdma::AsynchIO& aio, Rdma::Buffer* buff); + void initProtocolIn(Rdma::Buffer* buff); + + // Notifications + void full(Rdma::AsynchIO& aio); + void idle(Rdma::AsynchIO& aio); + void error(Rdma::AsynchIO& aio); +}; + +RdmaIOHandler::RdmaIOHandler(Rdma::Connection::intrusive_ptr& c, qpid::sys::ConnectionCodec::Factory* f) : + connection(c), + identifier(c->getPeerName()), + factory(f), + codec(0), + readError(false) +{ +} + +void RdmaIOHandler::init(Rdma::AsynchIO* a) { + aio = a; +} + +RdmaIOHandler::~RdmaIOHandler() { + if (codec) + codec->closed(); + delete codec; + + aio->deferDelete(); +} + +void RdmaIOHandler::write(const framing::ProtocolInitiation& data) +{ + QPID_LOG(debug, "Rdma: SENT [" << identifier << "] INIT(" << data << ")"); + Rdma::Buffer* buff = aio->getBuffer(); + framing::Buffer out(buff->bytes, buff->byteCount); + data.encode(out); + buff->dataCount = data.encodedSize(); + aio->queueWrite(buff); +} + +void RdmaIOHandler::close() { + aio->queueWriteClose(); +} + +void RdmaIOHandler::activateOutput() { + aio->notifyPendingWrite(); +} + +void RdmaIOHandler::idle(Rdma::AsynchIO&) { + // TODO: Shouldn't need this test as idle() should only ever be called when + // the connection is writable anyway + if ( !(aio->writable() && aio->bufferAvailable()) ) { + return; + } + if (codec == 0) return; + if (codec->canEncode()) { + Rdma::Buffer* buff = aio->getBuffer(); + size_t encoded=codec->encode(buff->bytes, buff->byteCount); + buff->dataCount = encoded; + aio->queueWrite(buff); + } + if (codec->isClosed()) + aio->queueWriteClose(); +} + +void RdmaIOHandler::initProtocolOut() { + // We mustn't have already started the conversation + // but we must be able to send + assert( codec == 0 ); + assert( aio->writable() && aio->bufferAvailable() ); + codec = factory->create(*this, identifier); + write(framing::ProtocolInitiation(codec->getVersion())); +} + +void RdmaIOHandler::error(Rdma::AsynchIO&) { + close(); +} + +void RdmaIOHandler::full(Rdma::AsynchIO&) { + QPID_LOG(debug, "Rdma: buffer full [" << identifier << "]"); +} + +// TODO: Dummy implementation of read throttling +void RdmaIOHandler::giveReadCredit(int32_t) { +} + +// The logic here is subtly different from TCP as RDMA is message oriented +// so we define that an RDMA message is a frame - in this case there is no putting back +// of any message remainder - there shouldn't be any. And what we read here can't be +// smaller than a frame +void RdmaIOHandler::readbuff(Rdma::AsynchIO&, Rdma::Buffer* buff) { + if (readError) { + return; + } + size_t decoded = 0; + try { + if (codec) { + decoded = codec->decode(buff->bytes+buff->dataStart, buff->dataCount); + }else{ + // Need to start protocol processing + initProtocolIn(buff); + } + }catch(const std::exception& e){ + QPID_LOG(error, e.what()); + readError = true; + aio->queueWriteClose(); + } +} + +void RdmaIOHandler::initProtocolIn(Rdma::Buffer* buff) { + framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount); + framing::ProtocolInitiation protocolInit; + size_t decoded = 0; + if (protocolInit.decode(in)) { + decoded = in.getPosition(); + QPID_LOG(debug, "Rdma: RECV [" << identifier << "] INIT(" << protocolInit << ")"); + + codec = factory->create(protocolInit.getVersion(), *this, identifier); + + // If we failed to create the codec then we don't understand the offered protocol version + if (!codec) { + // send valid version header & close connection. + write(framing::ProtocolInitiation(framing::highestProtocolVersion)); + readError = true; + aio->queueWriteClose(); + } + } +} + +class RdmaIOProtocolFactory : public ProtocolFactory { + auto_ptr<Rdma::Listener> listener; + const uint16_t listeningPort; + + public: + RdmaIOProtocolFactory(int16_t port, int backlog); + void accept(Poller::shared_ptr, ConnectionCodec::Factory*); + void connect(Poller::shared_ptr, const string& host, int16_t port, ConnectionCodec::Factory*, ConnectFailedCallback); + + uint16_t getPort() const; + string getHost() const; + + private: + bool request(Rdma::Connection::intrusive_ptr&, const Rdma::ConnectionParams&, ConnectionCodec::Factory*); + void established(Poller::shared_ptr, Rdma::Connection::intrusive_ptr&); + void connected(Poller::shared_ptr, Rdma::Connection::intrusive_ptr&, const Rdma::ConnectionParams&, ConnectionCodec::Factory*); + void connectionError(Rdma::Connection::intrusive_ptr&, Rdma::ErrorType); + void disconnected(Rdma::Connection::intrusive_ptr&); + void rejected(Rdma::Connection::intrusive_ptr&, const Rdma::ConnectionParams&, ConnectFailedCallback); +}; + +// Static instance to initialise plugin +static class RdmaIOPlugin : public Plugin { + void earlyInitialize(Target&) { + } + + void initialize(Target& target) { + // Check whether we actually have any rdma devices + if ( Rdma::deviceCount() == 0 ) { + QPID_LOG(info, "Rdma: Disabled: no rdma devices found"); + return; + } + + broker::Broker* broker = dynamic_cast<broker::Broker*>(&target); + // Only provide to a Broker + if (broker) { + const broker::Broker::Options& opts = broker->getOptions(); + ProtocolFactory::shared_ptr protocol(new RdmaIOProtocolFactory(opts.port, opts.connectionBacklog)); + QPID_LOG(notice, "Rdma: Listening on RDMA port " << protocol->getPort()); + broker->registerProtocolFactory("rdma", protocol); + } + } +} rdmaPlugin; + +RdmaIOProtocolFactory::RdmaIOProtocolFactory(int16_t port, int /*backlog*/) : + listeningPort(port) +{} + +void RdmaIOProtocolFactory::established(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci) { + RdmaIOHandler* async = ci->getContext<RdmaIOHandler>(); + async->start(poller); +} + +bool RdmaIOProtocolFactory::request(Rdma::Connection::intrusive_ptr& ci, const Rdma::ConnectionParams& cp, + ConnectionCodec::Factory* f) { + try { + RdmaIOHandler* async = new RdmaIOHandler(ci, f); + Rdma::AsynchIO* aio = + new Rdma::AsynchIO(ci->getQueuePair(), + cp.maxRecvBufferSize, cp.initialXmitCredit, Rdma::DEFAULT_WR_ENTRIES, + boost::bind(&RdmaIOHandler::readbuff, async, _1, _2), + boost::bind(&RdmaIOHandler::idle, async, _1), + 0, // boost::bind(&RdmaIOHandler::full, async, _1), + boost::bind(&RdmaIOHandler::error, async, _1)); + async->init(aio); + + // Record aio so we can get it back from a connection + ci->addContext(async); + return true; + } catch (const Rdma::Exception& e) { + QPID_LOG(error, "Rdma: Cannot accept new connection (Rdma exception): " << e.what()); + } catch (const std::exception& e) { + QPID_LOG(error, "Rdma: Cannot accept new connection (unknown exception): " << e.what()); + } + + // If we get here we caught an exception so reject connection + return false; +} + +void RdmaIOProtocolFactory::connectionError(Rdma::Connection::intrusive_ptr&, Rdma::ErrorType) { +} + +void RdmaIOProtocolFactory::disconnected(Rdma::Connection::intrusive_ptr& ci) { + // If we've got a connection already tear it down, otherwise ignore + RdmaIOHandler* async = ci->getContext<RdmaIOHandler>(); + if (async) { + async->close(); + } + delete async; +} + +uint16_t RdmaIOProtocolFactory::getPort() const { + return listeningPort; // Immutable no need for lock. +} + +string RdmaIOProtocolFactory::getHost() const { + //return listener.getSockname(); + return ""; +} + +void RdmaIOProtocolFactory::accept(Poller::shared_ptr poller, ConnectionCodec::Factory* fact) { + ::sockaddr_in sin; + + sin.sin_family = AF_INET; + sin.sin_port = htons(listeningPort); + sin.sin_addr.s_addr = INADDR_ANY; + + listener.reset( + new Rdma::Listener((const sockaddr&)(sin), + Rdma::ConnectionParams(65536, Rdma::DEFAULT_WR_ENTRIES), + boost::bind(&RdmaIOProtocolFactory::established, this, poller, _1), + boost::bind(&RdmaIOProtocolFactory::connectionError, this, _1, _2), + boost::bind(&RdmaIOProtocolFactory::disconnected, this, _1), + boost::bind(&RdmaIOProtocolFactory::request, this, _1, _2, fact))); + + listener->start(poller); +} + +// Only used for outgoing connections (in federation) +void RdmaIOProtocolFactory::rejected(Rdma::Connection::intrusive_ptr&, const Rdma::ConnectionParams&, ConnectFailedCallback failed) { + failed(-1, "Connection rejected"); +} + +// Do the same as connection request and established but mark a client too +void RdmaIOProtocolFactory::connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci, const Rdma::ConnectionParams& cp, + ConnectionCodec::Factory* f) { + (void) request(ci, cp, f); + established(poller, ci); + RdmaIOHandler* async = ci->getContext<RdmaIOHandler>(); + async->initProtocolOut(); +} + +void RdmaIOProtocolFactory::connect( + Poller::shared_ptr poller, + const std::string& host, int16_t p, + ConnectionCodec::Factory* f, + ConnectFailedCallback failed) +{ + ::addrinfo *res; + ::addrinfo hints = {}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + stringstream ss; ss << p; + string port = ss.str(); + int n = ::getaddrinfo(host.c_str(), port.c_str(), &hints, &res); + if (n<0) { + throw Exception(QPID_MSG("Rdma: Cannot resolve " << host << ": " << ::gai_strerror(n))); + } + + Rdma::Connector* c = + new Rdma::Connector( + *res->ai_addr, + Rdma::ConnectionParams(8000, Rdma::DEFAULT_WR_ENTRIES), + boost::bind(&RdmaIOProtocolFactory::connected, this, poller, _1, _2, f), + boost::bind(&RdmaIOProtocolFactory::connectionError, this, _1, _2), + boost::bind(&RdmaIOProtocolFactory::disconnected, this, _1), + boost::bind(&RdmaIOProtocolFactory::rejected, this, _1, _2, failed)); + + c->start(poller); +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/Runnable.cpp b/RC9/qpid/cpp/src/qpid/sys/Runnable.cpp new file mode 100644 index 0000000000..30122c682f --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Runnable.cpp @@ -0,0 +1,32 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "Runnable.h" +#include <boost/bind.hpp> + +namespace qpid { +namespace sys { + +Runnable::~Runnable() {} + +Runnable::Functor Runnable::functor() +{ + return boost::bind(&Runnable::run, this); +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/sys/Runnable.h b/RC9/qpid/cpp/src/qpid/sys/Runnable.h new file mode 100644 index 0000000000..fb3927c612 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Runnable.h @@ -0,0 +1,50 @@ +#ifndef _Runnable_ +#define _Runnable_ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/function.hpp> + +namespace qpid { +namespace sys { + +/** + * Interface for objects that can be run, e.g. in a thread. + */ +class Runnable +{ + public: + /** Type to represent a runnable as a Functor */ + typedef boost::function0<void> Functor; + + virtual ~Runnable(); + + /** Derived classes override run(). */ + virtual void run() = 0; + + /** Create a functor object that will call this->run(). */ + Functor functor(); +}; + +}} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/sys/ScopedIncrement.h b/RC9/qpid/cpp/src/qpid/sys/ScopedIncrement.h new file mode 100644 index 0000000000..8645ab2484 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ScopedIncrement.h @@ -0,0 +1,67 @@ +#ifndef _posix_ScopedIncrement_h +#define _posix_ScopedIncrement_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/noncopyable.hpp> +#include <boost/function.hpp> + +namespace qpid { +namespace sys { + +/** + * Increment counter in constructor and decrement in destructor. + * Optionally call a function if the decremented counter value is 0. + * Note the function must not throw, it is called in the destructor. + */ +template <class T, class F=boost::function<void()> > +class ScopedIncrement : boost::noncopyable +{ + public: + ScopedIncrement(T& c, F f=0) + : count(c), callback(f) { ++count; } + ~ScopedIncrement() { if (--count == 0 && callback) callback(); } + + private: + T& count; + F callback; +}; + + +/** Decrement counter in constructor and increment in destructor. */ +template <class T> +class ScopedDecrement : boost::noncopyable +{ + public: + ScopedDecrement(T& c) : count(c) { value = --count; } + ~ScopedDecrement() { ++count; } + + /** Return the value after the decrement. */ + operator long() { return value; } + + private: + T& count; + long value; +}; + + +}} + + +#endif // _posix_ScopedIncrement_h diff --git a/RC9/qpid/cpp/src/qpid/sys/Semaphore.h b/RC9/qpid/cpp/src/qpid/sys/Semaphore.h new file mode 100644 index 0000000000..3efb7ce2df --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Semaphore.h @@ -0,0 +1,67 @@ +#ifndef _sys_Semaphore_h +#define _sys_Semaphore_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "Monitor.h" + +namespace qpid { +namespace sys { + +class Semaphore +{ +public: + Semaphore(uint c = 1) : count(c) {} + + void lock() { acquire(); } + void unlock() { release(); } + bool trylock() { return tryAcquire(); } + + bool tryAcquire() + { + Monitor::ScopedLock l(monitor); + if (count) { + count--; + return true; + } else { + return false; + } + } + + void acquire() + { + Monitor::ScopedLock l(monitor); + while (count == 0) monitor.wait(); + count--; + } + + void release() + { + Monitor::ScopedLock l(monitor); + if (!count++) monitor.notifyAll(); + } + +private: + Monitor monitor; + uint count; +}; + +}} + +#endif /*!_sys_Semaphore_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/Shlib.cpp b/RC9/qpid/cpp/src/qpid/sys/Shlib.cpp new file mode 100644 index 0000000000..8fd3f42cc6 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Shlib.cpp @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Shlib.h" + +#include "qpid/log/Statement.h" + +namespace qpid { +namespace sys { + +AutoShlib::~AutoShlib() throw() { + try { + unload(); + } catch(const std::exception& e) { + QPID_LOG(error, "Unloading shared library: " << e.what()); + } +} + +// Note: Other functions are defined in apr/Shlib.cpp or posix/Shlib.cpp. + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/Shlib.h b/RC9/qpid/cpp/src/qpid/sys/Shlib.h new file mode 100644 index 0000000000..a6d94b42d4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Shlib.h @@ -0,0 +1,75 @@ +#ifndef QPID_SYS_SHLIB_H +#define QPID_SYS_SHLIB_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/noncopyable.hpp> +#include <iostream> + +namespace qpid { +namespace sys { + +/** Encapsulates a shared library handle. + *@see AutoShlib + */ +class Shlib { + public: + /** Load a shared library */ + Shlib(const char* libname) { load(libname); } + + /** Load a shared library */ + Shlib(const std::string& libname) { load(libname.c_str()); } + + /** Unload shared library. */ + void unload(); + + /** Look up symbol. */ + void* getSymbol(const char* symbol); + + /** Look up symbol in shared library, cast it to the desired + * pointer type, void* by default. + */ + template <class T> + T getSymbol(const char* symbol) { + // Double cast avoids warning about casting object to function pointer + return reinterpret_cast<T>(reinterpret_cast<intptr_t>( + this->getSymbol(symbol))); + } + + private: + void* handle; + void load(const char* libname); +}; + +/** A shared library handle that unloads the shlib in it's dtor */ +class AutoShlib : public Shlib { + public: + /** Load shared library */ + AutoShlib(const std::string& libname) : Shlib(libname) {} + /** Calls unload() */ + ~AutoShlib() throw(); +}; + + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_SHLIB_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/ShutdownHandler.h b/RC9/qpid/cpp/src/qpid/sys/ShutdownHandler.h new file mode 100644 index 0000000000..88baecb5b6 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ShutdownHandler.h @@ -0,0 +1,37 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ShutdownHandler_ +#define _ShutdownHandler_ + +namespace qpid { +namespace sys { + + class ShutdownHandler + { + public: + virtual void shutdown() = 0; + virtual ~ShutdownHandler(){} + }; + +} +} + +#endif diff --git a/RC9/qpid/cpp/src/qpid/sys/Socket.h b/RC9/qpid/cpp/src/qpid/sys/Socket.h new file mode 100644 index 0000000000..ae48b8104d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Socket.h @@ -0,0 +1,110 @@ +#ifndef _sys_Socket_h +#define _sys_Socket_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "IOHandle.h" +#include "qpid/sys/IntegerTypes.h" + +#include <string> + +struct sockaddr; + +namespace qpid { +namespace sys { + +class Duration; + +class Socket : public IOHandle +{ +public: + /** Create a socket wrapper for descriptor. */ + Socket(); + + /** Create an initialized TCP socket */ + void createTcp() const; + + /** Set timeout for read and write */ + void setTimeout(const Duration& interval) const; + + /** Set socket non blocking */ + void setNonblocking() const; + + void connect(const std::string& host, uint16_t port) const; + + void close() const; + + /** Bind to a port and start listening. + *@param port 0 means choose an available port. + *@param backlog maximum number of pending connections. + *@return The bound port. + */ + int listen(uint16_t port = 0, int backlog = 10) const; + + /** Returns the "socket name" ie the address bound to + * the near end of the socket + */ + std::string getSockname() const; + + /** Returns the "peer name" ie the address bound to + * the remote end of the socket + */ + std::string getPeername() const; + + /** + * Returns an address (host and port) for the remote end of the + * socket + */ + std::string getPeerAddress() const; + /** + * Returns an address (host and port) for the local end of the + * socket + */ + std::string getLocalAddress() const; + + uint16_t getLocalPort() const; + uint16_t getRemotePort() const; + + /** + * Returns the error code stored in the socket. This may be used + * to determine the result of a non-blocking connect. + */ + int getError() const; + + /** Accept a connection from a socket that is already listening + * and has an incoming connection + */ + Socket* accept(struct sockaddr *addr, socklen_t *addrlen) const; + + // TODO The following are raw operations, maybe they need better wrapping? + int read(void *buf, size_t count) const; + int write(const void *buf, size_t count) const; + + void setTcpNoDelay(bool nodelay) const; + +private: + Socket(IOHandlePrivate*); + mutable std::string connectname; +}; + +}} +#endif /*!_sys_Socket_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/SslPlugin.cpp b/RC9/qpid/cpp/src/qpid/sys/SslPlugin.cpp new file mode 100644 index 0000000000..14052c2ee4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/SslPlugin.cpp @@ -0,0 +1,184 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ProtocolFactory.h" + +#include "qpid/Plugin.h" +#include "qpid/sys/ssl/check.h" +#include "qpid/sys/ssl/util.h" +#include "qpid/sys/ssl/SslHandler.h" +#include "qpid/sys/ssl/SslIo.h" +#include "qpid/sys/ssl/SslSocket.h" +#include "qpid/broker/Broker.h" +#include "qpid/log/Statement.h" + +#include <boost/bind.hpp> +#include <memory> + + +namespace qpid { +namespace sys { + +struct SslServerOptions : ssl::SslOptions +{ + uint16_t port; + bool clientAuth; + + SslServerOptions() : port(5671), + clientAuth(false) + { + addOptions() + ("ssl-port", optValue(port, "PORT"), "Port on which to listen for SSL connections") + ("ssl-require-client-authentication", optValue(clientAuth), + "Forces clients to authenticate in order to establish an SSL connection"); + } +}; + +class SslProtocolFactory : public ProtocolFactory { + const bool tcpNoDelay; + qpid::sys::ssl::SslSocket listener; + const uint16_t listeningPort; + std::auto_ptr<qpid::sys::ssl::SslAcceptor> acceptor; + + public: + SslProtocolFactory(const SslServerOptions&, int backlog, bool nodelay); + void accept(Poller::shared_ptr, ConnectionCodec::Factory*); + void connect(Poller::shared_ptr, const std::string& host, int16_t port, + ConnectionCodec::Factory*, + boost::function2<void, int, std::string> failed); + + uint16_t getPort() const; + std::string getHost() const; + bool supports(const std::string& capability); + + private: + void established(Poller::shared_ptr, const qpid::sys::ssl::SslSocket&, ConnectionCodec::Factory*, + bool isClient); +}; + +// Static instance to initialise plugin +static struct SslPlugin : public Plugin { + SslServerOptions options; + + Options* getOptions() { return &options; } + + ~SslPlugin() { ssl::shutdownNSS(); } + + void earlyInitialize(Target&) { + } + + void initialize(Target& target) { + broker::Broker* broker = dynamic_cast<broker::Broker*>(&target); + // Only provide to a Broker + if (broker) { + if (options.certDbPath.empty()) { + QPID_LOG(warning, "SSL plugin not enabled, you must set --qpid-ssl-cert-db to enable it."); + } else { + try { + ssl::initNSS(options, true); + + const broker::Broker::Options& opts = broker->getOptions(); + ProtocolFactory::shared_ptr protocol(new SslProtocolFactory(options, + opts.connectionBacklog, opts.tcpNoDelay)); + QPID_LOG(notice, "Listening for SSL connections on TCP port " << protocol->getPort()); + broker->registerProtocolFactory("ssl", protocol); + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to initialise SSL plugin: " << e.what()); + } + } + } + } +} sslPlugin; + +SslProtocolFactory::SslProtocolFactory(const SslServerOptions& options, int backlog, bool nodelay) : + tcpNoDelay(nodelay), listeningPort(listener.listen(options.port, backlog, options.certName, options.clientAuth)) +{} + +void SslProtocolFactory::established(Poller::shared_ptr poller, const qpid::sys::ssl::SslSocket& s, + ConnectionCodec::Factory* f, bool isClient) { + qpid::sys::ssl::SslHandler* async = new qpid::sys::ssl::SslHandler(s.getPeerAddress(), f); + + if (tcpNoDelay) { + s.setTcpNoDelay(tcpNoDelay); + QPID_LOG(info, "Set TCP_NODELAY on connection to " << s.getPeerAddress()); + } + + if (isClient) + async->setClient(); + qpid::sys::ssl::SslIO* aio = new qpid::sys::ssl::SslIO(s, + boost::bind(&qpid::sys::ssl::SslHandler::readbuff, async, _1, _2), + boost::bind(&qpid::sys::ssl::SslHandler::eof, async, _1), + boost::bind(&qpid::sys::ssl::SslHandler::disconnect, async, _1), + boost::bind(&qpid::sys::ssl::SslHandler::closedSocket, async, _1, _2), + boost::bind(&qpid::sys::ssl::SslHandler::nobuffs, async, _1), + boost::bind(&qpid::sys::ssl::SslHandler::idle, async, _1)); + + async->init(aio, 4); + aio->start(poller); +} + +uint16_t SslProtocolFactory::getPort() const { + return listeningPort; // Immutable no need for lock. +} + +std::string SslProtocolFactory::getHost() const { + return listener.getSockname(); +} + +void SslProtocolFactory::accept(Poller::shared_ptr poller, + ConnectionCodec::Factory* fact) { + acceptor.reset( + new qpid::sys::ssl::SslAcceptor(listener, + boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, false))); + acceptor->start(poller); +} + +void SslProtocolFactory::connect( + Poller::shared_ptr poller, + const std::string& host, int16_t port, + ConnectionCodec::Factory* fact, + ConnectFailedCallback failed) +{ + // Note that the following logic does not cause a memory leak. + // The allocated Socket is freed either by the SslConnector + // upon connection failure or by the SslIoHandle upon connection + // shutdown. The allocated SslConnector frees itself when it + // is no longer needed. + + qpid::sys::ssl::SslSocket* socket = new qpid::sys::ssl::SslSocket(); + new qpid::sys::ssl::SslConnector (*socket, poller, host, port, + boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, true), + failed); +} + +namespace +{ +const std::string SSL = "ssl"; +} + +bool SslProtocolFactory::supports(const std::string& capability) +{ + std::string s = capability; + transform(s.begin(), s.end(), s.begin(), tolower); + return s == SSL; +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/StateMonitor.h b/RC9/qpid/cpp/src/qpid/sys/StateMonitor.h new file mode 100644 index 0000000000..5a92756f3a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/StateMonitor.h @@ -0,0 +1,78 @@ +#ifndef QPID_SYS_STATEMONITOR_H +#define QPID_SYS_STATEMONITOR_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/sys/Waitable.h" + +#include <bitset> + +namespace qpid { +namespace sys { + +/** + * A monitor with an enum state value. + * + *@param Enum: enum type to use for states. + *@param EnumMax: Highest enum value. + */ +template <class Enum, size_t MaxEnum> +class StateMonitor : public Waitable +{ + public: + struct Set : public std::bitset<MaxEnum + 1> { + Set() {} + Set(Enum s) { set(s); } + Set(Enum s, Enum t) { set(s).set(t); } + Set(Enum s, Enum t, Enum u) { set(s).set(t).set(u); } + Set(Enum s, Enum t, Enum u, Enum v) { set(s).set(t).set(u).set(v); } + }; + + + StateMonitor(Enum initial) { state=initial; } + + /** @pre Caller holds a ScopedLock. */ + void set(Enum s) { state=s; notifyAll(); } + /** @pre Caller holds a ScopedLock. */ + StateMonitor& operator=(Enum s) { set(s); return *this; } + + /** @pre Caller holds a ScopedLock. */ + Enum get() const { return state; } + /** @pre Caller holds a ScopedLock. */ + operator Enum() const { return state; } + + /** @pre Caller holds a ScopedLock */ + void waitFor(Enum s) { ScopedWait(*this); while (s != state) wait(); } + /** @pre Caller holds a ScopedLock */ + void waitFor(Set s) { ScopedWait(*this); while (!s.test(state)) wait(); } + /** @pre Caller holds a ScopedLock */ + void waitNot(Enum s) { ScopedWait(*this); while (s == state) wait(); } + /** @pre Caller holds a ScopedLock */ + void waitNot(Set s) { ScopedWait(*this); while (s.test(state)) wait(); } + + private: + Enum state; +}; + +}} + + +#endif /*!QPID_SYS_STATEMONITOR_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/StrError.h b/RC9/qpid/cpp/src/qpid/sys/StrError.h new file mode 100644 index 0000000000..3843f2abe1 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/StrError.h @@ -0,0 +1,35 @@ +#ifndef _sys_StrError_h +#define _sys_StrError_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <string> + +namespace qpid { +namespace sys { + +/** Get the error message for a system number err, e.g. errno. */ +std::string strError(int err); + +}} // namespace qpid + +#endif // _sys_StrError_h diff --git a/RC9/qpid/cpp/src/qpid/sys/SystemInfo.h b/RC9/qpid/cpp/src/qpid/sys/SystemInfo.h new file mode 100644 index 0000000000..d43fe34b04 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/SystemInfo.h @@ -0,0 +1,79 @@ +#ifndef QPID_SYS_SYSTEMINFO_H +#define QPID_SYS_SYSTEMINFO_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/Address.h" + +namespace qpid { +namespace sys { + +/** + * Retrieve information about the system we are running on. + * Results may be dependent on OS/hardware. + */ +namespace SystemInfo { + /** + * Estimate available concurrency, e.g. number of CPU cores. + * -1 means estimate not available on this platform. + */ + long concurrency(); + + /** + * Get the local host name and set it in the specified TcpAddress. + * Returns false if it can't be obtained and sets errno to any error value. + */ + bool getLocalHostname (TcpAddress &address); + + void getLocalIpAddresses (uint16_t port, std::vector<Address> &addrList); + + /** + * Retrieve system identifiers and versions. This is information that can + * generally be retrieved via POSIX uname(). + * + * @param osName Receives the OS name; e.g., GNU/Linux or Windows + * @param nodeName Receives the nodename. This may or may not match the + * set hostname from getLocalHostname(). + * @param release Receives the OS release identifier. + * @param version Receives the OS release version (kernel, build, sp, etc.) + * @param machine Receives the hardware type. + */ + void getSystemId (std::string &osName, + std::string &nodeName, + std::string &release, + std::string &version, + std::string &machine); + + /** + * Get the process ID of the current process. + */ + uint32_t getProcessId(); + + /** + * Get the process ID of the parent of the current process. + */ + uint32_t getParentProcessId(); + + +}}} // namespace qpid::sys::SystemInfo + +#endif /*!QPID_SYS_SYSTEMINFO_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp b/RC9/qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp new file mode 100644 index 0000000000..be091f86d8 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/TCPIOPlugin.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 "ProtocolFactory.h" +#include "AsynchIOHandler.h" +#include "AsynchIO.h" + +#include "qpid/Plugin.h" +#include "qpid/sys/Socket.h" +#include "qpid/broker/Broker.h" +#include "qpid/log/Statement.h" + +#include <boost/bind.hpp> +#include <memory> + +namespace qpid { +namespace sys { + +class AsynchIOProtocolFactory : public ProtocolFactory { + const bool tcpNoDelay; + Socket listener; + const uint16_t listeningPort; + std::auto_ptr<AsynchAcceptor> acceptor; + + public: + AsynchIOProtocolFactory(int16_t port, int backlog, bool nodelay); + void accept(Poller::shared_ptr, ConnectionCodec::Factory*); + void connect(Poller::shared_ptr, const std::string& host, int16_t port, + ConnectionCodec::Factory*, + boost::function2<void, int, std::string> failed); + + uint16_t getPort() const; + std::string getHost() const; + + private: + void established(Poller::shared_ptr, const Socket&, ConnectionCodec::Factory*, + bool isClient); +}; + +// Static instance to initialise plugin +static class TCPIOPlugin : public Plugin { + void earlyInitialize(Target&) { + } + + void initialize(Target& target) { + broker::Broker* broker = dynamic_cast<broker::Broker*>(&target); + // Only provide to a Broker + if (broker) { + const broker::Broker::Options& opts = broker->getOptions(); + if (opts.requireEncrypted) { + QPID_LOG(info, "Not accepting unencrypted connections on TCP"); + } else { + ProtocolFactory::shared_ptr protocol(new AsynchIOProtocolFactory(opts.port, opts.connectionBacklog, + opts.tcpNoDelay)); + QPID_LOG(notice, "Listening on TCP port " << protocol->getPort()); + broker->registerProtocolFactory("tcp", protocol); + } + } + } +} tcpPlugin; + +AsynchIOProtocolFactory::AsynchIOProtocolFactory(int16_t port, int backlog, bool nodelay) : + tcpNoDelay(nodelay), listeningPort(listener.listen(port, backlog)) +{} + +void AsynchIOProtocolFactory::established(Poller::shared_ptr poller, const Socket& s, + ConnectionCodec::Factory* f, bool isClient) { + AsynchIOHandler* async = new AsynchIOHandler(s.getPeerAddress(), f); + + if (tcpNoDelay) { + s.setTcpNoDelay(tcpNoDelay); + QPID_LOG(info, "Set TCP_NODELAY on connection to " << s.getPeerAddress()); + } + + if (isClient) + async->setClient(); + AsynchIO* aio = AsynchIO::create + (s, + boost::bind(&AsynchIOHandler::readbuff, async, _1, _2), + boost::bind(&AsynchIOHandler::eof, async, _1), + boost::bind(&AsynchIOHandler::disconnect, async, _1), + boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2), + boost::bind(&AsynchIOHandler::nobuffs, async, _1), + boost::bind(&AsynchIOHandler::idle, async, _1)); + + async->init(aio, 4); + aio->start(poller); +} + +uint16_t AsynchIOProtocolFactory::getPort() const { + return listeningPort; // Immutable no need for lock. +} + +std::string AsynchIOProtocolFactory::getHost() const { + return listener.getSockname(); +} + +void AsynchIOProtocolFactory::accept(Poller::shared_ptr poller, + ConnectionCodec::Factory* fact) { + acceptor.reset( + new AsynchAcceptor(listener, + boost::bind(&AsynchIOProtocolFactory::established, this, poller, _1, fact, false))); + acceptor->start(poller); +} + +void AsynchIOProtocolFactory::connect( + Poller::shared_ptr poller, + const std::string& host, int16_t port, + ConnectionCodec::Factory* fact, + ConnectFailedCallback failed) +{ + // Note that the following logic does not cause a memory leak. + // The allocated Socket is freed either by the AsynchConnector + // upon connection failure or by the AsynchIO upon connection + // shutdown. The allocated AsynchConnector frees itself when it + // is no longer needed. + + Socket* socket = new Socket(); + AsynchConnector::create (*socket, + poller, + host, + port, + boost::bind(&AsynchIOProtocolFactory::established, + this, poller, _1, fact, true), + failed); +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/Thread.h b/RC9/qpid/cpp/src/qpid/sys/Thread.h new file mode 100644 index 0000000000..2fd59621d8 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Thread.h @@ -0,0 +1,62 @@ +#ifndef _sys_Thread_h +#define _sys_Thread_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <boost/shared_ptr.hpp> + +#ifdef _WIN32 +# define QPID_TSS __declspec(thread) +#elif defined (gcc) +# define QPID_TSS __thread +#else +# define QPID_TSS +#endif + +namespace qpid { +namespace sys { + +class Runnable; +class ThreadPrivate; + +class Thread +{ + boost::shared_ptr<ThreadPrivate> impl; + + public: + Thread(); + explicit Thread(qpid::sys::Runnable*); + explicit Thread(qpid::sys::Runnable&); + + void join(); + + unsigned long id(); + + static Thread current(); + + /** ID of current thread for logging. + * Workaround for broken Thread::current() in APR + */ + static unsigned long logId() { return current().id(); } +}; + +}} +#endif /*!_sys_Thread_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/Time.h b/RC9/qpid/cpp/src/qpid/sys/Time.h new file mode 100644 index 0000000000..d39be95434 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Time.h @@ -0,0 +1,168 @@ +#ifndef _sys_Time_h +#define _sys_Time_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/IntegerTypes.h" +/* + * The platform defines its notion of time as a TimePrivate type. The + * platform's implementation knows how to handle this type. + */ +#if defined (_WIN32) +# include "windows/Time.h" +#else +# include "posix/Time.h" +#endif + +#include <limits> +#include <iosfwd> + +namespace qpid { +namespace sys { + +class Duration; + +/** + * @class AbsTime + * + * Class to represent an instant in time. + * + * The time resolution is in nanosecs, and this is held with 64 bits + * giving a total time span from about 25 million years ago to 25 million + * years hence. As an aside the internal time can sensibly be negative + * meaning before the epoch (probably 1/1/1970 although this class doesn't + * care). + * + * The AbsTime class is a value class and so you don't need to add any + * accessors to its internal state. If you think you want to replace its value, + * you need to construct a new AbsTime and assign it, viz: + * + * AbsTime when = AbsTime::now(); + * ... + * when = AbsTime(when, 2*TIME_SEC); // Advance timer 2 secs + * + * If for some reason you need access to the internal nanosec value you need + * to convert the AbsTime to a Duration and use its conversion to int64_t, viz: + * + * AbsTime now = AbsTime::now(); + * + * int64_t ns = Duration(now); + * + * However note that the nanosecond value that is returned here is not + * defined to be anything in particular and could vary from platform to + * platform. + * + * There are some sensible operations that are currently missing from + * AbsTime, but nearly all that's needed can be done with a mixture of + * AbsTimes and Durations. + * + * For example, convenience operators to add a Duration and AbsTime returning + * an AbsTime would fit here (although you can already perform the operation + * with one of the AbsTime constructors). However trying to add 2 AbsTimes + * doesn't make sense. + */ +class AbsTime { + friend class Duration; + + TimePrivate timepoint; + +public: + inline AbsTime() {} + AbsTime(const AbsTime& time0, const Duration& duration); + // Default assignment operation fine + // Default copy constructor fine + + static AbsTime now(); + static AbsTime FarFuture(); + const TimePrivate& getPrivate(void) const { return timepoint; } + bool operator==(const AbsTime& t) const { return t.timepoint == timepoint; } + template <class S> void serialize(S& s) { s(timepoint); } + + friend bool operator<(const AbsTime& a, const AbsTime& b); + friend bool operator>(const AbsTime& a, const AbsTime& b); + friend std::ostream& operator << (std::ostream&, const AbsTime&); +}; + +std::ostream& operator << (std::ostream&, const AbsTime&); + +/** + * @class Duration + * Class to represent the duration between instants of time. + * + * As AbsTime, this class also uses nanosecs for its time + * resolution where possible. For the most part a duration can be dealt + * with like a 64 bit integer, and indeed there is an implicit conversion which + * makes this quite convenient. + */ +class Duration { + static int64_t max() { return std::numeric_limits<int64_t>::max(); } + int64_t nanosecs; + + friend class AbsTime; + +public: + inline Duration(int64_t time0); + explicit Duration(const AbsTime& time0); + explicit Duration(const AbsTime& start, const AbsTime& finish); + inline operator int64_t() const; +}; + +std::ostream& operator << (std::ostream&, const Duration&); + +inline AbsTime now() { return AbsTime::now(); } + +inline bool operator<(const AbsTime& a, const AbsTime& b) +{ return a.timepoint < b.timepoint; } +inline bool operator>(const AbsTime& a, const AbsTime& b) +{ return a.timepoint > b.timepoint; } + +Duration::Duration(int64_t time0) : + nanosecs(time0) +{} + +Duration::operator int64_t() const +{ return nanosecs; } + +/** Nanoseconds per second. */ +const Duration TIME_SEC = 1000*1000*1000; +/** Nanoseconds per millisecond */ +const Duration TIME_MSEC = 1000*1000; +/** Nanoseconds per microseconds. */ +const Duration TIME_USEC = 1000; +/** Nanoseconds per nanosecond. */ +const Duration TIME_NSEC = 1; + +/** Value to represent an infinite timeout */ +const Duration TIME_INFINITE = std::numeric_limits<int64_t>::max(); + +/** Time greater than any other time */ +const AbsTime FAR_FUTURE = AbsTime::FarFuture(); + +/** Portable sleep for a number of seconds */ +void sleep(int secs); + +/** Portable sleep for a number of microseconds */ +void usleep(uint64_t usecs); + +}} + +#endif /*!_sys_Time_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/TimeoutHandler.h b/RC9/qpid/cpp/src/qpid/sys/TimeoutHandler.h new file mode 100644 index 0000000000..0c10709bbf --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/TimeoutHandler.h @@ -0,0 +1,39 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TimeoutHandler_ +#define _TimeoutHandler_ + +namespace qpid { +namespace sys { + + class TimeoutHandler + { + public: + virtual void idleOut() = 0; + virtual void idleIn() = 0; + virtual ~TimeoutHandler(){} + }; + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/sys/Waitable.h b/RC9/qpid/cpp/src/qpid/sys/Waitable.h new file mode 100644 index 0000000000..7701b6f97d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/Waitable.h @@ -0,0 +1,114 @@ +#ifndef QPID_SYS_WAITABLE_H +#define QPID_SYS_WAITABLE_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Monitor.h" +#include "qpid/sys/ExceptionHolder.h" +#include <assert.h> + +namespace qpid { +namespace sys { + +/** + * A monitor that keeps track of waiting threads. Threads declare a + * ScopedWait around wait() inside a ScopedLock to be considered + * waiters. + * + * Allows waiting threads to be interrupted by an exception. + */ +class Waitable : public Monitor { + public: + Waitable() : waiters(0) {} + + ~Waitable() { assert(waiters == 0); } + + /** Use this inside a scoped lock around the + * call to wait() to be counted as a waiter. + */ + struct ScopedWait { + Waitable& w; + ScopedWait(Waitable& w_) : w(w_) { ++w.waiters; } + ~ScopedWait() { if (--w.waiters==0) w.notifyAll(); } + }; + + /** Block till there are no more waiters in ScopedWaits. + * waitWaiters() does not raise an exception even if waiters + * were interrupted by one. + *@pre Must be called inside a ScopedLock but NOT a ScopedWait. + */ + void waitWaiters() { + while (waiters != 0) + Monitor::wait(); + } + + /** Returns the number of outstanding ScopedWaits. + * Must be called with the lock held. + */ + size_t hasWaiters() const { + return waiters; + } + + /** Set an execption to interrupt waiters in ScopedWait. + * Must be called with the lock held. + */ + void setException(const ExceptionHolder& e) { + exception = e; + notifyAll(); + + } + + /** True if the waitable has an exception */ + bool hasException() const { return exception; } + + /** Clear the exception if any */ + void resetException() { exception.reset(); } + + /** Throws an exception if one is set before or during the wait. */ + void wait() { + ExCheck e(exception); + Monitor::wait(); + } + + /** Throws an exception if one is set before or during the wait. */ + bool wait(const AbsTime& absoluteTime) { + ExCheck e(exception); + return Monitor::wait(absoluteTime); + } + + private: + struct ExCheck { + const ExceptionHolder& exception; + ExCheck(const ExceptionHolder& e) : exception(e) { e.raise(); } + ~ExCheck() { exception.raise(); } + }; + + size_t waiters; + ExceptionHolder exception; + + friend struct ScopedWait; +}; + +}} // namespace qpid::sys + + + +#endif /*!QPID_SYS_WAITABLE_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/apr/APRBase.cpp b/RC9/qpid/cpp/src/qpid/sys/apr/APRBase.cpp new file mode 100644 index 0000000000..724c489303 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/apr/APRBase.cpp @@ -0,0 +1,89 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include "qpid/log/Statement.h" +#include "APRBase.h" + +using namespace qpid::sys; + +APRBase* APRBase::instance = 0; + +APRBase* APRBase::getInstance(){ + if(instance == 0){ + instance = new APRBase(); + } + return instance; +} + + +APRBase::APRBase() : count(0){ + apr_initialize(); + CHECK_APR_SUCCESS(apr_pool_create(&pool, 0)); + CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, pool)); +} + +APRBase::~APRBase(){ + CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); + apr_pool_destroy(pool); + apr_terminate(); +} + +bool APRBase::_increment(){ + bool deleted(false); + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); + if(this == instance){ + count++; + }else{ + deleted = true; + } + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); + return !deleted; +} + +void APRBase::_decrement(){ + APRBase* copy = 0; + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); + if(--count == 0){ + copy = instance; + instance = 0; + } + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); + if(copy != 0){ + delete copy; + } +} + +void APRBase::increment(){ + int count = 0; + while(count++ < 2 && !getInstance()->_increment()) + QPID_LOG(warning, "APR initialization triggered concurrently with termination."); +} + +void APRBase::decrement(){ + getInstance()->_decrement(); +} + +std::string qpid::sys::get_desc(apr_status_t status){ + const int size = 50; + char tmp[size]; + return std::string(apr_strerror(status, tmp, size)); +} + diff --git a/RC9/qpid/cpp/src/qpid/sys/apr/APRBase.h b/RC9/qpid/cpp/src/qpid/sys/apr/APRBase.h new file mode 100644 index 0000000000..7b5644a129 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/apr/APRBase.h @@ -0,0 +1,74 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _APRBase_ +#define _APRBase_ + +#include <string> +#include <apr_thread_mutex.h> +#include <apr_errno.h> + +namespace qpid { +namespace sys { + + /** + * Use of APR libraries necessitates explicit init and terminate + * calls. Any class using APR libs should obtain the reference to + * this singleton and increment on construction, decrement on + * destruction. This class can then correctly initialise apr + * before the first use and terminate after the last use. + */ + class APRBase{ + static APRBase* instance; + apr_pool_t* pool; + apr_thread_mutex_t* mutex; + int count; + + APRBase(); + ~APRBase(); + static APRBase* getInstance(); + bool _increment(); + void _decrement(); + public: + static void increment(); + static void decrement(); + }; + + //this is also a convenient place for a helper function for error checking: + void check(apr_status_t status, const char* file, const int line); + std::string get_desc(apr_status_t status); + +#define CHECK_APR_SUCCESS(A) qpid::sys::check(A, __FILE__, __LINE__); + +} +} + +// Inlined as it is called *a lot* +void inline qpid::sys::check(apr_status_t status, const char* file, const int line){ + if (status != APR_SUCCESS){ + char tmp[256]; + throw Exception(QPID_MSG(apr_strerror(status, tmp, size))) + } +} + + + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/sys/apr/APRPool.cpp b/RC9/qpid/cpp/src/qpid/sys/apr/APRPool.cpp new file mode 100644 index 0000000000..e8b71f6e8a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/apr/APRPool.cpp @@ -0,0 +1,41 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "APRPool.h" +#include "APRBase.h" +#include <boost/pool/detail/singleton.hpp> + +using namespace qpid::sys; + +APRPool::APRPool(){ + APRBase::increment(); + CHECK_APR_SUCCESS(apr_pool_create(&pool, NULL)); +} + +APRPool::~APRPool(){ + apr_pool_destroy(pool); + APRBase::decrement(); +} + +apr_pool_t* APRPool::get() { + return boost::details::pool::singleton_default<APRPool>::instance().pool; +} + diff --git a/RC9/qpid/cpp/src/qpid/sys/apr/APRPool.h b/RC9/qpid/cpp/src/qpid/sys/apr/APRPool.h new file mode 100644 index 0000000000..da7661fcfa --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/apr/APRPool.h @@ -0,0 +1,50 @@ +#ifndef _APRPool_ +#define _APRPool_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <boost/noncopyable.hpp> +#include <apr_pools.h> + +namespace qpid { +namespace sys { +/** + * Singleton APR memory pool. + */ +class APRPool : private boost::noncopyable { + public: + APRPool(); + ~APRPool(); + + /** Get singleton instance */ + static apr_pool_t* get(); + + private: + apr_pool_t* pool; +}; + +}} + + + + + +#endif /*!_APRPool_*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/apr/Condition.h b/RC9/qpid/cpp/src/qpid/sys/apr/Condition.h new file mode 100644 index 0000000000..5e544219ab --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/apr/Condition.h @@ -0,0 +1,84 @@ +#ifndef _sys_apr_Condition_h +#define _sys_apr_Condition_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "APRPool.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Time.h" + +#include <sys/errno.h> +#include <boost/noncopyable.hpp> +#include <apr_thread_cond.h> + +namespace qpid { +namespace sys { + +/** + * A condition variable for thread synchronization. + */ +class Condition +{ + public: + inline Condition(); + inline ~Condition(); + inline void wait(Mutex&); + inline bool wait(Mutex&, const AbsTime& absoluteTime); + inline void notify(); + inline void notifyAll(); + + private: + apr_thread_cond_t* condition; +}; + + +Condition::Condition() { + CHECK_APR_SUCCESS(apr_thread_cond_create(&condition, APRPool::get())); +} + +Condition::~Condition() { + CHECK_APR_SUCCESS(apr_thread_cond_destroy(condition)); +} + +void Condition::wait(Mutex& mutex) { + CHECK_APR_SUCCESS(apr_thread_cond_wait(condition, mutex.mutex)); +} + +bool Condition::wait(Mutex& mutex, const AbsTime& absoluteTime){ + // APR uses microseconds. + apr_status_t status = + apr_thread_cond_timedwait( + condition, mutex.mutex, Duration(now(), absoluteTime)/TIME_USEC); + if(status != APR_TIMEUP) CHECK_APR_SUCCESS(status); + return status == 0; +} + +void Condition::notify(){ + CHECK_APR_SUCCESS(apr_thread_cond_signal(condition)); +} + +void Condition::notifyAll(){ + CHECK_APR_SUCCESS(apr_thread_cond_broadcast(condition)); +} + +}} +#endif /*!_sys_apr_Condition_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/apr/Mutex.h b/RC9/qpid/cpp/src/qpid/sys/apr/Mutex.h new file mode 100644 index 0000000000..51089c98ff --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/apr/Mutex.h @@ -0,0 +1,124 @@ +#ifndef _sys_apr_Mutex_h +#define _sys_apr_Mutex_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "APRBase.h" +#include "APRPool.h" + +#include <boost/noncopyable.hpp> +#include <apr_thread_mutex.h> + +namespace qpid { +namespace sys { + +class Condition; + +/** + * Mutex lock. + */ +class Mutex : private boost::noncopyable { + public: + typedef ScopedLock<Mutex> ScopedLock; + typedef ScopedUnlock<Mutex> ScopedUnlock; + + inline Mutex(); + inline ~Mutex(); + inline void lock(); + inline void unlock(); + inline bool trylock(); + + protected: + apr_thread_mutex_t* mutex; + friend class Condition; +}; + +Mutex::Mutex() { + CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, APRPool::get())); +} + +Mutex::~Mutex(){ + CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); +} + +void Mutex::lock() { + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); +} +void Mutex::unlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); +} + +bool Mutex::trylock() { + return apr_thread_mutex_trylock(mutex) == 0; +} + + +/** + * RW lock. + */ +class RWlock : private boost::noncopyable { + friend class Condition; + +public: + typedef ScopedRlock<RWlock> ScopedRlock; + typedef ScopedWlock<RWlock> ScopedWlock; + + inline RWlock(); + inline ~RWlock(); + inline void wlock(); // will write-lock + inline void rlock(); // will read-lock + inline void unlock(); + inline bool trywlock(); // will write-try + inline bool tryrlock(); // will read-try + + protected: + apr_thread_mutex_t* mutex; +}; + +RWlock::RWlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, APRPool::get())); +} + +RWlock::~RWlock(){ + CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); +} + +void RWlock::wlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); +} + +void RWlock::rlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); +} + +void RWlock::unlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); +} + +bool RWlock::trywlock() { + return apr_thread_mutex_trylock(mutex) == 0; +} + +bool RWlock::tryrlock() { + return apr_thread_mutex_trylock(mutex) == 0; +} + + +}} +#endif /*!_sys_apr_Mutex_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/apr/Shlib.cpp b/RC9/qpid/cpp/src/qpid/sys/apr/Shlib.cpp new file mode 100644 index 0000000000..b0ba706713 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/apr/Shlib.cpp @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Shlib.h" +#include "APRBase.h" +#include "APRPool.h" +#include <apr_dso.h> + +namespace qpid { +namespace sys { + +void Shlib::load(const char* libname) { + apr_dso_handle_t* aprHandle; + CHECK_APR_SUCCESS( + apr_dso_load(&aprHandle, libname, APRPool::get())); + handle=aprHandle; +} + +void Shlib::unload() { + CHECK_APR_SUCCESS( + apr_dso_unload(static_cast<apr_dso_handle_t*>(handle))); +} + +void* Shlib::getSymbol(const char* name) { + apr_dso_handle_sym_t symbol; + CHECK_APR_SUCCESS(apr_dso_sym(&symbol, + static_cast<apr_dso_handle_t*>(handle), + name)); + return (void*) symbol; +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/apr/Socket.cpp b/RC9/qpid/cpp/src/qpid/sys/apr/Socket.cpp new file mode 100644 index 0000000000..577268844a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/apr/Socket.cpp @@ -0,0 +1,114 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +#include "qpid/sys/Socket.h" + +#include "APRBase.h" +#include "APRPool.h" + +#include <apr_network_io.h> + +namespace qpid { +namespace sys { + +class SocketPrivate { +public: + SocketPrivate(apr_socket_t* s = 0) : + socket(s) + {} + + apr_socket_t* socket; +}; + +Socket::Socket() : + impl(new SocketPrivate) +{ + createTcp(); +} + +Socket::Socket(SocketPrivate* sp) : + impl(sp) +{} + +Socket::~Socket() { + delete impl; +} + +void Socket::createTcp() const { + apr_socket_t*& socket = impl->socket; + apr_socket_t* s; + CHECK_APR_SUCCESS( + apr_socket_create( + &s, APR_INET, SOCK_STREAM, APR_PROTO_TCP, + APRPool::get())); + socket = s; +} + +void Socket::setTimeout(const Duration& interval) const { + apr_socket_t*& socket = impl->socket; + apr_socket_timeout_set(socket, interval/TIME_USEC); +} + +void Socket::connect(const std::string& host, int port) const { + apr_socket_t*& socket = impl->socket; + apr_sockaddr_t* address; + CHECK_APR_SUCCESS( + apr_sockaddr_info_get( + &address, host.c_str(), APR_UNSPEC, port, APR_IPV4_ADDR_OK, + APRPool::get())); + CHECK_APR_SUCCESS(apr_socket_connect(socket, address)); +} + +void Socket::close() const { + apr_socket_t*& socket = impl->socket; + if (socket == 0) return; + CHECK_APR_SUCCESS(apr_socket_close(socket)); + socket = 0; +} + +ssize_t Socket::send(const void* data, size_t size) const +{ + apr_socket_t*& socket = impl->socket; + apr_size_t sent = size; + apr_status_t status = + apr_socket_send(socket, reinterpret_cast<const char*>(data), &sent); + if (APR_STATUS_IS_TIMEUP(status)) return SOCKET_TIMEOUT; + if (APR_STATUS_IS_EOF(status)) return SOCKET_EOF; + CHECK_APR_SUCCESS(status); + return sent; +} + +ssize_t Socket::recv(void* data, size_t size) const +{ + apr_socket_t*& socket = impl->socket; + apr_size_t received = size; + apr_status_t status = + apr_socket_recv(socket, reinterpret_cast<char*>(data), &received); + if (APR_STATUS_IS_TIMEUP(status)) + return SOCKET_TIMEOUT; + if (APR_STATUS_IS_EOF(status)) + return SOCKET_EOF; + CHECK_APR_SUCCESS(status); + return received; +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/apr/Thread.cpp b/RC9/qpid/cpp/src/qpid/sys/apr/Thread.cpp new file mode 100644 index 0000000000..3369ef7eb1 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/apr/Thread.cpp @@ -0,0 +1,34 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Thread.h" +#include "qpid/sys/Runnable.h" + +using namespace qpid::sys; +using qpid::sys::Runnable; + +void* APR_THREAD_FUNC Thread::runRunnable(apr_thread_t* thread, void *data) { + reinterpret_cast<Runnable*>(data)->run(); + CHECK_APR_SUCCESS(apr_thread_exit(thread, APR_SUCCESS)); + return NULL; +} + + diff --git a/RC9/qpid/cpp/src/qpid/sys/apr/Thread.h b/RC9/qpid/cpp/src/qpid/sys/apr/Thread.h new file mode 100644 index 0000000000..8cbbc0456e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/apr/Thread.h @@ -0,0 +1,106 @@ +#ifndef _sys_apr_Thread_h +#define _sys_apr_Thread_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "APRPool.h" +#include "APRBase.h" + +#include <apr_thread_proc.h> +#include <apr_portable.h> + +namespace qpid { +namespace sys { + +class Runnable; + +class Thread +{ + public: + inline static Thread current(); + + /** ID of current thread for logging. + * Workaround for broken Thread::current() in APR + */ + inline static long logId(); + + inline static void yield(); + + inline Thread(); + inline explicit Thread(qpid::sys::Runnable*); + inline explicit Thread(qpid::sys::Runnable&); + + inline void join(); + + inline long id(); + + private: + static void* APR_THREAD_FUNC runRunnable(apr_thread_t* thread, void *data); + inline Thread(apr_thread_t* t); + apr_thread_t* thread; +}; + +Thread::Thread() : thread(0) {} + +Thread::Thread(Runnable* runnable) { + CHECK_APR_SUCCESS( + apr_thread_create(&thread, 0, runRunnable, runnable, APRPool::get())); +} + +Thread::Thread(Runnable& runnable) { + CHECK_APR_SUCCESS( + apr_thread_create(&thread, 0, runRunnable, &runnable, APRPool::get())); +} + +void Thread::join(){ + apr_status_t status; + if (thread != 0) + CHECK_APR_SUCCESS(apr_thread_join(&status, thread)); +} + +long Thread::id() { + return long(thread); +} + +/** ID of current thread for logging. + * Workaround for broken Thread::current() in APR + */ +long Thread::logId() { + return static_cast<long>(apr_os_thread_current()); +} + +Thread::Thread(apr_thread_t* t) : thread(t) {} + +Thread Thread::current(){ + apr_thread_t* thr; + apr_os_thread_t osthr = apr_os_thread_current(); + CHECK_APR_SUCCESS(apr_os_thread_put(&thr, &osthr, APRPool::get())); + return Thread(thr); +} + +void Thread::yield() +{ + apr_thread_yield(); +} + +}} +#endif /*!_sys_apr_Thread_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/apr/Time.cpp b/RC9/qpid/cpp/src/qpid/sys/apr/Time.cpp new file mode 100644 index 0000000000..34e740b144 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/apr/Time.cpp @@ -0,0 +1,36 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Time.h" + +#include <apr_time.h> + +namespace qpid { +namespace sys { + +AbsTime AbsTime::now() { + AbsTime time_now; + time_now.time_ns = apr_time_now() * TIME_USEC; + return time_now; +} + +}} + diff --git a/RC9/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp b/RC9/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp new file mode 100644 index 0000000000..a1e624ea75 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp @@ -0,0 +1,371 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Poller.h" +#include "qpid/sys/IOHandle.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/DeletionManager.h" +#include "qpid/sys/posix/check.h" +#include "qpid/sys/posix/PrivatePosix.h" + +#include <sys/epoll.h> +#include <errno.h> + +#include <assert.h> +#include <vector> +#include <exception> + +namespace qpid { +namespace sys { + +// Deletion manager to handle deferring deletion of PollerHandles to when they definitely aren't being used +DeletionManager<PollerHandlePrivate> PollerHandleDeletionManager; + +// Instantiate (and define) class static for DeletionManager +template <> +DeletionManager<PollerHandlePrivate>::AllThreadsStatuses DeletionManager<PollerHandlePrivate>::allThreadsStatuses(0); + +class PollerHandlePrivate { + friend class Poller; + friend class PollerHandle; + + enum FDStat { + ABSENT, + MONITORED, + INACTIVE, + HUNGUP, + MONITORED_HUNGUP, + DELETED + }; + + int fd; + ::__uint32_t events; + PollerHandle* pollerHandle; + FDStat stat; + Mutex lock; + + PollerHandlePrivate(int f, PollerHandle* p) : + fd(f), + events(0), + pollerHandle(p), + stat(ABSENT) { + } + + bool isActive() const { + return stat == MONITORED || stat == MONITORED_HUNGUP; + } + + void setActive() { + stat = (stat == HUNGUP) ? MONITORED_HUNGUP : MONITORED; + } + + bool isInactive() const { + return stat == INACTIVE || stat == HUNGUP; + } + + void setInactive() { + stat = INACTIVE; + } + + bool isIdle() const { + return stat == ABSENT; + } + + void setIdle() { + stat = ABSENT; + } + + bool isHungup() const { + return stat == MONITORED_HUNGUP || stat == HUNGUP; + } + + void setHungup() { + assert(stat == MONITORED); + stat = HUNGUP; + } + + bool isDeleted() const { + return stat == DELETED; + } + + void setDeleted() { + stat = DELETED; + } +}; + +PollerHandle::PollerHandle(const IOHandle& h) : + impl(new PollerHandlePrivate(toFd(h.impl), this)) +{} + +PollerHandle::~PollerHandle() { + { + ScopedLock<Mutex> l(impl->lock); + if (impl->isDeleted()) { + return; + } + if (impl->isActive()) { + impl->setDeleted(); + } + } + PollerHandleDeletionManager.markForDeletion(impl); +} + +/** + * Concrete implementation of Poller to use the Linux specific epoll + * interface + */ +class PollerPrivate { + friend class Poller; + + static const int DefaultFds = 256; + + struct ReadablePipe { + int fds[2]; + + /** + * This encapsulates an always readable pipe which we can add + * to the epoll set to force epoll_wait to return + */ + ReadablePipe() { + QPID_POSIX_CHECK(::pipe(fds)); + // Just write the pipe's fds to the pipe + QPID_POSIX_CHECK(::write(fds[1], fds, 2)); + } + + ~ReadablePipe() { + ::close(fds[0]); + ::close(fds[1]); + } + + int getFD() { + return fds[0]; + } + }; + + static ReadablePipe alwaysReadable; + + const int epollFd; + bool isShutdown; + + static ::__uint32_t directionToEpollEvent(Poller::Direction dir) { + switch (dir) { + case Poller::INPUT: return ::EPOLLIN; + case Poller::OUTPUT: return ::EPOLLOUT; + case Poller::INOUT: return ::EPOLLIN | ::EPOLLOUT; + default: return 0; + } + } + + static Poller::EventType epollToDirection(::__uint32_t events) { + // POLLOUT & POLLHUP are mutually exclusive really, but at least socketpairs + // can give you both! + events = (events & ::EPOLLHUP) ? events & ~::EPOLLOUT : events; + ::__uint32_t e = events & (::EPOLLIN | ::EPOLLOUT); + switch (e) { + case ::EPOLLIN: return Poller::READABLE; + case ::EPOLLOUT: return Poller::WRITABLE; + case ::EPOLLIN | ::EPOLLOUT: return Poller::READ_WRITABLE; + default: + return (events & (::EPOLLHUP | ::EPOLLERR)) ? + Poller::DISCONNECTED : Poller::INVALID; + } + } + + PollerPrivate() : + epollFd(::epoll_create(DefaultFds)), + isShutdown(false) { + QPID_POSIX_CHECK(epollFd); + } + + ~PollerPrivate() { + // It's probably okay to ignore any errors here as there can't be data loss + ::close(epollFd); + } +}; + +PollerPrivate::ReadablePipe PollerPrivate::alwaysReadable; + +void Poller::addFd(PollerHandle& handle, Direction dir) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + ::epoll_event epe; + int op; + + if (eh.isIdle()) { + op = EPOLL_CTL_ADD; + epe.events = PollerPrivate::directionToEpollEvent(dir) | ::EPOLLONESHOT; + } else { + assert(eh.isActive()); + op = EPOLL_CTL_MOD; + epe.events = eh.events | PollerPrivate::directionToEpollEvent(dir); + } + epe.data.u64 = 0; // Keep valgrind happy + epe.data.ptr = &eh; + + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, op, eh.fd, &epe)); + + // Record monitoring state of this fd + eh.events = epe.events; + eh.setActive(); +} + +void Poller::delFd(PollerHandle& handle) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + assert(!eh.isIdle()); + int rc = ::epoll_ctl(impl->epollFd, EPOLL_CTL_DEL, eh.fd, 0); + // Ignore EBADF since deleting a nonexistent fd has the overall required result! + // And allows the case where a sloppy program closes the fd and then does the delFd() + if (rc == -1 && errno != EBADF) { + QPID_POSIX_CHECK(rc); + } + eh.setIdle(); +} + +// modFd is equivalent to delFd followed by addFd +void Poller::modFd(PollerHandle& handle, Direction dir) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + assert(!eh.isIdle()); + + ::epoll_event epe; + epe.events = PollerPrivate::directionToEpollEvent(dir) | ::EPOLLONESHOT; + epe.data.u64 = 0; // Keep valgrind happy + epe.data.ptr = &eh; + + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, eh.fd, &epe)); + + // Record monitoring state of this fd + eh.events = epe.events; + eh.setActive(); +} + +void Poller::rearmFd(PollerHandle& handle) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + assert(eh.isInactive()); + + ::epoll_event epe; + epe.events = eh.events; + epe.data.u64 = 0; // Keep valgrind happy + epe.data.ptr = &eh; + + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, eh.fd, &epe)); + + eh.setActive(); +} + +void Poller::shutdown() { + // NB: this function must be async-signal safe, it must not + // call any function that is not async-signal safe. + + // Allow sloppy code to shut us down more than once + if (impl->isShutdown) + return; + + // Don't use any locking here - isshutdown will be visible to all + // after the epoll_ctl() anyway (it's a memory barrier) + impl->isShutdown = true; + + // Add always readable fd to epoll (not EPOLLONESHOT) + int fd = impl->alwaysReadable.getFD(); + ::epoll_event epe; + epe.events = ::EPOLLIN; + epe.data.u64 = 0; // Keep valgrind happy - don't strictly need next line now + epe.data.ptr = 0; + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_ADD, fd, &epe)); +} + +Poller::Event Poller::wait(Duration timeout) { + epoll_event epe; + int timeoutMs = (timeout == TIME_INFINITE) ? -1 : timeout / TIME_MSEC; + + // Repeat until we weren't interupted + do { + PollerHandleDeletionManager.markAllUnusedInThisThread(); + int rc = ::epoll_wait(impl->epollFd, &epe, 1, timeoutMs); + + if (impl->isShutdown) { + PollerHandleDeletionManager.markAllUnusedInThisThread(); + return Event(0, SHUTDOWN); + } + + if (rc ==-1 && errno != EINTR) { + QPID_POSIX_CHECK(rc); + } else if (rc > 0) { + assert(rc == 1); + PollerHandlePrivate& eh = *static_cast<PollerHandlePrivate*>(epe.data.ptr); + + ScopedLock<Mutex> l(eh.lock); + + // the handle could have gone inactive since we left the epoll_wait + if (eh.isActive()) { + PollerHandle* handle = eh.pollerHandle; + + // If the connection has been hungup we could still be readable + // (just not writable), allow us to readable until we get here again + if (epe.events & ::EPOLLHUP) { + if (eh.isHungup()) { + return Event(handle, DISCONNECTED); + } + eh.setHungup(); + } else { + eh.setInactive(); + } + return Event(handle, PollerPrivate::epollToDirection(epe.events)); + } else if (eh.isDeleted()) { + // The handle has been deleted whilst still active and so must be removed + // from the poller + int rc = ::epoll_ctl(impl->epollFd, EPOLL_CTL_DEL, eh.fd, 0); + // Ignore EBADF since it's quite likely that we could race with closing the fd + if (rc == -1 && errno != EBADF) { + QPID_POSIX_CHECK(rc); + } + } + } + // We only get here if one of the following: + // * epoll_wait was interrupted by a signal + // * epoll_wait timed out + // * the state of the handle changed after being returned by epoll_wait + // + // The only things we can do here are return a timeout or wait more. + // Obviously if we timed out we return timeout; if the wait was meant to + // be indefinite then we should never return with a time out so we go again. + // If the wait wasn't indefinite, but we were interrupted then we have to return + // with a timeout as we don't know how long we've waited so far and so we can't + // continue the wait. + if (rc == 0 || timeoutMs != -1) { + PollerHandleDeletionManager.markAllUnusedInThisThread(); + return Event(0, TIMEOUT); + } + } while (true); +} + +// Concrete constructors +Poller::Poller() : + impl(new PollerPrivate()) +{} + +Poller::~Poller() { + delete impl; +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp b/RC9/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp new file mode 100644 index 0000000000..9a5798311b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp @@ -0,0 +1,595 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/AsynchIO.h" +#include "qpid/sys/Socket.h" +#include "qpid/sys/Time.h" +#include "qpid/log/Statement.h" + +#include "check.h" + +// TODO The basic algorithm here is not really POSIX specific and with a +// bit more abstraction could (should) be promoted to be platform portable +#include <unistd.h> +#include <sys/socket.h> +#include <signal.h> +#include <errno.h> +#include <string.h> + +#include <boost/bind.hpp> + +using namespace qpid::sys; + +namespace { + +struct StaticInit { + StaticInit() { + /** + * Make *process* not generate SIGPIPE when writing to closed + * pipe/socket (necessary as default action is to terminate process) + */ + ::signal(SIGPIPE, SIG_IGN); + }; +} init; + +/* + * We keep per thread state to avoid locking overhead. The assumption is that + * on average all the connections are serviced by all the threads so the state + * recorded in each thread is about the same. If this turns out not to be the + * case we could rebalance the info occasionally. + */ +__thread int threadReadTotal = 0; +__thread int threadMaxRead = 0; +__thread int threadReadCount = 0; +__thread int threadWriteTotal = 0; +__thread int threadWriteCount = 0; +__thread int64_t threadMaxReadTimeNs = 2 * 1000000; // start at 2ms +} + +/* + * Asynch Acceptor + */ +namespace qpid { +namespace sys { + +class AsynchAcceptorPrivate { +public: + AsynchAcceptorPrivate(const Socket& s, AsynchAcceptor::Callback callback); + void start(Poller::shared_ptr poller); + +private: + void readable(DispatchHandle& handle); + +private: + AsynchAcceptor::Callback acceptedCallback; + DispatchHandle handle; + const Socket& socket; + +}; + +}} // namespace qpid::sys + +AsynchAcceptor::AsynchAcceptor(const Socket& s, Callback callback) : + impl(new AsynchAcceptorPrivate(s, callback)) +{} + +AsynchAcceptor::~AsynchAcceptor() +{ delete impl;} + +void AsynchAcceptor::start(Poller::shared_ptr poller) { + impl->start(poller); +} + +AsynchAcceptorPrivate::AsynchAcceptorPrivate(const Socket& s, + AsynchAcceptor::Callback callback) : + acceptedCallback(callback), + handle(s, boost::bind(&AsynchAcceptorPrivate::readable, this, _1), 0, 0), + socket(s) { + + s.setNonblocking(); +} + +void AsynchAcceptorPrivate::start(Poller::shared_ptr poller) { + handle.startWatch(poller); +} + +/* + * We keep on accepting as long as there is something to accept + */ +void AsynchAcceptorPrivate::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(0, 0); + if (s) { + acceptedCallback(*s); + } else { + break; + } + } catch (const std::exception& e) { + QPID_LOG(error, "Could not accept socket: " << e.what()); + } + } while (true); + + h.rewatch(); +} + +/* + * Asynch Connector + */ +namespace qpid { +namespace sys { +namespace posix { + +/* + * POSIX version of AsynchIO TCP socket connector. + * + * The class is implemented in terms of DispatchHandle to allow it to be + * deleted by deleting the contained DispatchHandle. + */ +class AsynchConnector : public qpid::sys::AsynchConnector, + private DispatchHandle { + +private: + void connComplete(DispatchHandle& handle); + void failure(int, std::string); + +private: + ConnectedCallback connCallback; + FailedCallback failCallback; + const Socket& socket; + +public: + AsynchConnector(const Socket& socket, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb = 0); +}; + +AsynchConnector::AsynchConnector(const Socket& s, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb) : + DispatchHandle(s, + 0, + boost::bind(&AsynchConnector::connComplete, this, _1), + boost::bind(&AsynchConnector::connComplete, this, _1)), + connCallback(connCb), + failCallback(failCb), + socket(s) +{ + socket.setNonblocking(); + try { + socket.connect(hostname, port); + startWatch(poller); + } catch(std::exception& e) { + failure(-1, std::string(e.what())); + } +} + +void AsynchConnector::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 AsynchConnector::failure(int errCode, std::string message) +{ + if (failCallback) + failCallback(errCode, message); + + socket.close(); + delete &socket; + + DispatchHandle::doDelete(); +} + +} // namespace posix + + +AsynchConnector* qpid::sys::AsynchConnector::create(const Socket& s, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb) +{ + return new qpid::sys::posix::AsynchConnector(s, + poller, + hostname, + port, + connCb, + failCb); +} + +/* + * POSIX version of AsynchIO reader/writer + * + * The class is implemented in terms of DispatchHandle to allow it to be + * deleted by deleting the contained DispatchHandle. + */ +namespace posix { + +class AsynchIO : public qpid::sys::AsynchIO, private DispatchHandle { + +public: + AsynchIO(const Socket& s, + ReadCallback rCb, + EofCallback eofCb, + DisconnectCallback disCb, + ClosedCallback cCb = 0, + BuffersEmptyCallback eCb = 0, + IdleCallback iCb = 0); + + // Methods inherited from qpid::sys::AsynchIO + + virtual void queueForDeletion(); + + virtual void start(Poller::shared_ptr poller); + virtual void queueReadBuffer(BufferBase* buff); + virtual void unread(BufferBase* buff); + virtual void queueWrite(BufferBase* buff); + virtual void notifyPendingWrite(); + virtual void queueWriteClose(); + virtual bool writeQueueEmpty(); + virtual void startReading(); + virtual BufferBase* getQueuedBuffer(); + +private: + ~AsynchIO(); + + // Methods that are callback targets from Dispatcher. + void readable(DispatchHandle& handle); + void writeable(DispatchHandle& handle); + void disconnected(DispatchHandle& handle); + void close(DispatchHandle& handle); + +private: + ReadCallback readCallback; + EofCallback eofCallback; + DisconnectCallback disCallback; + ClosedCallback closedCallback; + BuffersEmptyCallback emptyCallback; + IdleCallback idleCallback; + const Socket& socket; + std::deque<BufferBase*> bufferQueue; + std::deque<BufferBase*> writeQueue; + bool queuedClose; + /** + * This flag is used to detect and handle concurrency between + * calls to notifyPendingWrite() (which can be made from any thread) and + * the execution of the writeable() method (which is always on the + * thread processing this handle. + */ + volatile bool writePending; +}; + +AsynchIO::AsynchIO(const Socket& s, + ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb, + ClosedCallback cCb, BuffersEmptyCallback eCb, IdleCallback iCb) : + + DispatchHandle(s, + boost::bind(&AsynchIO::readable, this, _1), + boost::bind(&AsynchIO::writeable, this, _1), + boost::bind(&AsynchIO::disconnected, this, _1)), + readCallback(rCb), + eofCallback(eofCb), + disCallback(disCb), + closedCallback(cCb), + emptyCallback(eCb), + idleCallback(iCb), + socket(s), + queuedClose(false), + writePending(false) { + + s.setNonblocking(); +} + +struct deleter +{ + template <typename T> + void operator()(T *ptr){ delete ptr;} +}; + +AsynchIO::~AsynchIO() { + std::for_each( bufferQueue.begin(), bufferQueue.end(), deleter()); + std::for_each( writeQueue.begin(), writeQueue.end(), deleter()); +} + +void AsynchIO::queueForDeletion() { + DispatchHandle::doDelete(); +} + +void AsynchIO::start(Poller::shared_ptr poller) { + DispatchHandle::startWatch(poller); +} + +void AsynchIO::queueReadBuffer(BufferBase* buff) { + assert(buff); + buff->dataStart = 0; + buff->dataCount = 0; + bufferQueue.push_back(buff); + DispatchHandle::rewatchRead(); +} + +void AsynchIO::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 AsynchIO::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 AsynchIO::notifyPendingWrite() { + writePending = true; + DispatchHandle::rewatchWrite(); +} + +void AsynchIO::queueWriteClose() { + queuedClose = true; + DispatchHandle::rewatchWrite(); +} + +bool AsynchIO::writeQueueEmpty() { + return writeQueue.empty(); +} + +void AsynchIO::startReading() { + DispatchHandle::rewatchRead(); +} + +/** Return a queued buffer if there are enough + * to spare + */ +AsynchIO::BufferBase* AsynchIO::getQueuedBuffer() { + // Always keep at least one buffer (it might have data that was "unread" in it) + if (bufferQueue.size()<=1) + return 0; + BufferBase* buff = bufferQueue.back(); + assert(buff); + buff->dataStart = 0; + buff->dataCount = 0; + bufferQueue.pop_back(); + return buff; +} + +/* + * We keep on reading as long as we have something to read and a buffer to put + * it in + */ +void AsynchIO::readable(DispatchHandle& h) { + int readTotal = 0; + AbsTime readStartTime = AbsTime::now(); + do { + // (Try to) get a buffer + if (!bufferQueue.empty()) { + // Read into buffer + BufferBase* buff = bufferQueue.front(); + assert(buff); + bufferQueue.pop_front(); + errno = 0; + int readCount = buff->byteCount-buff->dataCount; + int rc = socket.read(buff->bytes + buff->dataCount, readCount); + if (rc > 0) { + buff->dataCount += rc; + threadReadTotal += rc; + readTotal += rc; + + if (!readCallback(*this, buff)) { + // We were told to flow control reading at this point + h.unwatchRead(); + break; + } + + if (rc != readCount) { + // If we didn't fill the read buffer then time to stop reading + break; + } + + // Stop reading if we've overrun our timeslot + if (Duration(readStartTime, AbsTime::now()) > threadMaxReadTimeNs) { + break; + } + + } else { + // Put buffer back (at front so it doesn't interfere with unread buffers) + bufferQueue.push_front(buff); + assert(buff); + + // Eof or other side has gone away + if (rc == 0 || errno == ECONNRESET) { + eofCallback(*this); + h.unwatchRead(); + break; + } else if (errno == EAGAIN) { + // We have just put a buffer back so we know + // we can carry on watching for reads + break; + } else { + // Report error then just treat as a socket disconnect + QPID_LOG(error, "Error reading socket: " << qpid::sys::strError(rc) << "(" << rc << ")" ); + eofCallback(*this); + h.unwatchRead(); + break; + } + } + } else { + // Something to read but no buffer + if (emptyCallback) { + emptyCallback(*this); + } + // If we still have no buffers we can't do anything more + if (bufferQueue.empty()) { + h.unwatchRead(); + break; + } + + } + } while (true); + + ++threadReadCount; + threadMaxRead = std::max(threadMaxRead, readTotal); + return; +} + +/* + * We carry on writing whilst we have data to write and we can write + */ +void AsynchIO::writeable(DispatchHandle& h) { + int writeTotal = 0; + do { + // See if we've got something to write + if (!writeQueue.empty()) { + // Write buffer + BufferBase* buff = writeQueue.back(); + writeQueue.pop_back(); + errno = 0; + assert(buff->dataStart+buff->dataCount <= buff->byteCount); + int rc = socket.write(buff->bytes+buff->dataStart, buff->dataCount); + if (rc >= 0) { + threadWriteTotal += rc; + writeTotal += rc; + + // If we didn't write full buffer put rest back + if (rc != buff->dataCount) { + buff->dataStart += rc; + buff->dataCount -= rc; + writeQueue.push_back(buff); + break; + } + + // Recycle the buffer + queueReadBuffer(buff); + + // If we've already written more than the max for reading then stop + // (this is to stop writes dominating reads) + if (writeTotal > threadMaxRead) + break; + } else { + // Put buffer back + writeQueue.push_back(buff); + if (errno == ECONNRESET || errno == EPIPE) { + // Just stop watching for write here - we'll get a + // disconnect callback soon enough + h.unwatchWrite(); + break; + } else if (errno == EAGAIN) { + // We have just put a buffer back so we know + // we can carry on watching for writes + break; + } else { + QPID_POSIX_CHECK(rc); + } + } + } else { + // If we're waiting to close the socket then can do it now as there is nothing to write + if (queuedClose) { + close(h); + break; + } + // Fd is writable, but nothing to write + if (idleCallback) { + writePending = false; + idleCallback(*this); + } + // If we still have no buffers to write we can't do anything more + if (writeQueue.empty() && !writePending && !queuedClose) { + h.unwatchWrite(); + // The following handles the case where writePending is + // set to true after the test above; in this case its + // possible that the unwatchWrite overwrites the + // desired rewatchWrite so we correct that here + if (writePending) + h.rewatchWrite(); + break; + } + } + } while (true); + + ++threadWriteCount; + return; +} + +void AsynchIO::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 AsynchIO::close(DispatchHandle& h) { + h.stopWatch(); + socket.close(); + if (closedCallback) { + closedCallback(*this, socket); + } +} + +} // namespace posix + +AsynchIO* qpid::sys::AsynchIO::create(const Socket& s, + AsynchIO::ReadCallback rCb, + AsynchIO::EofCallback eofCb, + AsynchIO::DisconnectCallback disCb, + AsynchIO::ClosedCallback cCb, + AsynchIO::BuffersEmptyCallback eCb, + AsynchIO::IdleCallback iCb) +{ + return new qpid::sys::posix::AsynchIO(s, rCb, eofCb, disCb, cCb, eCb, iCb); +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/Condition.h b/RC9/qpid/cpp/src/qpid/sys/posix/Condition.h new file mode 100644 index 0000000000..86d6500ee9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/Condition.h @@ -0,0 +1,86 @@ +#ifndef _sys_posix_Condition_h +#define _sys_posix_Condition_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "PrivatePosix.h" + +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Time.h" + +#include <time.h> +#include <sys/errno.h> +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace sys { + +/** + * A condition variable for thread synchronization. + */ +class Condition +{ + public: + inline Condition(); + inline ~Condition(); + inline void wait(Mutex&); + inline bool wait(Mutex&, const AbsTime& absoluteTime); + inline void notify(); + inline void notifyAll(); + + private: + pthread_cond_t condition; +}; + +Condition::Condition() { + QPID_POSIX_ASSERT_THROW_IF(pthread_cond_init(&condition, 0)); +} + +Condition::~Condition() { + QPID_POSIX_ASSERT_THROW_IF(pthread_cond_destroy(&condition)); +} + +void Condition::wait(Mutex& mutex) { + QPID_POSIX_ASSERT_THROW_IF(pthread_cond_wait(&condition, &mutex.mutex)); +} + +bool Condition::wait(Mutex& mutex, const AbsTime& absoluteTime){ + struct timespec ts; + toTimespec(ts, Duration(absoluteTime)); + int status = pthread_cond_timedwait(&condition, &mutex.mutex, &ts); + if (status != 0) { + if (status == ETIMEDOUT) return false; + throw QPID_POSIX_ERROR(status); + } + return true; +} + +void Condition::notify(){ + QPID_POSIX_ASSERT_THROW_IF(pthread_cond_signal(&condition)); +} + +void Condition::notifyAll(){ + QPID_POSIX_ASSERT_THROW_IF(pthread_cond_broadcast(&condition)); +} + +}} +#endif /*!_sys_posix_Condition_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/FileSysDir.cpp b/RC9/qpid/cpp/src/qpid/sys/posix/FileSysDir.cpp new file mode 100755 index 0000000000..22dc487e74 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/FileSysDir.cpp @@ -0,0 +1,54 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/sys/FileSysDir.h" +#include "qpid/sys/StrError.h" +#include "qpid/Exception.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <cerrno> +#include <unistd.h> + +namespace qpid { +namespace sys { + +bool FileSysDir::exists (void) const +{ + const char *cpath = dirPath.c_str (); + struct stat s; + if (::stat(cpath, &s)) { + if (errno == ENOENT) { + return false; + } + throw qpid::Exception (strError(errno) + + ": Can't check directory: " + dirPath); + } + if (S_ISDIR(s.st_mode)) + return true; + throw qpid::Exception(dirPath + " is not a directory"); +} + +void FileSysDir::mkdir(void) +{ + if (::mkdir(dirPath.c_str(), 0755)) + throw Exception ("Can't create directory: " + dirPath); +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/Fork.cpp b/RC9/qpid/cpp/src/qpid/sys/posix/Fork.cpp new file mode 100644 index 0000000000..ec3af620ef --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/Fork.cpp @@ -0,0 +1,127 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "qpid/sys/Fork.h" +#include "qpid/log/Statement.h" +#include "qpid/Exception.h" + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +namespace qpid { +namespace sys { + +using namespace std; + +namespace { + +void writeStr(int fd, const std::string& str) { + const char* WRITE_ERR = "Error writing to parent process"; + int size = str.size(); + if (int(sizeof(size)) > ::write(fd, &size, sizeof(size))) throw ErrnoException(WRITE_ERR); + if (size > ::write(fd, str.data(), size)) throw ErrnoException(WRITE_ERR); +} + +string readStr(int fd) { + string value; + const char* READ_ERR = "Error reading from forked process"; + int size; + if (int(sizeof(size)) > ::read(fd, &size, sizeof(size))) throw ErrnoException(READ_ERR); + if (size > 0) { // Read string message + value.resize(size); + if (size > ::read(fd, const_cast<char*>(value.data()), size)) throw ErrnoException(READ_ERR); + } + return value; +} + +} // namespace + +Fork::Fork() {} +Fork::~Fork() {} + +void Fork::fork() { + pid_t pid = ::fork(); + if (pid < 0) throw ErrnoException("Failed to fork the process"); + if (pid == 0) child(); + else parent(pid); +} + +ForkWithMessage::ForkWithMessage() { + pipeFds[0] = pipeFds[1] = -1; +} + +struct AutoCloseFd { + int fd; + AutoCloseFd(int d) : fd(d) {} + ~AutoCloseFd() { ::close(fd); } +}; + +void ForkWithMessage::fork() { + if(::pipe(pipeFds) < 0) throw ErrnoException("Can't create pipe"); + pid_t pid = ::fork(); + if(pid < 0) throw ErrnoException("Fork fork failed"); + if (pid == 0) { // Child + AutoCloseFd ac(pipeFds[1]); // Write side. + ::close(pipeFds[0]); // Read side + try { + child(); + } + catch (const std::exception& e) { + QPID_LOG(error, "Error in forked child: " << e.what()); + std::string msg = e.what(); + if (msg.empty()) msg = " "; // Make sure we send a non-empty error string. + writeStr(pipeFds[1], msg); + } + } + else { // Parent + close(pipeFds[1]); // Write side. + AutoCloseFd ac(pipeFds[0]); // Read side + parent(pid); + } +} + +string ForkWithMessage::wait(int timeout) { // parent waits for child. + errno = 0; + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + + fd_set fds; + FD_ZERO(&fds); + FD_SET(pipeFds[0], &fds); + int n=select(FD_SETSIZE, &fds, 0, 0, &tv); + if(n<0) throw ErrnoException("Error waiting for fork"); + if (n==0) throw Exception("Timed out waiting for fork"); + + string error = readStr(pipeFds[0]); + if (error.empty()) return readStr(pipeFds[0]); + else throw Exception("Error in forked process: " + error); +} + +// Write empty error string followed by value string to pipe. +void ForkWithMessage::ready(const string& value) { // child + // Write empty string for error followed by value. + writeStr(pipeFds[1], string()); // No error + writeStr(pipeFds[1], value); +} + + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/Fork.h b/RC9/qpid/cpp/src/qpid/sys/posix/Fork.h new file mode 100644 index 0000000000..698c61ed30 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/Fork.h @@ -0,0 +1,82 @@ +#ifndef QPID_SYS_POSIX_FORK_H +#define QPID_SYS_POSIX_FORK_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <string> +#include <sys/types.h> + +namespace qpid { +namespace sys { + +/** + * Fork the process. Call parent() in parent and child() in child. + */ +class Fork { + public: + Fork(); + virtual ~Fork(); + + /** + * Fork the process. + * Calls parent() in the parent process, child() in the child. + */ + virtual void fork(); + + protected: + + /** Called in parent process. + *@child pid of child process + */ + virtual void parent(pid_t child) = 0; + + /** Called in child process */ + virtual void child() = 0; +}; + +/** + * Like Fork but also allows the child to send a string message + * or throw an exception to the parent. + */ +class ForkWithMessage : public Fork { + public: + ForkWithMessage(); + void fork(); + + protected: + /** Call from parent(): wait for child to send a value or throw exception. + * @timeout in seconds to wait for response. + * @return value passed by child to ready(). + */ + std::string wait(int timeout); + + /** Call from child(): Send a value to the parent. + *@param value returned by parent call to wait(). + */ + void ready(const std::string& value); + + private: + int pipeFds[2]; +}; + +}} // namespace qpid::sys + + + +#endif /*!QPID_SYS_POSIX_FORK_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/IOHandle.cpp b/RC9/qpid/cpp/src/qpid/sys/posix/IOHandle.cpp new file mode 100644 index 0000000000..80b487eadc --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/IOHandle.cpp @@ -0,0 +1,42 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/IOHandle.h" + +#include "PrivatePosix.h" + +namespace qpid { +namespace sys { + +int toFd(const IOHandlePrivate* h) +{ + return h->fd; +} + +IOHandle::IOHandle(IOHandlePrivate* h) : + impl(h) +{} + +IOHandle::~IOHandle() { + delete impl; +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/IntegerTypes.h b/RC9/qpid/cpp/src/qpid/sys/posix/IntegerTypes.h new file mode 100755 index 0000000000..ce97f7bde8 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/IntegerTypes.h @@ -0,0 +1,26 @@ +#ifndef QPID_SYS_POSIX_INTEGERTYPES_H +#define QPID_SYS_POSIX_INTEGERTYPES_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <stdint.h> + +#endif /*!QPID_SYS_INTEGERTYPES_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/LockFile.cpp b/RC9/qpid/cpp/src/qpid/sys/posix/LockFile.cpp new file mode 100755 index 0000000000..af9ecd7d66 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/LockFile.cpp @@ -0,0 +1,89 @@ +/* + * + * Copyright (c) 2008 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/sys/LockFile.h" + +#include <string> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "check.h" + +namespace qpid { +namespace sys { + +class LockFilePrivate { + friend class LockFile; + + int fd; + +public: + LockFilePrivate(int f) : fd(f) {} +}; + +LockFile::LockFile(const std::string& path_, bool create) + : path(path_), created(create) { + + errno = 0; + int flags=create ? O_WRONLY|O_CREAT|O_NOFOLLOW : O_RDWR; + int fd = ::open(path.c_str(), flags, 0644); + if (fd < 0) throw ErrnoException("Cannot open " + path, errno); + if (::lockf(fd, F_TLOCK, 0) < 0) { + ::close(fd); + throw ErrnoException("Cannot lock " + path, errno); + } + impl.reset(new LockFilePrivate(fd)); +} + +LockFile::~LockFile() { + if (impl) { + int f = impl->fd; + if (f >= 0) { + (void) ::lockf(f, F_ULOCK, 0); // Suppress warnings about ignoring return value. + ::close(f); + impl->fd = -1; + } + } +} + +pid_t LockFile::readPid(void) const { + if (!impl) + throw Exception("Lock file not open"); + + pid_t pid; + int desired_read = sizeof(pid_t); + if (desired_read > ::read(impl->fd, &pid, desired_read) ) { + throw Exception("Cannot read lock file " + path); + } + return pid; +} + +void LockFile::writePid(void) { + if (!impl) + throw Exception("Lock file not open"); + + pid_t pid = getpid(); + int desired_write = sizeof(pid_t); + if (desired_write > ::write(impl->fd, &pid, desired_write)) { + throw Exception("Cannot write lock file " + path); + } +} + +}} /* namespace qpid::sys */ diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/Mutex.cpp b/RC9/qpid/cpp/src/qpid/sys/posix/Mutex.cpp new file mode 100644 index 0000000000..0e1f0d30c2 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/Mutex.cpp @@ -0,0 +1,46 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/sys/Mutex.h" + +namespace qpid { +namespace sys { + +/** + * Initialise a recursive mutex attr for use in creating mutexes later + * (we use pthread_once to make sure it is initialised exactly once) + */ + +namespace { +pthread_once_t onceControl = PTHREAD_ONCE_INIT; +pthread_mutexattr_t mutexattr; + +void initMutexattr() { + pthread_mutexattr_init(&mutexattr); + pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE); +} +} + +const pthread_mutexattr_t* Mutex::getAttribute() { + pthread_once(&onceControl, initMutexattr); + return &mutexattr; +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/Mutex.h b/RC9/qpid/cpp/src/qpid/sys/posix/Mutex.h new file mode 100644 index 0000000000..cd5a8affd4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/Mutex.h @@ -0,0 +1,158 @@ +#ifndef _sys_posix_Mutex_h +#define _sys_posix_Mutex_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "check.h" + +#include <pthread.h> +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace sys { + +class Condition; + +/** + * Mutex lock. + */ +class Mutex : private boost::noncopyable { + friend class Condition; + static const pthread_mutexattr_t* getAttribute(); + +public: + typedef ::qpid::sys::ScopedLock<Mutex> ScopedLock; + typedef ::qpid::sys::ScopedUnlock<Mutex> ScopedUnlock; + + inline Mutex(); + inline ~Mutex(); + inline void lock(); + inline void unlock(); + inline bool trylock(); + + +protected: + pthread_mutex_t mutex; +}; + +/** + * RW lock. + */ +class RWlock : private boost::noncopyable { + friend class Condition; + +public: + typedef ::qpid::sys::ScopedRlock<RWlock> ScopedRlock; + typedef ::qpid::sys::ScopedWlock<RWlock> ScopedWlock; + + inline RWlock(); + inline ~RWlock(); + inline void wlock(); // will write-lock + inline void rlock(); // will read-lock + inline void unlock(); + inline void trywlock(); // will write-try + inline void tryrlock(); // will read-try + +protected: + pthread_rwlock_t rwlock; +}; + + +/** + * PODMutex is a POD, can be static-initialized with + * PODMutex m = QPID_PODMUTEX_INITIALIZER + */ +struct PODMutex +{ + typedef ::qpid::sys::ScopedLock<PODMutex> ScopedLock; + + inline void lock(); + inline void unlock(); + inline bool trylock(); + + // Must be public to be a POD: + pthread_mutex_t mutex; +}; + +#define QPID_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER } + +void PODMutex::lock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_lock(&mutex)); +} + +void PODMutex::unlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_unlock(&mutex)); +} + +bool PODMutex::trylock() { + return pthread_mutex_trylock(&mutex) == 0; +} + +Mutex::Mutex() { + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_init(&mutex, getAttribute())); +} + +Mutex::~Mutex(){ + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_destroy(&mutex)); +} + +void Mutex::lock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_lock(&mutex)); +} + +void Mutex::unlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_unlock(&mutex)); +} + +bool Mutex::trylock() { + return pthread_mutex_trylock(&mutex) == 0; +} + + +RWlock::RWlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_init(&rwlock, NULL)); +} + +RWlock::~RWlock(){ + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_destroy(&rwlock)); +} + +void RWlock::wlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_wrlock(&rwlock)); +} + +void RWlock::rlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_rdlock(&rwlock)); +} + +void RWlock::unlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_unlock(&rwlock)); +} + +void RWlock::trywlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_trywrlock(&rwlock)); +} + +void RWlock::tryrlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_tryrdlock(&rwlock)); +} + + +}} +#endif /*!_sys_posix_Mutex_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/PollableCondition.cpp b/RC9/qpid/cpp/src/qpid/sys/posix/PollableCondition.cpp new file mode 100644 index 0000000000..0c55fd3c0d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/PollableCondition.cpp @@ -0,0 +1,96 @@ +#ifndef QPID_SYS_LINUX_POLLABLECONDITION_CPP +#define QPID_SYS_LINUX_POLLABLECONDITION_CPP + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "PollableCondition.h" +#include "qpid/sys/posix/PrivatePosix.h" +#include "qpid/Exception.h" + +#include <unistd.h> +#include <fcntl.h> + +namespace qpid { +namespace sys { + +PollableCondition::PollableCondition() : IOHandle(new sys::IOHandlePrivate) { + int fds[2]; + if (::pipe(fds) == -1) + throw ErrnoException(QPID_MSG("Can't create PollableCondition")); + impl->fd = fds[0]; + writeFd = fds[1]; + if (::fcntl(impl->fd, F_SETFL, O_NONBLOCK) == -1) + throw ErrnoException(QPID_MSG("Can't create PollableCondition")); + if (::fcntl(writeFd, F_SETFL, O_NONBLOCK) == -1) + throw ErrnoException(QPID_MSG("Can't create PollableCondition")); +} + +bool PollableCondition::clear() { + char buf[256]; + ssize_t n; + bool wasSet = false; + while ((n = ::read(impl->fd, buf, sizeof(buf))) > 0) + wasSet = true; + if (n == -1 && errno != EAGAIN) throw ErrnoException(QPID_MSG("Error clearing PollableCondition")); + return wasSet; +} + +void PollableCondition::set() { + static const char dummy=0; + ssize_t n = ::write(writeFd, &dummy, 1); + if (n == -1 && errno != EAGAIN) throw ErrnoException("Error setting PollableCondition"); +} + + +#if 0 +// FIXME aconway 2008-08-12: More efficient Linux implementation using +// eventfd system call. Move to separate file & do configure.ac test +// to enable this when ::eventfd() is available. + +#include <sys/eventfd.h> + +namespace qpid { +namespace sys { + +PollableCondition::PollableCondition() : IOHandle(new sys::IOHandlePrivate) { + impl->fd = ::eventfd(0, 0); + if (impl->fd < 0) throw ErrnoException("conditionfd() failed"); +} + +bool PollableCondition::clear() { + char buf[8]; + ssize_t n = ::read(impl->fd, buf, 8); + if (n != 8) throw ErrnoException("read failed on conditionfd"); + return *reinterpret_cast<uint64_t*>(buf); +} + +void PollableCondition::set() { + static const uint64_t value=1; + ssize_t n = ::write(impl->fd, reinterpret_cast<const void*>(&value), 8); + if (n != 8) throw ErrnoException("write failed on conditionfd"); +} + +#endif + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_LINUX_POLLABLECONDITION_CPP*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/PollableCondition.h b/RC9/qpid/cpp/src/qpid/sys/posix/PollableCondition.h new file mode 100644 index 0000000000..4ec277b0ec --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/PollableCondition.h @@ -0,0 +1,56 @@ +#ifndef QPID_SYS_POSIX_POLLABLECONDITION_H +#define QPID_SYS_POSIX_POLLABLECONDITION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/IOHandle.h" + +namespace qpid { +namespace sys { + +/** + * A pollable condition to integrate in-process conditions with IO + * conditions in a polling loop. + * + * Setting the condition makes it readable for a poller. + * + * Writable/disconnected conditions are undefined and should not be + * polled for. + */ +class PollableCondition : public sys::IOHandle { + public: + PollableCondition(); + + /** Set the condition, triggers readable in a poller. */ + void set(); + + /** Get the current state of the condition, then clear it. + *@return The state of the condition before it was cleared. + */ + bool clear(); + + private: + int writeFd; +}; +}} // namespace qpid::sys + +#endif /*!QPID_SYS_POSIX_POLLABLECONDITION_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/PrivatePosix.h b/RC9/qpid/cpp/src/qpid/sys/posix/PrivatePosix.h new file mode 100644 index 0000000000..33c0cd81bc --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/PrivatePosix.h @@ -0,0 +1,52 @@ +#ifndef _sys_posix_PrivatePosix_h +#define _sys_posix_PrivatePosix_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Time.h" + +struct timespec; +struct timeval; + +namespace qpid { +namespace sys { + +// Private Time related implementation details +struct timespec& toTimespec(struct timespec& ts, const Duration& t); +struct timeval& toTimeval(struct timeval& tv, const Duration& t); +Duration toTime(const struct timespec& ts); + +// Private fd related implementation details +class IOHandlePrivate { +public: + IOHandlePrivate(int f = -1) : + fd(f) + {} + + int fd; +}; + +int toFd(const IOHandlePrivate* h); + +}} + +#endif /*!_sys_posix_PrivatePosix_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/Shlib.cpp b/RC9/qpid/cpp/src/qpid/sys/posix/Shlib.cpp new file mode 100644 index 0000000000..62dbfb3dd9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/Shlib.cpp @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Shlib.h" +#include "qpid/Exception.h" +#include <dlfcn.h> + + +namespace qpid { +namespace sys { + +void Shlib::load(const char* name) { + dlerror(); + handle = ::dlopen(name, RTLD_NOW); + const char* error = ::dlerror(); + if (error) { + throw Exception(QPID_MSG(error << ": " << name)); + } +} + +void Shlib::unload() { + if (handle) { + ::dlerror(); + ::dlclose(handle); + const char* error = ::dlerror(); + if (error) { + throw Exception(QPID_MSG(error)); + } + handle = 0; + } +} + +void* Shlib::getSymbol(const char* name) { + ::dlerror(); + void* sym = ::dlsym(handle, name); + const char* error = ::dlerror(); + if (error) + throw Exception(QPID_MSG(error << ": " << name)); + return sym; +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/Socket.cpp b/RC9/qpid/cpp/src/qpid/sys/posix/Socket.cpp new file mode 100644 index 0000000000..415d5293ef --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/Socket.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 "qpid/sys/Socket.h" + +#include "check.h" +#include "PrivatePosix.h" + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <cstdlib> +#include <string.h> +#include <iostream> + +#include <boost/format.hpp> + +namespace qpid { +namespace sys { + +namespace { +std::string getName(int fd, bool local, bool includeService = false) +{ + ::sockaddr_storage name; // big enough for any socket address + ::socklen_t namelen = sizeof(name); + + int result = -1; + if (local) { + result = ::getsockname(fd, (::sockaddr*)&name, &namelen); + } else { + result = ::getpeername(fd, (::sockaddr*)&name, &namelen); + } + + QPID_POSIX_CHECK(result); + + char servName[NI_MAXSERV]; + char dispName[NI_MAXHOST]; + if (includeService) { + if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName), + servName, sizeof(servName), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw QPID_POSIX_ERROR(rc); + return std::string(dispName) + ":" + std::string(servName); + + } else { + if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName), 0, 0, NI_NUMERICHOST) != 0) + throw QPID_POSIX_ERROR(rc); + return dispName; + } +} + +std::string getService(int fd, bool local) +{ + ::sockaddr_storage name; // big enough for any socket address + ::socklen_t namelen = sizeof(name); + + int result = -1; + if (local) { + result = ::getsockname(fd, (::sockaddr*)&name, &namelen); + } else { + result = ::getpeername(fd, (::sockaddr*)&name, &namelen); + } + + QPID_POSIX_CHECK(result); + + char servName[NI_MAXSERV]; + if (int rc=::getnameinfo((::sockaddr*)&name, namelen, 0, 0, + servName, sizeof(servName), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw QPID_POSIX_ERROR(rc); + return servName; +} +} + +Socket::Socket() : + IOHandle(new IOHandlePrivate) +{ + createTcp(); +} + +Socket::Socket(IOHandlePrivate* h) : + IOHandle(h) +{} + +void Socket::createTcp() const +{ + int& socket = impl->fd; + if (socket != -1) Socket::close(); + int s = ::socket (PF_INET, SOCK_STREAM, 0); + if (s < 0) throw QPID_POSIX_ERROR(errno); + socket = s; +} + +void Socket::setTimeout(const Duration& interval) const +{ + const int& socket = impl->fd; + struct timeval tv; + toTimeval(tv, interval); + setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); +} + +void Socket::setNonblocking() const { + QPID_POSIX_CHECK(::fcntl(impl->fd, F_SETFL, O_NONBLOCK)); +} + +namespace { +const char* h_errstr(int e) { + switch (e) { + case HOST_NOT_FOUND: return "Host not found"; + case NO_ADDRESS: return "Name does not have an IP address"; + case TRY_AGAIN: return "A temporary error occurred on an authoritative name server."; + case NO_RECOVERY: return "Non-recoverable name server error"; + default: return "Unknown error"; + } +} +} + +void Socket::connect(const std::string& host, uint16_t port) const +{ + std::stringstream namestream; + namestream << host << ":" << port; + connectname = namestream.str(); + + const int& socket = impl->fd; + struct sockaddr_in name; + name.sin_family = AF_INET; + name.sin_port = htons(port); + // TODO: Be good to make this work for IPv6 as well as IPv4 + // Use more modern lookup functions + struct hostent* hp = gethostbyname ( host.c_str() ); + if (hp == 0) + throw Exception(QPID_MSG("Cannot resolve " << host << ": " << h_errstr(h_errno))); + ::memcpy(&name.sin_addr.s_addr, hp->h_addr_list[0], hp->h_length); + if ((::connect(socket, (struct sockaddr*)(&name), sizeof(name)) < 0) && + (errno != EINPROGRESS)) + throw qpid::Exception(QPID_MSG(strError(errno) << ": " << host << ":" << port)); +} + +void +Socket::close() const +{ + int& socket = impl->fd; + if (socket == -1) return; + if (::close(socket) < 0) throw QPID_POSIX_ERROR(errno); + socket = -1; +} + +int Socket::listen(uint16_t port, int backlog) const +{ + 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); +} + +Socket* Socket::accept(struct sockaddr *addr, socklen_t *addrlen) const +{ + int afd = ::accept(impl->fd, addr, addrlen); + if ( afd >= 0) + return new Socket(new IOHandlePrivate(afd)); + else if (errno == EAGAIN) + return 0; + else throw QPID_POSIX_ERROR(errno); +} + +int Socket::read(void *buf, size_t count) const +{ + return ::read(impl->fd, buf, count); +} + +int Socket::write(const void *buf, size_t count) const +{ + return ::write(impl->fd, buf, count); +} + +std::string Socket::getSockname() const +{ + return getName(impl->fd, true); +} + +std::string Socket::getPeername() const +{ + return getName(impl->fd, false); +} + +std::string Socket::getPeerAddress() const +{ + if (!connectname.empty()) + return std::string (connectname); + return getName(impl->fd, false, true); +} + +std::string Socket::getLocalAddress() const +{ + return getName(impl->fd, true, true); +} + +uint16_t Socket::getLocalPort() const +{ + return std::atoi(getService(impl->fd, true).c_str()); +} + +uint16_t Socket::getRemotePort() const +{ + return atoi(getService(impl->fd, true).c_str()); +} + +int Socket::getError() const +{ + int result; + socklen_t rSize = sizeof (result); + + if (::getsockopt(impl->fd, SOL_SOCKET, SO_ERROR, &result, &rSize) < 0) + throw QPID_POSIX_ERROR(errno); + + return result; +} + +void Socket::setTcpNoDelay(bool nodelay) const +{ + if (nodelay) { + int flag = 1; + int result = setsockopt(impl->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); + QPID_POSIX_CHECK(result); + } +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/StrError.cpp b/RC9/qpid/cpp/src/qpid/sys/posix/StrError.cpp new file mode 100644 index 0000000000..633e20213c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/StrError.cpp @@ -0,0 +1,41 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/StrError.h" + +#include <string.h> + +namespace qpid { +namespace sys { + +std::string strError(int err) { + char buf[512] = "Unknown error"; +#ifdef _GNU_SOURCE + // GNU strerror_r returns the message + return ::strerror_r(err, buf, sizeof(buf)); +#else + // POSIX strerror_r doesn't return the buffer + ::strerror_r(err, buf, sizeof(buf)); + return std::string(buf); +#endif +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/SystemInfo.cpp b/RC9/qpid/cpp/src/qpid/sys/posix/SystemInfo.cpp new file mode 100755 index 0000000000..938d4861c4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/SystemInfo.cpp @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/SystemInfo.h" + +#include <sys/ioctl.h> +#include <sys/utsname.h> +#include <net/if.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <unistd.h> + +#ifndef HOST_NAME_MAX +# define HOST_NAME_MAX 256 +#endif + +using namespace std; + +namespace qpid { +namespace sys { + +long SystemInfo::concurrency() { +#ifdef _SC_NPROCESSORS_ONLN // Linux specific. + return sysconf(_SC_NPROCESSORS_ONLN); +#else + return -1; +#endif +} + +bool SystemInfo::getLocalHostname (TcpAddress &address) { + char name[HOST_NAME_MAX]; + if (::gethostname(name, sizeof(name)) != 0) + return false; + address.host = name; + return true; +} + +static const string LOCALHOST("127.0.0.1"); + +void SystemInfo::getLocalIpAddresses (uint16_t port, + std::vector<Address> &addrList) { + int s = socket (PF_INET, SOCK_STREAM, 0); + for (int i=1;;i++) { + struct ifreq ifr; + ifr.ifr_ifindex = i; + if (::ioctl (s, SIOCGIFNAME, &ifr) < 0) + break; + /* now ifr.ifr_name is set */ + if (::ioctl (s, SIOCGIFADDR, &ifr) < 0) + continue; + struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr; + string addr(inet_ntoa(sin->sin_addr)); + if (addr != LOCALHOST) + addrList.push_back(TcpAddress(addr, port)); + } + if (addrList.empty()) { + addrList.push_back(TcpAddress(LOCALHOST, port)); + } + close (s); +} + +void SystemInfo::getSystemId (std::string &osName, + std::string &nodeName, + std::string &release, + std::string &version, + std::string &machine) +{ + struct utsname _uname; + if (uname (&_uname) == 0) + { + osName = _uname.sysname; + nodeName = _uname.nodename; + release = _uname.release; + version = _uname.version; + machine = _uname.machine; + } +} + +uint32_t SystemInfo::getProcessId() +{ + return (uint32_t) ::getpid(); +} + +uint32_t SystemInfo::getParentProcessId() +{ + return (uint32_t) ::getppid(); +} + + + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/Thread.cpp b/RC9/qpid/cpp/src/qpid/sys/posix/Thread.cpp new file mode 100644 index 0000000000..bb5641bc53 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/Thread.cpp @@ -0,0 +1,75 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Thread.h" + +#include "qpid/sys/Runnable.h" +#include "check.h" + +#include <pthread.h> + +namespace qpid { +namespace sys { + +namespace { +void* runRunnable(void* p) +{ + static_cast<Runnable*>(p)->run(); + return 0; +} +} + +struct ThreadPrivate { + pthread_t thread; + + ThreadPrivate(Runnable* runnable) { + QPID_POSIX_ASSERT_THROW_IF(::pthread_create(&thread, NULL, runRunnable, runnable)); + } + + ThreadPrivate() : thread(::pthread_self()) {} +}; + +Thread::Thread() {} + +Thread::Thread(Runnable* runnable) : impl(new ThreadPrivate(runnable)) {} + +Thread::Thread(Runnable& runnable) : impl(new ThreadPrivate(&runnable)) {} + +void Thread::join(){ + if (impl) { + QPID_POSIX_ASSERT_THROW_IF(::pthread_join(impl->thread, 0)); + } +} + +unsigned long Thread::id() { + if (impl) + return impl->thread; + else + return 0; +} + +Thread Thread::current() { + Thread t; + t.impl.reset(new ThreadPrivate()); + return t; +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/Time.cpp b/RC9/qpid/cpp/src/qpid/sys/posix/Time.cpp new file mode 100644 index 0000000000..d8d0a58d2e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/Time.cpp @@ -0,0 +1,110 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "PrivatePosix.h" + +#include "qpid/sys/Time.h" +#include <ostream> +#include <time.h> +#include <stdio.h> +#include <sys/time.h> +#include <unistd.h> + +namespace { +int64_t max_abstime() { return std::numeric_limits<int64_t>::max(); } +} + +namespace qpid { +namespace sys { + +AbsTime::AbsTime(const AbsTime& t, const Duration& d) : + timepoint(d == Duration::max() ? max_abstime() : t.timepoint+d.nanosecs) +{} + +AbsTime AbsTime::FarFuture() { + AbsTime ff; ff.timepoint = max_abstime(); return ff; +} + +AbsTime AbsTime::now() { + struct timespec ts; + ::clock_gettime(CLOCK_REALTIME, &ts); + AbsTime time_now; + time_now.timepoint = toTime(ts).nanosecs; + return time_now; +} + +Duration::Duration(const AbsTime& time0) : + nanosecs(time0.timepoint) +{} + +Duration::Duration(const AbsTime& start, const AbsTime& finish) : + nanosecs(finish.timepoint - start.timepoint) +{} + +struct timespec& toTimespec(struct timespec& ts, const Duration& t) { + ts.tv_sec = t / TIME_SEC; + ts.tv_nsec = t % TIME_SEC; + return ts; +} + +struct timeval& toTimeval(struct timeval& tv, const Duration& t) { + tv.tv_sec = t/TIME_SEC; + tv.tv_usec = (t%TIME_SEC)/TIME_USEC; + return tv; +} + +Duration toTime(const struct timespec& ts) { + return ts.tv_sec*TIME_SEC + ts.tv_nsec; +} + +std::ostream& operator<<(std::ostream& o, const Duration& d) { + return o << int64_t(d) << "ns"; +} + +std::ostream& operator<<(std::ostream& o, const AbsTime& t) { + static const char * month_abbrevs[] = { + "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" + }; + struct tm * timeinfo; + time_t rawtime(t.timepoint/TIME_SEC); + timeinfo = localtime (&rawtime); + char time_string[100]; + sprintf ( time_string, + "%d-%s-%02d %02d:%02d:%02d", + 1900 + timeinfo->tm_year, + month_abbrevs[timeinfo->tm_mon], + timeinfo->tm_mday, + timeinfo->tm_hour, + timeinfo->tm_min, + timeinfo->tm_sec + ); + return o << time_string; +} + +void sleep(int secs) { + ::sleep(secs); +} + +void usleep(uint64_t usecs) { + ::usleep(usecs); +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/Time.h b/RC9/qpid/cpp/src/qpid/sys/posix/Time.h new file mode 100755 index 0000000000..62d734c816 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/Time.h @@ -0,0 +1,34 @@ +#ifndef QPID_SYS_POSIX_TIME_H +#define QPID_SYS_POSIX_TIME_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/sys/IntegerTypes.h" + +namespace qpid { +namespace sys { + +/** + * Class to represent an instant in time. + */ +typedef int64_t TimePrivate; + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_POSIX_TIME_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/posix/check.h b/RC9/qpid/cpp/src/qpid/sys/posix/check.h new file mode 100644 index 0000000000..f3031b7593 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/posix/check.h @@ -0,0 +1,49 @@ +#ifndef _posix_check_h +#define _posix_check_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Exception.h" + +#include <cerrno> +#include <assert.h> +#include <stdio.h> + +#define QPID_POSIX_ERROR(ERRNO) qpid::Exception(QPID_MSG(qpid::sys::strError(ERRNO))) + +/** THROW QPID_POSIX_ERROR(errno) if RESULT is less than zero */ +#define QPID_POSIX_CHECK(RESULT) \ + if ((RESULT) < 0) throw QPID_POSIX_ERROR((errno)) + +/** Throw a posix error if ERRNO is non-zero */ +#define QPID_POSIX_THROW_IF(ERRNO) \ + do { int e=(ERRNO); if (e) throw QPID_POSIX_ERROR(e); } while(0) + +/** Same as _THROW_IF in a release build, but abort a debug build */ +#ifdef NDEBUG +#define QPID_POSIX_ASSERT_THROW_IF(ERRNO) QPID_POSIX_THROW_IF(ERRNO) +#else +#define QPID_POSIX_ASSERT_THROW_IF(ERRNO) \ + do { int e=(ERRNO); if (e) { errno=e; ::perror(0); assert(0); } } while(0) +#endif + +#endif /*!_posix_check_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp b/RC9/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp new file mode 100644 index 0000000000..0d3dd83131 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp @@ -0,0 +1,207 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "RdmaIO.h" +#include "qpid/sys/Time.h" + +#include <netdb.h> +#include <arpa/inet.h> + +#include <vector> +#include <string> +#include <iostream> +#include <algorithm> +#include <cmath> +#include <boost/bind.hpp> + +using std::vector; +using std::string; +using std::cout; +using std::cerr; +using std::copy; +using std::rand; + +using qpid::sys::Poller; +using qpid::sys::Dispatcher; +using qpid::sys::AbsTime; +using qpid::sys::Duration; +using qpid::sys::TIME_SEC; +using qpid::sys::TIME_INFINITE; + +// count of messages +int64_t smsgs = 0; +int64_t sbytes = 0; +int64_t rmsgs = 0; +int64_t rbytes = 0; + +int target = 1000000; +int msgsize = 200; +AbsTime startTime; +Duration sendingDuration(TIME_INFINITE); +Duration fullTestDuration(TIME_INFINITE); + +vector<char> testString; + +void write(Rdma::AsynchIO& aio) { + while (aio.writable()) { + if (smsgs >= target) + return; + Rdma::Buffer* b = aio.getBuffer(); + std::copy(testString.begin(), testString.end(), b->bytes); + b->dataCount = msgsize; + aio.queueWrite(b); + ++smsgs; + sbytes += msgsize; + } +} + +void dataError(Rdma::AsynchIO&) { + cout << "Data error:\n"; +} + +void data(Poller::shared_ptr p, Rdma::AsynchIO& aio, Rdma::Buffer* b) { + ++rmsgs; + rbytes += b->dataCount; + + // When all messages have been recvd stop + if (rmsgs < target) { + write(aio); + } else { + fullTestDuration = std::min(fullTestDuration, Duration(startTime, AbsTime::now())); + if (aio.incompletedWrites() == 0) + p->shutdown(); + } +} + +void full(Rdma::AsynchIO& a, Rdma::Buffer* b) { + // Warn as we shouldn't get here anymore + cerr << "!"; + + // Don't need to keep buffer just adjust the counts + --smsgs; + sbytes -= b->dataCount; + + // Give buffer back + a.returnBuffer(b); +} + +void idle(Poller::shared_ptr p, Rdma::AsynchIO& aio) { + if (smsgs < target) { + write(aio); + } else { + sendingDuration = std::min(sendingDuration, Duration(startTime, AbsTime::now())); + if (rmsgs >= target && aio.incompletedWrites() == 0) + p->shutdown(); + } +} + +void connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci, const Rdma::ConnectionParams& cp) { + cout << "Connected\n"; + Rdma::QueuePair::intrusive_ptr q = ci->getQueuePair(); + + Rdma::AsynchIO* aio = new Rdma::AsynchIO(ci->getQueuePair(), + cp.maxRecvBufferSize, cp.initialXmitCredit , Rdma::DEFAULT_WR_ENTRIES, + boost::bind(&data, poller, _1, _2), + boost::bind(&idle, poller, _1), + &full, + dataError); + + startTime = AbsTime::now(); + write(*aio); + + aio->start(poller); +} + +void disconnected(boost::shared_ptr<Poller> p, Rdma::Connection::intrusive_ptr&) { + cout << "Disconnected\n"; + p->shutdown(); +} + +void connectionError(boost::shared_ptr<Poller> p, Rdma::Connection::intrusive_ptr&, const Rdma::ErrorType) { + cout << "Connection error\n"; + p->shutdown(); +} + +void rejected(boost::shared_ptr<Poller> p, Rdma::Connection::intrusive_ptr&, const Rdma::ConnectionParams&) { + cout << "Connection rejected\n"; + p->shutdown(); +} + +int main(int argc, char* argv[]) { + vector<string> args(&argv[0], &argv[argc]); + + ::addrinfo *res; + ::addrinfo hints = {}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + string port = (args.size() < 3) ? "20079" : args[2]; + int n = ::getaddrinfo(args[1].c_str(), port.c_str(), &hints, &res); + if (n<0) { + cerr << "Can't find information for: " << args[1] << "\n"; + return 1; + } else { + cout << "Connecting to: " << args[1] << ":" << port <<"\n"; + } + + if (args.size() > 3) + msgsize = atoi(args[3].c_str()); + cout << "Message size: " << msgsize << "\n"; + + // Make a random message of that size + testString.resize(msgsize); + for (int i = 0; i < msgsize; ++i) { + testString[i] = 32 + (rand() & 0x3f); + } + + try { + boost::shared_ptr<Poller> p(new Poller()); + Dispatcher d(p); + + Rdma::Connector c( + *res->ai_addr, + Rdma::ConnectionParams(msgsize, Rdma::DEFAULT_WR_ENTRIES), + boost::bind(&connected, p, _1, _2), + boost::bind(&connectionError, p, _1, _2), + boost::bind(&disconnected, p, _1), + boost::bind(&rejected, p, _1, _2)); + + c.start(p); + d.run(); + } catch (Rdma::Exception& e) { + int err = e.getError(); + cerr << "Error: " << e.what() << "(" << err << ")\n"; + } + + cout + << "Sent: " << smsgs + << "msgs (" << sbytes + << "bytes) in: " << double(sendingDuration)/TIME_SEC + << "s: " << double(smsgs)*TIME_SEC/sendingDuration + << "msgs/s(" << double(sbytes)*TIME_SEC/sendingDuration + << "bytes/s)\n"; + cout + << "Recd: " << rmsgs + << "msgs (" << rbytes + << "bytes) in: " << double(fullTestDuration)/TIME_SEC + << "s: " << double(rmsgs)*TIME_SEC/fullTestDuration + << "msgs/s(" << double(rbytes)*TIME_SEC/fullTestDuration + << "bytes/s)\n"; + +} diff --git a/RC9/qpid/cpp/src/qpid/sys/rdma/RdmaIO.cpp b/RC9/qpid/cpp/src/qpid/sys/rdma/RdmaIO.cpp new file mode 100644 index 0000000000..77e766dd79 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/rdma/RdmaIO.cpp @@ -0,0 +1,617 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "RdmaIO.h" + +#include "qpid/log/Statement.h" + + +#include <iostream> +#include <boost/bind.hpp> + +using qpid::sys::DispatchHandle; +using qpid::sys::Poller; + +namespace Rdma { + AsynchIO::AsynchIO( + QueuePair::intrusive_ptr q, + int size, + int xCredit, + int rCount, + ReadCallback rc, + IdleCallback ic, + FullCallback fc, + ErrorCallback ec + ) : + qp(q), + dataHandle(*qp, boost::bind(&AsynchIO::dataEvent, this, _1), 0, 0), + bufferSize(size), + recvCredit(0), + xmitCredit(xCredit), + recvBufferCount(rCount), + xmitBufferCount(xCredit), + outstandingWrites(0), + closed(false), + deleting(false), + state(IDLE), + readCallback(rc), + idleCallback(ic), + fullCallback(fc), + errorCallback(ec) + { + qp->nonblocking(); + qp->notifyRecv(); + qp->notifySend(); + + // Prepost some recv buffers before we go any further + for (int i = 0; i<recvBufferCount; ++i) { + // Allocate recv buffer + Buffer* b = qp->createBuffer(bufferSize); + buffers.push_front(b); + b->dataCount = b->byteCount; + qp->postRecv(b); + } + + for (int i = 0; i<xmitBufferCount; ++i) { + // Allocate xmit buffer + Buffer* b = qp->createBuffer(bufferSize); + buffers.push_front(b); + bufferQueue.push_front(b); + b->dataCount = 0; + b->dataStart = 0; + } + } + + AsynchIO::~AsynchIO() { + // Warn if we are deleting whilst there are still unreclaimed write buffers + if ( outstandingWrites>0 ) + QPID_LOG(error, "RDMA: qp=" << qp << ": Deleting queue before all write buffers finished"); + + // Turn off callbacks (before doing the deletes) + dataHandle.stopWatch(); + + // The buffers ptr_deque automatically deletes all the buffers we've allocated + // TODO: It might turn out to be more efficient in high connection loads to reuse the + // buffers rather than having to reregister them all the time (this would be straightforward if all + // connections haver the same buffer size and harder otherwise) + } + + void AsynchIO::start(Poller::shared_ptr poller) { + dataHandle.startWatch(poller); + } + + // Mark for deletion/Delete this object when we have no outstanding writes + void AsynchIO::deferDelete() { + State oldState; + State newState; + bool doReturn; + //qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock); + // It is safe to assign to deleting here as we either delete ourselves + // before leaving this function or deleting is set on exit + do { + newState = oldState = state.get(); + doReturn = false; + if (outstandingWrites > 0 || oldState != IDLE) { + deleting = true; + doReturn = true; + } else{ + newState = DELETED; // Stop any read callback before the dataHandle.stopWatch() in the destructor + } + } while (!state.boolCompareAndSwap(oldState, newState)); + if (doReturn) { + return; + } + delete this; + } + + void AsynchIO::queueWrite(Buffer* buff) { + // Make sure we don't overrun our available buffers + // either at our end or the known available at the peers end + if (writable()) { + // TODO: We might want to batch up sending credit + if (recvCredit > 0) { + int creditSent = recvCredit & ~FlagsMask; + qp->postSend(creditSent, buff); + recvCredit -= creditSent; + } else { + qp->postSend(buff); + } + ++outstandingWrites; + --xmitCredit; + } else { + if (fullCallback) { + fullCallback(*this, buff); + } else { + QPID_LOG(error, "RDMA: qp=" << qp << ": Write queue full, but no callback, throwing buffer away"); + returnBuffer(buff); + } + } + } + + // Mark now closed (so we don't accept any more writes or make any idle callbacks) + void AsynchIO::queueWriteClose() { + // Don't think we actually need to lock here as transition is 1 way only to closed + closed = true; + } + + void AsynchIO::notifyPendingWrite() { + // As notifyPendingWrite can be called on an arbitrary thread it must check whether we are processing or not. + // If we are then we just return as we know that we will eventually do the idle callback anyway. + // + // qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock); + // We can get here in any state (as the caller could be in any thread) + State oldState; + State newState; + bool doReturn; + do { + newState = oldState = state.get(); + doReturn = false; + switch (oldState) { + case NOTIFY_WRITE: + case PENDING_NOTIFY: + // We only need to note a pending notify if we're already doing a notify as data processing + // is always followed by write notification processing + newState = PENDING_NOTIFY; + doReturn = true; + break; + case PENDING_DATA: + doReturn = true; + break; + case DATA: + // Only need to return here as data processing will do the idleCallback itself anyway + doReturn = true; + break; + case IDLE: + newState = NOTIFY_WRITE; + break; + case DELETED: + assert(oldState!=DELETED); + doReturn = true; + }; + } while (!state.boolCompareAndSwap(oldState, newState)); + if (doReturn) { + return; + } + + doWriteCallback(); + + // Keep track of what we need to do so that we can release the lock + enum {COMPLETION, NOTIFY, RETURN, EXIT} action; + // If there was pending data whilst we were doing this, process it now + // + // Using NOTIFY_WRITE for both NOTIFY & COMPLETION is a bit strange, but we're making sure we get the + // correct result if we reenter notifyPendingWrite(), in which case we want to + // end up in PENDING_NOTIFY (entering dataEvent doesn't matter as it only checks + // not IDLE) + do { + //qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock); + do { + newState = oldState = state.get(); + action = RETURN; // Anything but COMPLETION + switch (oldState) { + case NOTIFY_WRITE: + newState = IDLE; + action = (action == COMPLETION) ? EXIT : RETURN; + break; + case PENDING_DATA: + newState = NOTIFY_WRITE; + action = COMPLETION; + break; + case PENDING_NOTIFY: + newState = NOTIFY_WRITE; + action = NOTIFY; + break; + default: + assert(oldState!=IDLE && oldState!=DATA && oldState!=DELETED); + action = RETURN; + } + } while (!state.boolCompareAndSwap(oldState, newState)); + + // Note we only get here if we were in the PENDING_DATA or PENDING_NOTIFY state + // so that we do need to process completions or notifications now + switch (action) { + case COMPLETION: + processCompletions(); + // Fall through + case NOTIFY: + doWriteCallback(); + break; + case RETURN: + return; + case EXIT: + // If we just processed completions we might need to delete ourselves + if (deleting && outstandingWrites == 0) { + delete this; + } + return; + } + } while (true); + } + + void AsynchIO::dataEvent(qpid::sys::DispatchHandle&) { + // Keep track of writable notifications + // qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock); + State oldState; + State newState; + bool doReturn; + do { + newState = oldState = state.get(); + doReturn = false; + // We're already processing a notification + switch (oldState) { + case IDLE: + newState = DATA; + break; + default: + // Can't get here in DATA state as that would violate the serialisation rules + assert( oldState!=DATA ); + newState = PENDING_DATA; + doReturn = true; + } + } while (!state.boolCompareAndSwap(oldState, newState)); + if (doReturn) { + return; + } + + processCompletions(); + + //qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock); + do { + newState = oldState = state.get(); + assert( oldState==DATA ); + newState = NOTIFY_WRITE; + } while (!state.boolCompareAndSwap(oldState, newState)); + + do { + doWriteCallback(); + + // qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock); + bool doBreak; + do { + newState = oldState = state.get(); + doBreak = false; + if ( oldState==NOTIFY_WRITE ) { + newState = IDLE; + doBreak = true; + } else { + // Can't get DATA/PENDING_DATA here as dataEvent cannot be reentered + assert( oldState==PENDING_NOTIFY ); + newState = NOTIFY_WRITE; + } + } while (!state.boolCompareAndSwap(oldState, newState)); + if (doBreak) { + break; + } + } while (true); + + // We might need to delete ourselves + if (deleting && outstandingWrites == 0) { + delete this; + } + } + + void AsynchIO::processCompletions() { + QueuePair::intrusive_ptr q = qp->getNextChannelEvent(); + + // Re-enable notification for queue: + // This needs to happen before we could do anything that could generate more work completion + // events (ie the callbacks etc. in the following). + // This can't make us reenter this code as the handle attached to the completion queue will still be + // disabled by the poller until we leave this code + qp->notifyRecv(); + qp->notifySend(); + + int recvEvents = 0; + int sendEvents = 0; + + // If no event do nothing + if (!q) + return; + + assert(q == qp); + + // Repeat until no more events + do { + QueuePairEvent e(qp->getNextEvent()); + if (!e) + break; + + ::ibv_wc_status status = e.getEventStatus(); + if (status != IBV_WC_SUCCESS) { + errorCallback(*this); + // TODO: Probably need to flush queues at this point + return; + } + + // Test if recv (or recv with imm) + //::ibv_wc_opcode eventType = e.getEventType(); + Buffer* b = e.getBuffer(); + QueueDirection dir = e.getDirection(); + if (dir == RECV) { + ++recvEvents; + + // Get our xmitCredit if it was sent + bool dataPresent = true; + if (e.immPresent() ) { + xmitCredit += (e.getImm() & ~FlagsMask); + dataPresent = ((e.getImm() & IgnoreData) == 0); + } + + // if there was no data sent then the message was only to update our credit + if ( dataPresent ) { + readCallback(*this, b); + } + + // At this point the buffer has been consumed so put it back on the recv queue + b->dataStart = 0; + b->dataCount = 0; + qp->postRecv(b); + + // Received another message + ++recvCredit; + + // Send recvCredit if it is large enough (it will have got this large because we've not sent anything recently) + if (recvCredit > recvBufferCount/2) { + // TODO: This should use RDMA write with imm as there might not ever be a buffer to receive this message + // but this is a little unlikely, as to get in this state we have to have received messages without sending any + // for a while so its likely we've received an credit update from the far side. + if (writable()) { + Buffer* ob = getBuffer(); + // Have to send something as adapters hate it when you try to transfer 0 bytes + *reinterpret_cast< uint32_t* >(ob->bytes) = htonl(recvCredit); + ob->dataCount = sizeof(uint32_t); + + int creditSent = recvCredit & ~FlagsMask; + qp->postSend(creditSent | IgnoreData, ob); + recvCredit -= creditSent; + ++outstandingWrites; + --xmitCredit; + } else { + QPID_LOG(warning, "RDMA: qp=" << qp << ": Unable to send unsolicited credit"); + } + } + } else { + ++sendEvents; + { + qpid::sys::ScopedLock<qpid::sys::Mutex> l(bufferQueueLock); + bufferQueue.push_front(b); + } + --outstandingWrites; + } + } while (true); + + // Not sure if this is expected or not + if (recvEvents == 0 && sendEvents == 0) { + QPID_LOG(debug, "RDMA: qp=" << qp << ": Got channel event with no recv/send completions"); + } + } + + void AsynchIO::doWriteCallback() { + // TODO: maybe don't call idle unless we're low on write buffers + // Keep on calling the idle routine as long as we are writable and we got something to write last call + while (writable()) { + int xc = xmitCredit; + idleCallback(*this); + // Check whether we actually wrote anything + if (xmitCredit == xc) { + QPID_LOG(debug, "RDMA: qp=" << qp << ": Called for data, but got none: xmitCredit=" << xmitCredit); + return; + } + } + } + + Buffer* AsynchIO::getBuffer() { + qpid::sys::ScopedLock<qpid::sys::Mutex> l(bufferQueueLock); + assert(!bufferQueue.empty()); + Buffer* b = bufferQueue.front(); + bufferQueue.pop_front(); + b->dataCount = 0; + b->dataStart = 0; + return b; + } + + void AsynchIO::returnBuffer(Buffer* b) { + qpid::sys::ScopedLock<qpid::sys::Mutex> l(bufferQueueLock); + bufferQueue.push_front(b); + b->dataCount = 0; + b->dataStart = 0; + } + + ConnectionManager::ConnectionManager( + ErrorCallback errc, + DisconnectedCallback dc + ) : + ci(Connection::make()), + handle(*ci, boost::bind(&ConnectionManager::event, this, _1), 0, 0), + errorCallback(errc), + disconnectedCallback(dc) + { + ci->nonblocking(); + } + + void ConnectionManager::start(Poller::shared_ptr poller) { + startConnection(ci); + handle.startWatch(poller); + } + + void ConnectionManager::event(DispatchHandle&) { + connectionEvent(ci); + } + + Listener::Listener( + const sockaddr& src, + const ConnectionParams& cp, + EstablishedCallback ec, + ErrorCallback errc, + DisconnectedCallback dc, + ConnectionRequestCallback crc + ) : + ConnectionManager(errc, dc), + src_addr(src), + checkConnectionParams(cp), + connectionRequestCallback(crc), + establishedCallback(ec) + { + } + + void Listener::startConnection(Connection::intrusive_ptr ci) { + ci->bind(src_addr); + ci->listen(); + } + + void Listener::connectionEvent(Connection::intrusive_ptr ci) { + ConnectionEvent e(ci->getNextEvent()); + + // If (for whatever reason) there was no event do nothing + if (!e) + return; + + // Important documentation ommision the new rdma_cm_id + // you get from CONNECT_REQUEST has the same context info + // as its parent listening rdma_cm_id + ::rdma_cm_event_type eventType = e.getEventType(); + ::rdma_conn_param conn_param = e.getConnectionParam(); + Rdma::Connection::intrusive_ptr id = e.getConnection(); + + switch (eventType) { + case RDMA_CM_EVENT_CONNECT_REQUEST: { + // Make sure peer has sent params we can use + if (!conn_param.private_data || conn_param.private_data_len < sizeof(ConnectionParams)) { + id->reject(); + break; + } + ConnectionParams cp = *static_cast<const ConnectionParams*>(conn_param.private_data); + + // Reject if requested msg size is bigger than we allow + if (cp.maxRecvBufferSize > checkConnectionParams.maxRecvBufferSize) { + id->reject(&checkConnectionParams); + break; + } + + bool accept = true; + if (connectionRequestCallback) + accept = connectionRequestCallback(id, cp); + + if (accept) { + // Accept connection + cp.initialXmitCredit = checkConnectionParams.initialXmitCredit; + id->accept(conn_param, &cp); + } else { + // Reject connection + id->reject(); + } + break; + } + case RDMA_CM_EVENT_ESTABLISHED: + establishedCallback(id); + break; + case RDMA_CM_EVENT_DISCONNECTED: + disconnectedCallback(id); + break; + case RDMA_CM_EVENT_CONNECT_ERROR: + errorCallback(id, CONNECT_ERROR); + break; + default: + // Unexpected response + errorCallback(id, UNKNOWN); + //std::cerr << "Warning: unexpected response to listen - " << eventType << "\n"; + } + } + + Connector::Connector( + const sockaddr& dst, + const ConnectionParams& cp, + ConnectedCallback cc, + ErrorCallback errc, + DisconnectedCallback dc, + RejectedCallback rc + ) : + ConnectionManager(errc, dc), + dst_addr(dst), + connectionParams(cp), + rejectedCallback(rc), + connectedCallback(cc) + { + } + + void Connector::startConnection(Connection::intrusive_ptr ci) { + ci->resolve_addr(dst_addr); + } + + void Connector::connectionEvent(Connection::intrusive_ptr ci) { + ConnectionEvent e(ci->getNextEvent()); + + // If (for whatever reason) there was no event do nothing + if (!e) + return; + + ::rdma_cm_event_type eventType = e.getEventType(); + ::rdma_conn_param conn_param = e.getConnectionParam(); + Rdma::Connection::intrusive_ptr id = e.getConnection(); + switch (eventType) { + case RDMA_CM_EVENT_ADDR_RESOLVED: + // RESOLVE_ADDR + ci->resolve_route(); + break; + case RDMA_CM_EVENT_ADDR_ERROR: + // RESOLVE_ADDR + errorCallback(ci, ADDR_ERROR); + break; + case RDMA_CM_EVENT_ROUTE_RESOLVED: + // RESOLVE_ROUTE: + ci->connect(&connectionParams); + break; + case RDMA_CM_EVENT_ROUTE_ERROR: + // RESOLVE_ROUTE: + errorCallback(ci, ROUTE_ERROR); + break; + case RDMA_CM_EVENT_CONNECT_ERROR: + // CONNECTING + errorCallback(ci, CONNECT_ERROR); + break; + case RDMA_CM_EVENT_UNREACHABLE: + // CONNECTING + errorCallback(ci, UNREACHABLE); + break; + case RDMA_CM_EVENT_REJECTED: { + // CONNECTING + // Extract private data from event + assert(conn_param.private_data && conn_param.private_data_len >= sizeof(ConnectionParams)); + ConnectionParams cp = *static_cast<const ConnectionParams*>(conn_param.private_data); + rejectedCallback(ci, cp); + break; + } + case RDMA_CM_EVENT_ESTABLISHED: { + // CONNECTING + // Extract private data from event + assert(conn_param.private_data && conn_param.private_data_len >= sizeof(ConnectionParams)); + ConnectionParams cp = *static_cast<const ConnectionParams*>(conn_param.private_data); + connectedCallback(ci, cp); + break; + } + case RDMA_CM_EVENT_DISCONNECTED: + // ESTABLISHED + disconnectedCallback(ci); + break; + default: + QPID_LOG(warning, "RDMA: Unexpected event in connect: " << eventType); + } + } +} diff --git a/RC9/qpid/cpp/src/qpid/sys/rdma/RdmaIO.h b/RC9/qpid/cpp/src/qpid/sys/rdma/RdmaIO.h new file mode 100644 index 0000000000..577c22d053 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/rdma/RdmaIO.h @@ -0,0 +1,222 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef Rdma_Acceptor_h +#define Rdma_Acceptor_h + +#include "rdma_wrap.h" + +#include "qpid/sys/AtomicValue.h" +#include "qpid/sys/Dispatcher.h" +#include "qpid/sys/DispatchHandle.h" +#include "qpid/sys/Mutex.h" + +#include <netinet/in.h> + +#include <boost/function.hpp> +#include <boost/ptr_container/ptr_deque.hpp> +#include <deque> + +namespace Rdma { + + class Connection; + + class AsynchIO + { + typedef boost::function1<void, AsynchIO&> ErrorCallback; + typedef boost::function2<void, AsynchIO&, Buffer*> ReadCallback; + typedef boost::function1<void, AsynchIO&> IdleCallback; + typedef boost::function2<void, AsynchIO&, Buffer*> FullCallback; + + QueuePair::intrusive_ptr qp; + qpid::sys::DispatchHandleRef dataHandle; + int bufferSize; + int recvCredit; + int xmitCredit; + int recvBufferCount; + int xmitBufferCount; + int outstandingWrites; + bool closed; // TODO: Perhaps (probably) this state can be merged with the following... + bool deleting; // TODO: Perhaps (probably) this state can be merged with the following... + enum State { IDLE, DATA, PENDING_DATA, NOTIFY_WRITE, PENDING_NOTIFY, DELETED }; + qpid::sys::AtomicValue<State> state; + //qpid::sys::Mutex stateLock; + std::deque<Buffer*> bufferQueue; + qpid::sys::Mutex bufferQueueLock; + boost::ptr_deque<Buffer> buffers; + + ReadCallback readCallback; + IdleCallback idleCallback; + FullCallback fullCallback; + ErrorCallback errorCallback; + + public: + // TODO: Instead of specifying a buffer size specify the amount of memory the AsynchIO class can use + // for buffers both read and write (allocate half to each up front) and fail if we cannot allocate that much + // locked memory + AsynchIO( + QueuePair::intrusive_ptr q, + int size, + int xCredit, + int rCount, + ReadCallback rc, + IdleCallback ic, + FullCallback fc, + ErrorCallback ec + ); + + void start(qpid::sys::Poller::shared_ptr poller); + bool writable() const; + bool bufferAvailable() const; + void queueWrite(Buffer* buff); + void notifyPendingWrite(); + void queueWriteClose(); + void deferDelete(); + int incompletedWrites() const; + Buffer* getBuffer(); + void returnBuffer(Buffer*); + + private: + // Don't let anyone else delete us to make sure there can't be outstanding callbacks + ~AsynchIO(); + + // Constants for the peer-peer command messages + // These are sent in the high bits if the imm data of an rdma message + // The low bits are used to send the credit + const static int FlagsMask = 0x10000000; // Mask for all flag bits - be sure to update this if you add more command bits + const static int IgnoreData = 0x10000000; // Message contains no application data + + void dataEvent(qpid::sys::DispatchHandle& handle); + void processCompletions(); + void doWriteCallback(); + }; + + inline bool AsynchIO::writable() const { + return (!closed && outstandingWrites < xmitBufferCount && xmitCredit > 0); + } + + inline int AsynchIO::incompletedWrites() const { + return outstandingWrites; + } + + inline bool AsynchIO::bufferAvailable() const { + return !bufferQueue.empty(); + } + // These are the parameters necessary to start the conversation + // * Each peer HAS to allocate buffers of the size of the maximum receive from its peer + // * Each peer HAS to know the initial "credit" it has for transmitting to its peer + struct ConnectionParams { + int maxRecvBufferSize; + int initialXmitCredit ; + + ConnectionParams(int s, int c) : + maxRecvBufferSize(s), + initialXmitCredit(c) + {} + }; + + enum ErrorType { + ADDR_ERROR, + ROUTE_ERROR, + CONNECT_ERROR, + UNREACHABLE, + UNKNOWN + }; + + typedef boost::function2<void, Rdma::Connection::intrusive_ptr&, ErrorType> ErrorCallback; + typedef boost::function1<void, Rdma::Connection::intrusive_ptr&> DisconnectedCallback; + + class ConnectionManager { + Connection::intrusive_ptr ci; + qpid::sys::DispatchHandle handle; + + protected: + ErrorCallback errorCallback; + DisconnectedCallback disconnectedCallback; + + public: + ConnectionManager( + ErrorCallback errc, + DisconnectedCallback dc + ); + + virtual ~ConnectionManager() {} + + void start(qpid::sys::Poller::shared_ptr poller); + + private: + void event(qpid::sys::DispatchHandle& handle); + + virtual void startConnection(Connection::intrusive_ptr ci) = 0; + virtual void connectionEvent(Connection::intrusive_ptr ci) = 0; + }; + + typedef boost::function2<bool, Rdma::Connection::intrusive_ptr&, const ConnectionParams&> ConnectionRequestCallback; + typedef boost::function1<void, Rdma::Connection::intrusive_ptr&> EstablishedCallback; + + class Listener : public ConnectionManager + { + sockaddr src_addr; + ConnectionParams checkConnectionParams; + ConnectionRequestCallback connectionRequestCallback; + EstablishedCallback establishedCallback; + + public: + Listener( + const sockaddr& src, + const ConnectionParams& cp, + EstablishedCallback ec, + ErrorCallback errc, + DisconnectedCallback dc, + ConnectionRequestCallback crc = 0 + ); + + private: + void startConnection(Connection::intrusive_ptr ci); + void connectionEvent(Connection::intrusive_ptr ci); + }; + + typedef boost::function2<void, Rdma::Connection::intrusive_ptr&, const ConnectionParams&> RejectedCallback; + typedef boost::function2<void, Rdma::Connection::intrusive_ptr&, const ConnectionParams&> ConnectedCallback; + + class Connector : public ConnectionManager + { + sockaddr dst_addr; + ConnectionParams connectionParams; + RejectedCallback rejectedCallback; + ConnectedCallback connectedCallback; + + public: + Connector( + const sockaddr& dst, + const ConnectionParams& cp, + ConnectedCallback cc, + ErrorCallback errc, + DisconnectedCallback dc, + RejectedCallback rc = 0 + ); + + private: + void startConnection(Connection::intrusive_ptr ci); + void connectionEvent(Connection::intrusive_ptr ci); + }; +} + +#endif // Rdma_Acceptor_h diff --git a/RC9/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp b/RC9/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp new file mode 100644 index 0000000000..594578a265 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/rdma/RdmaServer.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 "RdmaIO.h" + +#include <arpa/inet.h> + +#include <vector> +#include <queue> +#include <string> +#include <iostream> + +#include <boost/bind.hpp> + +using std::vector; +using std::queue; +using std::string; +using std::cout; +using std::cerr; + +using qpid::sys::Poller; +using qpid::sys::Dispatcher; + +// All the accepted connections +struct ConRec { + Rdma::Connection::intrusive_ptr connection; + Rdma::AsynchIO* data; + queue<Rdma::Buffer*> queuedWrites; + + ConRec(Rdma::Connection::intrusive_ptr c) : + connection(c) + {} +}; + +void dataError(Rdma::AsynchIO&) { + cout << "Data error:\n"; +} + +void idle(ConRec* cr, Rdma::AsynchIO& a) { + // Need to make sure full is not called as it would reorder messages + while (!cr->queuedWrites.empty() && a.writable()) { + Rdma::Buffer* buf = cr->queuedWrites.front(); + cr->queuedWrites.pop(); + a.queueWrite(buf); + } +} + +void data(ConRec* cr, Rdma::AsynchIO& a, Rdma::Buffer* b) { + // Echo data back + Rdma::Buffer* buf = a.getBuffer(); + std::copy(b->bytes+b->dataStart, b->bytes+b->dataStart+b->dataCount, buf->bytes); + buf->dataCount = b->dataCount; + if (cr->queuedWrites.empty()) { + // If can't write then full will be called and push buffer on back of queue + a.queueWrite(buf); + } else { + cr->queuedWrites.push(buf); + // Try to empty queue + idle(cr, a); + } +} + +void full(ConRec* cr, Rdma::AsynchIO&, Rdma::Buffer* buf) { + cr->queuedWrites.push(buf); +} + +void disconnected(Rdma::Connection::intrusive_ptr& ci) { + ConRec* cr = ci->getContext<ConRec>(); + cr->connection->disconnect(); + cr->data->queueWriteClose(); + delete cr; + cout << "Disconnected: " << cr << "\n"; +} + +void connectionError(Rdma::Connection::intrusive_ptr& ci, Rdma::ErrorType) { + ConRec* cr = ci->getContext<ConRec>(); + cr->connection->disconnect(); + if (cr) { + cr->data->queueWriteClose(); + delete cr; + } + cout << "Connection error: " << cr << "\n"; +} + +bool connectionRequest(Rdma::Connection::intrusive_ptr& ci, const Rdma::ConnectionParams& cp) { + cout << "Incoming connection: "; + + // For fun reject alternate connection attempts + static bool x = false; + x = true; + + // Must create aio here so as to prepost buffers *before* we accept connection + if (x) { + ConRec* cr = new ConRec(ci); + Rdma::AsynchIO* aio = + new Rdma::AsynchIO(ci->getQueuePair(), + cp.maxRecvBufferSize, cp.initialXmitCredit, Rdma::DEFAULT_WR_ENTRIES, + boost::bind(data, cr, _1, _2), + boost::bind(idle, cr, _1), + boost::bind(full, cr, _1, _2), + dataError); + ci->addContext(cr); + cr->data = aio; + cout << "Accept=>" << cr << "\n"; + } else { + cout << "Reject\n"; + } + + return x; +} + +void connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci) { + static int cnt = 0; + ConRec* cr = ci->getContext<ConRec>(); + cout << "Connected: " << cr << "(" << ++cnt << ")\n"; + + cr->data->start(poller); +} + +int main(int argc, char* argv[]) { + vector<string> args(&argv[0], &argv[argc]); + + ::sockaddr_in sin; + + int port = (args.size() < 2) ? 20079 : atoi(args[1].c_str()); + cout << "Listening on port: " << port << "\n"; + + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = INADDR_ANY; + + try { + boost::shared_ptr<Poller> p(new Poller()); + Dispatcher d(p); + + Rdma::Listener a((const sockaddr&)(sin), + Rdma::ConnectionParams(16384, Rdma::DEFAULT_WR_ENTRIES), + boost::bind(connected, p, _1), + connectionError, + disconnected, + connectionRequest); + + + a.start(p); + d.run(); + } catch (Rdma::Exception& e) { + int err = e.getError(); + cerr << "Error: " << e.what() << "(" << err << ")\n"; + } +} diff --git a/RC9/qpid/cpp/src/qpid/sys/rdma/rdma_exception.h b/RC9/qpid/cpp/src/qpid/sys/rdma/rdma_exception.h new file mode 100644 index 0000000000..7867aef2e4 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/rdma/rdma_exception.h @@ -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. + * + */ +#ifndef RDMA_EXCEPTION_H +#define RDMA_EXCEPTION_H + +#include <exception> + +#include <errno.h> +#include <string.h> + +namespace Rdma { + static __thread char s[50]; + class Exception : public std::exception { + int err; + + public: + Exception(int e) : err(e) {} + int getError() { return err; } + const char* what() const throw() { + return ::strerror_r(err, s, 50); + } + }; + + inline void THROW_ERRNO() { + throw Rdma::Exception(errno); + } + + inline void CHECK(int rc) { + if (rc != 0) + throw Rdma::Exception((rc == -1) ? errno : rc >0 ? rc : -rc); + } + + inline void CHECK_IBV(int rc) { + if (rc != 0) + throw Rdma::Exception(rc); + } + + template <typename T> + inline + T* CHECK_NULL(T* rc) { + if (rc == 0) + THROW_ERRNO(); + return rc; + } +} + +#endif // RDMA_EXCEPTION_H diff --git a/RC9/qpid/cpp/src/qpid/sys/rdma/rdma_factories.cpp b/RC9/qpid/cpp/src/qpid/sys/rdma/rdma_factories.cpp new file mode 100644 index 0000000000..c6e8df814b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/rdma/rdma_factories.cpp @@ -0,0 +1,64 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "rdma_factories.h" + +namespace Rdma { + void acker(::rdma_cm_event* e) throw () { + if (e) + // Intentionally ignore return value - we can't do anything about it here + (void) ::rdma_ack_cm_event(e); + } + + void destroyEChannel(::rdma_event_channel* c) throw () { + if (c) + // Intentionally ignore return value - we can't do anything about it here + (void) ::rdma_destroy_event_channel(c); + } + + void destroyId(::rdma_cm_id* i) throw () { + if (i) + // Intentionally ignore return value - we can't do anything about it here + (void) ::rdma_destroy_id(i); + } + + void deallocPd(::ibv_pd* p) throw () { + if (p) + // Intentionally ignore return value - we can't do anything about it here + (void) ::ibv_dealloc_pd(p); + } + + void destroyCChannel(::ibv_comp_channel* c) throw () { + if (c) + // Intentionally ignore return value - we can't do anything about it here + (void) ::ibv_destroy_comp_channel(c); + } + + void destroyCq(::ibv_cq* cq) throw () { + if (cq) + (void) ::ibv_destroy_cq(cq); + } + + void destroyQp(::ibv_qp* qp) throw () { + if (qp) + (void) ::ibv_destroy_qp(qp); + } + +} diff --git a/RC9/qpid/cpp/src/qpid/sys/rdma/rdma_factories.h b/RC9/qpid/cpp/src/qpid/sys/rdma/rdma_factories.h new file mode 100644 index 0000000000..8d024f37aa --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/rdma/rdma_factories.h @@ -0,0 +1,69 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef RDMA_FACTORIES_H +#define RDMA_FACTORIES_H + +#include "rdma_exception.h" + +#include <rdma/rdma_cma.h> + +#include <boost/shared_ptr.hpp> + +namespace Rdma { + // These allow us to use simple shared_ptrs to do ref counting + void acker(::rdma_cm_event* e) throw (); + void destroyEChannel(::rdma_event_channel* c) throw (); + void destroyId(::rdma_cm_id* i) throw (); + void deallocPd(::ibv_pd* p) throw (); + void destroyCChannel(::ibv_comp_channel* c) throw (); + void destroyCq(::ibv_cq* cq) throw (); + void destroyQp(::ibv_qp* qp) throw (); + + inline boost::shared_ptr< ::rdma_event_channel > mkEChannel() { + ::rdma_event_channel* c = CHECK_NULL(::rdma_create_event_channel()); + return boost::shared_ptr< ::rdma_event_channel >(c, destroyEChannel); + } + + inline boost::shared_ptr< ::rdma_cm_id > + mkId(::rdma_event_channel* ec, void* context, ::rdma_port_space ps) { + ::rdma_cm_id* i; + CHECK(::rdma_create_id(ec, &i, context, ps)); + return boost::shared_ptr< ::rdma_cm_id >(i, destroyId); + } + + inline boost::shared_ptr< ::ibv_pd > allocPd(::ibv_context* c) { + ::ibv_pd* pd = CHECK_NULL(ibv_alloc_pd(c)); + return boost::shared_ptr< ::ibv_pd >(pd, deallocPd); + } + + inline boost::shared_ptr< ::ibv_comp_channel > mkCChannel(::ibv_context* c) { + ::ibv_comp_channel* cc = CHECK_NULL(::ibv_create_comp_channel(c)); + return boost::shared_ptr< ::ibv_comp_channel >(cc, destroyCChannel); + } + + inline boost::shared_ptr< ::ibv_cq > + mkCq(::ibv_context* c, int cqe, void* context, ::ibv_comp_channel* cc) { + ::ibv_cq* cq = CHECK_NULL(ibv_create_cq(c, cqe, context, cc, 0)); + return boost::shared_ptr< ::ibv_cq >(cq, destroyCq); + } +} + +#endif // RDMA_FACTORIES_H diff --git a/RC9/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.cpp b/RC9/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.cpp new file mode 100644 index 0000000000..e105eb68c6 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.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 "rdma_wrap.h" + +namespace Rdma { + const ::rdma_conn_param DEFAULT_CONNECT_PARAM = { + 0, // .private_data + 0, // .private_data_len + 4, // .responder_resources + 4, // .initiator_depth + 0, // .flow_control + 5, // .retry_count + 7 // .rnr_retry_count + }; + + // This is moderately inefficient so don't use in a critical path + int deviceCount() { + int count; + ::ibv_free_device_list(::ibv_get_device_list(&count)); + return count; + } + + ::rdma_conn_param ConnectionEvent::getConnectionParam() const { + // It's badly documented, but it seems from the librdma source code that all the following + // event types have a valid param.conn + switch (event->event) { + case RDMA_CM_EVENT_CONNECT_REQUEST: + case RDMA_CM_EVENT_ESTABLISHED: + case RDMA_CM_EVENT_REJECTED: + case RDMA_CM_EVENT_DISCONNECTED: + case RDMA_CM_EVENT_CONNECT_ERROR: + return event->param.conn; + default: + ::rdma_conn_param p = {}; + return p; + } + } + + QueuePair::QueuePair(boost::shared_ptr< ::rdma_cm_id > i) : + qpid::sys::IOHandle(new qpid::sys::IOHandlePrivate), + pd(allocPd(i->verbs)), + cchannel(mkCChannel(i->verbs)), + scq(mkCq(i->verbs, DEFAULT_CQ_ENTRIES, 0, cchannel.get())), + rcq(mkCq(i->verbs, DEFAULT_CQ_ENTRIES, 0, cchannel.get())), + outstandingSendEvents(0), + outstandingRecvEvents(0) + { + impl->fd = cchannel->fd; + + // Set cq context to this QueuePair object so we can find + // ourselves again + scq->cq_context = this; + rcq->cq_context = this; + + ::ibv_qp_init_attr qp_attr = {}; + + // TODO: make a default struct for this + qp_attr.cap.max_send_wr = DEFAULT_WR_ENTRIES; + qp_attr.cap.max_send_sge = 4; + qp_attr.cap.max_recv_wr = DEFAULT_WR_ENTRIES; + qp_attr.cap.max_recv_sge = 4; + + qp_attr.send_cq = scq.get(); + qp_attr.recv_cq = rcq.get(); + qp_attr.qp_type = IBV_QPT_RC; + + CHECK(::rdma_create_qp(i.get(), pd.get(), &qp_attr)); + qp = boost::shared_ptr< ::ibv_qp >(i->qp, destroyQp); + + // Set the qp context to this so we can find ourselves again + qp->qp_context = this; + } + + QueuePair::~QueuePair() { + if (outstandingSendEvents > 0) + ::ibv_ack_cq_events(scq.get(), outstandingSendEvents); + if (outstandingRecvEvents > 0) + ::ibv_ack_cq_events(rcq.get(), outstandingRecvEvents); + + // Reset back pointer in case someone else has the qp + qp->qp_context = 0; + } + + void QueuePair::postRecv(Buffer* buf) { + ::ibv_recv_wr rwr = {}; + ::ibv_sge sge; + + sge.addr = (uintptr_t) buf->bytes+buf->dataStart; + sge.length = buf->dataCount; + sge.lkey = buf->mr->lkey; + + rwr.wr_id = reinterpret_cast<uint64_t>(buf); + rwr.sg_list = &sge; + rwr.num_sge = 1; + + ::ibv_recv_wr* badrwr = 0; + CHECK_IBV(::ibv_post_recv(qp.get(), &rwr, &badrwr)); + if (badrwr) + throw std::logic_error("ibv_post_recv(): Bad rwr"); + } + + void QueuePair::postSend(Buffer* buf) { + ::ibv_send_wr swr = {}; + ::ibv_sge sge; + + sge.addr = (uintptr_t) buf->bytes+buf->dataStart; + sge.length = buf->dataCount; + sge.lkey = buf->mr->lkey; + + swr.wr_id = reinterpret_cast<uint64_t>(buf); + swr.opcode = IBV_WR_SEND; + swr.send_flags = IBV_SEND_SIGNALED; + swr.sg_list = &sge; + swr.num_sge = 1; + + ::ibv_send_wr* badswr = 0; + CHECK_IBV(::ibv_post_send(qp.get(), &swr, &badswr)); + if (badswr) + throw std::logic_error("ibv_post_send(): Bad swr"); + } + + void QueuePair::postSend(uint32_t imm, Buffer* buf) { + ::ibv_send_wr swr = {}; + ::ibv_sge sge; + + sge.addr = (uintptr_t) buf->bytes+buf->dataStart; + sge.length = buf->dataCount; + sge.lkey = buf->mr->lkey; + swr.send_flags = IBV_SEND_SIGNALED; + + swr.wr_id = reinterpret_cast<uint64_t>(buf); + swr.imm_data = htonl(imm); + swr.opcode = IBV_WR_SEND_WITH_IMM; + swr.sg_list = &sge; + swr.num_sge = 1; + + ::ibv_send_wr* badswr = 0; + CHECK_IBV(::ibv_post_send(qp.get(), &swr, &badswr)); + if (badswr) + throw std::logic_error("ibv_post_send(): Bad swr"); + } +} + +std::ostream& operator<<(std::ostream& o, ::rdma_cm_event_type t) { +# define CHECK_TYPE(t) case t: o << #t; break; + switch(t) { + CHECK_TYPE(RDMA_CM_EVENT_ADDR_RESOLVED) + CHECK_TYPE(RDMA_CM_EVENT_ADDR_ERROR) + CHECK_TYPE(RDMA_CM_EVENT_ROUTE_RESOLVED) + CHECK_TYPE(RDMA_CM_EVENT_ROUTE_ERROR) + CHECK_TYPE(RDMA_CM_EVENT_CONNECT_REQUEST) + CHECK_TYPE(RDMA_CM_EVENT_CONNECT_RESPONSE) + CHECK_TYPE(RDMA_CM_EVENT_CONNECT_ERROR) + CHECK_TYPE(RDMA_CM_EVENT_UNREACHABLE) + CHECK_TYPE(RDMA_CM_EVENT_REJECTED) + CHECK_TYPE(RDMA_CM_EVENT_ESTABLISHED) + CHECK_TYPE(RDMA_CM_EVENT_DISCONNECTED) + CHECK_TYPE(RDMA_CM_EVENT_DEVICE_REMOVAL) + CHECK_TYPE(RDMA_CM_EVENT_MULTICAST_JOIN) + CHECK_TYPE(RDMA_CM_EVENT_MULTICAST_ERROR) + } +# undef CHECK_TYPE + return o; +} diff --git a/RC9/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.h b/RC9/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.h new file mode 100644 index 0000000000..7812a02532 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.h @@ -0,0 +1,498 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef RDMA_WRAP_H +#define RDMA_WRAP_H + +#include "rdma_factories.h" + +#include <rdma/rdma_cma.h> + +#include "qpid/RefCounted.h" +#include "qpid/sys/IOHandle.h" +#include "qpid/sys/posix/PrivatePosix.h" + +#include <fcntl.h> + +#include <netdb.h> + +#include <vector> +#include <algorithm> +#include <iostream> +#include <stdexcept> +#include <boost/shared_ptr.hpp> +#include <boost/intrusive_ptr.hpp> + +namespace Rdma { + const int DEFAULT_TIMEOUT = 2000; // 2 secs + const int DEFAULT_BACKLOG = 100; + const int DEFAULT_CQ_ENTRIES = 256; + const int DEFAULT_WR_ENTRIES = 64; + extern const ::rdma_conn_param DEFAULT_CONNECT_PARAM; + + int deviceCount(); + + struct Buffer { + friend class QueuePair; + + char* const bytes; + const int32_t byteCount; + int32_t dataStart; + int32_t dataCount; + + Buffer(::ibv_pd* pd, char* const b, const int32_t s) : + bytes(b), + byteCount(s), + dataStart(0), + dataCount(0), + mr(CHECK_NULL(::ibv_reg_mr( + pd, bytes, byteCount, + ::IBV_ACCESS_LOCAL_WRITE))) + {} + + ~Buffer() { + (void) ::ibv_dereg_mr(mr); + delete [] bytes; + } + + private: + ::ibv_mr* mr; + }; + + class Connection; + + enum QueueDirection { + NONE, + SEND, + RECV + }; + + class QueuePairEvent { + boost::shared_ptr< ::ibv_cq > cq; + ::ibv_wc wc; + QueueDirection dir; + + friend class QueuePair; + + QueuePairEvent() : + dir(NONE) + {} + + QueuePairEvent( + const ::ibv_wc& w, + boost::shared_ptr< ::ibv_cq > c, + QueueDirection d) : + cq(c), + wc(w), + dir(d) + { + assert(dir != NONE); + } + + public: + operator bool() const { + return dir != NONE; + } + + bool immPresent() const { + return wc.wc_flags & IBV_WC_WITH_IMM; + } + + uint32_t getImm() const { + return ntohl(wc.imm_data); + } + + QueueDirection getDirection() const { + return dir; + } + + ::ibv_wc_opcode getEventType() const { + return wc.opcode; + } + + ::ibv_wc_status getEventStatus() const { + return wc.status; + } + + Buffer* getBuffer() const { + Buffer* b = reinterpret_cast<Buffer*>(wc.wr_id); + b->dataCount = wc.byte_len; + return b; + } + }; + + // 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 { + boost::shared_ptr< ::ibv_pd > pd; + boost::shared_ptr< ::ibv_comp_channel > cchannel; + boost::shared_ptr< ::ibv_cq > scq; + boost::shared_ptr< ::ibv_cq > rcq; + boost::shared_ptr< ::ibv_qp > qp; + int outstandingSendEvents; + int outstandingRecvEvents; + + friend class Connection; + + QueuePair(boost::shared_ptr< ::rdma_cm_id > id); + ~QueuePair(); + + public: + typedef boost::intrusive_ptr<QueuePair> intrusive_ptr; + + // Create a buffer to use for writing + Buffer* createBuffer(int s) { + return new Buffer(pd.get(), new char[s], s); + } + + // Make channel non-blocking by making + // associated fd nonblocking + void nonblocking() { + ::fcntl(cchannel->fd, F_SETFL, O_NONBLOCK); + } + + // If we get EAGAIN because the channel has been set non blocking + // and we'd have to wait then return an empty event + QueuePair::intrusive_ptr getNextChannelEvent() { + // First find out which cq has the event + ::ibv_cq* cq; + void* ctx; + int rc = ::ibv_get_cq_event(cchannel.get(), &cq, &ctx); + if (rc == -1 && errno == EAGAIN) + return 0; + CHECK(rc); + + // Batch acknowledge the event + if (cq == scq.get()) { + if (++outstandingSendEvents > DEFAULT_CQ_ENTRIES / 2) { + ::ibv_ack_cq_events(cq, outstandingSendEvents); + outstandingSendEvents = 0; + } + } else if (cq == rcq.get()) { + if (++outstandingRecvEvents > DEFAULT_CQ_ENTRIES / 2) { + ::ibv_ack_cq_events(cq, outstandingRecvEvents); + outstandingRecvEvents = 0; + } + } + + return static_cast<QueuePair*>(ctx); + } + + QueuePairEvent getNextEvent() { + ::ibv_wc w; + if (::ibv_poll_cq(scq.get(), 1, &w) == 1) + return QueuePairEvent(w, scq, SEND); + else if (::ibv_poll_cq(rcq.get(), 1, &w) == 1) + return QueuePairEvent(w, rcq, RECV); + else + return QueuePairEvent(); + } + + void postRecv(Buffer* buf); + void postSend(Buffer* buf); + void postSend(uint32_t imm, Buffer* buf); + void notifyRecv(); + void notifySend(); + }; + + class ConnectionEvent { + friend class Connection; + + // The order of the members is important as we have to acknowledge + // the event before destroying the ids on destruction + boost::intrusive_ptr<Connection> id; + boost::intrusive_ptr<Connection> listen_id; + boost::shared_ptr< ::rdma_cm_event > event; + + ConnectionEvent() {} + ConnectionEvent(::rdma_cm_event* e); + + // Default copy, assignment and destructor ok + public: + operator bool() const { + return event; + } + + ::rdma_cm_event_type getEventType() const { + return event->event; + } + + ::rdma_conn_param getConnectionParam() const; + + boost::intrusive_ptr<Connection> getConnection () const { + return id; + } + + boost::intrusive_ptr<Connection> getListenId() const { + return listen_id; + } + }; + + // For the moment this is a fairly simple wrapper for rdma_cm_id. + // + // NB: It allocates a protection domain (pd) per connection which means that + // registered buffers can't be shared between different connections + // (this can only happen between connections on the same controller in any case, + // so needs careful management if used) + class Connection : public qpid::sys::IOHandle, public qpid::RefCounted { + boost::shared_ptr< ::rdma_event_channel > channel; + boost::shared_ptr< ::rdma_cm_id > id; + QueuePair::intrusive_ptr qp; + + void* context; + + friend class ConnectionEvent; + friend class QueuePair; + + // Wrap the passed in rdma_cm_id with a Connection + // this basically happens only on connection request + Connection(::rdma_cm_id* i) : + qpid::sys::IOHandle(new qpid::sys::IOHandlePrivate), + id(i, destroyId), + context(0) + { + impl->fd = id->channel->fd; + + // Just overwrite the previous context as it will + // have come from the listening connection + if (i) + i->context = this; + } + + Connection() : + qpid::sys::IOHandle(new qpid::sys::IOHandlePrivate), + channel(mkEChannel()), + id(mkId(channel.get(), this, RDMA_PS_TCP)), + context(0) + { + impl->fd = channel->fd; + } + + ~Connection() { + // Reset the id context in case someone else has it + id->context = 0; + } + + // Default destructor fine + + void ensureQueuePair() { + assert(id.get()); + + // Only allocate a queue pair if there isn't one already + if (qp) + return; + + qp = new QueuePair(id); + } + + public: + typedef boost::intrusive_ptr<Connection> intrusive_ptr; + + static intrusive_ptr make() { + return new Connection(); + } + + static intrusive_ptr find(::rdma_cm_id* i) { + if (!i) + return 0; + Connection* id = static_cast< Connection* >(i->context); + if (!id) + throw std::logic_error("Couldn't find existing Connection"); + return id; + } + + template <typename T> + void addContext(T* c) { + // Don't allow replacing context + if (!context) + context = c; + } + + template <typename T> + T* getContext() { + return static_cast<T*>(context); + } + + // Make channel non-blocking by making + // associated fd nonblocking + void nonblocking() { + assert(id.get()); + ::fcntl(id->channel->fd, F_SETFL, O_NONBLOCK); + } + + // If we get EAGAIN because the channel has been set non blocking + // and we'd have to wait then return an empty event + ConnectionEvent getNextEvent() { + assert(id.get()); + ::rdma_cm_event* e; + int rc = ::rdma_get_cm_event(id->channel, &e); + if (rc == -1 && errno == EAGAIN) + return ConnectionEvent(); + CHECK(rc); + return ConnectionEvent(e); + } + + void bind(sockaddr& src_addr) const { + assert(id.get()); + CHECK(::rdma_bind_addr(id.get(), &src_addr)); + } + + void listen(int backlog = DEFAULT_BACKLOG) const { + assert(id.get()); + CHECK(::rdma_listen(id.get(), backlog)); + } + + void resolve_addr( + sockaddr& dst_addr, + sockaddr* src_addr = 0, + int timeout_ms = DEFAULT_TIMEOUT) const + { + assert(id.get()); + CHECK(::rdma_resolve_addr(id.get(), src_addr, &dst_addr, timeout_ms)); + } + + void resolve_route(int timeout_ms = DEFAULT_TIMEOUT) const { + assert(id.get()); + CHECK(::rdma_resolve_route(id.get(), timeout_ms)); + } + + void disconnect() const { + assert(id.get()); + CHECK(::rdma_disconnect(id.get())); + } + + // TODO: Currently you can only connect with the default connection parameters + void connect() { + assert(id.get()); + + // Need to have a queue pair before we can connect + ensureQueuePair(); + + ::rdma_conn_param p = DEFAULT_CONNECT_PARAM; + CHECK(::rdma_connect(id.get(), &p)); + } + + template <typename T> + void connect(const T* data) { + assert(id.get()); + // Need to have a queue pair before we can connect + ensureQueuePair(); + + ::rdma_conn_param p = DEFAULT_CONNECT_PARAM; + p.private_data = data; + p.private_data_len = sizeof(T); + CHECK(::rdma_connect(id.get(), &p)); + } + + // TODO: Not sure how to default accept params - they come from the connection request + // event + template <typename T> + void accept(const ::rdma_conn_param& param, const T* data) { + assert(id.get()); + // Need to have a queue pair before we can accept + ensureQueuePair(); + + ::rdma_conn_param p = param; + p.private_data = data; + p.private_data_len = sizeof(T); + CHECK(::rdma_accept(id.get(), &p)); + } + + void accept(const ::rdma_conn_param& param) { + assert(id.get()); + // Need to have a queue pair before we can accept + ensureQueuePair(); + + ::rdma_conn_param p = param; + p.private_data = 0; + p.private_data_len = 0; + CHECK(::rdma_accept(id.get(), &p)); + } + + template <typename T> + void reject(const T* data) const { + assert(id.get()); + CHECK(::rdma_reject(id.get(), data, sizeof(T))); + } + + void reject() const { + assert(id.get()); + CHECK(::rdma_reject(id.get(), 0, 0)); + } + + QueuePair::intrusive_ptr getQueuePair() { + assert(id.get()); + + ensureQueuePair(); + + return qp; + } + + std::string getLocalName() const { + ::sockaddr* addr = ::rdma_get_local_addr(id.get()); + char hostName[NI_MAXHOST]; + char portName[NI_MAXSERV]; + CHECK_IBV(::getnameinfo( + addr, sizeof(::sockaddr_storage), + hostName, sizeof(hostName), + portName, sizeof(portName), + NI_NUMERICHOST | NI_NUMERICSERV)); + std::string r(hostName); + r += ":"; + r += portName; + return r; + } + + std::string getPeerName() const { + ::sockaddr* addr = ::rdma_get_peer_addr(id.get()); + char hostName[NI_MAXHOST]; + char portName[NI_MAXSERV]; + CHECK_IBV(::getnameinfo( + addr, sizeof(::sockaddr_storage), + hostName, sizeof(hostName), + portName, sizeof(portName), + NI_NUMERICHOST | NI_NUMERICSERV)); + std::string r(hostName); + r += ":"; + r += portName; + return r; + } + }; + + inline void QueuePair::notifyRecv() { + CHECK_IBV(ibv_req_notify_cq(rcq.get(), 0)); + } + + inline void QueuePair::notifySend() { + CHECK_IBV(ibv_req_notify_cq(scq.get(), 0)); + } + + inline ConnectionEvent::ConnectionEvent(::rdma_cm_event* e) : + id((e->event != RDMA_CM_EVENT_CONNECT_REQUEST) ? + Connection::find(e->id) : new Connection(e->id)), + listen_id(Connection::find(e->listen_id)), + event(e, acker) + {} +} + +std::ostream& operator<<(std::ostream& o, ::rdma_cm_event_type t); + +#endif // RDMA_WRAP_H diff --git a/RC9/qpid/cpp/src/qpid/sys/solaris/ECFPoller.cpp b/RC9/qpid/cpp/src/qpid/sys/solaris/ECFPoller.cpp new file mode 100644 index 0000000000..783f84576b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/solaris/ECFPoller.cpp @@ -0,0 +1,301 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/log/Logger.h" +#include "qpid/sys/Poller.h" +#include "qpid/sys/IOHandle.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/DeletionManager.h" +#include "qpid/sys/posix/check.h" +#include "qpid/sys/posix/PrivatePosix.h" + +#include <port.h> +#include <poll.h> +#include <errno.h> + +#include <assert.h> +#include <vector> +#include <exception> + + +//TODO: Remove this +#include "qpid/sys/Dispatcher.h" + +namespace qpid { +namespace sys { + +// Deletion manager to handle deferring deletion of PollerHandles to when they definitely aren't being used +DeletionManager<PollerHandle> PollerHandleDeletionManager; + +// Instantiate (and define) class static for DeletionManager +template <> +DeletionManager<PollerHandle>::AllThreadsStatuses DeletionManager<PollerHandle>::allThreadsStatuses(0); + +class PollerHandlePrivate { + friend class Poller; + friend class PollerHandle; + + enum FDStat { + ABSENT, + MONITORED, + INACTIVE, + HUNGUP, + MONITORED_HUNGUP + }; + + int fd; + uint32_t events; + FDStat stat; + Mutex lock; + + PollerHandlePrivate(int f) : + fd(f), + events(0), + stat(ABSENT) { + } + + bool isActive() const { + return stat == MONITORED || stat == MONITORED_HUNGUP; + } + + void setActive() { + stat = (stat == HUNGUP) ? MONITORED_HUNGUP : MONITORED; + } + + bool isInactive() const { + return stat == INACTIVE || stat == HUNGUP; + } + + void setInactive() { + stat = INACTIVE; + } + + bool isIdle() const { + return stat == ABSENT; + } + + void setIdle() { + stat = ABSENT; + } + + bool isHungup() const { + return stat == MONITORED_HUNGUP || stat == HUNGUP; + } + + void setHungup() { + assert(stat == MONITORED); + stat = HUNGUP; + } +}; + +PollerHandle::PollerHandle(const IOHandle& h) : + impl(new PollerHandlePrivate(toFd(h.impl))) +{} + +PollerHandle::~PollerHandle() { + delete impl; +} + +void PollerHandle::deferDelete() { + PollerHandleDeletionManager.markForDeletion(this); +} + +/** + * Concrete implementation of Poller to use the Solaris Event Completion + * Framework interface + */ +class PollerPrivate { + friend class Poller; + + const int portId; + + static uint32_t directionToPollEvent(Poller::Direction dir) { + switch (dir) { + case Poller::INPUT: return POLLIN; + case Poller::OUTPUT: return POLLOUT; + case Poller::INOUT: return POLLIN | POLLOUT; + default: return 0; + } + } + + static Poller::EventType pollToDirection(uint32_t events) { + uint32_t e = events & (POLLIN | POLLOUT); + switch (e) { + case POLLIN: return Poller::READABLE; + case POLLOUT: return Poller::WRITABLE; + case POLLIN | POLLOUT: return Poller::READ_WRITABLE; + default: + return (events & (POLLHUP | POLLERR)) ? + Poller::DISCONNECTED : Poller::INVALID; + } + } + + PollerPrivate() : + portId(::port_create()) { + } + + ~PollerPrivate() { + } +}; + +void Poller::addFd(PollerHandle& handle, Direction dir) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + + uint32_t events = 0; + + if (eh.isIdle()) { + events = PollerPrivate::directionToPollEvent(dir); + } else { + assert(eh.isActive()); + events = eh.events | PollerPrivate::directionToPollEvent(dir); + } + + //port_associate can be used to add an association or modify an + //existing one + QPID_POSIX_CHECK(::port_associate(impl->portId, PORT_SOURCE_FD, (uintptr_t) eh.fd, events, &handle)); + eh.events = events; + eh.setActive(); + QPID_LOG(trace, "Poller::addFd(handle=" << &handle + << "[" << typeid(&handle).name() + << "], fd=" << eh.fd << ")"); + //assert(dynamic_cast<DispatchHandle*>(&handle)); +} + +void Poller::delFd(PollerHandle& handle) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + assert(!eh.isIdle()); + int rc = ::port_dissociate(impl->portId, PORT_SOURCE_FD, (uintptr_t) eh.fd); + //Allow closing an invalid fd, allowing users to close fd before + //doing delFd() + if (rc == -1 && errno != EBADFD) { + QPID_POSIX_CHECK(rc); + } + eh.setIdle(); + QPID_LOG(trace, "Poller::delFd(handle=" << &handle + << ", fd=" << eh.fd << ")"); +} + +// modFd is equivalent to delFd followed by addFd +void Poller::modFd(PollerHandle& handle, Direction dir) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + assert(!eh.isIdle()); + + eh.events = PollerPrivate::directionToPollEvent(dir); + + //If fd is already associated, events and user arguments are updated + //So, no need to check if fd is already associated + QPID_POSIX_CHECK(::port_associate(impl->portId, PORT_SOURCE_FD, (uintptr_t) eh.fd, eh.events, &handle)); + eh.setActive(); + QPID_LOG(trace, "Poller::modFd(handle=" << &handle + << ", fd=" << eh.fd << ")"); +} + +void Poller::rearmFd(PollerHandle& handle) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + assert(eh.isInactive()); + + QPID_POSIX_CHECK(::port_associate(impl->portId, PORT_SOURCE_FD, (uintptr_t) eh.fd, eh.events, &handle)); + eh.setActive(); + QPID_LOG(trace, "Poller::rearmdFd(handle=" << &handle + << ", fd=" << eh.fd << ")"); +} + +void Poller::shutdown() { + //Send an Alarm to the port + //We need to send a nonzero event mask, using POLLHUP, but + //The wait method will only look for a PORT_ALERT_SET + QPID_POSIX_CHECK(::port_alert(impl->portId, PORT_ALERT_SET, POLLHUP, NULL)); + QPID_LOG(trace, "Poller::shutdown"); +} + +Poller::Event Poller::wait(Duration timeout) { + timespec_t tout; + timespec_t* ptout = NULL; + port_event_t pe; + + if (timeout != TIME_INFINITE) { + tout.tv_sec = 0; + tout.tv_nsec = timeout; + ptout = &tout; + } + + do { + PollerHandleDeletionManager.markAllUnusedInThisThread(); + QPID_LOG(trace, "About to enter port_get. Thread " + << pthread_self() + << ", timeout=" << timeout); + + int rc = ::port_get(impl->portId, &pe, ptout); + + if (rc < 0) { + switch (errno) { + case EINTR: + continue; + case ETIME: + return Event(0, TIMEOUT); + default: + QPID_POSIX_CHECK(rc); + } + } else { + //We use alert mode to notify the shutdown of the Poller + if (pe.portev_source == PORT_SOURCE_ALERT) { + return Event(0, SHUTDOWN); + } + if (pe.portev_source == PORT_SOURCE_FD) { + PollerHandle *handle = static_cast<PollerHandle*>(pe.portev_user); + PollerHandlePrivate& eh = *handle->impl; + ScopedLock<Mutex> l(eh.lock); + QPID_LOG(trace, "About to send handle: " << handle); + + if (eh.isActive()) { + if (pe.portev_events & POLLHUP) { + if (eh.isHungup()) { + return Event(handle, DISCONNECTED); + } + eh.setHungup(); + } else { + eh.setInactive(); + } + QPID_LOG(trace, "Sending event (thread: " + << pthread_self() << ") for handle " << handle + << ", direction= " + << PollerPrivate::pollToDirection(pe.portev_events)); + return Event(handle, PollerPrivate::pollToDirection(pe.portev_events)); + } + } + } + } while (true); +} + +// Concrete constructors +Poller::Poller() : + impl(new PollerPrivate()) +{} + +Poller::~Poller() { + delete impl; +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp b/RC9/qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp new file mode 100644 index 0000000000..3c7e2190e7 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp @@ -0,0 +1,181 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "SslHandler.h" + +#include "SslIo.h" +#include "SslSocket.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace sys { +namespace ssl { + + +// Buffer definition +struct Buff : public SslIO::BufferBase { + Buff() : + SslIO::BufferBase(new char[65536], 65536) + {} + ~Buff() + { delete [] bytes;} +}; + +SslHandler::SslHandler(std::string id, ConnectionCodec::Factory* f) : + identifier(id), + aio(0), + factory(f), + codec(0), + readError(false), + isClient(false) +{} + +SslHandler::~SslHandler() { + if (codec) + codec->closed(); + delete codec; +} + +void SslHandler::init(SslIO* a, int numBuffs) { + aio = a; + + // Give connection some buffers to use + for (int i = 0; i < numBuffs; i++) { + aio->queueReadBuffer(new Buff); + } +} + +void SslHandler::write(const framing::ProtocolInitiation& data) +{ + QPID_LOG(debug, "SENT [" << identifier << "] INIT(" << data << ")"); + SslIO::BufferBase* buff = aio->getQueuedBuffer(); + if (!buff) + buff = new Buff; + framing::Buffer out(buff->bytes, buff->byteCount); + data.encode(out); + buff->dataCount = data.encodedSize(); + aio->queueWrite(buff); +} + +void SslHandler::activateOutput() { + aio->notifyPendingWrite(); +} + +void SslHandler::giveReadCredit(int32_t) { + // FIXME aconway 2008-12-05: not yet implemented. +} + +// Input side +void SslHandler::readbuff(SslIO& , SslIO::BufferBase* buff) { + if (readError) { + return; + } + size_t decoded = 0; + if (codec) { // Already initiated + try { + decoded = codec->decode(buff->bytes+buff->dataStart, buff->dataCount); + }catch(const std::exception& e){ + QPID_LOG(error, e.what()); + readError = true; + aio->queueWriteClose(); + } + }else{ + framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount); + framing::ProtocolInitiation protocolInit; + if (protocolInit.decode(in)) { + decoded = in.getPosition(); + QPID_LOG(debug, "RECV [" << identifier << "] INIT(" << protocolInit << ")"); + try { + codec = factory->create(protocolInit.getVersion(), *this, identifier); + 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); + write(framing::ProtocolInitiation(codec->getVersion())); + return; + } + if (codec == 0) return; + if (codec->canEncode()) { + // Try and get a queued buffer if not then construct new one + SslIO::BufferBase* buff = aio->getQueuedBuffer(); + if (!buff) buff = new Buff; + size_t encoded=codec->encode(buff->bytes, buff->byteCount); + buff->dataCount = encoded; + aio->queueWrite(buff); + } + if (codec->isClosed()) + aio->queueWriteClose(); +} + + +}}} // namespace qpid::sys::ssl diff --git a/RC9/qpid/cpp/src/qpid/sys/ssl/SslHandler.h b/RC9/qpid/cpp/src/qpid/sys/ssl/SslHandler.h new file mode 100644 index 0000000000..ae654d7ad2 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ssl/SslHandler.h @@ -0,0 +1,76 @@ +#ifndef QPID_SYS_SSL_SSLHANDLER_H +#define QPID_SYS_SSL_SSLHANDLER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/ConnectionCodec.h" +#include "qpid/sys/OutputControl.h" + +namespace qpid { + +namespace framing { + class ProtocolInitiation; +} + +namespace sys { +namespace ssl { + +class SslIO; +class SslIOBufferBase; +class SslSocket; + +class SslHandler : public OutputControl { + std::string identifier; + SslIO* aio; + ConnectionCodec::Factory* factory; + ConnectionCodec* codec; + bool readError; + bool isClient; + + void write(const framing::ProtocolInitiation&); + + public: + SslHandler(std::string id, ConnectionCodec::Factory* f); + ~SslHandler(); + void init(SslIO* a, int numBuffs); + + void setClient() { isClient = true; } + + // Output side + void close(); + 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/RC9/qpid/cpp/src/qpid/sys/ssl/SslIo.cpp b/RC9/qpid/cpp/src/qpid/sys/ssl/SslIo.cpp new file mode 100644 index 0000000000..9be75af47d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ssl/SslIo.cpp @@ -0,0 +1,433 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SslIo.h" +#include "SslSocket.h" + +#include "qpid/sys/Time.h" +#include "qpid/sys/posix/check.h" +#include "qpid/log/Statement.h" + +// TODO The basic algorithm here is not really POSIX specific and with a bit more abstraction +// could (should) be promoted to be platform portable +#include <unistd.h> +#include <sys/socket.h> +#include <signal.h> +#include <errno.h> +#include <string.h> + +#include <boost/bind.hpp> + +using namespace qpid::sys; +using namespace qpid::sys::ssl; + +namespace { + +/* + * Make *process* not generate SIGPIPE when writing to closed + * pipe/socket (necessary as default action is to terminate process) + */ +void ignoreSigpipe() { + ::signal(SIGPIPE, SIG_IGN); +} + +/* + * We keep per thread state to avoid locking overhead. The assumption is that + * on average all the connections are serviced by all the threads so the state + * recorded in each thread is about the same. If this turns out not to be the + * case we could rebalance the info occasionally. + */ +__thread int threadReadTotal = 0; +__thread int threadMaxRead = 0; +__thread int threadReadCount = 0; +__thread int threadWriteTotal = 0; +__thread int threadWriteCount = 0; +__thread int64_t threadMaxReadTimeNs = 2 * 1000000; // start at 2ms +} + +/* + * Asynch Acceptor + */ + +SslAcceptor::SslAcceptor(const SslSocket& s, Callback callback) : + acceptedCallback(callback), + handle(s, boost::bind(&SslAcceptor::readable, this, _1), 0, 0), + socket(s) { + + s.setNonblocking(); + ignoreSigpipe(); +} + +void SslAcceptor::start(Poller::shared_ptr poller) { + handle.startWatch(poller); +} + +/* + * We keep on accepting as long as there is something to accept + */ +void SslAcceptor::readable(DispatchHandle& h) { + SslSocket* s; + do { + errno = 0; + // TODO: Currently we ignore the peers address, perhaps we should + // log it or use it for connection acceptance. + try { + s = socket.accept(0, 0); + if (s) { + acceptedCallback(*s); + } else { + break; + } + } catch (const std::exception& e) { + QPID_LOG(error, "Could not accept socket: " << e.what()); + } + } while (true); + + h.rewatch(); +} + +/* + * Asynch Connector + */ + +SslConnector::SslConnector(const SslSocket& s, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb) : + DispatchHandle(s, + 0, + boost::bind(&SslConnector::connComplete, this, _1), + boost::bind(&SslConnector::connComplete, this, _1)), + connCallback(connCb), + failCallback(failCb), + socket(s) +{ + //TODO: would be better for connect to be performed on a + //non-blocking socket, but that doesn't work at present so connect + //blocks until complete + try { + socket.connect(hostname, port); + socket.setNonblocking(); + startWatch(poller); + } catch(std::exception& e) { + failure(-1, std::string(e.what())); + } +} + +void SslConnector::connComplete(DispatchHandle& h) +{ + int errCode = socket.getError(); + + h.stopWatch(); + if (errCode == 0) { + connCallback(socket); + DispatchHandle::doDelete(); + } else { + // TODO: This need to be fixed as strerror isn't thread safe + failure(errCode, std::string(::strerror(errCode))); + } +} + +void SslConnector::failure(int errCode, std::string message) +{ + if (failCallback) + failCallback(errCode, message); + + socket.close(); + delete &socket; + + DispatchHandle::doDelete(); +} + +/* + * Asynch reader/writer + */ +SslIO::SslIO(const SslSocket& s, + ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb, + ClosedCallback cCb, BuffersEmptyCallback eCb, IdleCallback iCb) : + + DispatchHandle(s, + boost::bind(&SslIO::readable, this, _1), + boost::bind(&SslIO::writeable, this, _1), + boost::bind(&SslIO::disconnected, this, _1)), + readCallback(rCb), + eofCallback(eofCb), + disCallback(disCb), + closedCallback(cCb), + emptyCallback(eCb), + idleCallback(iCb), + socket(s), + queuedClose(false), + writePending(false) { + + s.setNonblocking(); +} + +struct deleter +{ + template <typename T> + void operator()(T *ptr){ delete ptr;} +}; + +SslIO::~SslIO() { + std::for_each( bufferQueue.begin(), bufferQueue.end(), deleter()); + std::for_each( writeQueue.begin(), writeQueue.end(), deleter()); +} + +void SslIO::queueForDeletion() { + DispatchHandle::doDelete(); +} + +void SslIO::start(Poller::shared_ptr poller) { + DispatchHandle::startWatch(poller); +} + +void SslIO::queueReadBuffer(BufferBase* buff) { + assert(buff); + buff->dataStart = 0; + buff->dataCount = 0; + bufferQueue.push_back(buff); + DispatchHandle::rewatchRead(); +} + +void SslIO::unread(BufferBase* buff) { + assert(buff); + if (buff->dataStart != 0) { + memmove(buff->bytes, buff->bytes+buff->dataStart, buff->dataCount); + buff->dataStart = 0; + } + bufferQueue.push_front(buff); + DispatchHandle::rewatchRead(); +} + +void SslIO::queueWrite(BufferBase* buff) { + assert(buff); + // If we've already closed the socket then throw the write away + if (queuedClose) { + bufferQueue.push_front(buff); + return; + } else { + writeQueue.push_front(buff); + } + writePending = false; + DispatchHandle::rewatchWrite(); +} + +void SslIO::notifyPendingWrite() { + writePending = true; + DispatchHandle::rewatchWrite(); +} + +void SslIO::queueWriteClose() { + queuedClose = true; + DispatchHandle::rewatchWrite(); +} + +/** Return a queued buffer if there are enough + * to spare + */ +SslIO::BufferBase* SslIO::getQueuedBuffer() { + // Always keep at least one buffer (it might have data that was "unread" in it) + if (bufferQueue.size()<=1) + return 0; + BufferBase* buff = bufferQueue.back(); + assert(buff); + buff->dataStart = 0; + buff->dataCount = 0; + bufferQueue.pop_back(); + return buff; +} + +/* + * We keep on reading as long as we have something to read and a buffer to put + * it in + */ +void SslIO::readable(DispatchHandle& h) { + int readTotal = 0; + AbsTime readStartTime = AbsTime::now(); + do { + // (Try to) get a buffer + if (!bufferQueue.empty()) { + // Read into buffer + BufferBase* buff = bufferQueue.front(); + assert(buff); + bufferQueue.pop_front(); + errno = 0; + int readCount = buff->byteCount-buff->dataCount; + int rc = socket.read(buff->bytes + buff->dataCount, readCount); + if (rc > 0) { + buff->dataCount += rc; + threadReadTotal += rc; + readTotal += rc; + + readCallback(*this, buff); + if (rc != readCount) { + // If we didn't fill the read buffer then time to stop reading + break; + } + + // Stop reading if we've overrun our timeslot + if (Duration(readStartTime, AbsTime::now()) > threadMaxReadTimeNs) { + break; + } + + } else { + // Put buffer back (at front so it doesn't interfere with unread buffers) + bufferQueue.push_front(buff); + assert(buff); + + // Eof or other side has gone away + if (rc == 0 || errno == ECONNRESET) { + eofCallback(*this); + h.unwatchRead(); + break; + } else if (errno == EAGAIN) { + // We have just put a buffer back so we know + // we can carry on watching for reads + break; + } else { + // Report error then just treat as a socket disconnect + QPID_LOG(error, "Error reading socket: " << qpid::sys::strError(rc) << "(" << rc << ")" ); + eofCallback(*this); + h.unwatchRead(); + break; + } + } + } else { + // Something to read but no buffer + if (emptyCallback) { + emptyCallback(*this); + } + // If we still have no buffers we can't do anything more + if (bufferQueue.empty()) { + h.unwatchRead(); + break; + } + + } + } while (true); + + ++threadReadCount; + threadMaxRead = std::max(threadMaxRead, readTotal); + return; +} + +/* + * We carry on writing whilst we have data to write and we can write + */ +void SslIO::writeable(DispatchHandle& h) { + int writeTotal = 0; + do { + // See if we've got something to write + if (!writeQueue.empty()) { + // Write buffer + BufferBase* buff = writeQueue.back(); + writeQueue.pop_back(); + errno = 0; + assert(buff->dataStart+buff->dataCount <= buff->byteCount); + int rc = socket.write(buff->bytes+buff->dataStart, buff->dataCount); + if (rc >= 0) { + threadWriteTotal += rc; + writeTotal += rc; + + // If we didn't write full buffer put rest back + if (rc != buff->dataCount) { + buff->dataStart += rc; + buff->dataCount -= rc; + writeQueue.push_back(buff); + break; + } + + // Recycle the buffer + queueReadBuffer(buff); + + // If we've already written more than the max for reading then stop + // (this is to stop writes dominating reads) + if (writeTotal > threadMaxRead) + break; + } else { + // Put buffer back + writeQueue.push_back(buff); + if (errno == ECONNRESET || errno == EPIPE) { + // Just stop watching for write here - we'll get a + // disconnect callback soon enough + h.unwatchWrite(); + break; + } else if (errno == EAGAIN) { + // We have just put a buffer back so we know + // we can carry on watching for writes + break; + } else { + QPID_POSIX_CHECK(rc); + } + } + } else { + // If we're waiting to close the socket then can do it now as there is nothing to write + if (queuedClose) { + close(h); + break; + } + // Fd is writable, but nothing to write + if (idleCallback) { + writePending = false; + idleCallback(*this); + } + // If we still have no buffers to write we can't do anything more + if (writeQueue.empty() && !writePending && !queuedClose) { + h.unwatchWrite(); + // The following handles the case where writePending is + // set to true after the test above; in this case its + // possible that the unwatchWrite overwrites the + // desired rewatchWrite so we correct that here + if (writePending) + h.rewatchWrite(); + break; + } + } + } while (true); + + ++threadWriteCount; + return; +} + +void SslIO::disconnected(DispatchHandle& h) { + // If we've already queued close do it instead of disconnected callback + if (queuedClose) { + close(h); + } else if (disCallback) { + disCallback(*this); + h.unwatch(); + } +} + +/* + * Close the socket and callback to say we've done it + */ +void SslIO::close(DispatchHandle& h) { + h.stopWatch(); + socket.close(); + if (closedCallback) { + closedCallback(*this, socket); + } +} + diff --git a/RC9/qpid/cpp/src/qpid/sys/ssl/SslIo.h b/RC9/qpid/cpp/src/qpid/sys/ssl/SslIo.h new file mode 100644 index 0000000000..ba6483282b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ssl/SslIo.h @@ -0,0 +1,167 @@ +#ifndef _sys_ssl_SslIO +#define _sys_ssl_SslIO +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/DispatchHandle.h" + +#include <boost/function.hpp> +#include <deque> + +namespace qpid { +namespace sys { +namespace ssl { + +class SslSocket; + +/* + * Asynchronous ssl acceptor: accepts connections then does a callback + * with the accepted fd + */ +class SslAcceptor { +public: + typedef boost::function1<void, const SslSocket&> Callback; + +private: + Callback acceptedCallback; + qpid::sys::DispatchHandle handle; + const SslSocket& socket; + +public: + SslAcceptor(const SslSocket& s, Callback callback); + void start(qpid::sys::Poller::shared_ptr poller); + +private: + void readable(qpid::sys::DispatchHandle& handle); +}; + +/* + * Asynchronous ssl connector: starts the process of initiating a + * connection and invokes a callback when completed or failed. + */ +class SslConnector : private qpid::sys::DispatchHandle { +public: + typedef boost::function1<void, const SslSocket&> ConnectedCallback; + typedef boost::function2<void, int, std::string> FailedCallback; + +private: + ConnectedCallback connCallback; + FailedCallback failCallback; + const SslSocket& socket; + +public: + SslConnector(const SslSocket& socket, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb = 0); + +private: + void connComplete(DispatchHandle& handle); + void failure(int, std::string); +}; + +struct SslIOBufferBase { + char* const bytes; + const int32_t byteCount; + int32_t dataStart; + int32_t dataCount; + + SslIOBufferBase(char* const b, const int32_t s) : + bytes(b), + byteCount(s), + dataStart(0), + dataCount(0) + {} + + virtual ~SslIOBufferBase() + {} +}; + +/* + * Asychronous reader/writer: + * Reader accepts buffers to read into; reads into the provided buffers + * and then does a callback with the buffer and amount read. Optionally it can callback + * when there is something to read but no buffer to read it into. + * + * Writer accepts a buffer and queues it for writing; can also be given + * a callback for when writing is "idle" (ie fd is writable, but nothing to write) + * + * The class is implemented in terms of DispatchHandle to allow it to be deleted by deleting + * the contained DispatchHandle + */ +class SslIO : private qpid::sys::DispatchHandle { +public: + typedef SslIOBufferBase BufferBase; + + typedef boost::function2<void, SslIO&, BufferBase*> ReadCallback; + typedef boost::function1<void, SslIO&> EofCallback; + typedef boost::function1<void, SslIO&> DisconnectCallback; + typedef boost::function2<void, SslIO&, const SslSocket&> ClosedCallback; + typedef boost::function1<void, SslIO&> BuffersEmptyCallback; + typedef boost::function1<void, SslIO&> IdleCallback; + +private: + ReadCallback readCallback; + EofCallback eofCallback; + DisconnectCallback disCallback; + ClosedCallback closedCallback; + BuffersEmptyCallback emptyCallback; + IdleCallback idleCallback; + const SslSocket& socket; + std::deque<BufferBase*> bufferQueue; + std::deque<BufferBase*> writeQueue; + bool queuedClose; + /** + * This flag is used to detect and handle concurrency between + * calls to notifyPendingWrite() (which can be made from any thread) and + * the execution of the writeable() method (which is always on the + * thread processing this handle. + */ + volatile bool writePending; + +public: + SslIO(const SslSocket& s, + ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb, + ClosedCallback cCb = 0, BuffersEmptyCallback eCb = 0, IdleCallback iCb = 0); + void queueForDeletion(); + + void start(qpid::sys::Poller::shared_ptr poller); + void queueReadBuffer(BufferBase* buff); + void unread(BufferBase* buff); + void queueWrite(BufferBase* buff); + void notifyPendingWrite(); + void queueWriteClose(); + bool writeQueueEmpty() { return writeQueue.empty(); } + BufferBase* getQueuedBuffer(); + +private: + ~SslIO(); + void readable(qpid::sys::DispatchHandle& handle); + void writeable(qpid::sys::DispatchHandle& handle); + void disconnected(qpid::sys::DispatchHandle& handle); + void close(qpid::sys::DispatchHandle& handle); +}; + +}}} + +#endif // _sys_ssl_SslIO diff --git a/RC9/qpid/cpp/src/qpid/sys/ssl/SslSocket.cpp b/RC9/qpid/cpp/src/qpid/sys/ssl/SslSocket.cpp new file mode 100644 index 0000000000..597fbe57db --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ssl/SslSocket.cpp @@ -0,0 +1,279 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SslSocket.h" +#include "check.h" +#include "util.h" +#include "qpid/Exception.h" +#include "qpid/sys/posix/check.h" +#include "qpid/sys/posix/PrivatePosix.h" + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <cstdlib> +#include <string.h> +#include <iostream> + +#include <nspr4/private/pprio.h> +#include <nss3/nss.h> +#include <nss3/pk11pub.h> +#include <nss3/ssl.h> +#include <nss3/key.h> + +#include <boost/format.hpp> + +namespace qpid { +namespace sys { +namespace ssl { + +namespace { +std::string getName(int fd, bool local, bool includeService = false) +{ + ::sockaddr_storage name; // big enough for any socket address + ::socklen_t namelen = sizeof(name); + + int result = -1; + if (local) { + result = ::getsockname(fd, (::sockaddr*)&name, &namelen); + } else { + result = ::getpeername(fd, (::sockaddr*)&name, &namelen); + } + + QPID_POSIX_CHECK(result); + + char servName[NI_MAXSERV]; + char dispName[NI_MAXHOST]; + if (includeService) { + if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName), + servName, sizeof(servName), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw QPID_POSIX_ERROR(rc); + return std::string(dispName) + ":" + std::string(servName); + + } else { + if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName), 0, 0, NI_NUMERICHOST) != 0) + throw QPID_POSIX_ERROR(rc); + return dispName; + } +} + +std::string getService(int fd, bool local) +{ + ::sockaddr_storage name; // big enough for any socket address + ::socklen_t namelen = sizeof(name); + + int result = -1; + if (local) { + result = ::getsockname(fd, (::sockaddr*)&name, &namelen); + } else { + result = ::getpeername(fd, (::sockaddr*)&name, &namelen); + } + + QPID_POSIX_CHECK(result); + + char servName[NI_MAXSERV]; + if (int rc=::getnameinfo((::sockaddr*)&name, namelen, 0, 0, + servName, sizeof(servName), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw QPID_POSIX_ERROR(rc); + return servName; +} + +} + +SslSocket::SslSocket() : IOHandle(new IOHandlePrivate()), socket(0), prototype(0) +{ + impl->fd = ::socket (PF_INET, SOCK_STREAM, 0); + if (impl->fd < 0) throw QPID_POSIX_ERROR(errno); + socket = SSL_ImportFD(0, PR_ImportTCPSocket(impl->fd)); +} + +/** + * This form of the constructor is used with the server-side sockets + * returned from accept. Because we use posix accept rather than + * PR_Accept, we have to reset the handshake. + */ +SslSocket::SslSocket(IOHandlePrivate* ioph, PRFileDesc* model) : IOHandle(ioph), socket(0), prototype(0) +{ + socket = SSL_ImportFD(model, PR_ImportTCPSocket(impl->fd)); + NSS_CHECK(SSL_ResetHandshake(socket, true)); + NSS_CHECK(SSL_ForceHandshake(socket)); +} + +void SslSocket::setNonblocking() const +{ + PRSocketOptionData option; + option.option = PR_SockOpt_Nonblocking; + option.value.non_blocking = true; + PR_SetSocketOption(socket, &option); +} + +void SslSocket::connect(const std::string& host, uint16_t port) const +{ + std::stringstream namestream; + namestream << host << ":" << port; + connectname = namestream.str(); + + void* arg = SslOptions::global.certName.empty() ? 0 : const_cast<char*>(SslOptions::global.certName.c_str()); + NSS_CHECK(SSL_GetClientAuthDataHook(socket, NSS_GetClientAuthData, arg)); + NSS_CHECK(SSL_SetURL(socket, host.data())); + + char hostBuffer[PR_NETDB_BUF_SIZE]; + PRHostEnt hostEntry; + PR_CHECK(PR_GetHostByName(host.data(), hostBuffer, PR_NETDB_BUF_SIZE, &hostEntry)); + PRNetAddr address; + int value = PR_EnumerateHostEnt(0, &hostEntry, port, &address); + if (value < 0) { + throw Exception(QPID_MSG("Error getting address for host: " << ErrorString())); + } else if (value == 0) { + throw Exception(QPID_MSG("Could not resolve address for host.")); + } + PR_CHECK(PR_Connect(socket, &address, PR_INTERVAL_NO_TIMEOUT)); + NSS_CHECK(SSL_ForceHandshake(socket)); +} + +void SslSocket::close() const +{ + if (impl->fd > 0) { + PR_Close(socket); + impl->fd = -1; + } +} + +int SslSocket::listen(uint16_t port, int backlog, const std::string& certName, bool clientAuth) const +{ + //configure prototype socket: + prototype = SSL_ImportFD(0, PR_NewTCPSocket()); + if (clientAuth) { + NSS_CHECK(SSL_OptionSet(prototype, SSL_REQUEST_CERTIFICATE, PR_TRUE)); + NSS_CHECK(SSL_OptionSet(prototype, SSL_REQUIRE_CERTIFICATE, PR_TRUE)); + } + + //get certificate and key (is this the correct way?) + CERTCertificate *cert = PK11_FindCertFromNickname(const_cast<char*>(certName.c_str()), 0); + if (!cert) throw Exception(QPID_MSG("Failed to load certificate '" << certName << "'")); + SECKEYPrivateKey *key = PK11_FindKeyByAnyCert(cert, 0); + if (!key) throw Exception(QPID_MSG("Failed to retrieve private key from certificate")); + NSS_CHECK(SSL_ConfigSecureServer(prototype, cert, key, NSS_FindCertKEAType(cert))); + SECKEY_DestroyPrivateKey(key); + CERT_DestroyCertificate(cert); + + //bind and listen + const int& socket = impl->fd; + int yes=1; + QPID_POSIX_CHECK(setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes))); + struct sockaddr_in name; + name.sin_family = AF_INET; + name.sin_port = htons(port); + name.sin_addr.s_addr = 0; + if (::bind(socket, (struct sockaddr*)&name, sizeof(name)) < 0) + throw Exception(QPID_MSG("Can't bind to port " << port << ": " << strError(errno))); + if (::listen(socket, backlog) < 0) + throw Exception(QPID_MSG("Can't listen on port " << port << ": " << strError(errno))); + + socklen_t namelen = sizeof(name); + if (::getsockname(socket, (struct sockaddr*)&name, &namelen) < 0) + throw QPID_POSIX_ERROR(errno); + + return ntohs(name.sin_port); +} + +SslSocket* SslSocket::accept(struct sockaddr *addr, socklen_t *addrlen) const +{ + int afd = ::accept(impl->fd, addr, addrlen); + if ( afd >= 0) { + return new SslSocket(new IOHandlePrivate(afd), prototype); + } else if (errno == EAGAIN) { + return 0; + } else { + throw QPID_POSIX_ERROR(errno); + } +} + +int SslSocket::read(void *buf, size_t count) const +{ + return PR_Read(socket, buf, count); +} + +int SslSocket::write(const void *buf, size_t count) const +{ + return PR_Write(socket, buf, count); +} + +std::string SslSocket::getSockname() const +{ + return getName(impl->fd, true); +} + +std::string SslSocket::getPeername() const +{ + return getName(impl->fd, false); +} + +std::string SslSocket::getPeerAddress() const +{ + if (!connectname.empty()) + return connectname; + return getName(impl->fd, false, true); +} + +std::string SslSocket::getLocalAddress() const +{ + return getName(impl->fd, true, true); +} + +uint16_t SslSocket::getLocalPort() const +{ + return std::atoi(getService(impl->fd, true).c_str()); +} + +uint16_t SslSocket::getRemotePort() const +{ + return atoi(getService(impl->fd, true).c_str()); +} + +int SslSocket::getError() const +{ + int result; + socklen_t rSize = sizeof (result); + + if (::getsockopt(impl->fd, SOL_SOCKET, SO_ERROR, &result, &rSize) < 0) + throw QPID_POSIX_ERROR(errno); + + return result; +} + +void SslSocket::setTcpNoDelay(bool nodelay) const +{ + if (nodelay) { + PRSocketOptionData option; + option.option = PR_SockOpt_NoDelay; + option.value.no_delay = true; + PR_SetSocketOption(socket, &option); + } +} + +}}} // namespace qpid::sys::ssl diff --git a/RC9/qpid/cpp/src/qpid/sys/ssl/SslSocket.h b/RC9/qpid/cpp/src/qpid/sys/ssl/SslSocket.h new file mode 100644 index 0000000000..a82e9133e8 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ssl/SslSocket.h @@ -0,0 +1,117 @@ +#ifndef _sys_ssl_Socket_h +#define _sys_ssl_Socket_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/IOHandle.h" +#include <nspr4/nspr.h> + +#include <string> + +struct sockaddr; + +namespace qpid { +namespace sys { + +class Duration; + +namespace ssl { + +class SslSocket : public qpid::sys::IOHandle +{ +public: + /** Create a socket wrapper for descriptor. */ + SslSocket(); + + /** Set socket non blocking */ + void setNonblocking() const; + + /** Set tcp-nodelay */ + void setTcpNoDelay(bool nodelay) const; + + void connect(const std::string& host, uint16_t port) const; + + void close() const; + + /** Bind to a port and start listening. + *@param port 0 means choose an available port. + *@param backlog maximum number of pending connections. + *@param certName name of certificate to use to identify the server + *@return The bound port. + */ + int listen(uint16_t port = 0, int backlog = 10, const std::string& certName = "localhost.localdomain", bool clientAuth = false) const; + + /** + * Accept a connection from a socket that is already listening + * and has an incoming connection + */ + SslSocket* accept(struct sockaddr *addr, socklen_t *addrlen) const; + + // TODO The following are raw operations, maybe they need better wrapping? + int read(void *buf, size_t count) const; + int write(const void *buf, size_t count) const; + + /** Returns the "socket name" ie the address bound to + * the near end of the socket + */ + std::string getSockname() const; + + /** Returns the "peer name" ie the address bound to + * the remote end of the socket + */ + std::string getPeername() const; + + /** + * Returns an address (host and port) for the remote end of the + * socket + */ + std::string getPeerAddress() const; + /** + * Returns an address (host and port) for the local end of the + * socket + */ + std::string getLocalAddress() const; + + uint16_t getLocalPort() const; + uint16_t getRemotePort() const; + + /** + * Returns the error code stored in the socket. This may be used + * to determine the result of a non-blocking connect. + */ + int getError() const; + +private: + mutable std::string connectname; + mutable PRFileDesc* socket; + /** + * 'model' socket, with configuration to use when importing + * accepted sockets for use as ssl sockets. Set on listen(), used + * in accept to pass through to newly created socket instances. + */ + mutable PRFileDesc* prototype; + + SslSocket(IOHandlePrivate* ioph, PRFileDesc* model); +}; + +}}} +#endif /*!_sys_ssl_Socket_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/ssl/check.cpp b/RC9/qpid/cpp/src/qpid/sys/ssl/check.cpp new file mode 100644 index 0000000000..b580e9bcf5 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ssl/check.cpp @@ -0,0 +1,72 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "check.h" +#include <nss3/secerr.h> +#include <nss3/sslerr.h> +#include <boost/format.hpp> + +using boost::format; +using boost::str; + +namespace qpid { +namespace sys { +namespace ssl { + +const std::string SSL_ERROR_BAD_CERT_DOMAIN_STR = + "Unable to communicate securely with peer: requested domain name does not match the server's certificate."; +const std::string SSL_ERROR_BAD_CERT_ALERT_STR = "SSL peer cannot verify your certificate."; +const std::string SEC_ERROR_BAD_DATABASE_STR = "Security library: bad database."; +const std::string SSL_ERROR_NO_CERTIFICATE_STR = "Unable to find the certificate or key necessary for authentication."; +const std::string SSL_ERROR_UNKNOWN = "Unknown NSS error code."; + +ErrorString::ErrorString() : code(PR_GetError()), buffer(new char[PR_GetErrorTextLength()]), used(PR_GetErrorText(buffer)) {} + +ErrorString::~ErrorString() +{ + delete[] buffer; +} + +std::string ErrorString::getString() const +{ + std::string msg = std::string(buffer, used); + if (!used) { + //seems most of the NSPR/NSS errors don't have text set for + //them, add a few specific ones in here. (TODO: more complete + //list?): + switch (code) { + case SSL_ERROR_BAD_CERT_DOMAIN: msg = SSL_ERROR_BAD_CERT_DOMAIN_STR; break; + case SSL_ERROR_BAD_CERT_ALERT: msg = SSL_ERROR_BAD_CERT_ALERT_STR; break; + case SEC_ERROR_BAD_DATABASE: msg = SEC_ERROR_BAD_DATABASE_STR; break; + case SSL_ERROR_NO_CERTIFICATE: msg = SSL_ERROR_NO_CERTIFICATE_STR; break; + default: msg = SSL_ERROR_UNKNOWN; break; + } + } + return str(format("%1% [%2%]") % msg % code); +} + +std::ostream& operator<<(std::ostream& out, const ErrorString& err) +{ + out << err.getString(); + return out; +} + + +}}} // namespace qpid::sys::ssl diff --git a/RC9/qpid/cpp/src/qpid/sys/ssl/check.h b/RC9/qpid/cpp/src/qpid/sys/ssl/check.h new file mode 100644 index 0000000000..6217a39429 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ssl/check.h @@ -0,0 +1,53 @@ +#ifndef QPID_SYS_SSL_CHECK_H +#define QPID_SYS_SSL_CHECK_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include <string> +#include <nspr4/nspr.h> +#include <nss3/nss.h> + +namespace qpid { +namespace sys { +namespace ssl { + +class ErrorString +{ + public: + ErrorString(); + ~ErrorString(); + std::string getString() const; + private: + const int code; + char* const buffer; + const size_t used; +}; + +std::ostream& operator<<(std::ostream& out, const ErrorString& err); + +}}} // namespace qpid::sys::ssl + + +#define NSS_CHECK(value) if (value != SECSuccess) { throw Exception(QPID_MSG("Failed: " << qpid::sys::ssl::ErrorString())); } +#define PR_CHECK(value) if (value != PR_SUCCESS) { throw Exception(QPID_MSG("Failed: " << qpid::sys::ssl::ErrorString())); } + +#endif /*!QPID_SYS_SSL_CHECK_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/ssl/util.cpp b/RC9/qpid/cpp/src/qpid/sys/ssl/util.cpp new file mode 100644 index 0000000000..97b00f19de --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ssl/util.cpp @@ -0,0 +1,118 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "util.h" +#include "check.h" +#include "qpid/Exception.h" +#include "qpid/sys/SystemInfo.h" + +#include <unistd.h> +#include <nspr4/nspr.h> +#include <nss3/nss.h> +#include <nss3/pk11pub.h> +#include <nss3/ssl.h> + +#include <iostream> +#include <fstream> +#include <boost/filesystem/operations.hpp> +#include <boost/filesystem/path.hpp> + +namespace qpid { +namespace sys { +namespace ssl { + +std::string defaultCertName() +{ + TcpAddress address; + if (SystemInfo::getLocalHostname(address)) { + return address.host; + } else { + return "localhost"; + } +} + +SslOptions::SslOptions() : qpid::Options("SSL Settings"), + certName(defaultCertName()), + exportPolicy(false) +{ + addOptions() + ("ssl-use-export-policy", optValue(exportPolicy), "Use NSS export policy") + ("ssl-cert-password-file", optValue(certPasswordFile, "PATH"), "File containing password to use for accessing certificate database") + ("ssl-cert-db", optValue(certDbPath, "PATH"), "Path to directory containing certificate database") + ("ssl-cert-name", optValue(certName, "NAME"), "Name of the certificate to use"); +} + +SslOptions& SslOptions::operator=(const SslOptions& o) +{ + certDbPath = o.certDbPath; + certName = o.certName; + certPasswordFile = o.certPasswordFile; + exportPolicy = o.exportPolicy; + return *this; +} + +char* promptForPassword(PK11SlotInfo*, PRBool retry, void*) +{ + if (retry) return 0; + //TODO: something else? + return PL_strdup(getpass("Please enter the password for accessing the certificate database:")); +} + +SslOptions SslOptions::global; + +char* readPasswordFromFile(PK11SlotInfo*, PRBool retry, void*) +{ + const std::string& passwordFile = SslOptions::global.certPasswordFile; + if (retry || passwordFile.empty() || !boost::filesystem::exists(passwordFile)) { + return 0; + } else { + std::ifstream file(passwordFile.c_str()); + std::string password; + file >> password; + return PL_strdup(password.c_str()); + } +} + +void initNSS(const SslOptions& options, bool server) +{ + SslOptions::global = options; + if (options.certPasswordFile.empty()) { + PK11_SetPasswordFunc(promptForPassword); + } else { + PK11_SetPasswordFunc(readPasswordFromFile); + } + NSS_CHECK(NSS_Init(options.certDbPath.c_str())); + if (options.exportPolicy) { + NSS_CHECK(NSS_SetExportPolicy()); + } else { + NSS_CHECK(NSS_SetDomesticPolicy()); + } + if (server) { + //use defaults for all args, TODO: may want to make this configurable + SSL_ConfigServerSessionIDCache(0, 0, 0, 0); + } +} + +void shutdownNSS() +{ + NSS_Shutdown(); +} + +}}} // namespace qpid::sys::ssl diff --git a/RC9/qpid/cpp/src/qpid/sys/ssl/util.h b/RC9/qpid/cpp/src/qpid/sys/ssl/util.h new file mode 100644 index 0000000000..f34adab7be --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/ssl/util.h @@ -0,0 +1,50 @@ +#ifndef QPID_SYS_SSL_UTIL_H +#define QPID_SYS_SSL_UTIL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Options.h" +#include <string> + +namespace qpid { +namespace sys { +namespace ssl { + +struct SslOptions : qpid::Options +{ + static SslOptions global; + + std::string certDbPath; + std::string certName; + std::string certPasswordFile; + bool exportPolicy; + + SslOptions(); + SslOptions& operator=(const SslOptions&); +}; + +void initNSS(const SslOptions& options, bool server = false); +void shutdownNSS(); + +}}} // namespace qpid::sys::ssl + +#endif /*!QPID_SYS_SSL_UTIL_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/uuid.h b/RC9/qpid/cpp/src/qpid/sys/uuid.h new file mode 100644 index 0000000000..804ab34463 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/uuid.h @@ -0,0 +1,28 @@ +#ifndef _sys_uuid_h +#define _sys_uuid_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef _WIN32 +# include "qpid/sys/windows/uuid.h" +#else +# include <uuid/uuid.h> +#endif /* _WIN32 */ + +#endif /* _sys_uuid_h */ diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp b/RC9/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp new file mode 100644 index 0000000000..ca56efd8dd --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp @@ -0,0 +1,741 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "AsynchIoResult.h" +#include "IoHandlePrivate.h" +#include "qpid/sys/AsynchIO.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Socket.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Time.h" +#include "qpid/log/Statement.h" + +#include "check.h" + +#include <boost/thread/once.hpp> + +#include <queue> +#include <winsock2.h> +#include <mswsock.h> +#include <windows.h> + +#include <boost/bind.hpp> + +namespace { + + typedef qpid::sys::ScopedLock<qpid::sys::Mutex> QLock; + +/* + * 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. + */ +QPID_TSS int threadReadTotal = 0; +QPID_TSS int threadMaxRead = 0; +QPID_TSS int threadReadCount = 0; +QPID_TSS int threadWriteTotal = 0; +QPID_TSS int threadWriteCount = 0; +QPID_TSS int64_t threadMaxReadTimeNs = 2 * 1000000; // start at 2ms + +/* + * The function pointers for AcceptEx and ConnectEx need to be looked up + * at run time. Make sure this is done only once. + */ +boost::once_flag lookUpAcceptExOnce = BOOST_ONCE_INIT; +LPFN_ACCEPTEX fnAcceptEx = 0; +typedef void (*lookUpFunc)(const qpid::sys::Socket &); + +void lookUpAcceptEx() { + SOCKET h = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + GUID guidAcceptEx = WSAID_ACCEPTEX; + DWORD dwBytes = 0; + WSAIoctl(h, + SIO_GET_EXTENSION_FUNCTION_POINTER, + &guidAcceptEx, + sizeof(guidAcceptEx), + &fnAcceptEx, + sizeof(fnAcceptEx), + &dwBytes, + NULL, + NULL); + closesocket(h); + if (fnAcceptEx == 0) + throw qpid::Exception(QPID_MSG("Failed to look up AcceptEx")); +} + +} + +namespace qpid { +namespace sys { + +/* + * Asynch Acceptor + * + * This implementation uses knowledge that the DispatchHandle handle member + * is derived from PollerHandle, which has a reference to the Socket. + * No dispatching features of DispatchHandle are used - we just use the + * conduit to the Socket. + * + * AsynchAcceptor uses an AsynchAcceptResult object to track completion + * and status of each accept operation outstanding. + */ + +class AsynchAcceptorPrivate { + + friend class AsynchAcceptResult; + +public: + AsynchAcceptorPrivate(const Socket& s, AsynchAcceptor::Callback callback); + ~AsynchAcceptorPrivate(); + void start(Poller::shared_ptr poller); + +private: + void restart(void); + + AsynchAcceptor::Callback acceptedCallback; + const Socket& socket; +}; + +AsynchAcceptor::AsynchAcceptor(const Socket& s, Callback callback) : + impl(new AsynchAcceptorPrivate(s, callback)) +{} + +AsynchAcceptor::~AsynchAcceptor() +{ delete impl; } + +void AsynchAcceptor::start(Poller::shared_ptr poller) { + impl->start(poller); +} + +AsynchAcceptorPrivate::AsynchAcceptorPrivate(const Socket& s, + AsynchAcceptor::Callback callback) + : acceptedCallback(callback), + socket(s) { + + s.setNonblocking(); +#if (BOOST_VERSION >= 103500) /* boost 1.35 or later reversed the args */ + boost::call_once(lookUpAcceptExOnce, lookUpAcceptEx); +#else + boost::call_once(lookUpAcceptEx, lookUpAcceptExOnce); +#endif +} + +AsynchAcceptorPrivate::~AsynchAcceptorPrivate(void) { + socket.close(); +} + +void AsynchAcceptorPrivate::start(Poller::shared_ptr poller) { + poller->addFd(PollerHandle(socket), Poller::INPUT); + restart (); +} + +void AsynchAcceptorPrivate::restart(void) { + DWORD bytesReceived = 0; // Not used, needed for AcceptEx API + AsynchAcceptResult *result = new AsynchAcceptResult(acceptedCallback, + this, + toFd(socket.impl)); + BOOL status; + status = ::fnAcceptEx(toFd(socket.impl), + toFd(result->newSocket->impl), + result->addressBuffer, + 0, + AsynchAcceptResult::SOCKADDRMAXLEN, + AsynchAcceptResult::SOCKADDRMAXLEN, + &bytesReceived, + result->overlapped()); + QPID_WINDOWS_CHECK_ASYNC_START(status); +} + + +AsynchAcceptResult::AsynchAcceptResult(AsynchAcceptor::Callback cb, + AsynchAcceptorPrivate *acceptor, + SOCKET listener) + : callback(cb), acceptor(acceptor), listener(listener) { + newSocket.reset (new Socket()); +} + +void AsynchAcceptResult::success(size_t /*bytesTransferred*/) { + ::setsockopt (toFd(newSocket->impl), + SOL_SOCKET, + SO_UPDATE_ACCEPT_CONTEXT, + (char*)&listener, + sizeof (listener)); + callback(*(newSocket.release())); + acceptor->restart (); + delete this; +} + +void AsynchAcceptResult::failure(int status) { + //if (status != WSA_OPERATION_ABORTED) + // Can there be anything else? ; + delete this; +} + +namespace windows { + +/* + * AsynchConnector does synchronous connects for now... to do asynch the + * IocpPoller will need some extension to register an event handle as a + * CONNECT-type "direction", the connect completion/result will need an + * event handle to associate with the connecting handle. But there's no + * time for that right now... + */ +class AsynchConnector : public qpid::sys::AsynchConnector { +private: + ConnectedCallback connCallback; + FailedCallback failCallback; + const Socket& socket; + +public: + AsynchConnector(const Socket& socket, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb = 0); +}; + +AsynchConnector::AsynchConnector(const Socket& sock, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb) + : connCallback(connCb), failCallback(failCb), socket(sock) { + socket.setNonblocking(); + try { + socket.connect(hostname, port); + connCallback(socket); + } catch(std::exception& e) { + if (failCallback) + failCallback(-1, std::string(e.what())); + socket.close(); + delete &socket; + } +} + +} // namespace windows + +AsynchConnector* qpid::sys::AsynchConnector::create(const Socket& s, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb) +{ + return new qpid::sys::windows::AsynchConnector(s, + poller, + hostname, + port, + connCb, + failCb); +} + + +/* + * Asynch reader/writer + */ + +namespace windows { + +class AsynchIO : public qpid::sys::AsynchIO { +public: + AsynchIO(const Socket& s, + ReadCallback rCb, + EofCallback eofCb, + DisconnectCallback disCb, + ClosedCallback cCb = 0, + BuffersEmptyCallback eCb = 0, + IdleCallback iCb = 0); + ~AsynchIO(); + + // Methods inherited from qpid::sys::AsynchIO + + /** + * Notify the object is should delete itself as soon as possible. + */ + virtual void queueForDeletion(); + + /// Take any actions needed to prepare for working with the poller. + virtual void start(Poller::shared_ptr poller); + virtual void queueReadBuffer(BufferBase* buff); + virtual void unread(BufferBase* buff); + virtual void queueWrite(BufferBase* buff); + virtual void notifyPendingWrite(); + virtual void queueWriteClose(); + virtual bool writeQueueEmpty(); + virtual void startReading(); + + /** + * getQueuedBuffer returns a buffer from the buffer queue, if one is + * available. + * + * @retval Pointer to BufferBase buffer; 0 if none is available. + */ + virtual BufferBase* getQueuedBuffer(); + +private: + ReadCallback readCallback; + EofCallback eofCallback; + DisconnectCallback disCallback; + ClosedCallback closedCallback; + BuffersEmptyCallback emptyCallback; + IdleCallback idleCallback; + const Socket& socket; + Poller::shared_ptr poller; + + std::deque<BufferBase*> bufferQueue; + std::deque<BufferBase*> writeQueue; + /* The MSVC-supplied deque is not thread-safe; keep locks to serialize + * access to the buffer queue and write queue. + */ + Mutex bufferQueueLock; + + // Number of outstanding I/O operations. + volatile LONG opsInProgress; + // Is there a write in progress? + volatile bool writeInProgress; + // Deletion requested, but there are callbacks in progress. + volatile bool queuedDelete; + // Socket close requested, but there are operations in progress. + volatile bool queuedClose; + +private: + // Dispatch events that have completed. + void notifyEof(void); + void notifyDisconnect(void); + void notifyClosed(void); + void notifyBuffersEmpty(void); + void notifyIdle(void); + + /** + * Initiate a write of the specified buffer. There's no callback for + * write completion to the AsynchIO object. + */ + void startWrite(AsynchIO::BufferBase* buff); + + void close(void); + + /** + * readComplete is called when a read request is complete. + * + * @param result Results of the operation. + */ + void readComplete(AsynchReadResult *result); + + /** + * writeComplete is called when a write request is complete. + * + * @param result Results of the operation. + */ + void writeComplete(AsynchWriteResult *result); + + /** + * Queue of completions to run. This queue enforces the requirement + * from upper layers that only one thread at a time is allowed to act + * on any given connection. Once a thread is busy processing a completion + * on this object, other threads that dispatch completions queue the + * completions here for the in-progress thread to handle when done. + * Thus, any threads can dispatch a completion from the IocpPoller, but + * this class ensures that actual processing at the connection level is + * only on one thread at a time. + */ + std::queue<AsynchIoResult *> completionQueue; + volatile bool working; + Mutex completionLock; + + /** + * Called when there's a completion to process. + */ + void completion(AsynchIoResult *result); +}; + +AsynchIO::AsynchIO(const Socket& s, + ReadCallback rCb, + EofCallback eofCb, + DisconnectCallback disCb, + ClosedCallback cCb, + BuffersEmptyCallback eCb, + IdleCallback iCb) : + + readCallback(rCb), + eofCallback(eofCb), + disCallback(disCb), + closedCallback(cCb), + emptyCallback(eCb), + idleCallback(iCb), + socket(s), + opsInProgress(0), + writeInProgress(false), + queuedDelete(false), + queuedClose(false), + working(false) { +} + +struct deleter +{ + template <typename T> + void operator()(T *ptr){ delete ptr;} +}; + +AsynchIO::~AsynchIO() { + std::for_each( bufferQueue.begin(), bufferQueue.end(), deleter()); + std::for_each( writeQueue.begin(), writeQueue.end(), deleter()); +} + +void AsynchIO::queueForDeletion() { + queuedDelete = true; + if (opsInProgress > 0) { + QPID_LOG(info, "Delete AsynchIO queued; ops in progress"); + // AsynchIOHandler calls this then deletes itself; don't do any more + // callbacks. + readCallback = 0; + eofCallback = 0; + disCallback = 0; + closedCallback = 0; + emptyCallback = 0; + idleCallback = 0; + } + else { + delete this; + } +} + +void AsynchIO::start(Poller::shared_ptr poller0) { + poller = poller0; + poller->addFd(PollerHandle(socket), Poller::INPUT); + if (writeQueue.size() > 0) // Already have data queued for write + notifyPendingWrite(); + startReading(); +} + +void AsynchIO::queueReadBuffer(AsynchIO::BufferBase* buff) { + assert(buff); + buff->dataStart = 0; + buff->dataCount = 0; + QLock l(bufferQueueLock); + bufferQueue.push_back(buff); +} + +void AsynchIO::unread(AsynchIO::BufferBase* buff) { + assert(buff); + if (buff->dataStart != 0) { + memmove(buff->bytes, buff->bytes+buff->dataStart, buff->dataCount); + buff->dataStart = 0; + } + QLock l(bufferQueueLock); + bufferQueue.push_front(buff); +} + +void AsynchIO::queueWrite(AsynchIO::BufferBase* buff) { + assert(buff); + QLock l(bufferQueueLock); + writeQueue.push_back(buff); + if (!writeInProgress) + notifyPendingWrite(); +} + +void AsynchIO::notifyPendingWrite() { + // This method is generally called from a processing thread; transfer + // work on this to an I/O thread. Much of the upper layer code assumes + // that all I/O-related things happen in an I/O thread. + if (poller == 0) // Not really going yet... + return; + + InterlockedIncrement(&opsInProgress); + IOHandlePrivate *hp = + new IOHandlePrivate (INVALID_SOCKET, + boost::bind(&AsynchIO::completion, this, _1)); + IOHandle h(hp); + PollerHandle ph(h); + poller->addFd(ph, Poller::OUTPUT); +} + +void AsynchIO::queueWriteClose() { + queuedClose = true; + if (!writeInProgress) + notifyPendingWrite(); +} + +bool AsynchIO::writeQueueEmpty() { + QLock l(bufferQueueLock); + return writeQueue.size() == 0; +} + +/* + * Initiate a read operation. AsynchIO::readComplete() will be + * called when the read is complete and data is available. + */ +void AsynchIO::startReading() { + if (queuedDelete) + return; + + // (Try to) get a buffer; look on the front since there may be an + // "unread" one there with data remaining from last time. + AsynchIO::BufferBase *buff = 0; + { + QLock l(bufferQueueLock); + + if (!bufferQueue.empty()) { + buff = bufferQueue.front(); + assert(buff); + bufferQueue.pop_front(); + } + } + if (buff != 0) { + int readCount = buff->byteCount - buff->dataCount; + AsynchReadResult *result = + new AsynchReadResult(boost::bind(&AsynchIO::completion, this, _1), + buff, + readCount); + DWORD bytesReceived = 0, flags = 0; + InterlockedIncrement(&opsInProgress); + int status = WSARecv(toFd(socket.impl), + const_cast<LPWSABUF>(result->getWSABUF()), 1, + &bytesReceived, + &flags, + result->overlapped(), + 0); + if (status != 0) { + int error = WSAGetLastError(); + if (error != WSA_IO_PENDING) { + result->failure(error); + result = 0; // result is invalid here + return; + } + } + // On status 0 or WSA_IO_PENDING, completion will handle the rest. + } + else { + notifyBuffersEmpty(); + } + return; +} + +/** + * Return a queued buffer if there are enough to spare. + */ +AsynchIO::BufferBase* AsynchIO::getQueuedBuffer() { + QLock l(bufferQueueLock); + // Always keep at least one buffer (it might have data that was + // "unread" in it). + if (bufferQueue.size() <= 1) + return 0; + BufferBase* buff = bufferQueue.back(); + assert(buff); + bufferQueue.pop_back(); + return buff; +} + +void AsynchIO::notifyEof(void) { + if (eofCallback) + eofCallback(*this); +} + +void AsynchIO::notifyDisconnect(void) { + if (disCallback) + disCallback(*this); +} + +void AsynchIO::notifyClosed(void) { + if (closedCallback) + closedCallback(*this, socket); +} + +void AsynchIO::notifyBuffersEmpty(void) { + if (emptyCallback) + emptyCallback(*this); +} + +void AsynchIO::notifyIdle(void) { + if (idleCallback) + idleCallback(*this); +} + +/* + * Asynch reader/writer using overlapped I/O + */ + +void AsynchIO::startWrite(AsynchIO::BufferBase* buff) { + writeInProgress = true; + InterlockedIncrement(&opsInProgress); + int writeCount = buff->byteCount-buff->dataCount; + AsynchWriteResult *result = + new AsynchWriteResult(boost::bind(&AsynchIO::completion, this, _1), + buff, + buff->dataCount); + DWORD bytesSent = 0; + int status = WSASend(toFd(socket.impl), + const_cast<LPWSABUF>(result->getWSABUF()), 1, + &bytesSent, + 0, + result->overlapped(), + 0); + if (status != 0) { + int error = WSAGetLastError(); + if (error != WSA_IO_PENDING) { + result->failure(error); // Also decrements in-progress count + result = 0; // result is invalid here + return; + } + } + // On status 0 or WSA_IO_PENDING, completion will handle the rest. + return; +} + +/* + * Close the socket and callback to say we've done it + */ +void AsynchIO::close(void) { + socket.close(); + notifyClosed(); +} + +void AsynchIO::readComplete(AsynchReadResult *result) { + ++threadReadCount; + int status = result->getStatus(); + size_t bytes = result->getTransferred(); + if (status == 0 && bytes > 0) { + bool restartRead = true; // May not if receiver doesn't want more + threadReadTotal += bytes; + if (readCallback) + restartRead = readCallback(*this, result->getBuff()); + if (restartRead) + startReading(); + } + else { + // No data read, so put the buffer back. It may be partially filled, + // so "unread" it back to the front of the queue. + unread(result->getBuff()); + if (status == 0) + notifyEof(); + else + notifyDisconnect(); + } +} + +/* + * NOTE - this completion is called for completed writes and also when + * a write is desired. The difference is in the buff - if a write is desired + * the buff is 0. + */ +void AsynchIO::writeComplete(AsynchWriteResult *result) { + int status = result->getStatus(); + size_t bytes = result->getTransferred(); + AsynchIO::BufferBase *buff = result->getBuff(); + if (buff != 0) { + ++threadWriteCount; + writeInProgress = false; + if (status == 0 && bytes > 0) { + threadWriteTotal += bytes; + if (bytes < result->getRequested()) // Still more to go; resubmit + startWrite(buff); + else + queueReadBuffer(buff); // All done; back to the pool + } + else { + // An error... if it's a connection close, ignore it - it will be + // noticed and handled on a read completion any moment now. + // What to do with real error??? Save the Buffer? + } + } + + // If there are no writes outstanding, the priority is to write any + // remaining buffers first (either queued or via idle), then close the + // socket if that's queued. + // opsInProgress handled in completion() + if (!writeInProgress) { + bool writing = false; + { + QLock l(bufferQueueLock); + if (writeQueue.size() > 0) { + buff = writeQueue.front(); + assert(buff); + writeQueue.pop_front(); + startWrite(buff); + writing = true; + } + } + if (!writing) { + if (queuedClose) + close(); + else + notifyIdle(); + } + } + return; +} + +void AsynchIO::completion(AsynchIoResult *result) { + { + ScopedLock<Mutex> l(completionLock); + if (working) { + completionQueue.push(result); + return; + } + + // First thread in with something to do; note we're working then keep + // handling completions. + working = true; + while (result != 0) { + // New scope to unlock temporarily. + { + ScopedUnlock<Mutex> ul(completionLock); + AsynchReadResult *r = dynamic_cast<AsynchReadResult*>(result); + if (r != 0) + readComplete(r); + else { + AsynchWriteResult *w = + dynamic_cast<AsynchWriteResult*>(result); + writeComplete(w); + } + delete result; + result = 0; + InterlockedDecrement(&opsInProgress); + } + // Lock is held again. + if (completionQueue.empty()) + continue; + result = completionQueue.front(); + completionQueue.pop(); + } + working = false; + } + // Lock released; ok to delete if all is done. + if (opsInProgress == 0 && queuedDelete) + delete this; +} + +} // namespace windows + +AsynchIO* qpid::sys::AsynchIO::create(const Socket& s, + AsynchIO::ReadCallback rCb, + AsynchIO::EofCallback eofCb, + AsynchIO::DisconnectCallback disCb, + AsynchIO::ClosedCallback cCb, + AsynchIO::BuffersEmptyCallback eCb, + AsynchIO::IdleCallback iCb) +{ + return new qpid::sys::windows::AsynchIO(s, rCb, eofCb, disCb, cCb, eCb, iCb); +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/AsynchIoResult.h b/RC9/qpid/cpp/src/qpid/sys/windows/AsynchIoResult.h new file mode 100755 index 0000000000..7db4e9c331 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/AsynchIoResult.h @@ -0,0 +1,185 @@ +#ifndef _windows_asynchIoResult_h +#define _windows_asynchIoResult_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/AsynchIO.h" +#include "qpid/sys/Socket.h" +#include <memory.h> +#include <winsock2.h> +#include <ws2tcpip.h> + +namespace qpid { +namespace sys { + +/* + * AsynchIoResult defines the class that receives the result of an + * asynchronous I/O operation, either send/recv or accept/connect. + * + * Operation factories should set one of these up before beginning the + * operation. Poller knows how to dispatch completion to this class. + * This class must be subclassed for needed operations; this class provides + * an interface only and cannot be instantiated. + * + * This class is tied to Windows; it inherits from OVERLAPPED so that the + * IocpPoller can cast OVERLAPPED pointers back to AsynchIoResult and call + * the completion handler. + */ +class AsynchResult : private OVERLAPPED { +public: + LPOVERLAPPED overlapped(void) { return this; } + static AsynchResult* from_overlapped(LPOVERLAPPED ol) { + return static_cast<AsynchResult*>(ol); + } + virtual void success (size_t bytesTransferred) { + bytes = bytesTransferred; + status = 0; + complete(); + } + virtual void failure (int error) { + bytes = 0; + status = error; + complete(); + } + size_t getTransferred(void) const { return bytes; } + int getStatus(void) const { return status; } + +protected: + AsynchResult() : bytes(0), status(0) + { memset(overlapped(), 0, sizeof(OVERLAPPED)); } + ~AsynchResult() {} + virtual void complete(void) = 0; + + size_t bytes; + int status; +}; + +class AsynchAcceptorPrivate; +class AsynchAcceptResult : public AsynchResult { + + friend class AsynchAcceptorPrivate; + +public: + AsynchAcceptResult(AsynchAcceptor::Callback cb, + AsynchAcceptorPrivate *acceptor, + SOCKET listener); + virtual void success (size_t bytesTransferred); + virtual void failure (int error); + +private: + virtual void complete(void) {} // No-op for this class. + + std::auto_ptr<qpid::sys::Socket> newSocket; + AsynchAcceptor::Callback callback; + AsynchAcceptorPrivate *acceptor; + SOCKET listener; + + // AcceptEx needs a place to write the local and remote addresses + // when accepting the connection. Place those here; get enough for + // IPv6 addresses, even if the socket is IPv4. + enum { SOCKADDRMAXLEN = sizeof sockaddr_in6 + 16, + SOCKADDRBUFLEN = 2 * SOCKADDRMAXLEN }; + char addressBuffer[SOCKADDRBUFLEN]; +}; + +class AsynchIoResult : public AsynchResult { +public: + typedef boost::function1<void, AsynchIoResult *> Completer; + + virtual ~AsynchIoResult() {} + AsynchIO::BufferBase *getBuff(void) const { return iobuff; } + size_t getRequested(void) const { return requested; } + const WSABUF *getWSABUF(void) const { return &wsabuf; } + +protected: + void setBuff (AsynchIO::BufferBase *buffer) { iobuff = buffer; } + +protected: + AsynchIoResult(Completer cb, + AsynchIO::BufferBase *buff, size_t length) + : completionCallback(cb), iobuff(buff), requested(length) {} + + virtual void complete(void) = 0; + WSABUF wsabuf; + Completer completionCallback; + +private: + AsynchIO::BufferBase *iobuff; + size_t requested; // Number of bytes in original I/O request +}; + +class AsynchReadResult : public AsynchIoResult { + + // complete() updates buffer then does completion callback. + virtual void complete(void) { + getBuff()->dataCount += bytes; + completionCallback(this); + } + +public: + AsynchReadResult(AsynchIoResult::Completer cb, + AsynchIO::BufferBase *buff, + size_t length) + : AsynchIoResult(cb, buff, length) { + wsabuf.buf = buff->bytes + buff->dataCount; + wsabuf.len = length; + } +}; + +class AsynchWriteResult : public AsynchIoResult { + + // complete() updates buffer then does completion callback. + virtual void complete(void) { + AsynchIO::BufferBase *b = getBuff(); + b->dataStart += bytes; + b->dataCount -= bytes; + completionCallback(this); + } + +public: + AsynchWriteResult(AsynchIoResult::Completer cb, + AsynchIO::BufferBase *buff, + size_t length) + : AsynchIoResult(cb, buff, length) { + wsabuf.buf = buff ? buff->bytes : 0; + wsabuf.len = length; + } +}; + +class AsynchWriteWanted : public AsynchWriteResult { + + // complete() just does completion callback; no buffers used. + virtual void complete(void) { + completionCallback(this); + } + +public: + AsynchWriteWanted(AsynchIoResult::Completer cb) + : AsynchWriteResult(cb, 0, 0) { + wsabuf.buf = 0; + wsabuf.len = 0; + } +}; + +}} + +#endif /*!_windows_asynchIoResult_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/Condition.h b/RC9/qpid/cpp/src/qpid/sys/windows/Condition.h new file mode 100755 index 0000000000..979fae9b0a --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/Condition.h @@ -0,0 +1,80 @@ +#ifndef _sys_windows_Condition_h +#define _sys_windows_Condition_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Time.h" + +#include <time.h> +#include <boost/noncopyable.hpp> +#include <boost/thread/condition.hpp> +#include <boost/thread/thread_time.hpp> +#include <windows.h> + +namespace qpid { +namespace sys { + +// Private Time related implementation details +void toPtime(boost::posix_time::ptime& pt, const AbsTime& t); + +/** + * A condition variable for thread synchronization. + */ +class Condition : private boost::noncopyable +{ + public: + inline Condition(); + inline ~Condition(); + inline void wait(Mutex&); + inline bool wait(Mutex&, const AbsTime& absoluteTime); + inline void notify(); + inline void notifyAll(); + + private: + boost::condition_variable_any condition; +}; + +Condition::Condition() { +} + +Condition::~Condition() { +} + +void Condition::wait(Mutex& mutex) { + condition.wait(mutex.mutex); +} + +bool Condition::wait(Mutex& mutex, const AbsTime& absoluteTime){ + return condition.timed_wait(mutex.mutex, absoluteTime.getPrivate()); +} + +void Condition::notify(){ + condition.notify_one(); +} + +void Condition::notifyAll(){ + condition.notify_all(); +} + +}} +#endif /*!_sys_windows_Condition_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/FileSysDir.cpp b/RC9/qpid/cpp/src/qpid/sys/windows/FileSysDir.cpp new file mode 100644 index 0000000000..88f1637d48 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/FileSysDir.cpp @@ -0,0 +1,53 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/sys/FileSysDir.h" +#include "qpid/sys/StrError.h" +#include "qpid/Exception.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <direct.h> +#include <errno.h> + +namespace qpid { +namespace sys { + +bool FileSysDir::exists (void) const +{ + const char *cpath = dirPath.c_str (); + struct _stat s; + if (::_stat(cpath, &s)) { + if (errno == ENOENT) { + return false; + } + throw qpid::Exception (strError(errno) + + ": Can't check directory: " + dirPath); + } + if (s.st_mode & _S_IFDIR) + return true; + throw qpid::Exception(dirPath + " is not a directory"); +} + +void FileSysDir::mkdir(void) +{ + if (::_mkdir(dirPath.c_str()) == -1) + throw Exception ("Can't create directory: " + dirPath); +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/IOHandle.cpp b/RC9/qpid/cpp/src/qpid/sys/windows/IOHandle.cpp new file mode 100755 index 0000000000..ba544c8c90 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/IOHandle.cpp @@ -0,0 +1,42 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/IOHandle.h" +#include "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/RC9/qpid/cpp/src/qpid/sys/windows/IntegerTypes.h b/RC9/qpid/cpp/src/qpid/sys/windows/IntegerTypes.h new file mode 100755 index 0000000000..80168fab88 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/IntegerTypes.h @@ -0,0 +1,40 @@ +#ifndef QPID_SYS_WINDOWS_INTEGERTYPES_H +#define QPID_SYS_WINDOWS_INTEGERTYPES_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +typedef unsigned char uint8_t; +typedef char int8_t; +typedef unsigned short uint16_t; +typedef short int16_t; +typedef unsigned int uint32_t; +typedef int int32_t; +typedef unsigned __int64 uint64_t; +typedef __int64 int64_t; + +// Visual Studio doesn't define other common types, so set them up here too. +typedef int pid_t; +typedef int socklen_t; +typedef unsigned int size_t; +typedef int ssize_t; +typedef unsigned int uint; + +#endif /*!QPID_SYS_WINDOWS_INTEGERTYPES_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/IoHandlePrivate.h b/RC9/qpid/cpp/src/qpid/sys/windows/IoHandlePrivate.h new file mode 100755 index 0000000000..18e75047ed --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/IoHandlePrivate.h @@ -0,0 +1,52 @@ +#ifndef _sys_windows_IoHandlePrivate_h +#define _sys_windows_IoHandlePrivate_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "AsynchIoResult.h" + +#include <winsock2.h> + +namespace qpid { +namespace sys { + +// Private fd related implementation details +// There should be either a valid socket handle or a completer callback. +// Handle is used to associate with poller's iocp; completer is used to +// inject a completion that will very quickly trigger a callback to the +// completer from an I/O thread. +class IOHandlePrivate { +public: + IOHandlePrivate(SOCKET f = INVALID_SOCKET, + AsynchIoResult::Completer cb = 0) : + fd(f), event(cb) + {} + + SOCKET fd; + AsynchIoResult::Completer event; +}; + +SOCKET toFd(const IOHandlePrivate* h); + +}} + +#endif /* _sys_windows_IoHandlePrivate_h */ diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/IocpDispatcher.cpp b/RC9/qpid/cpp/src/qpid/sys/windows/IocpDispatcher.cpp new file mode 100755 index 0000000000..1a0f6ce927 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/IocpDispatcher.cpp @@ -0,0 +1,54 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Dispatcher.h" + +#include <assert.h> + +namespace qpid { +namespace sys { + +Dispatcher::Dispatcher(Poller::shared_ptr poller0) : + poller(poller0) { +} + +Dispatcher::~Dispatcher() { +} + +void Dispatcher::run() { + do { + Poller::Event event = poller->wait(); + + // Handle shutdown + switch (event.type) { + case Poller::SHUTDOWN: + return; + break; + case Poller::INVALID: // On any type of success or fail completion + break; + default: + // This should be impossible + assert(false); + } + } while (true); +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/IocpPoller.cpp b/RC9/qpid/cpp/src/qpid/sys/windows/IocpPoller.cpp new file mode 100755 index 0000000000..44298ac8ea --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/IocpPoller.cpp @@ -0,0 +1,176 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Poller.h" +#include "qpid/sys/Mutex.h" + +#include "AsynchIoResult.h" +#include "IoHandlePrivate.h" +#include "check.h" + +#include <winsock2.h> +#include <windows.h> + +#include <assert.h> +#include <vector> +#include <exception> + +namespace qpid { +namespace sys { + +class PollerHandlePrivate { + friend class Poller; + friend class PollerHandle; + + SOCKET fd; + AsynchIoResult::Completer cb; + + PollerHandlePrivate(SOCKET f, AsynchIoResult::Completer cb0 = 0) : + fd(f), cb(cb0) + { + } + +}; + +PollerHandle::PollerHandle(const IOHandle& h) : + impl(new PollerHandlePrivate(toFd(h.impl), h.impl->event)) +{} + +PollerHandle::~PollerHandle() { + delete impl; +} + +/** + * Concrete implementation of Poller to use the Windows I/O Completion + * port (IOCP) facility. + */ +class PollerPrivate { + friend class Poller; + + const HANDLE iocp; + + // The number of threads running the event loop. + volatile LONG threadsRunning; + + // Shutdown request is handled by setting isShutdown and injecting a + // well-formed completion event into the iocp. + bool isShutdown; + + PollerPrivate() : + iocp(::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)), + threadsRunning(0), + isShutdown(false) { + QPID_WINDOWS_CHECK_NULL(iocp); + } + + ~PollerPrivate() { + // It's probably okay to ignore any errors here as there can't be + // data loss + ::CloseHandle(iocp); + } +}; + +void Poller::addFd(PollerHandle& handle, Direction dir) { + HANDLE h = (HANDLE)(handle.impl->fd); + if (h != INVALID_HANDLE_VALUE) { + HANDLE iocpHandle = ::CreateIoCompletionPort (h, impl->iocp, 0, 0); + QPID_WINDOWS_CHECK_NULL(iocpHandle); + } + else { + AsynchWriteWanted *result = new AsynchWriteWanted(handle.impl->cb); + PostQueuedCompletionStatus(impl->iocp, 0, 0, result->overlapped()); + } +} + +void Poller::shutdown() { + // Allow sloppy code to shut us down more than once. + if (impl->isShutdown) + return; + ULONG_PTR key = 1; // Tell wait() it's a shutdown, not I/O + PostQueuedCompletionStatus(impl->iocp, 0, key, 0); +} + +// All no-ops... +void Poller::delFd(PollerHandle& handle) {} +void Poller::modFd(PollerHandle& handle, Direction dir) {} +void Poller::rearmFd(PollerHandle& handle) {} + +Poller::Event Poller::wait(Duration timeout) { + DWORD timeoutMs = 0; + DWORD numTransferred = 0; + ULONG_PTR completionKey = 0; + OVERLAPPED *overlapped = 0; + AsynchResult *result = 0; + + // Wait for either an I/O operation to finish (thus signaling the + // IOCP handle) or a shutdown request to be made (thus signaling the + // shutdown event). + if (timeout == TIME_INFINITE) + timeoutMs = INFINITE; + else + timeoutMs = static_cast<DWORD>(timeout / TIME_MSEC); + + InterlockedIncrement(&impl->threadsRunning); + bool goodOp = ::GetQueuedCompletionStatus (impl->iocp, + &numTransferred, + &completionKey, + &overlapped, + timeoutMs); + LONG remainingThreads = InterlockedDecrement(&impl->threadsRunning); + if (goodOp) { + // Dequeued a successful completion. If it's a posted packet from + // shutdown() the overlapped ptr is 0 and key is 1. Else downcast + // the OVERLAPPED pointer to an AsynchIoResult and call the + // completion handler. + if (overlapped == 0 && completionKey == 1) { + // If there are other threads still running this wait, re-post + // the completion. + if (remainingThreads > 0) + PostQueuedCompletionStatus(impl->iocp, 0, completionKey, 0); + return Event(0, SHUTDOWN); + } + + result = AsynchResult::from_overlapped(overlapped); + result->success (static_cast<size_t>(numTransferred)); + } + else { + if (overlapped != 0) { + // Dequeued a completion for a failed operation. Downcast back + // to the result object and inform it that the operation failed. + DWORD status = ::GetLastError(); + result = AsynchResult::from_overlapped(overlapped); + result->failure (static_cast<int>(status)); + } + } + return Event(0, INVALID); // TODO - this may need to be changed. + +} + +// Concrete constructors +Poller::Poller() : + impl(new PollerPrivate()) +{} + +Poller::~Poller() { + delete impl; +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/LockFile.cpp b/RC9/qpid/cpp/src/qpid/sys/windows/LockFile.cpp new file mode 100755 index 0000000000..9804020167 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/LockFile.cpp @@ -0,0 +1,83 @@ +/* + * + * Copyright (c) 2008 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/sys/LockFile.h" +#include "check.h" + +#include <windows.h> + +namespace qpid { +namespace sys { + +class LockFilePrivate { + friend class LockFile; + + HANDLE fd; + +public: + LockFilePrivate(HANDLE f) : fd(f) {} +}; + +LockFile::LockFile(const std::string& path_, bool create) + : path(path_), created(create) { + + HANDLE h = CreateFile(path.c_str(), + GENERIC_READ|GENERIC_WRITE, + 0, /* Disable opens by any other attempter */ + 0, /* Default security */ + OPEN_ALWAYS, /* Create if needed */ + FILE_FLAG_DELETE_ON_CLOSE, /* Delete file when closed */ + NULL); + QPID_WINDOWS_CHECK_NOT(h, INVALID_HANDLE_VALUE); + impl.reset(new LockFilePrivate(h)); +} + +LockFile::~LockFile() { + if (impl) { + if (impl->fd != INVALID_HANDLE_VALUE) { + CloseHandle(impl->fd); + } + } +} + +pid_t LockFile::readPid(void) const { + if (!impl) + throw Exception("Lock file not open"); + + pid_t pid; + DWORD desired_read = sizeof(pid_t); + DWORD actual_read = 0; + if (!ReadFile(impl->fd, &pid, desired_read, &actual_read, 0)) { + throw Exception("Cannot read lock file " + path); + } + return pid; +} + +void LockFile::writePid(void) { + if (!impl) + throw Exception("Lock file not open"); + + pid_t pid = GetCurrentProcessId(); + DWORD desired_write = sizeof(pid_t); + DWORD written = 0; + if (!WriteFile(impl->fd, &pid, desired_write, &written, 0)) { + throw Exception("Cannot write lock file " + path); + } +} + +}} /* namespace qpid::sys */ diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/Mutex.h b/RC9/qpid/cpp/src/qpid/sys/windows/Mutex.h new file mode 100755 index 0000000000..08de0712b9 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/Mutex.h @@ -0,0 +1,188 @@ +#ifndef _sys_windows_Mutex_h +#define _sys_windows_Mutex_h + +/* + * + * Copyright (c) 2008 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "check.h" + +#include <boost/version.hpp> +#if (BOOST_VERSION < 103500) +#error The Windows port requires Boost version 1.35.0 or later +#endif + +#include <boost/noncopyable.hpp> +#include <boost/thread/recursive_mutex.hpp> +#include <boost/thread/shared_mutex.hpp> +#include <boost/thread/thread_time.hpp> +#include <boost/thread/tss.hpp> + +namespace qpid { +namespace sys { + +class Condition; + +/** + * Mutex lock. + */ +class Mutex : private boost::noncopyable { + friend class Condition; + +public: + typedef ::qpid::sys::ScopedLock<Mutex> ScopedLock; + typedef ::qpid::sys::ScopedUnlock<Mutex> ScopedUnlock; + + inline Mutex(); + inline ~Mutex(); + inline void lock(); + inline void unlock(); + inline bool trylock(); + + +protected: + boost::recursive_mutex mutex; +}; + +/** + * RW lock. + */ +class RWlock : private boost::noncopyable { + friend class Condition; + +public: + typedef ::qpid::sys::ScopedRlock<RWlock> ScopedRlock; + typedef ::qpid::sys::ScopedWlock<RWlock> ScopedWlock; + + inline RWlock(); + inline ~RWlock(); + inline void wlock(); // will write-lock + inline void rlock(); // will read-lock + inline void unlock(); + inline void trywlock(); // will write-try + inline void tryrlock(); // will read-try + +protected: + boost::shared_mutex rwMutex; + boost::thread_specific_ptr<bool> haveWrite; + + inline bool &write (void); +}; + + +/** + * PODMutex is a POD, can be static-initialized with + * PODMutex m = QPID_PODMUTEX_INITIALIZER + */ +struct PODMutex +{ + typedef ::qpid::sys::ScopedLock<PODMutex> ScopedLock; + + inline void lock(); + inline void unlock(); + inline bool trylock(); + + // Must be public to be a POD: + boost::recursive_mutex mutex; +}; + +#define QPID_MUTEX_INITIALIZER 0 + +void PODMutex::lock() { + mutex.lock(); +} + +void PODMutex::unlock() { + mutex.unlock(); +} + +bool PODMutex::trylock() { + return mutex.try_lock(); +} + +Mutex::Mutex() { +} + +Mutex::~Mutex(){ +} + +void Mutex::lock() { + mutex.lock(); +} + +void Mutex::unlock() { + mutex.unlock(); +} + +bool Mutex::trylock() { + return mutex.try_lock(); +} + + +RWlock::RWlock() { +} + +RWlock::~RWlock(){ +} + +void RWlock::wlock() { + bool &writer = write(); + rwMutex.lock(); + writer = true; // Remember this thread has write lock held. +} + +void RWlock::rlock() { + bool &writer = write(); + rwMutex.lock_shared(); + writer = false; // Remember this thread has shared lock held. +} + +void RWlock::unlock() { + bool &writer = write(); + if (writer) + rwMutex.unlock(); + else + rwMutex.unlock_shared(); +} + +void RWlock::trywlock() { + bool &writer = write(); + // shared_mutex::try_lock() seems to not be available... emulate it with + // a timed lock(). + boost::system_time now = boost::get_system_time(); + if (rwMutex.timed_lock(now)) + writer = true; +} + +void RWlock::tryrlock() { + bool &writer = write(); + if (rwMutex.try_lock_shared()) + writer = false; +} + +bool & RWlock::write (void) { + // Accessing thread-specific and stack-local info, so no locks needed. + bool *writePtr = haveWrite.get(); + if (writePtr == 0) { + writePtr = new bool(false); + haveWrite.reset(writePtr); + } + return *writePtr; +} + +}} +#endif /*!_sys_windows_Mutex_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/Shlib.cpp b/RC9/qpid/cpp/src/qpid/sys/windows/Shlib.cpp new file mode 100644 index 0000000000..38027de93f --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/Shlib.cpp @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Shlib.h" +#include "qpid/Exception.h" +#include "qpid/sys/windows/check.h" +#include <windows.h> + +namespace qpid { +namespace sys { + +void Shlib::load(const char* name) { + HMODULE h = LoadLibrary(name); + if (h == NULL) { + throw QPID_WINDOWS_ERROR(GetLastError()); + } + handle = static_cast<void*>(h); +} + +void Shlib::unload() { + if (handle) { + if (FreeLibrary(static_cast<HMODULE>(handle)) == 0) { + throw QPID_WINDOWS_ERROR(GetLastError()); + } + handle = 0; + } +} + +void* Shlib::getSymbol(const char* name) { + void* sym = GetProcAddress(static_cast<HMODULE>(handle), name); + if (sym == NULL) + throw QPID_WINDOWS_ERROR(GetLastError()); + return sym; +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/Socket.cpp b/RC9/qpid/cpp/src/qpid/sys/windows/Socket.cpp new file mode 100755 index 0000000000..a9959bf43e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/Socket.cpp @@ -0,0 +1,329 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR 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 "IoHandlePrivate.h" +#include "check.h" +#include "qpid/sys/Time.h" + +#include <cstdlib> +#include <string.h> +#include <iostream> +#include <memory.h> + +#include <winsock2.h> +#include <ws2tcpip.h> + +#include <boost/format.hpp> + +// Need to initialize WinSock. Ideally, this would be a singleton or embedded +// in some one-time initialization function. I tried boost singleton and could +// not get it to compile (and others located in google had the same problem). +// So, this simple static with an interlocked increment will do for known +// use cases at this time. Since this will only shut down winsock at process +// termination, there may be some problems with client programs that also +// expect to load and unload winsock, but we'll see... +// If someone does get an easy-to-use singleton sometime, converting to it +// may be preferable. + +namespace { + +static LONG volatile initialized = 0; + +class WinSockSetup { + // : public boost::details::pool::singleton_default<WinSockSetup> { + +public: + WinSockSetup() { + LONG timesEntered = InterlockedIncrement(&initialized); + if (timesEntered > 1) + return; + err = 0; + WORD wVersionRequested; + WSADATA wsaData; + + /* Request WinSock 2.2 */ + wVersionRequested = MAKEWORD(2, 2); + err = WSAStartup(wVersionRequested, &wsaData); + } + + ~WinSockSetup() { + WSACleanup(); + } + +public: + int error(void) const { return err; } + +protected: + DWORD err; +}; + +static WinSockSetup setup; + +} /* namespace */ + +namespace qpid { +namespace sys { + +namespace { + +std::string getName(SOCKET fd, bool local, bool includeService = false) +{ + sockaddr_in name; // big enough for any socket address + socklen_t namelen = sizeof(name); + if (local) { + QPID_WINSOCK_CHECK(::getsockname(fd, (sockaddr*)&name, &namelen)); + } else { + QPID_WINSOCK_CHECK(::getpeername(fd, (sockaddr*)&name, &namelen)); + } + + char servName[NI_MAXSERV]; + char dispName[NI_MAXHOST]; + if (includeService) { + if (int rc = ::getnameinfo((sockaddr*)&name, namelen, + dispName, sizeof(dispName), + servName, sizeof(servName), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw qpid::Exception(QPID_MSG(gai_strerror(rc))); + return std::string(dispName) + ":" + std::string(servName); + } else { + if (int rc = ::getnameinfo((sockaddr*)&name, namelen, + dispName, sizeof(dispName), + 0, 0, + NI_NUMERICHOST) != 0) + throw qpid::Exception(QPID_MSG(gai_strerror(rc))); + return dispName; + } +} + +std::string getService(SOCKET fd, bool local) +{ + sockaddr_in name; // big enough for any socket address + socklen_t namelen = sizeof(name); + + if (local) { + QPID_WINSOCK_CHECK(::getsockname(fd, (sockaddr*)&name, &namelen)); + } else { + QPID_WINSOCK_CHECK(::getpeername(fd, (sockaddr*)&name, &namelen)); + } + + char servName[NI_MAXSERV]; + if (int rc = ::getnameinfo((sockaddr*)&name, namelen, + 0, 0, + servName, sizeof(servName), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw qpid::Exception(QPID_MSG(gai_strerror(rc))); + return servName; +} +} // namespace + +Socket::Socket() : + IOHandle(new IOHandlePrivate) +{ + createTcp(); +} + +Socket::Socket(IOHandlePrivate* h) : + IOHandle(h) +{} + +void Socket::createTcp() const +{ + SOCKET& socket = impl->fd; + if (socket != INVALID_SOCKET) Socket::close(); + SOCKET s = ::socket (PF_INET, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) throw QPID_WINDOWS_ERROR(WSAGetLastError()); + socket = s; +} + +void Socket::setTimeout(const Duration& interval) const +{ + const SOCKET& socket = impl->fd; + int64_t nanosecs = interval; + nanosecs /= (1000 * 1000); // nsecs -> usec -> msec + int msec = 0; + if (nanosecs > std::numeric_limits<int>::max()) + msec = std::numeric_limits<int>::max(); + else + msec = static_cast<int>(nanosecs); + setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&msec, sizeof(msec)); + setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&msec, sizeof(msec)); +} + +void Socket::setNonblocking() const { + u_long nonblock = 1; + QPID_WINSOCK_CHECK(ioctlsocket(impl->fd, FIONBIO, &nonblock)); +} + +void Socket::connect(const std::string& host, uint16_t port) const +{ + std::stringstream portstream; + portstream << port << std::ends; + std::string portstr = portstream.str(); + std::stringstream namestream; + namestream << host << ":" << port; + connectname = namestream.str(); + + const SOCKET& socket = impl->fd; + // TODO: Be good to make this work for IPv6 as well as IPv4. Would require + // other changes, such as waiting to create the socket until after we + // have the address family. Maybe unbundle the translation of names here; + // use TcpAddress to resolve things and make this class take a TcpAddress + // and grab its address family to create the socket. + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; // We always creating AF_INET-only sockets. + hints.ai_socktype = SOCK_STREAM; // We always do TCP + addrinfo *addrs; + int status = getaddrinfo(host.c_str(), portstr.c_str(), &hints, &addrs); + if (status != 0) + throw Exception(QPID_MSG("Cannot resolve " << host << ": " << + gai_strerror(status))); + addrinfo *addr = addrs; + int error = 0; + WSASetLastError(0); + while (addr != 0) { + if ((::connect(socket, addr->ai_addr, addr->ai_addrlen) == 0) || + (WSAGetLastError() == WSAEWOULDBLOCK)) + break; + // Error... save this error code and see if there are other address + // to try before throwing the exception. + error = WSAGetLastError(); + addr = addr->ai_next; + } + freeaddrinfo(addrs); + if (error) + throw qpid::Exception(QPID_MSG(strError(error) << ": " << connectname)); +} + +void +Socket::close() const +{ + SOCKET& socket = impl->fd; + if (socket == INVALID_SOCKET) return; + QPID_WINSOCK_CHECK(closesocket(socket)); + socket = INVALID_SOCKET; +} + + +int Socket::write(const void *buf, size_t count) const +{ + const SOCKET& socket = impl->fd; + int sent = ::send(socket, (const char *)buf, count, 0); + if (sent == SOCKET_ERROR) + return -1; + return sent; +} + +int Socket::read(void *buf, size_t count) const +{ + const SOCKET& socket = impl->fd; + int received = ::recv(socket, (char *)buf, count, 0); + if (received == SOCKET_ERROR) + return -1; + return received; +} + +int Socket::listen(uint16_t port, int backlog) const +{ + const SOCKET& socket = impl->fd; + BOOL yes=1; + QPID_WINSOCK_CHECK(setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes))); + struct sockaddr_in name; + memset(&name, 0, sizeof(name)); + name.sin_family = AF_INET; + name.sin_port = htons(port); + name.sin_addr.s_addr = 0; + if (::bind(socket, (struct sockaddr*)&name, sizeof(name)) == SOCKET_ERROR) + throw Exception(QPID_MSG("Can't bind to port " << port << ": " << strError(WSAGetLastError()))); + if (::listen(socket, backlog) == SOCKET_ERROR) + throw Exception(QPID_MSG("Can't listen on port " << port << ": " << strError(WSAGetLastError()))); + + socklen_t namelen = sizeof(name); + QPID_WINSOCK_CHECK(::getsockname(socket, (struct sockaddr*)&name, &namelen)); + return ntohs(name.sin_port); +} + +Socket* Socket::accept(struct sockaddr *addr, socklen_t *addrlen) const +{ + SOCKET afd = ::accept(impl->fd, addr, addrlen); + if (afd != INVALID_SOCKET) + return new Socket(new IOHandlePrivate(afd)); + else if (WSAGetLastError() == EAGAIN) + return 0; + else throw QPID_WINDOWS_ERROR(WSAGetLastError()); +} + +std::string Socket::getSockname() const +{ + return getName(impl->fd, true); +} + +std::string Socket::getPeername() const +{ + return getName(impl->fd, false); +} + +std::string Socket::getPeerAddress() const +{ + if (!connectname.empty()) + return std::string (connectname); + return getName(impl->fd, false, true); +} + +std::string Socket::getLocalAddress() const +{ + return getName(impl->fd, true, true); +} + +uint16_t Socket::getLocalPort() const +{ + return atoi(getService(impl->fd, true).c_str()); +} + +uint16_t Socket::getRemotePort() const +{ + return atoi(getService(impl->fd, true).c_str()); +} + +int Socket::getError() const +{ + int result; + socklen_t rSize = sizeof (result); + + QPID_WINSOCK_CHECK(::getsockopt(impl->fd, SOL_SOCKET, SO_ERROR, (char *)&result, &rSize)); + return result; +} + +void Socket::setTcpNoDelay(bool nodelay) const +{ + if (nodelay) { + int flag = 1; + int result = setsockopt(impl->fd, + IPPROTO_TCP, + TCP_NODELAY, + (char *)&flag, + sizeof(flag)); + QPID_WINSOCK_CHECK(result); + } +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/StrError.cpp b/RC9/qpid/cpp/src/qpid/sys/windows/StrError.cpp new file mode 100755 index 0000000000..9c1bfcd79c --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/StrError.cpp @@ -0,0 +1,47 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/StrError.h" +#include <string> +#include <string.h> +#include <windows.h> + +namespace qpid { +namespace sys { + +std::string strError(int err) { + const size_t bufsize = 512; + char buf[bufsize]; + if (0 == FormatMessage (FORMAT_MESSAGE_MAX_WIDTH_MASK + | FORMAT_MESSAGE_FROM_SYSTEM, + 0, + err, + 0, // Default language + buf, + bufsize, + 0)) + { + strerror_s (buf, bufsize, err); + } + return std::string(buf); +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/SystemInfo.cpp b/RC9/qpid/cpp/src/qpid/sys/windows/SystemInfo.cpp new file mode 100755 index 0000000000..b887cac58b --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/SystemInfo.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. + * + */ + +/* GetNativeSystemInfo call requires _WIN32_WINNT 0x0501 or higher */ +#ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0501 +#endif + +#include "qpid/sys/IntegerTypes.h" +#include "qpid/sys/SystemInfo.h" + +#include <winsock2.h> +#include <ws2tcpip.h> +#include <windows.h> + +#ifndef HOST_NAME_MAX +# define HOST_NAME_MAX 256 +#endif + +namespace qpid { +namespace sys { + +long SystemInfo::concurrency() { + SYSTEM_INFO sys_info; + ::GetSystemInfo (&sys_info); + long activeProcessors = 0; + DWORD_PTR mask = sys_info.dwActiveProcessorMask; + while (mask != 0) { + if (mask & 1) + ++activeProcessors; + mask >>= 1; + } + return activeProcessors; +} + +bool SystemInfo::getLocalHostname (TcpAddress &address) { + char name[HOST_NAME_MAX]; + if (::gethostname(name, sizeof(name)) != 0) { + errno = WSAGetLastError(); + return false; + } + address.host = name; + return true; +} + +void SystemInfo::getLocalIpAddresses (uint16_t port, + std::vector<Address> &addrList) { + enum { MAX_URL_INTERFACES = 100 }; + static const std::string LOCALHOST("127.0.0.1"); + + SOCKET s = socket (PF_INET, SOCK_STREAM, 0); + if (s != INVALID_SOCKET) { + INTERFACE_INFO interfaces[MAX_URL_INTERFACES]; + DWORD filledBytes = 0; + WSAIoctl (s, + SIO_GET_INTERFACE_LIST, + 0, + 0, + interfaces, + sizeof (interfaces), + &filledBytes, + 0, + 0); + unsigned int interfaceCount = filledBytes / sizeof (INTERFACE_INFO); + for (unsigned int i = 0; i < interfaceCount; ++i) { + if (interfaces[i].iiFlags & IFF_UP) { + std::string addr(inet_ntoa(interfaces[i].iiAddress.AddressIn.sin_addr)); + if (addr != LOCALHOST) + addrList.push_back(TcpAddress(addr, port)); + } + } + closesocket (s); + } +} + +void SystemInfo::getSystemId (std::string &osName, + std::string &nodeName, + std::string &release, + std::string &version, + std::string &machine) +{ + osName = "Microsoft Windows"; + + char node[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD nodelen = MAX_COMPUTERNAME_LENGTH + 1; + GetComputerName (node, &nodelen); + nodeName = node; + + OSVERSIONINFOEX vinfo; + vinfo.dwOSVersionInfoSize = sizeof(vinfo); + GetVersionEx ((OSVERSIONINFO *)&vinfo); + + SYSTEM_INFO sinfo; + GetNativeSystemInfo(&sinfo); + + switch(vinfo.dwMajorVersion) { + case 5: + switch(vinfo.dwMinorVersion) { + case 0: + release ="2000"; + break; + case 1: + release = "XP"; + break; + case 2: + if (sinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || + sinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) + release = "XP-64"; + else + release = "Server 2003"; + break; + default: + release = "Windows"; + } + break; + case 6: + if (vinfo.wProductType == VER_NT_SERVER) + release = "Server 2008"; + else + release = "Vista"; + break; + default: + release = "Microsoft Windows"; + } + version = vinfo.szCSDVersion; + + switch(sinfo.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_AMD64: + machine = "x86-64"; + break; + case PROCESSOR_ARCHITECTURE_IA64: + machine = "IA64"; + break; + case PROCESSOR_ARCHITECTURE_INTEL: + machine = "x86"; + break; + default: + machine = "unknown"; + break; + } +} + +}} // namespace qpid::sys diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/Thread.cpp b/RC9/qpid/cpp/src/qpid/sys/windows/Thread.cpp new file mode 100755 index 0000000000..6d5d78393e --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/Thread.cpp @@ -0,0 +1,88 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Thread.h" +#include "qpid/sys/Runnable.h" +#include "check.h" + +#include <process.h> +#include <windows.h> + +namespace { +unsigned __stdcall runRunnable(void* p) +{ + static_cast<qpid::sys::Runnable*>(p)->run(); + _endthreadex(0); + return 0; +} +} + +namespace qpid { +namespace sys { + +class ThreadPrivate { + friend class Thread; + + HANDLE threadHandle; + unsigned threadId; + + ThreadPrivate(Runnable* runnable) { + uintptr_t h = _beginthreadex(0, + 0, + runRunnable, + runnable, + 0, + &threadId); + QPID_WINDOWS_CHECK_CRT_NZ(h); + threadHandle = reinterpret_cast<HANDLE>(h); + } + + ThreadPrivate() + : threadHandle(GetCurrentThread()), threadId(GetCurrentThreadId()) {} +}; + +Thread::Thread() {} + +Thread::Thread(Runnable* runnable) : impl(new ThreadPrivate(runnable)) {} + +Thread::Thread(Runnable& runnable) : impl(new ThreadPrivate(&runnable)) {} + +void Thread::join() { + if (impl) { + DWORD status = WaitForSingleObject (impl->threadHandle, INFINITE); + QPID_WINDOWS_CHECK_NOT(status, WAIT_FAILED); + CloseHandle (impl->threadHandle); + impl->threadHandle = 0; + } +} + +unsigned long Thread::id() { + return impl ? impl->threadId : 0; +} + +/* static */ +Thread Thread::current() { + Thread t; + t.impl.reset(new ThreadPrivate()); + return t; +} + +}} /* qpid::sys */ diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/Time.cpp b/RC9/qpid/cpp/src/qpid/sys/windows/Time.cpp new file mode 100644 index 0000000000..2390826831 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/Time.cpp @@ -0,0 +1,90 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Time.h" +#include <ostream> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/thread/thread_time.hpp> +#include <windows.h> + +using namespace boost::posix_time; + +namespace qpid { +namespace sys { + +AbsTime::AbsTime(const AbsTime& t, const Duration& d) { + if (d == Duration::max()) { + timepoint = ptime(max_date_time); + } + else { + time_duration td = microseconds(d.nanosecs / 1000); + timepoint = t.timepoint + td; + } +} + +AbsTime AbsTime::FarFuture() { + AbsTime ff; + ptime maxd(max_date_time); + ff.timepoint = maxd; + return ff; +} + +AbsTime AbsTime::now() { + AbsTime time_now; + time_now.timepoint = boost::get_system_time(); + return time_now; +} + +Duration::Duration(const AbsTime& time0) : nanosecs(0) { + time_period p(ptime(min_date_time), time0.timepoint); + nanosecs = p.length().total_nanoseconds(); +} + +Duration::Duration(const AbsTime& start, const AbsTime& finish) { + time_duration d = finish.timepoint - start.timepoint; + nanosecs = d.total_nanoseconds(); +} + +std::ostream& operator<<(std::ostream& o, const Duration& d) { + return o << int64_t(d) << "ns"; +} + +std::ostream& operator<<(std::ostream& o, const AbsTime& t) { + std::string time_string = to_simple_string(t.timepoint); + return o << time_string; +} + +void toPtime(ptime& pt, const AbsTime& t) { + pt = t.getPrivate(); +} + +void sleep(int secs) { + ::Sleep(secs * 1000); +} + +void usleep(uint64_t usecs) { + DWORD msecs = usecs / 1000; + if (msecs == 0) + msecs = 1; + ::Sleep(msecs); +} + +}} diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/Time.h b/RC9/qpid/cpp/src/qpid/sys/windows/Time.h new file mode 100644 index 0000000000..49b3c4bab3 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/Time.h @@ -0,0 +1,36 @@ +#ifndef QPID_SYS_WINDOWS_TIME_H +#define QPID_SYS_WINDOWS_TIME_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/date_time/posix_time/posix_time.hpp> + +namespace qpid { +namespace sys { + +/** + * Class to represent an instant in time. Boost has this stuff already done + * so just reuse it. We can also grab this for quick use with the Condition + * wait operations. + */ +typedef boost::posix_time::ptime TimePrivate; + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_WINDOWS_TIME_H*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/check.h b/RC9/qpid/cpp/src/qpid/sys/windows/check.h new file mode 100755 index 0000000000..aba38814b2 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/check.h @@ -0,0 +1,48 @@ +#ifndef _windows_check_h +#define _windows_check_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Exception.h" +#include "qpid/sys/StrError.h" + +#define QPID_WINDOWS_ERROR(ERRVAL) qpid::Exception(QPID_MSG(qpid::sys::strError(ERRVAL))) +#define QPID_WINDOWS_CRT_ERROR(ERRNO) qpid::Exception(QPID_MSG(qpid::sys::strError(ERRNO))) + +/** THROW QPID_WINDOWS_ERROR(::GetLastError()) if RESULT is NULL */ +#define QPID_WINDOWS_CHECK_NULL(RESULT) \ + if ((RESULT) == NULL) throw QPID_WINDOWS_ERROR((::GetLastError())) + +#define QPID_WINDOWS_CHECK_NOT(RESULT,VAL) \ + if ((RESULT) == (VAL)) throw QPID_WINDOWS_ERROR((::GetLastError())) + +#define QPID_WINDOWS_CHECK_ASYNC_START(STATUS) \ + if (!(STATUS) && ::WSAGetLastError() != ERROR_IO_PENDING) \ + throw QPID_WINDOWS_ERROR((::WSAGetLastError())) + +#define QPID_WINDOWS_CHECK_CRT_NZ(VAL) \ + if ((VAL) == 0) throw QPID_WINDOWS_CRT_ERROR(errno) + +#define QPID_WINSOCK_CHECK(OP) \ + if ((OP) == SOCKET_ERROR) throw QPID_WINDOWS_ERROR((::WSAGetLastError())) + +#endif /*!_windows_check_h*/ diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/uuid.cpp b/RC9/qpid/cpp/src/qpid/sys/windows/uuid.cpp new file mode 100644 index 0000000000..92f60e04b1 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/uuid.cpp @@ -0,0 +1,54 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "uuid.h" + +#include <string.h> + +void uuid_clear (uuid_t uu) { + UuidCreateNil (reinterpret_cast<UUID*>(uu)); +} + +void uuid_copy (uuid_t dst, const uuid_t src) { + memcpy (dst, src, qpid::sys::UuidSize); +} + +void uuid_generate (uuid_t out) { + UuidCreate (reinterpret_cast<UUID*>(out)); +} + +int uuid_is_null (const uuid_t uu) { + RPC_STATUS unused; + return UuidIsNil ((UUID*)uu, &unused); +} + +int uuid_parse (const char *in, uuid_t uu) { + return UuidFromString ((unsigned char*)in, (UUID*)uu) == RPC_S_OK ? 0 : -1; +} + +void uuid_unparse (const uuid_t uu, char *out) { + unsigned char *formatted; + if (UuidToString((UUID*)uu, &formatted) == RPC_S_OK) { + strncpy_s (out, 36+1, (char*)formatted, _TRUNCATE); + RpcStringFree(&formatted); + } +} + diff --git a/RC9/qpid/cpp/src/qpid/sys/windows/uuid.h b/RC9/qpid/cpp/src/qpid/sys/windows/uuid.h new file mode 100644 index 0000000000..a44ef2e9a3 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/sys/windows/uuid.h @@ -0,0 +1,41 @@ +#ifndef _sys_windows_uuid_h +#define _sys_windows_uuid_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <Rpc.h> + +#ifdef uuid_t /* Done in rpcdce.h */ +# undef uuid_t +#endif +#include <qpid/sys/IntegerTypes.h> +namespace qpid { namespace sys { const size_t UuidSize = 16; }} +typedef uint8_t uuid_t[qpid::sys::UuidSize]; + +void uuid_clear (uuid_t uu); +void uuid_copy (uuid_t dst, const uuid_t src); +void uuid_generate (uuid_t out); +int uuid_is_null (const uuid_t uu); // Returns 1 if null, else 0 +int uuid_parse (const char *in, uuid_t uu); // Returns 0 on success, else -1 +void uuid_unparse (const uuid_t uu, char *out); + +#endif /*!_sys_windows_uuid_h*/ diff --git a/RC9/qpid/cpp/src/qpid/xml/XmlBinding.h b/RC9/qpid/cpp/src/qpid/xml/XmlBinding.h new file mode 100644 index 0000000000..cc6b4dca5d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/xml/XmlBinding.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. + * + */ +#include <qpid/framing/FieldTable> +#include <string> + +#ifndef _XmlBinding_ +#define _XmlBinding_ + +namespace qpid { +namespace client { + +class XmlBinding : public framing::FieldTable { + public: + setQuery(string query) { setString("xquery", query); } +}; + +} +} +#endif diff --git a/RC9/qpid/cpp/src/qpid/xml/XmlExchange.cpp b/RC9/qpid/cpp/src/qpid/xml/XmlExchange.cpp new file mode 100644 index 0000000000..5197b239d0 --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/xml/XmlExchange.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 "config.h" + +#include "XmlExchange.h" + +#include "qpid/broker/DeliverableMessage.h" + +#include "qpid/log/Statement.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" +#include "qpid/framing/reply_exceptions.h" + +#include "qpid/Plugin.h" + +#include <xercesc/framework/MemBufInputSource.hpp> +#include <xqilla/context/ItemFactory.hpp> +#include <xqilla/xqilla-simple.hpp> + +#include <iostream> +#include <sstream> + +using namespace qpid::framing; +using namespace qpid::sys; +using qpid::management::Manageable; +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace qpid { +namespace broker { + + +XmlExchange::XmlExchange(const string& _name, Manageable* _parent) : Exchange(_name, _parent) +{ + if (mgmtExchange != 0) + mgmtExchange->set_type (typeName); +} + +XmlExchange::XmlExchange(const std::string& _name, bool _durable, + const FieldTable& _args, Manageable* _parent) : + Exchange(_name, _durable, _args, _parent) +{ + if (mgmtExchange != 0) + mgmtExchange->set_type (typeName); +} + +/* + * Use the name of the query as the binding key. + * + * The first time a given name is used in a binding, the query body + * must be provided.After that, no query body should be present. + * + * To modify an installed query, the user must first unbind the + * existing query, then replace it by binding again with the same + * name. + * + */ + + // #### TODO: The Binding should take the query text + // #### only. Consider encapsulating the entire block, including + // #### the if condition. + + +bool XmlExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* bindingArguments) +{ + string queryText = bindingArguments->getAsString("xquery"); + + try { + RWlock::ScopedWlock l(lock); + + XmlBinding::vector& bindings(bindingsMap[routingKey]); + XmlBinding::vector::ConstPtr p = bindings.snapshot(); + if (!p || std::find_if(p->begin(), p->end(), MatchQueue(queue)) == p->end()) { + Query query(xqilla.parse(X(queryText.c_str()))); + XmlBinding::shared_ptr binding(new XmlBinding (routingKey, queue, this, query)); + bindings.add(binding); + QPID_LOG(trace, "Bound successfully with query: " << queryText ); + + if (mgmtExchange != 0) { + mgmtExchange->inc_bindingCount(); + ((_qmf::Queue*) queue->GetManagementObject())->inc_bindingCount(); + } + } else { + return false; + } + } + catch (XQException& e) { + throw InternalErrorException(QPID_MSG("Could not parse xquery:"+ queryText)); + } + catch (...) { + throw InternalErrorException(QPID_MSG("Unexpected error - Could not parse xquery:"+ queryText)); + } + routeIVE(); + return true; +} + +bool XmlExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/) +{ + RWlock::ScopedWlock l(lock); + if (bindingsMap[routingKey].remove_if(MatchQueue(queue))) { + if (mgmtExchange != 0) { + mgmtExchange->dec_bindingCount(); + ((_qmf::Queue*) queue->GetManagementObject())->dec_bindingCount(); + } + return true; + } else { + return false; + } +} + +bool XmlExchange::matches(Query& query, Deliverable& msg, const qpid::framing::FieldTable* args) +{ + // ### TODO: Need istream for frameset + // Hack alert - the following code does not work for really large messages + + string msgContent; + + try { + msg.getMessage().getFrames().getContent(msgContent); + + QPID_LOG(trace, "matches: query is [" << UTF8(query->getQueryText()) << "]"); + QPID_LOG(trace, "matches: message content is [" << msgContent << "]"); + + boost::scoped_ptr<DynamicContext> context(query->createDynamicContext()); + if (!context.get()) { + throw InternalErrorException(QPID_MSG("Query context looks munged ...")); + } + + XERCES_CPP_NAMESPACE::MemBufInputSource xml((const XMLByte*) msgContent.c_str(), + msgContent.length(), "input" ); + Sequence seq(context->parseDocument(xml)); + + if (args) { + FieldTable::ValueMap::const_iterator v = args->begin(); + for(; v != args->end(); ++v) { + // ### TODO: Do types properly + if (v->second->convertsTo<std::string>()) { + QPID_LOG(trace, "XmlExchange, external variable: " << v->first << " = " << v->second->getData().getString().c_str()); + Item::Ptr value = context->getItemFactory()->createString(X(v->second->getData().getString().c_str()), context.get()); + context->setExternalVariable(X(v->first.c_str()), value); + } + } + } + + if(!seq.isEmpty() && seq.first()->isNode()) { + context->setContextItem(seq.first()); + context->setContextPosition(1); + context->setContextSize(1); + } + Result result = query->execute(context.get()); + return result->getEffectiveBooleanValue(context.get(), 0); + } + catch (XQException& e) { + QPID_LOG(warning, "Could not parse XML content (or message headers):" << msgContent); + return 0; + } + catch (...) { + QPID_LOG(warning, "Unexpected error routing message: " << msgContent); + return 0; + } + return 0; +} + +void XmlExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* args) +{ + PreRoute pr(msg, this); + try { + XmlBinding::vector::ConstPtr p; + { + RWlock::ScopedRlock l(lock); + p = bindingsMap[routingKey].snapshot(); + if (!p) return; + } + int count(0); + + for (std::vector<XmlBinding::shared_ptr>::const_iterator i = p->begin(); i != p->end(); i++) { + if ((*i)->xquery && matches((*i)->xquery, msg, args)) { // Overly defensive? There should always be a query ... + msg.deliverTo((*i)->queue); + count++; + QPID_LOG(trace, "Delivered to queue" ); + + if ((*i)->mgmtBinding != 0) + (*i)->mgmtBinding->inc_msgMatched (); + } + } + if (!count) { + QPID_LOG(warning, "XMLExchange " << getName() << ": could not route message with query " << routingKey); + if (mgmtExchange != 0) { + mgmtExchange->inc_msgDrops (); + mgmtExchange->inc_byteDrops (msg.contentSize ()); + } + } else { + if (mgmtExchange != 0) { + mgmtExchange->inc_msgRoutes (count); + mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + } + } + + if (mgmtExchange != 0) { + mgmtExchange->inc_msgReceives (); + mgmtExchange->inc_byteReceives (msg.contentSize ()); + } + } catch (...) { + QPID_LOG(warning, "XMLExchange " << getName() << ": exception routing message with query " << routingKey); + } + + +} + + +bool XmlExchange::isBound(Queue::shared_ptr queue, const string* const routingKey, const FieldTable* const) +{ + RWlock::ScopedRlock l(lock); + if (routingKey) { + XmlBindingsMap::iterator i = bindingsMap.find(*routingKey); + + if (i == bindingsMap.end()) + return false; + if (!queue) + return true; + XmlBinding::vector::ConstPtr p = i->second.snapshot(); + return p && std::find_if(p->begin(), p->end(), MatchQueue(queue)) != p->end(); + } else if (!queue) { + //if no queue or routing key is specified, just report whether any bindings exist + return bindingsMap.size() > 0; + } else { + for (XmlBindingsMap::iterator i = bindingsMap.begin(); i != bindingsMap.end(); i++) { + XmlBinding::vector::ConstPtr p = i->second.snapshot(); + if (p && std::find_if(p->begin(), p->end(), MatchQueue(queue)) != p->end()) return true; + } + return false; + } + +} + + +XmlExchange::~XmlExchange() +{ + bindingsMap.clear(); +} + +const std::string XmlExchange::typeName("xml"); + +} +} diff --git a/RC9/qpid/cpp/src/qpid/xml/XmlExchange.h b/RC9/qpid/cpp/src/qpid/xml/XmlExchange.h new file mode 100644 index 0000000000..066a26489d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/xml/XmlExchange.h @@ -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. + * + */ +#ifndef _XmlExchange_ +#define _XmlExchange_ + +#include "qpid/broker/Exchange.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/CopyOnWriteArray.h" +#include "qpid/sys/Monitor.h" +#include "qpid/broker/Queue.h" + +#include <xqilla/xqilla-simple.hpp> + +#include <boost/scoped_ptr.hpp> + +#include <map> +#include <vector> + +namespace qpid { +namespace broker { + +class XmlExchange : public virtual Exchange { + + typedef boost::shared_ptr<XQQuery> Query; + + struct XmlBinding : public Exchange::Binding { + typedef boost::shared_ptr<XmlBinding> shared_ptr; + typedef qpid::sys::CopyOnWriteArray<XmlBinding::shared_ptr> vector; + + boost::shared_ptr<XQQuery> xquery; + + XmlBinding(const std::string& key, const Queue::shared_ptr queue, Exchange* parent, Query query): + Binding(key, queue, parent), xquery(query) {} + }; + + + typedef std::map<string, XmlBinding::vector > XmlBindingsMap; + + XmlBindingsMap bindingsMap; + XQilla xqilla; + qpid::sys::RWlock lock; + + bool matches(Query& query, Deliverable& msg, const qpid::framing::FieldTable* args); + + public: + static const std::string typeName; + + XmlExchange(const std::string& name, management::Manageable* parent = 0); + XmlExchange(const string& _name, bool _durable, + const qpid::framing::FieldTable& _args, management::Manageable* parent = 0); + + virtual std::string getType() const { return typeName; } + + virtual bool bind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args); + + virtual ~XmlExchange(); +}; + + +} +} + + +#endif diff --git a/RC9/qpid/cpp/src/qpid/xml/XmlExchangePlugin.cpp b/RC9/qpid/cpp/src/qpid/xml/XmlExchangePlugin.cpp new file mode 100644 index 0000000000..97e221589d --- /dev/null +++ b/RC9/qpid/cpp/src/qpid/xml/XmlExchangePlugin.cpp @@ -0,0 +1,67 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <sstream> +#include "qpid/acl/Acl.h" +#include "qpid/broker/Broker.h" +#include "qpid/Plugin.h" +#include "qpid/shared_ptr.h" +#include "qpid/log/Statement.h" + +#include <boost/utility/in_place_factory.hpp> + +#include "XmlExchange.h" + +namespace qpid { +namespace broker { // ACL uses the acl namespace here - should I? + +using namespace std; + +Exchange::shared_ptr create(const std::string& name, bool durable, + const framing::FieldTable& args, + management::Manageable* parent) +{ + Exchange::shared_ptr e(new XmlExchange(name, durable, args, parent)); + return e; +} + + +class XmlExchangePlugin : public Plugin +{ +public: + void earlyInitialize(Plugin::Target& target); + void initialize(Plugin::Target& target); +}; + + +void XmlExchangePlugin::initialize(Plugin::Target& target) +{ + Broker* broker = dynamic_cast<broker::Broker*>(&target); + if (broker) { + broker->getExchanges().registerType(XmlExchange::typeName, &create); + QPID_LOG(info, "Registered xml exchange"); + } +} + +void XmlExchangePlugin::earlyInitialize(Target&) {} + + +static XmlExchangePlugin matchingPlugin; + + +}} // namespace qpid::acl diff --git a/RC9/qpid/cpp/src/qpidd.cpp b/RC9/qpid/cpp/src/qpidd.cpp new file mode 100644 index 0000000000..330f12ae83 --- /dev/null +++ b/RC9/qpid/cpp/src/qpidd.cpp @@ -0,0 +1,83 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpidd.h" +#include "qpid/Plugin.h" +#include "qpid/Version.h" +#include "qpid/log/Logger.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/Shlib.h" + +#include <iostream> +#include <memory> +using namespace std; + +auto_ptr<QpiddOptions> options; + +int main(int argc, char* argv[]) +{ + try + { + { + BootstrapOptions bootOptions(argv[0]); + string defaultPath (bootOptions.module.loadDir); + // Parse only the common, load, and log options to see which + // modules need to be loaded. Once the modules are loaded, + // the command line will be re-parsed with all of the + // module-supplied options. + bootOptions.parse (argc, argv, bootOptions.common.config, true); + qpid::log::Logger::instance().configure(bootOptions.log); + + for (vector<string>::iterator iter = bootOptions.module.load.begin(); + iter != bootOptions.module.load.end(); + iter++) + qpid::tryShlib (iter->data(), false); + + if (!bootOptions.module.noLoad) { + bool isDefault = defaultPath == bootOptions.module.loadDir; + qpid::loadModuleDir (bootOptions.module.loadDir, isDefault); + } + } + + // Parse options + options.reset(new QpiddOptions(argv[0])); + options->parse(argc, argv, options->common.config); + + // Options that just print information. + if (options->common.help || options->common.version) { + if (options->common.version) + cout << "qpidd (" << qpid::product << ") version " + << qpid::version << endl; + else if (options->common.help) + options->usage(); + return 0; + } + + // Everything else is driven by the platform-specific broker + // logic. + QpiddBroker broker; + return broker.execute(options.get()); + } + catch(const exception& e) { + QPID_LOG(critical, "Broker start-up failed: " << e.what()); + } + return 1; +} diff --git a/RC9/qpid/cpp/src/qpidd.h b/RC9/qpid/cpp/src/qpidd.h new file mode 100644 index 0000000000..c702270e80 --- /dev/null +++ b/RC9/qpid/cpp/src/qpidd.h @@ -0,0 +1,70 @@ +#ifndef QPID_H +#define QPID_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/Modules.h" +#include "qpid/Options.h" +#include "qpid/broker/Broker.h" +#include "qpid/log/Options.h" + +#include <memory> + +// BootstrapOptions is a minimal subset of options used for a pre-parse +// of the command line to discover which plugin modules need to be loaded. +// The pre-parse is necessary because plugin modules may supply their own +// set of options. CommonOptions is needed to properly support loading +// from a configuration file. +struct BootstrapOptions : public qpid::Options { + qpid::CommonOptions common; + qpid::ModuleOptions module; + qpid::log::Options log; + + BootstrapOptions(const char *argv0); +}; + +// Each platform derives an options struct from QpiddOptionsPrivate, adding +// platform-specific option types. QpiddOptions needs to allocation one of +// these derived structs from its constructor. +struct QpiddOptions; +struct QpiddOptionsPrivate { + QpiddOptions *options; + QpiddOptionsPrivate(QpiddOptions *parent) : options(parent) {} + virtual ~QpiddOptionsPrivate() {} +protected: + QpiddOptionsPrivate() {} +}; + +struct QpiddOptions : public qpid::Options { + qpid::CommonOptions common; + qpid::ModuleOptions module; + qpid::broker::Broker::Options broker; + qpid::log::Options log; + std::auto_ptr<QpiddOptionsPrivate> platform; + + QpiddOptions(const char *argv0); + void usage() const; +}; + +class QpiddBroker { +public: + int execute (QpiddOptions *options); +}; + +#endif /*!QPID_H*/ diff --git a/RC9/qpid/cpp/src/ssl.mk b/RC9/qpid/cpp/src/ssl.mk new file mode 100644 index 0000000000..5b0ef3d8ae --- /dev/null +++ b/RC9/qpid/cpp/src/ssl.mk @@ -0,0 +1,64 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# +# Makefile fragment, conditionally included in Makefile.am +# +libsslcommon_la_SOURCES = \ + qpid/sys/ssl/check.h \ + qpid/sys/ssl/check.cpp \ + qpid/sys/ssl/util.h \ + qpid/sys/ssl/util.cpp \ + qpid/sys/ssl/SslSocket.h \ + qpid/sys/ssl/SslSocket.cpp \ + qpid/sys/ssl/SslIo.h \ + qpid/sys/ssl/SslIo.cpp + +libsslcommon_la_LIBADD= -lnss3 -lssl3 -lnspr4 libqpidcommon.la + +libsslcommon_la_CXXFLAGS=$(AM_CXXFLAGS) $(SSL_CFLAGS) + +lib_LTLIBRARIES += libsslcommon.la + +ssl_la_SOURCES = \ + qpid/sys/SslPlugin.cpp \ + qpid/sys/ssl/SslHandler.h \ + qpid/sys/ssl/SslHandler.cpp + +ssl_la_LIBADD= libqpidbroker.la libsslcommon.la + +ssl_la_CXXFLAGS=$(AM_CXXFLAGS) $(SSL_CFLAGS) + +ssl_la_LDFLAGS = $(PLUGINLDFLAGS) + +dmodule_LTLIBRARIES += ssl.la + + +sslconnector_la_SOURCES = \ + qpid/client/SslConnector.cpp + +sslconnector_la_LIBADD = \ + libqpidclient.la \ + libsslcommon.la + +sslconnector_la_CXXFLAGS = $(AM_CXXFLAGS) -DCONF_FILE=\"$(confdir)/qpidc.conf\" + +sslconnector_la_LDFLAGS = $(PLUGINLDFLAGS) + +cmodule_LTLIBRARIES += \ + sslconnector.la diff --git a/RC9/qpid/cpp/src/tests/.valgrind.supp b/RC9/qpid/cpp/src/tests/.valgrind.supp new file mode 100644 index 0000000000..7ac34fde5d --- /dev/null +++ b/RC9/qpid/cpp/src/tests/.valgrind.supp @@ -0,0 +1,203 @@ +{ + Benign error in libcpg. + Memcheck:Param + socketcall.sendmsg(msg.msg_iov[i]) + obj:*/libpthread-2.5.so + obj:*/libcpg.so.2.0.0 +} + +{ + Uninitialised value problem in _dl_relocate (F7, F8) + Memcheck:Cond + fun:_dl_relocate_object + fun:*dl_* +} + +{ + False "possibly leaked" in boost program_options - global std::string var. + Memcheck:Leak + fun:_Znwj + fun:_ZNSs4_Rep9_S_createEjjRKSaIcE + obj:/usr/lib/libstdc++.so.6.0.8 + fun:_ZNSsC1EPKcRKSaIcE + obj:/usr/lib/libboost_program_options.so.1.33.1 +} + +{ + boost 103200 -- we think Boost is responsible for these leaks. + Memcheck:Leak + fun:_Znwm + fun:_ZN5boost15program_options??options_description* +} + +{ + boost 103200 -- we think Boost is responsible for these leaks. + Memcheck:Leak + fun:_Znwm + fun:_ZN5boost9unit_test9test_case* +} + +{ + boost 103200 -- we think Boost is responsible for these leaks. + Memcheck:Leak + fun:calloc + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 + fun:_ZN4qpid3sys5Shlib4loadEPKc + fun:_Z9testShlibv + fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor8functionEv + obj:/usr/lib64/libboost_unit_test_framework.so.1.32.0 + fun:_ZN5boost17execution_monitor7executeEbi + fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor21execute_and_translateEPNS0_9test_caseEMS3_FvvEi + fun:_ZN5boost9unit_test9test_case3runEv + fun:_ZN5boost9unit_test10test_suite6do_runEv + fun:_ZN5boost9unit_test9test_case3runEv + fun:main +} + +{ + boost 103200 -- we think Boost is responsible for these leaks. + Memcheck:Leak + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_ZN4qpid6broker5Timer5startEv + fun:_ZN4qpid6broker5TimerC1Ev + fun:_ZN4qpid6broker10DtxManagerC1Ev + fun:_ZN4qpid6broker6BrokerC1ERKNS1_7OptionsE + fun:_ZN4qpid6broker6Broker6createERKNS1_7OptionsE + fun:_ZN15SessionFixtureTI15ProxyConnectionEC2Ev + fun:_Z14testQueueQueryv + fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor8functionEv + obj:/usr/lib64/libboost_unit_test_framework.so.1.32.0 + fun:_ZN5boost17execution_monitor7executeEbi + fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor21execute_and_translateEPNS0_9test_caseEMS3_FvvEi + fun:_ZN5boost9unit_test9test_case3runEv + fun:_ZN5boost9unit_test10test_suite6do_runEv + fun:_ZN5boost9unit_test9test_case3runEv + fun:main +} + +{ + INVESTIGATE + Memcheck:Leak + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_ZN4qpid6client9Connector4initEv + fun:_ZN4qpid6client14ConnectionImpl4openERKSsiS3_S3_S3_ +} + +{ + INVESTIGATE + Memcheck:Param + write(buf) + obj:/lib64/tls/libc-2.3.4.so + fun:_ZNK4qpid3sys6Socket5writeEPKvm + fun:_ZN4qpid3sys8AsynchIO9writeableERNS0_14DispatchHandleE +} + +{ + "Conditional jump or move depends on uninitialised value(s)" from Xerces parser + Memcheck:Cond + fun:_ZN11xercesc_2_717XMLUTF8Transcoder13transcodeFromEPKhjPtjRjPh + fun:_ZN11xercesc_2_79XMLReader14xcodeMoreCharsEPtPhj + fun:_ZN11xercesc_2_79XMLReader17refreshCharBufferEv +} + +{ + boost 103200 -- mgoulish -- fix this, sometime + Memcheck:Leak + fun:* + fun:* + obj:* + fun:* + fun:_ZN4qpid34options_description_less_easy_initclEPKcPKN5boost15program_options14value_semanticES2_ +} + +{ + boost 103200 -- mgoulish -- fix this, sometime + Memcheck:Leak + fun:* + fun:* + fun:* + fun:_ZN4qpid34options_description_less_easy_initclEPKcPKN5boost15program_options14value_semanticES2_ +} + +{ + INVESTIGATE + Memcheck:Param + socketcall.sendto(msg) + fun:send + fun:get_mapping + fun:__nscd_get_map_ref + fun:nscd_gethst_r + fun:__nscd_gethostbyname_r + fun:gethostbyname_r@@GLIBC_2.2.5 + fun:gethostbyname + fun:_ZNK4qpid3sys6Socket7connectERKSsi +} + +{ + INVESTIGATE + Memcheck:Leak + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_ZN4qpid6broker5Timer5startEv + fun:_ZN4qpid6broker5TimerC1Ev + fun:_ZN4qpid6broker10DtxManagerC1Ev + fun:_ZN4qpid6broker6BrokerC1ERKNS1_7OptionsE + fun:_ZN4qpid6broker6Broker6createERKNS1_7OptionsE + fun:_ZN20ClientSessionFixtureC1Ev + fun:_Z14testQueueQueryv + fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor8functionEv + obj:/usr/lib64/libboost_unit_test_framework.so.1.32.0 + fun:_ZN5boost17execution_monitor7executeEbi + fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor21execute_and_translateEPNS0_9test_caseEMS3_FvvEi + fun:_ZN5boost9unit_test9test_case3runEv + fun:_ZN5boost9unit_test10test_suite6do_runEv + fun:_ZN5boost9unit_test9test_case3runEv + fun:main +} + +{ + INVESTIGATE + Memcheck:Leak + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_ZN4qpid6client9Connector4initEv +} + +{ + MICK -- FIX + Memcheck:Leak + fun:_Znam + fun:_ZN4qpid7Options5parseEiPPcRKSsb +} + +{ + MICK -- FIX + Memcheck:Leak + fun:malloc + fun:strdup + fun:_ZN4qpid7Options5parseEiPPcRKSsb +} + +{ + CPG error - seems benign. + Memcheck:Param + socketcall.sendmsg(msg.msg_iov[i]) + obj:* + obj:*/libcpg.so.2.0.0 +} + +{ + Known leak in boost.thread 1.33.1. Wildcards for 64/32 bit diffs. + Memcheck:Leak + fun:* + obj:/usr/*/libboost_thread.so.1.33.1 + fun:_ZN5boost6detail3tss3setEPv +} + diff --git a/RC9/qpid/cpp/src/tests/AccumulatedAckTest.cpp b/RC9/qpid/cpp/src/tests/AccumulatedAckTest.cpp new file mode 100644 index 0000000000..028ce71907 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/AccumulatedAckTest.cpp @@ -0,0 +1,232 @@ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/framing/AccumulatedAck.h" +#include "unit_test.h" +#include <iostream> +#include <list> + +using std::list; +using namespace qpid::framing; + + +bool covers(const AccumulatedAck& ack, int i) +{ + return ack.covers(SequenceNumber(i)); +} + +void update(AccumulatedAck& ack, int start, int end) +{ + ack.update(SequenceNumber(start), SequenceNumber(end)); +} + +QPID_AUTO_TEST_SUITE(AccumulatedAckTestSuite) + +QPID_AUTO_TEST_CASE(testGeneral) +{ + AccumulatedAck ack(0); + ack.clear(); + update(ack, 3,3); + update(ack, 7,7); + update(ack, 9,9); + update(ack, 1,2); + update(ack, 4,5); + update(ack, 6,6); + + for(int i = 1; i <= 7; i++) BOOST_CHECK(covers(ack, i)); + BOOST_CHECK(covers(ack, 9)); + + BOOST_CHECK(!covers(ack, 8)); + BOOST_CHECK(!covers(ack, 10)); + + ack.consolidate(); + + for(int i = 1; i <= 7; i++) BOOST_CHECK(covers(ack, i)); + BOOST_CHECK(covers(ack, 9)); + + BOOST_CHECK(!covers(ack, 8)); + BOOST_CHECK(!covers(ack, 10)); +} + +QPID_AUTO_TEST_CASE(testCovers) +{ + AccumulatedAck ack(5); + update(ack, 7, 7); + update(ack, 9, 9); + + BOOST_CHECK(covers(ack, 1)); + BOOST_CHECK(covers(ack, 2)); + BOOST_CHECK(covers(ack, 3)); + BOOST_CHECK(covers(ack, 4)); + BOOST_CHECK(covers(ack, 5)); + BOOST_CHECK(covers(ack, 7)); + BOOST_CHECK(covers(ack, 9)); + + BOOST_CHECK(!covers(ack, 6)); + BOOST_CHECK(!covers(ack, 8)); + BOOST_CHECK(!covers(ack, 10)); +} + +QPID_AUTO_TEST_CASE(testUpdateFromCompletionData) +{ + AccumulatedAck ack(0); + SequenceNumber mark(2); + SequenceNumberSet ranges; + ranges.addRange(SequenceNumber(5), SequenceNumber(8)); + ranges.addRange(SequenceNumber(10), SequenceNumber(15)); + ranges.addRange(SequenceNumber(9), SequenceNumber(9)); + ranges.addRange(SequenceNumber(3), SequenceNumber(4)); + + ack.update(mark, ranges); + + for(int i = 0; i <= 15; i++) { + BOOST_CHECK(covers(ack, i)); + } + BOOST_CHECK(!covers(ack, 16)); + BOOST_CHECK_EQUAL((uint32_t) 15, ack.mark.getValue()); +} + +QPID_AUTO_TEST_CASE(testCase1) +{ + AccumulatedAck ack(3); + update(ack, 1,2); + for(int i = 1; i <= 3; i++) BOOST_CHECK(covers(ack, i)); + BOOST_CHECK(!covers(ack, 4)); +} + +QPID_AUTO_TEST_CASE(testCase2) +{ + AccumulatedAck ack(3); + update(ack, 3,6); + for(int i = 1; i <= 6; i++) BOOST_CHECK(covers(ack, i)); + BOOST_CHECK(!covers(ack, 7)); +} + +QPID_AUTO_TEST_CASE(testCase3) +{ + AccumulatedAck ack(3); + update(ack, 4,6); + for(int i = 1; i <= 6; i++) { + BOOST_CHECK(covers(ack, i)); + } + BOOST_CHECK(!covers(ack, 7)); +} + +QPID_AUTO_TEST_CASE(testCase4) +{ + AccumulatedAck ack(3); + update(ack, 5,6); + for(int i = 1; i <= 6; i++) { + if (i == 4) BOOST_CHECK(!covers(ack, i)); + else BOOST_CHECK(covers(ack, i)); + } + BOOST_CHECK(!covers(ack, 7)); +} + +QPID_AUTO_TEST_CASE(testConsolidation1) +{ + AccumulatedAck ack(3); + update(ack, 7,7); + BOOST_CHECK_EQUAL((uint32_t) 3, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + + update(ack, 8,9); + BOOST_CHECK_EQUAL((uint32_t) 3, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + + update(ack, 1,2); + BOOST_CHECK_EQUAL((uint32_t) 3, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + + update(ack, 4,5); + BOOST_CHECK_EQUAL((uint32_t) 5, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + + update(ack, 6,6); + BOOST_CHECK_EQUAL((uint32_t) 9, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 0, ack.ranges.size()); + + for(int i = 1; i <= 9; i++) BOOST_CHECK(covers(ack, i)); + BOOST_CHECK(!covers(ack, 10)); +} + +QPID_AUTO_TEST_CASE(testConsolidation2) +{ + AccumulatedAck ack(0); + update(ack, 10,12); + BOOST_CHECK_EQUAL((uint32_t) 0, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + + update(ack, 7,9); + BOOST_CHECK_EQUAL((uint32_t) 0, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + BOOST_CHECK_EQUAL((uint32_t) 7, ack.ranges.front().start.getValue()); + BOOST_CHECK_EQUAL((uint32_t) 12, ack.ranges.front().end.getValue()); + + update(ack, 5,7); + BOOST_CHECK_EQUAL((uint32_t) 0, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + BOOST_CHECK_EQUAL((uint32_t) 5, ack.ranges.front().start.getValue()); + BOOST_CHECK_EQUAL((uint32_t) 12, ack.ranges.front().end.getValue()); + + update(ack, 3,4); + BOOST_CHECK_EQUAL((uint32_t) 0, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + BOOST_CHECK_EQUAL((uint32_t) 3, ack.ranges.front().start.getValue()); + BOOST_CHECK_EQUAL((uint32_t) 12, ack.ranges.front().end.getValue()); + + update(ack, 1,2); + BOOST_CHECK_EQUAL((uint32_t) 12, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 0, ack.ranges.size()); + + for(int i = 1; i <= 12; i++) BOOST_CHECK(covers(ack, i)); + BOOST_CHECK(!covers(ack, 13)); +} + +QPID_AUTO_TEST_CASE(testConsolidation3) +{ + AccumulatedAck ack(0); + update(ack, 10,12); + update(ack, 6,7); + update(ack, 3,4); + update(ack, 1,15); + BOOST_CHECK_EQUAL((uint32_t) 15, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 0, ack.ranges.size()); +} + +QPID_AUTO_TEST_CASE(testConsolidation4) +{ + AccumulatedAck ack(0); + ack.update(SequenceNumber(0), SequenceNumber(2)); + ack.update(SequenceNumber(5), SequenceNumber(8)); + ack.update(SequenceNumber(10), SequenceNumber(15)); + ack.update(SequenceNumber(9), SequenceNumber(9)); + ack.update(SequenceNumber(3), SequenceNumber(4)); + + for(int i = 0; i <= 15; i++) { + BOOST_CHECK(covers(ack, i)); + } + BOOST_CHECK(!covers(ack, 16)); + BOOST_CHECK_EQUAL((uint32_t) 15, ack.mark.getValue()); +} + +QPID_AUTO_TEST_SUITE_END() + diff --git a/RC9/qpid/cpp/src/tests/Array.cpp b/RC9/qpid/cpp/src/tests/Array.cpp new file mode 100644 index 0000000000..c779cbe901 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/Array.cpp @@ -0,0 +1,79 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include <sstream> +#include "qpid/framing/Array.h" +#include "qpid/framing/FieldValue.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(ArrayTestSuite) + +using namespace qpid::framing; + +void populate(std::vector<std::string>& data, int count = 10) +{ + for (int i = 0; i < count; i++) { + std::stringstream out; + out << "item-" << i; + data.push_back(out.str()); + } +} + +QPID_AUTO_TEST_CASE(testEncodeDecode) +{ + std::vector<std::string> data; + populate(data); + + Array a(data); + + char buff[200]; + Buffer wbuffer(buff, 200); + a.encode(wbuffer); + + Array b; + Buffer rbuffer(buff, 200); + b.decode(rbuffer); + BOOST_CHECK_EQUAL(a, b); + + std::vector<std::string> data2; + b.collect(data2); + //BOOST_CHECK_EQUAL(data, data2); + BOOST_CHECK(data == data2); +} + +QPID_AUTO_TEST_CASE(testArrayAssignment) +{ + std::vector<std::string> data; + populate(data); + Array b; + { + Array a(data); + b = a; + BOOST_CHECK_EQUAL(a, b); + } + std::vector<std::string> data2; + b.collect(data2); + //BOOST_CHECK_EQUAL(data, data2); + BOOST_CHECK(data == data2); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/AsyncCompletion.cpp b/RC9/qpid/cpp/src/tests/AsyncCompletion.cpp new file mode 100644 index 0000000000..e33b2dc35d --- /dev/null +++ b/RC9/qpid/cpp/src/tests/AsyncCompletion.cpp @@ -0,0 +1,100 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#include "unit_test.h" +#include "test_tools.h" +#include "BrokerFixture.h" +#include "qpid/broker/NullMessageStore.h" +#include "qpid/sys/BlockingQueue.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/sys/Time.h" + +using namespace std; +using namespace qpid; +using namespace client; +using namespace framing; + +namespace qpid { namespace broker { +class TransactionContext; +class PersistableQueue; +}} + +using broker::PersistableMessage; +using broker::NullMessageStore; +using broker::TransactionContext; +using broker::PersistableQueue; +using sys::TIME_SEC; +using boost::intrusive_ptr; + +/** @file Unit tests for async completion. + * Using a dummy store, verify that the broker indicates async completion of + * message enqueues at the correct time. + */ + +class AsyncCompletionMessageStore : public NullMessageStore { + public: + sys::BlockingQueue<boost::intrusive_ptr<PersistableMessage> > enqueued; + + AsyncCompletionMessageStore() : NullMessageStore() {} + ~AsyncCompletionMessageStore(){} + + void enqueue(TransactionContext*, + const boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& ) + { + enqueued.push(msg); + } +}; + +QPID_AUTO_TEST_SUITE(AsyncCompletionTestSuite) + +QPID_AUTO_TEST_CASE(testWaitTillComplete) { + AsyncCompletionMessageStore* store = new AsyncCompletionMessageStore; + SessionFixture fix; + fix.broker->setStore(store); // Broker will delete store. + AsyncSession s = fix.session; + + static const int count = 3; + + s.queueDeclare("q", arg::durable=true); + Completion transfers[count]; + for (int i = 0; i < count; ++i) { + Message msg(boost::lexical_cast<string>(i), "q"); + msg.getDeliveryProperties().setDeliveryMode(PERSISTENT); + transfers[i] = s.messageTransfer(arg::content=msg); + } + + // Get hold of the broker-side messages. + typedef vector<intrusive_ptr<PersistableMessage> > BrokerMessages; + BrokerMessages enqueued; + for (int j = 0; j < count; ++j) + enqueued.push_back(store->enqueued.pop(TIME_SEC)); + + // Send a sync, make sure it does not complete till all messages are complete. + // In reverse order for fun. + Completion sync = s.executionSync(arg::sync=true); + for (int k = count-1; k >= 0; --k) { + BOOST_CHECK(!transfers[k].isComplete()); // Should not be complete yet. + BOOST_CHECK(!sync.isComplete()); // Should not be complete yet. + enqueued[k]->enqueueComplete(); + } + sync.wait(); // Should complete now, all messages are completed. +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/AtomicValue.cpp b/RC9/qpid/cpp/src/tests/AtomicValue.cpp new file mode 100644 index 0000000000..05083ad177 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/AtomicValue.cpp @@ -0,0 +1,49 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#include "unit_test.h" +#include "test_tools.h" +#include "qpid/sys/AtomicValue.h" + +QPID_AUTO_TEST_SUITE(AtomicValueTestSuite) + +QPID_AUTO_TEST_CASE(test) { + qpid::sys::AtomicValue<int> x(0); + BOOST_CHECK_EQUAL(++x, 1); + BOOST_CHECK_EQUAL(--x,0); + BOOST_CHECK_EQUAL(x+=5,5); + BOOST_CHECK_EQUAL(x-=10,-5); + BOOST_CHECK_EQUAL(x.fetchAndAdd(7), -5); + BOOST_CHECK_EQUAL(x.get(),2); + BOOST_CHECK_EQUAL(x.fetchAndSub(3), 2); + BOOST_CHECK_EQUAL(x.get(),-1); + + BOOST_CHECK_EQUAL(x.valueCompareAndSwap(-1,10), -1); + BOOST_CHECK_EQUAL(x.get(), 10); + BOOST_CHECK_EQUAL(x.valueCompareAndSwap(5, 6), 10); + BOOST_CHECK_EQUAL(x.get(), 10); + + BOOST_CHECK(!x.boolCompareAndSwap(5, 6)); + BOOST_CHECK_EQUAL(x.get(), 10); + BOOST_CHECK(x.boolCompareAndSwap(10, 6)); + BOOST_CHECK_EQUAL(x.get(), 6); +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/BasicP2PTest.cpp b/RC9/qpid/cpp/src/tests/BasicP2PTest.cpp new file mode 100644 index 0000000000..f4a4cce7f2 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/BasicP2PTest.cpp @@ -0,0 +1,66 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "BasicP2PTest.h" + +using namespace qpid; +using namespace qpid::client; + +class BasicP2PTest::Receiver : public Worker, public MessageListener +{ + const std::string queue; + std::string tag; +public: + Receiver(ConnectionOptions& options, const std::string& _queue, const int _messages) + : Worker(options, _messages), queue(_queue){} + void init() + { + Queue q(queue, true); + channel.declareQueue(q); + framing::FieldTable args; + channel.bind(Exchange::STANDARD_DIRECT_EXCHANGE, q, queue, args); + channel.consume(q, tag, this); + channel.start(); + } + + void start() + { + } + + void received(Message&) + { + count++; + } +}; + +void BasicP2PTest::assign(const std::string& role, framing::FieldTable& params, ConnectionOptions& options) +{ + std::string queue = params.getString("P2P_QUEUE_AND_KEY_NAME"); + int messages = params.getInt("P2P_NUM_MESSAGES"); + if (role == "SENDER") { + worker = std::auto_ptr<Worker>(new Sender(options, Exchange::STANDARD_DIRECT_EXCHANGE, queue, messages)); + } else if(role == "RECEIVER"){ + worker = std::auto_ptr<Worker>(new Receiver(options, queue, messages)); + } else { + throw Exception("unrecognised role"); + } + worker->init(); +} diff --git a/RC9/qpid/cpp/src/tests/BasicP2PTest.h b/RC9/qpid/cpp/src/tests/BasicP2PTest.h new file mode 100644 index 0000000000..b2611f0301 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/BasicP2PTest.h @@ -0,0 +1,46 @@ +#ifndef _BasicP2PTest_ +#define _BasicP2PTest_ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <memory> +#include <sstream> + +#include "qpid/Exception.h" +#include "qpid/client/Channel.h" +#include "qpid/client/Message.h" +#include "qpid/client/Connection.h" +#include "qpid/client/MessageListener.h" +#include "SimpleTestCaseBase.h" + + +namespace qpid { + +class BasicP2PTest : public SimpleTestCaseBase +{ + class Receiver; +public: + void assign(const std::string& role, framing::FieldTable& params, ConnectionOptions& options); +}; + +} + +#endif diff --git a/RC9/qpid/cpp/src/tests/BasicPubSubTest.cpp b/RC9/qpid/cpp/src/tests/BasicPubSubTest.cpp new file mode 100644 index 0000000000..1e9ff364f1 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/BasicPubSubTest.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 "BasicPubSubTest.h" + +using namespace qpid; + +class BasicPubSubTest::Receiver : public Worker, public MessageListener +{ + const Exchange& exchange; + const std::string queue; + const std::string key; + std::string tag; +public: + Receiver(ConnectionOptions& options, const Exchange& _exchange, const std::string& _queue, const std::string& _key, const int _messages) + : Worker(options, _messages), exchange(_exchange), queue(_queue), key(_key){} + + void init() + { + Queue q(queue, true); + channel.declareQueue(q); + framing::FieldTable args; + channel.bind(exchange, q, key, args); + channel.consume(q, tag, this); + channel.start(); + } + + void start(){ + } + + void received(Message&) + { + count++; + } +}; + +class BasicPubSubTest::MultiReceiver : public Worker, public MessageListener +{ + typedef boost::ptr_vector<Receiver> ReceiverList; + ReceiverList receivers; + +public: + MultiReceiver(ConnectionOptions& options, const Exchange& exchange, const std::string& key, const int _messages, int receiverCount) + : Worker(options, _messages) + { + for (int i = 0; i != receiverCount; i++) { + std::string queue = (boost::format("%1%_%2%") % options.clientid % i).str(); + receivers.push_back(new Receiver(options, exchange, queue, key, _messages)); + } + } + + void init() + { + for (ReceiverList::size_type i = 0; i != receivers.size(); i++) { + receivers[i].init(); + } + } + + void start() + { + for (ReceiverList::size_type i = 0; i != receivers.size(); i++) { + receivers[i].start(); + } + } + + void received(Message& msg) + { + for (ReceiverList::size_type i = 0; i != receivers.size(); i++) { + receivers[i].received(msg); + } + } + + virtual int getCount() + { + count = 0; + for (ReceiverList::size_type i = 0; i != receivers.size(); i++) { + count += receivers[i].getCount(); + } + return count; + } + virtual void stop() + { + for (ReceiverList::size_type i = 0; i != receivers.size(); i++) { + receivers[i].stop(); + } + } +}; + +void BasicPubSubTest::assign(const std::string& role, framing::FieldTable& params, ConnectionOptions& options) +{ + std::string key = params.getString("PUBSUB_KEY"); + int messages = params.getInt("PUBSUB_NUM_MESSAGES"); + int receivers = params.getInt("PUBSUB_NUM_RECEIVERS"); + if (role == "SENDER") { + worker = std::auto_ptr<Worker>(new Sender(options, Exchange::STANDARD_TOPIC_EXCHANGE, key, messages)); + } else if(role == "RECEIVER"){ + worker = std::auto_ptr<Worker>(new MultiReceiver(options, Exchange::STANDARD_TOPIC_EXCHANGE, key, messages, receivers)); + } else { + throw Exception("unrecognised role"); + } + worker->init(); +} + diff --git a/RC9/qpid/cpp/src/tests/BasicPubSubTest.h b/RC9/qpid/cpp/src/tests/BasicPubSubTest.h new file mode 100644 index 0000000000..242d2847d7 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/BasicPubSubTest.h @@ -0,0 +1,51 @@ +#ifndef _BasicPubSubTest_ +#define _BasicPubSubTest_ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <memory> +#include <sstream> + +#include "qpid/Exception.h" +#include "qpid/client/Channel.h" +#include "qpid/client/Message.h" +#include "qpid/client/Connection.h" +#include "qpid/client/MessageListener.h" +#include "SimpleTestCaseBase.h" +#include <boost/ptr_container/ptr_vector.hpp> +#include <boost/format.hpp> + + +namespace qpid { + +using namespace qpid::client; + +class BasicPubSubTest : public SimpleTestCaseBase +{ + class Receiver; + class MultiReceiver; +public: + void assign(const std::string& role, framing::FieldTable& params, ConnectionOptions& options); +}; + +} + +#endif diff --git a/RC9/qpid/cpp/src/tests/Blob.cpp b/RC9/qpid/cpp/src/tests/Blob.cpp new file mode 100644 index 0000000000..c40e43b96e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/Blob.cpp @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "qpid/framing/Blob.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(BlobTestSuite) + +using namespace std; +using namespace qpid::framing; + +struct Base { + int id; + int magic; + + Base(int n) : id(n), magic(42) {} + Base(const Base& c) : id(c.id), magic(42) {} + ~Base() { BOOST_CHECK_EQUAL(42, magic); } // Detect random data. +}; + +template <class T> struct Count : public Base { + static int instances; + bool destroyed; + + Count(int n) : Base(n), destroyed(false) { ++instances; } + Count(const Count& c) : Base(c), destroyed(false) { ++instances; } + ~Count() { + BOOST_CHECK(!destroyed); // Detect double-destructor + destroyed=true; + BOOST_CHECK(--instances >= 0); + } +}; + +template <class T> int Count<T>::instances = 0; + +struct Foo : public Count<Foo> { Foo(int n) : Count<Foo>(n) {}; }; +struct Bar : public Count<Bar> { Bar(int n) : Count<Bar>(n) {}; }; + +typedef Blob<sizeof(Foo), Base> TestBlob ; + +QPID_AUTO_TEST_CASE(testBlobCtor) { + { + TestBlob empty; + BOOST_CHECK(empty.empty()); + BOOST_CHECK(empty.get() == 0); + + TestBlob empty2(empty); + BOOST_CHECK(empty2.empty()); + + TestBlob foo(in_place<Foo>(1)); + BOOST_CHECK(!foo.empty()); + BOOST_CHECK_EQUAL(1, foo.get()->id); + BOOST_CHECK_EQUAL(1, Foo::instances); + + TestBlob foo2(foo); + BOOST_CHECK(!foo2.empty()); + BOOST_CHECK_EQUAL(1, foo2.get()->id); + BOOST_CHECK_EQUAL(2, Foo::instances); + } + + BOOST_CHECK_EQUAL(0, Foo::instances); + BOOST_CHECK_EQUAL(0, Bar::instances); +} + + +QPID_AUTO_TEST_CASE(testAssign) { + { + TestBlob b; + b = Foo(2); + BOOST_CHECK_EQUAL(2, b.get()->id); + BOOST_CHECK_EQUAL(1, Foo::instances); + + TestBlob b2(b); + BOOST_CHECK_EQUAL(2, b.get()->id); + BOOST_CHECK_EQUAL(2, Foo::instances); + + b2 = Bar(3); + BOOST_CHECK_EQUAL(3, b2.get()->id); + BOOST_CHECK_EQUAL(1, Foo::instances); + BOOST_CHECK_EQUAL(1, Bar::instances); + + b2 = in_place<Foo>(4); + BOOST_CHECK_EQUAL(4, b2.get()->id); + BOOST_CHECK_EQUAL(2, Foo::instances); + BOOST_CHECK_EQUAL(0, Bar::instances); + + b2.clear(); + BOOST_CHECK(b2.empty()); + BOOST_CHECK_EQUAL(1, Foo::instances); + } + BOOST_CHECK_EQUAL(0, Foo::instances); + BOOST_CHECK_EQUAL(0, Bar::instances); +} + + +QPID_AUTO_TEST_CASE(testClear) { + TestBlob b(in_place<Foo>(5)); + TestBlob c(b); + BOOST_CHECK(!c.empty()); + BOOST_CHECK(!b.empty()); + BOOST_CHECK_EQUAL(2, Foo::instances); + + c.clear(); + BOOST_CHECK(c.empty()); + BOOST_CHECK_EQUAL(1, Foo::instances); + + b.clear(); + BOOST_CHECK(b.empty()); + BOOST_CHECK_EQUAL(0, Foo::instances); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/BrokerFixture.h b/RC9/qpid/cpp/src/tests/BrokerFixture.h new file mode 100644 index 0000000000..2a4faa2fd4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/BrokerFixture.h @@ -0,0 +1,130 @@ +#ifndef TESTS_BROKERFIXTURE_H +#define TESTS_BROKERFIXTURE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SocketProxy.h" + +#include "qpid/broker/Broker.h" +#include "qpid/client/Connection.h" +#include "qpid/client/ConnectionImpl.h" +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/log/Logger.h" +#include "qpid/log/Options.h" +#include "qpid/sys/Thread.h" +#include <boost/noncopyable.hpp> + +/** + * A fixture with an in-process broker. + */ +struct BrokerFixture : private boost::noncopyable { + typedef qpid::broker::Broker Broker; + typedef boost::intrusive_ptr<Broker> BrokerPtr; + + BrokerPtr broker; + qpid::sys::Thread brokerThread; + + BrokerFixture(Broker::Options opts=Broker::Options()) { + // Keep the tests quiet unless logging env. vars have been set by user. + if (!::getenv("QPID_LOG_ENABLE") && !::getenv("QPID_TRACE")) { + qpid::log::Options logOpts; + logOpts.selectors.clear(); + logOpts.selectors.push_back("error+"); + qpid::log::Logger::instance().configure(logOpts); + } + opts.port=0; + // Management doesn't play well with multiple in-process brokers. + opts.enableMgmt=false; + opts.workerThreads=1; + opts.dataDir=""; + opts.auth=false; + broker = Broker::create(opts); + // TODO aconway 2007-12-05: At one point BrokerFixture + // tests could hang in Connection ctor if the following + // line is removed. This may not be an issue anymore. + broker->getPort(qpid::broker::Broker::TCP_TRANSPORT); + brokerThread = qpid::sys::Thread(*broker); + }; + + ~BrokerFixture() { + broker->shutdown(); + brokerThread.join(); + } + + /** Open a connection to the broker. */ + void open(qpid::client::Connection& c) { + c.open("localhost", broker->getPort(qpid::broker::Broker::TCP_TRANSPORT)); + } + + uint16_t getPort() { return broker->getPort(qpid::broker::Broker::TCP_TRANSPORT); } +}; + +/** Connection that opens in its constructor */ +struct LocalConnection : public qpid::client::Connection { + LocalConnection(uint16_t port) { open("localhost", port); } +}; + +/** A local client connection via a socket proxy. */ +struct ProxyConnection : public qpid::client::Connection { + SocketProxy proxy; + ProxyConnection(int brokerPort) : proxy(brokerPort) { + open("localhost", proxy.getPort()); + } + ~ProxyConnection() { close(); } +}; + +/** Convenience class to create and open a connection and session + * and some related useful objects. + */ +template <class ConnectionType=LocalConnection, class SessionType=qpid::client::Session> +struct ClientT { + ConnectionType connection; + SessionType session; + qpid::client::SubscriptionManager subs; + qpid::client::LocalQueue lq; + ClientT(uint16_t port, const std::string& name=std::string()) + : connection(port), session(connection.newSession(name)), subs(session) {} + + ~ClientT() { connection.close(); } +}; + +typedef ClientT<> Client; + +/** + * A BrokerFixture and ready-connected BrokerFixture::Client all in one. + */ +template <class ConnectionType, class SessionType=qpid::client::Session> +struct SessionFixtureT : BrokerFixture, ClientT<ConnectionType,SessionType> { + + SessionFixtureT(Broker::Options opts=Broker::Options()) : + BrokerFixture(opts), + ClientT<ConnectionType,SessionType>(broker->getPort(qpid::broker::Broker::TCP_TRANSPORT)) + {} + +}; + +typedef SessionFixtureT<LocalConnection> SessionFixture; +typedef SessionFixtureT<ProxyConnection> ProxySessionFixture; + + +#endif /*!TESTS_BROKERFIXTURE_H*/ diff --git a/RC9/qpid/cpp/src/tests/ClientSessionTest.cpp b/RC9/qpid/cpp/src/tests/ClientSessionTest.cpp new file mode 100644 index 0000000000..5d047dcd0e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ClientSessionTest.cpp @@ -0,0 +1,452 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "unit_test.h" +#include "test_tools.h" +#include "BrokerFixture.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/sys/Monitor.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Runnable.h" +#include "qpid/client/Session.h" +#include "qpid/framing/TransferContent.h" +#include "qpid/framing/reply_exceptions.h" + +#include <boost/optional.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/bind.hpp> +#include <boost/ptr_container/ptr_vector.hpp> + +#include <vector> + +QPID_AUTO_TEST_SUITE(ClientSessionTest) + +using namespace qpid::client; +using namespace qpid::framing; +using namespace qpid; +using qpid::sys::Monitor; +using qpid::sys::Thread; +using qpid::sys::TIME_SEC; +using qpid::broker::Broker; +using std::string; +using std::cout; +using std::endl; + + +struct DummyListener : public sys::Runnable, public MessageListener { + std::vector<Message> messages; + string name; + uint expected; + SubscriptionManager submgr; + + DummyListener(Session& session, const string& n, uint ex) : + name(n), expected(ex), submgr(session) {} + + void run() + { + submgr.subscribe(*this, name); + submgr.run(); + } + + void received(Message& msg) + { + messages.push_back(msg); + if (--expected == 0) { + submgr.stop(); + } + } +}; + +struct SimpleListener : public MessageListener +{ + Monitor lock; + std::vector<Message> messages; + + void received(Message& msg) + { + Monitor::ScopedLock l(lock); + messages.push_back(msg); + lock.notifyAll(); + } + + void waitFor(const uint n) + { + Monitor::ScopedLock l(lock); + while (messages.size() < n) { + lock.wait(); + } + } +}; + +struct ClientSessionFixture : public ProxySessionFixture +{ + ClientSessionFixture(Broker::Options opts = Broker::Options()) : ProxySessionFixture(opts) { + session.queueDeclare(arg::queue="my-queue"); + } +}; + +QPID_AUTO_TEST_CASE(testQueueQuery) { + ClientSessionFixture fix; + fix.session = fix.connection.newSession(); + fix.session.queueDeclare(arg::queue="q", arg::alternateExchange="amq.fanout", + arg::exclusive=true, arg::autoDelete=true); + QueueQueryResult result = fix.session.queueQuery("q"); + BOOST_CHECK_EQUAL(false, result.getDurable()); + BOOST_CHECK_EQUAL(true, result.getExclusive()); + BOOST_CHECK_EQUAL("amq.fanout", result.getAlternateExchange()); +} + +QPID_AUTO_TEST_CASE(testDispatcher) +{ + ClientSessionFixture fix; + fix.session =fix.connection.newSession(); + size_t count = 100; + for (size_t i = 0; i < count; ++i) + fix.session.messageTransfer(arg::content=TransferContent(boost::lexical_cast<string>(i), "my-queue")); + DummyListener listener(fix.session, "my-queue", count); + listener.run(); + BOOST_CHECK_EQUAL(count, listener.messages.size()); + for (size_t i = 0; i < count; ++i) + BOOST_CHECK_EQUAL(boost::lexical_cast<string>(i), listener.messages[i].getData()); +} + +QPID_AUTO_TEST_CASE(testDispatcherThread) +{ + ClientSessionFixture fix; + fix.session =fix.connection.newSession(); + size_t count = 10; + DummyListener listener(fix.session, "my-queue", count); + sys::Thread t(listener); + for (size_t i = 0; i < count; ++i) { + fix.session.messageTransfer(arg::content=TransferContent(boost::lexical_cast<string>(i), "my-queue")); + } + t.join(); + BOOST_CHECK_EQUAL(count, listener.messages.size()); + for (size_t i = 0; i < count; ++i) + BOOST_CHECK_EQUAL(boost::lexical_cast<string>(i), listener.messages[i].getData()); +} + +QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(testSuspend0Timeout, 1) +{ + ClientSessionFixture fix; + fix.session.suspend(); // session has 0 timeout. + try { + fix.connection.resume(fix.session); + BOOST_FAIL("Expected InvalidArgumentException."); + } catch(const InternalErrorException&) {} +} + +QPID_AUTO_TEST_CASE(testUseSuspendedError) +{ + ClientSessionFixture fix; + fix.session.timeout(60); + fix.session.suspend(); + try { + fix.session.exchangeQuery(arg::exchange="amq.fanout"); + BOOST_FAIL("Expected session suspended exception"); + } catch(const NotAttachedException&) {} +} + +QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(testSuspendResume, 1) +{ + ClientSessionFixture fix; + fix.session.timeout(60); + fix.session.suspend(); + // Make sure we are still subscribed after resume. + fix.connection.resume(fix.session); + fix.session.messageTransfer(arg::content=TransferContent("my-message", "my-queue")); + FrameSet::shared_ptr msg = fix.session.get(); + BOOST_CHECK_EQUAL(string("my-message"), msg->getContent()); +} + + +QPID_AUTO_TEST_CASE(testSendToSelf) { + ClientSessionFixture fix; + SimpleListener mylistener; + fix.session.queueDeclare(arg::queue="myq", arg::exclusive=true, arg::autoDelete=true); + fix.subs.subscribe(mylistener, "myq"); + sys::Thread runner(fix.subs);//start dispatcher thread + string data("msg"); + Message msg(data, "myq"); + const uint count=10; + for (uint i = 0; i < count; ++i) { + fix.session.messageTransfer(arg::content=msg); + } + mylistener.waitFor(count); + fix.subs.cancel("myq"); + fix.subs.stop(); + runner.join(); + fix.session.close(); + BOOST_CHECK_EQUAL(mylistener.messages.size(), count); + for (uint j = 0; j < count; ++j) { + BOOST_CHECK_EQUAL(mylistener.messages[j].getData(), data); + } +} + +QPID_AUTO_TEST_CASE(testLocalQueue) { + ClientSessionFixture fix; + fix.session.queueDeclare(arg::queue="lq", arg::exclusive=true, arg::autoDelete=true); + LocalQueue lq; + fix.subs.subscribe(lq, "lq", FlowControl(2, FlowControl::UNLIMITED, false)); + fix.session.messageTransfer(arg::content=Message("foo0", "lq")); + fix.session.messageTransfer(arg::content=Message("foo1", "lq")); + fix.session.messageTransfer(arg::content=Message("foo2", "lq")); + BOOST_CHECK_EQUAL("foo0", lq.pop().getData()); + BOOST_CHECK_EQUAL("foo1", lq.pop().getData()); + BOOST_CHECK(lq.empty()); // Credit exhausted. + fix.subs.getSubscription("lq").setFlowControl(FlowControl::unlimited()); + BOOST_CHECK_EQUAL("foo2", lq.pop().getData()); +} + +struct DelayedTransfer : sys::Runnable +{ + ClientSessionFixture& fixture; + + DelayedTransfer(ClientSessionFixture& f) : fixture(f) {} + + void run() + { + sleep(1); + fixture.session.messageTransfer(arg::content=Message("foo2", "getq")); + } +}; + +QPID_AUTO_TEST_CASE(testGet) { + ClientSessionFixture fix; + fix.session.queueDeclare(arg::queue="getq", arg::exclusive=true, arg::autoDelete=true); + fix.session.messageTransfer(arg::content=Message("foo0", "getq")); + fix.session.messageTransfer(arg::content=Message("foo1", "getq")); + Message got; + BOOST_CHECK(fix.subs.get(got, "getq", TIME_SEC)); + BOOST_CHECK_EQUAL("foo0", got.getData()); + BOOST_CHECK(fix.subs.get(got, "getq", TIME_SEC)); + BOOST_CHECK_EQUAL("foo1", got.getData()); + BOOST_CHECK(!fix.subs.get(got, "getq")); + DelayedTransfer sender(fix); + Thread t(sender); + //test timed get where message shows up after a short delay + BOOST_CHECK(fix.subs.get(got, "getq", 5*TIME_SEC)); + BOOST_CHECK_EQUAL("foo2", got.getData()); + t.join(); +} + +QPID_AUTO_TEST_CASE(testOpenFailure) { + BrokerFixture b; + Connection c; + string host("unknowable-host"); + try { + c.open(host); + } catch (const Exception&) { + BOOST_CHECK(!c.isOpen()); + } + b.open(c); + BOOST_CHECK(c.isOpen()); + c.close(); + BOOST_CHECK(!c.isOpen()); +} + +QPID_AUTO_TEST_CASE(testPeriodicExpiration) { + Broker::Options opts; + opts.queueCleanInterval = 1; + ClientSessionFixture fix(opts); + fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true); + + for (uint i = 0; i < 10; i++) { + Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + if (i % 2) m.getDeliveryProperties().setTtl(500); + fix.session.messageTransfer(arg::content=m); + } + + BOOST_CHECK_EQUAL(fix.session.queueQuery(string("my-queue")).getMessageCount(), 10u); + sleep(2); + BOOST_CHECK_EQUAL(fix.session.queueQuery(string("my-queue")).getMessageCount(), 5u); +} + +QPID_AUTO_TEST_CASE(testExpirationOnPop) { + ClientSessionFixture fix; + fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true); + + for (uint i = 0; i < 10; i++) { + Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + if (i % 2) m.getDeliveryProperties().setTtl(200); + fix.session.messageTransfer(arg::content=m); + } + + ::usleep(300* 1000); + + for (uint i = 0; i < 10; i++) { + if (i % 2) continue; + Message m; + BOOST_CHECK(fix.subs.get(m, "my-queue", TIME_SEC)); + BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData()); + } +} + +QPID_AUTO_TEST_CASE(testRelease) { + ClientSessionFixture fix; + + const uint count=10; + for (uint i = 0; i < count; i++) { + Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + fix.session.messageTransfer(arg::content=m); + } + + fix.subs.setAutoStop(false); + fix.subs.start(); + SubscriptionSettings settings; + settings.autoAck = 0; + + SimpleListener l1; + Subscription s1 = fix.subs.subscribe(l1, "my-queue", settings); + l1.waitFor(count); + s1.cancel(); + + for (uint i = 0; i < count; i++) { + BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), l1.messages[i].getData()); + } + s1.release(s1.getUnaccepted()); + + //check that released messages are redelivered + settings.autoAck = 1; + SimpleListener l2; + Subscription s2 = fix.subs.subscribe(l2, "my-queue", settings); + l2.waitFor(count); + for (uint i = 0; i < count; i++) { + BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), l2.messages[i].getData()); + } + + fix.subs.stop(); + fix.subs.wait(); + fix.session.close(); +} + +QPID_AUTO_TEST_CASE(testCompleteOnAccept) { + ClientSessionFixture fix; + const uint count = 8; + const uint chunk = 4; + for (uint i = 0; i < count; i++) { + Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + fix.session.messageTransfer(arg::content=m); + } + + SubscriptionSettings settings; + settings.autoAck = 0; + settings.completionMode = COMPLETE_ON_ACCEPT; + settings.flowControl = FlowControl::messageWindow(chunk); + + LocalQueue q; + Subscription s = fix.subs.subscribe(q, "my-queue", settings); + fix.session.messageFlush(arg::destination=s.getName()); + SequenceSet accepted; + for (uint i = 0; i < chunk; i++) { + Message m; + BOOST_CHECK(q.get(m)); + BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData()); + accepted.add(m.getId()); + } + Message m; + BOOST_CHECK(!q.get(m)); + + s.accept(accepted); + fix.session.messageFlush(arg::destination=s.getName()); + accepted.clear(); + + for (uint i = chunk; i < count; i++) { + Message m; + BOOST_CHECK(q.get(m)); + BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData()); + accepted.add(m.getId()); + } + fix.session.messageAccept(accepted); +} + +namespace +{ +struct Publisher : qpid::sys::Runnable +{ + AsyncSession session; + Message message; + uint count; + Thread thread; + + Publisher(Connection& con, Message m, uint c) : session(con.newSession()), message(m), count(c) {} + + void start() + { + thread = Thread(*this); + } + + void join() + { + thread.join(); + } + + void run() + { + for (uint i = 0; i < count; i++) { + session.messageTransfer(arg::content=message); + } + session.sync(); + session.close(); + } +}; +} + +QPID_AUTO_TEST_CASE(testConcurrentSenders) +{ + //Ensure concurrent publishing sessions on a connection don't + //cause assertions, deadlocks or other undesirables: + BrokerFixture fix; + Connection connection; + ConnectionSettings settings; + settings.maxFrameSize = 1024; + settings.port = fix.broker->getPort(qpid::broker::Broker::TCP_TRANSPORT); + connection.open(settings); + AsyncSession session = connection.newSession(); + Message message(string(512, 'X')); + + boost::ptr_vector<Publisher> publishers; + for (size_t i = 0; i < 5; i++) { + publishers.push_back(new Publisher(connection, message, 100)); + } + for_each(publishers.begin(), publishers.end(), boost::bind(&Publisher::start, _1)); + for_each(publishers.begin(), publishers.end(), boost::bind(&Publisher::join, _1)); + connection.close(); +} + + +QPID_AUTO_TEST_CASE(testExclusiveSubscribe) +{ + ClientSessionFixture fix; + fix.session.queueDeclare(arg::queue="myq", arg::exclusive=true, arg::autoDelete=true); + SubscriptionSettings settings; + settings.exclusive = true; + LocalQueue q; + fix.subs.subscribe(q, "myq", settings, "first"); + //attempt to create new subscriber should fail + ScopedSuppressLogging sl; + BOOST_CHECK_THROW(fix.subs.subscribe(q, "myq", "second"), ResourceLockedException); + ; + +} + +QPID_AUTO_TEST_SUITE_END() + + diff --git a/RC9/qpid/cpp/src/tests/ConnectionOptions.h b/RC9/qpid/cpp/src/tests/ConnectionOptions.h new file mode 100644 index 0000000000..0130842668 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ConnectionOptions.h @@ -0,0 +1,54 @@ +#ifndef QPID_CLIENT_CONNECTIONOPTIONS_H +#define QPID_CLIENT_CONNECTIONOPTIONS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/client/ConnectionSettings.h" +#include "qpid/Options.h" + +/** + * Options parser for ConnectionOptions. + */ +struct ConnectionOptions : public qpid::Options, + public qpid::client::ConnectionSettings +{ + ConnectionOptions() : qpid::Options("Connection Settings") + { + using namespace qpid; + addOptions() + ("broker,b", optValue(host, "HOST"), "Broker host to connect to") + ("port,p", optValue(port, "PORT"), "Broker port to connect to") + ("protocol,P", optValue(protocol, "tcp|rdma"), "Protocol to use for broker connection") + ("virtualhost,v", optValue(virtualhost, "VHOST"), "virtual host") + ("username", optValue(username, "USER"), "user name for broker log in.") + ("password", optValue(password, "PASSWORD"), "password for broker log in.") + ("mechanism", optValue(mechanism, "MECH"), "SASL mechanism to use when authenticating.") + ("locale", optValue(locale, "LOCALE"), "locale to use.") + ("max-channels", optValue(maxChannels, "N"), "the maximum number of channels the client requires.") + ("max-frame-size", optValue(maxFrameSize, "N"), "the maximum frame size to request.") + ("bounds-multiplier", optValue(bounds, "N"), + "bound size of write queue (as a multiple of the max frame size).") + ("tcp-nodelay", optValue(tcpNoDelay), "Turn on tcp-nodelay"); + } +}; + +#endif /*!QPID_CLIENT_CONNECTIONOPTIONS_H*/ diff --git a/RC9/qpid/cpp/src/tests/ConsoleTest.cpp b/RC9/qpid/cpp/src/tests/ConsoleTest.cpp new file mode 100644 index 0000000000..1d55b13f3c --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ConsoleTest.cpp @@ -0,0 +1,43 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/console/Package.h" +#include "qpid/console/ClassKey.h" +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(ConsoleTestSuite) + +using namespace qpid::framing; +using namespace qpid::console; + +QPID_AUTO_TEST_CASE(testClassKey) { + uint8_t hash[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + ClassKey k("com.redhat.test", "class", hash); + + BOOST_CHECK_EQUAL(k.getPackageName(), "com.redhat.test"); + BOOST_CHECK_EQUAL(k.getClassName(), "class"); + BOOST_CHECK_EQUAL(k.getHashString(), "00010203-04050607-08090a0b-0c0d0e0f"); + BOOST_CHECK_EQUAL(k.str(), "com.redhat.test:class(00010203-04050607-08090a0b-0c0d0e0f)"); +} + +QPID_AUTO_TEST_SUITE_END() + + diff --git a/RC9/qpid/cpp/src/tests/DeliveryRecordTest.cpp b/RC9/qpid/cpp/src/tests/DeliveryRecordTest.cpp new file mode 100644 index 0000000000..47c7157749 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/DeliveryRecordTest.cpp @@ -0,0 +1,62 @@ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/DeliveryRecord.h" +#include "unit_test.h" +#include <iostream> +#include <memory> +#include <boost/format.hpp> + +using namespace qpid::broker; +using namespace qpid::sys; +using namespace qpid::framing; +using boost::dynamic_pointer_cast; +using std::list; + +QPID_AUTO_TEST_SUITE(DeliveryRecordTestSuite) + +QPID_AUTO_TEST_CASE(testSort) +{ + list<SequenceNumber> ids; + ids.push_back(SequenceNumber(6)); + ids.push_back(SequenceNumber(2)); + ids.push_back(SequenceNumber(4)); + ids.push_back(SequenceNumber(5)); + ids.push_back(SequenceNumber(1)); + ids.push_back(SequenceNumber(3)); + + list<DeliveryRecord> records; + for (list<SequenceNumber>::iterator i = ids.begin(); i != ids.end(); i++) { + DeliveryRecord r(QueuedMessage(0), Queue::shared_ptr(), "tag", false, false, false); + r.setId(*i); + records.push_back(r); + } + records.sort(); + + SequenceNumber expected(0); + for (list<DeliveryRecord>::iterator i = records.begin(); i != records.end(); i++) { + BOOST_CHECK(i->matches(++expected)); + } +} + + +QPID_AUTO_TEST_SUITE_END() + diff --git a/RC9/qpid/cpp/src/tests/DispatcherTest.cpp b/RC9/qpid/cpp/src/tests/DispatcherTest.cpp new file mode 100644 index 0000000000..7631956acc --- /dev/null +++ b/RC9/qpid/cpp/src/tests/DispatcherTest.cpp @@ -0,0 +1,128 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Poller.h" +#include "qpid/sys/Dispatcher.h" +#include "qpid/sys/Thread.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +#include <iostream> +#include <boost/bind.hpp> + +using namespace std; +using namespace qpid::sys; + +int writeALot(int fd, const string& s) { + int bytesWritten = 0; + do { + errno = 0; + int lastWrite = ::write(fd, s.c_str(), s.size()); + if ( lastWrite >= 0) { + bytesWritten += lastWrite; + } + } while (errno != EAGAIN); + return bytesWritten; +} + +int readALot(int fd) { + int bytesRead = 0; + char buf[10240]; + + do { + errno = 0; + int lastRead = ::read(fd, buf, sizeof(buf)); + if ( lastRead >= 0) { + bytesRead += lastRead; + } + } while (errno != EAGAIN); + return bytesRead; +} + +int64_t writtenBytes = 0; +int64_t readBytes = 0; + +void writer(DispatchHandle& h, int fd, const string& s) { + writtenBytes += writeALot(fd, s); + h.rewatch(); +} + +void reader(DispatchHandle& h, int fd) { + readBytes += readALot(fd); + h.rewatch(); +} + +int main(int argc, char** argv) +{ + // Create poller + Poller::shared_ptr poller(new Poller); + + // Create dispatcher thread + Dispatcher d(poller); + Dispatcher d1(poller); + //Dispatcher d2(poller); + //Dispatcher d3(poller); + Thread dt(d); + Thread dt1(d1); + //Thread dt2(d2); + //Thread dt3(d3); + + // Setup sender and receiver + int sv[2]; + int rc = ::socketpair(AF_LOCAL, SOCK_STREAM, 0, sv); + assert(rc >= 0); + + // Set non-blocking + rc = ::fcntl(sv[0], F_SETFL, O_NONBLOCK); + assert(rc >= 0); + + rc = ::fcntl(sv[1], F_SETFL, O_NONBLOCK); + assert(rc >= 0); + + // Make up a large string + string testString = "This is only a test ... 1,2,3,4,5,6,7,8,9,10;"; + for (int i = 0; i < 8; i++) + testString += testString; + + DispatchHandle rh(sv[0], boost::bind(reader, _1, sv[0]), 0); + DispatchHandle wh(sv[1], 0, boost::bind(writer, _1, sv[1], testString)); + + rh.watch(poller); + wh.watch(poller); + + // wait 2 minutes then shutdown + sleep(60); + + poller->shutdown(); + dt.join(); + dt1.join(); + //dt2.join(); + //dt3.join(); + + cout << "Wrote: " << writtenBytes << "\n"; + cout << "Read: " << readBytes << "\n"; + + return 0; +} diff --git a/RC9/qpid/cpp/src/tests/DtxWorkRecordTest.cpp b/RC9/qpid/cpp/src/tests/DtxWorkRecordTest.cpp new file mode 100644 index 0000000000..c7c1b460ff --- /dev/null +++ b/RC9/qpid/cpp/src/tests/DtxWorkRecordTest.cpp @@ -0,0 +1,189 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/DtxWorkRecord.h" +#include "unit_test.h" +#include <iostream> +#include <vector> +#include "TxMocks.h" + +using namespace qpid::broker; +using boost::static_pointer_cast; + +QPID_AUTO_TEST_SUITE(DtxWorkRecordTestSuite) + +QPID_AUTO_TEST_CASE(testOnePhaseCommit){ + MockTransactionalStore store; + store.expectBegin().expectCommit(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectCommit(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectPrepare().expectCommit(); + + DtxBuffer::shared_ptr bufferA(new DtxBuffer()); + bufferA->enlist(static_pointer_cast<TxOp>(opA)); + bufferA->markEnded(); + DtxBuffer::shared_ptr bufferB(new DtxBuffer()); + bufferB->enlist(static_pointer_cast<TxOp>(opB)); + bufferB->markEnded(); + + DtxWorkRecord work("my-xid", &store); + work.add(bufferA); + work.add(bufferB); + + work.commit(true); + + store.check(); + BOOST_CHECK(store.isCommitted()); + opA->check(); + opB->check(); +} + +QPID_AUTO_TEST_CASE(testFailOnOnePhaseCommit){ + MockTransactionalStore store; + store.expectBegin().expectAbort(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp(true)); + opB->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opC(new MockTxOp()); + opC->expectRollback(); + + DtxBuffer::shared_ptr bufferA(new DtxBuffer()); + bufferA->enlist(static_pointer_cast<TxOp>(opA)); + bufferA->markEnded(); + DtxBuffer::shared_ptr bufferB(new DtxBuffer()); + bufferB->enlist(static_pointer_cast<TxOp>(opB)); + bufferB->markEnded(); + DtxBuffer::shared_ptr bufferC(new DtxBuffer()); + bufferC->enlist(static_pointer_cast<TxOp>(opC)); + bufferC->markEnded(); + + DtxWorkRecord work("my-xid", &store); + work.add(bufferA); + work.add(bufferB); + work.add(bufferC); + + work.commit(true); + + BOOST_CHECK(store.isAborted()); + store.check(); + + opA->check(); + opB->check(); + opC->check(); +} + +QPID_AUTO_TEST_CASE(testTwoPhaseCommit){ + MockTransactionalStore store; + store.expectBegin2PC().expectPrepare().expectCommit(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectCommit(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectPrepare().expectCommit(); + + DtxBuffer::shared_ptr bufferA(new DtxBuffer()); + bufferA->enlist(static_pointer_cast<TxOp>(opA)); + bufferA->markEnded(); + DtxBuffer::shared_ptr bufferB(new DtxBuffer()); + bufferB->enlist(static_pointer_cast<TxOp>(opB)); + bufferB->markEnded(); + + DtxWorkRecord work("my-xid", &store); + work.add(bufferA); + work.add(bufferB); + + BOOST_CHECK(work.prepare()); + BOOST_CHECK(store.isPrepared()); + work.commit(false); + store.check(); + BOOST_CHECK(store.isCommitted()); + opA->check(); + opB->check(); +} + +QPID_AUTO_TEST_CASE(testFailOnTwoPhaseCommit){ + MockTransactionalStore store; + store.expectBegin2PC().expectAbort(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp(true)); + opB->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opC(new MockTxOp()); + opC->expectRollback(); + + DtxBuffer::shared_ptr bufferA(new DtxBuffer()); + bufferA->enlist(static_pointer_cast<TxOp>(opA)); + bufferA->markEnded(); + DtxBuffer::shared_ptr bufferB(new DtxBuffer()); + bufferB->enlist(static_pointer_cast<TxOp>(opB)); + bufferB->markEnded(); + DtxBuffer::shared_ptr bufferC(new DtxBuffer()); + bufferC->enlist(static_pointer_cast<TxOp>(opC)); + bufferC->markEnded(); + + DtxWorkRecord work("my-xid", &store); + work.add(bufferA); + work.add(bufferB); + work.add(bufferC); + + BOOST_CHECK(!work.prepare()); + BOOST_CHECK(store.isAborted()); + store.check(); + opA->check(); + opB->check(); + opC->check(); +} + +QPID_AUTO_TEST_CASE(testRollback){ + MockTransactionalStore store; + store.expectBegin2PC().expectPrepare().expectAbort(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectPrepare().expectRollback(); + + DtxBuffer::shared_ptr bufferA(new DtxBuffer()); + bufferA->enlist(static_pointer_cast<TxOp>(opA)); + bufferA->markEnded(); + DtxBuffer::shared_ptr bufferB(new DtxBuffer()); + bufferB->enlist(static_pointer_cast<TxOp>(opB)); + bufferB->markEnded(); + + DtxWorkRecord work("my-xid", &store); + work.add(bufferA); + work.add(bufferB); + + BOOST_CHECK(work.prepare()); + BOOST_CHECK(store.isPrepared()); + work.rollback(); + store.check(); + BOOST_CHECK(store.isAborted()); + opA->check(); + opB->check(); +} + +QPID_AUTO_TEST_SUITE_END() + diff --git a/RC9/qpid/cpp/src/tests/ExchangeTest.cpp b/RC9/qpid/cpp/src/tests/ExchangeTest.cpp new file mode 100644 index 0000000000..0946d3115d --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ExchangeTest.cpp @@ -0,0 +1,284 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Exception.h" +#include "qpid/broker/Exchange.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/DeliverableMessage.h" +#include "qpid/broker/DirectExchange.h" +#include "qpid/broker/ExchangeRegistry.h" +#include "qpid/broker/FanOutExchange.h" +#include "qpid/broker/HeadersExchange.h" +#include "qpid/broker/TopicExchange.h" +#include "qpid/framing/reply_exceptions.h" +#include "unit_test.h" +#include <iostream> +#include "MessageUtils.h" + +using boost::intrusive_ptr; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; +using namespace qpid; + +QPID_AUTO_TEST_SUITE(ExchangeTestSuite) + +QPID_AUTO_TEST_CASE(testMe) +{ + Queue::shared_ptr queue(new Queue("queue", true)); + Queue::shared_ptr queue2(new Queue("queue2", true)); + + TopicExchange topic("topic"); + topic.bind(queue, "abc", 0); + topic.bind(queue2, "abc", 0); + + DirectExchange direct("direct"); + direct.bind(queue, "abc", 0); + direct.bind(queue2, "abc", 0); + + queue.reset(); + queue2.reset(); + + intrusive_ptr<Message> msgPtr(MessageUtils::createMessage("exchange", "key", "id")); + DeliverableMessage msg(msgPtr); + topic.route(msg, "abc", 0); + direct.route(msg, "abc", 0); + +} + +QPID_AUTO_TEST_CASE(testIsBound) +{ + Queue::shared_ptr a(new Queue("a", true)); + Queue::shared_ptr b(new Queue("b", true)); + Queue::shared_ptr c(new Queue("c", true)); + Queue::shared_ptr d(new Queue("d", true)); + + string k1("abc"); + string k2("def"); + string k3("xyz"); + + FanOutExchange fanout("fanout"); + BOOST_CHECK(fanout.bind(a, "", 0)); + BOOST_CHECK(fanout.bind(b, "", 0)); + BOOST_CHECK(fanout.bind(c, "", 0)); + + BOOST_CHECK(fanout.isBound(a, 0, 0)); + BOOST_CHECK(fanout.isBound(b, 0, 0)); + BOOST_CHECK(fanout.isBound(c, 0, 0)); + BOOST_CHECK(!fanout.isBound(d, 0, 0)); + + DirectExchange direct("direct"); + BOOST_CHECK(direct.bind(a, k1, 0)); + BOOST_CHECK(direct.bind(a, k3, 0)); + BOOST_CHECK(direct.bind(b, k2, 0)); + BOOST_CHECK(direct.bind(c, k1, 0)); + + BOOST_CHECK(direct.isBound(a, 0, 0)); + BOOST_CHECK(direct.isBound(a, &k1, 0)); + BOOST_CHECK(direct.isBound(a, &k3, 0)); + BOOST_CHECK(!direct.isBound(a, &k2, 0)); + BOOST_CHECK(direct.isBound(b, 0, 0)); + BOOST_CHECK(direct.isBound(b, &k2, 0)); + BOOST_CHECK(direct.isBound(c, &k1, 0)); + BOOST_CHECK(!direct.isBound(d, 0, 0)); + BOOST_CHECK(!direct.isBound(d, &k1, 0)); + BOOST_CHECK(!direct.isBound(d, &k2, 0)); + BOOST_CHECK(!direct.isBound(d, &k3, 0)); + + TopicExchange topic("topic"); + BOOST_CHECK(topic.bind(a, k1, 0)); + BOOST_CHECK(topic.bind(a, k3, 0)); + BOOST_CHECK(topic.bind(b, k2, 0)); + BOOST_CHECK(topic.bind(c, k1, 0)); + + BOOST_CHECK(topic.isBound(a, 0, 0)); + BOOST_CHECK(topic.isBound(a, &k1, 0)); + BOOST_CHECK(topic.isBound(a, &k3, 0)); + BOOST_CHECK(!topic.isBound(a, &k2, 0)); + BOOST_CHECK(topic.isBound(b, 0, 0)); + BOOST_CHECK(topic.isBound(b, &k2, 0)); + BOOST_CHECK(topic.isBound(c, &k1, 0)); + BOOST_CHECK(!topic.isBound(d, 0, 0)); + BOOST_CHECK(!topic.isBound(d, &k1, 0)); + BOOST_CHECK(!topic.isBound(d, &k2, 0)); + BOOST_CHECK(!topic.isBound(d, &k3, 0)); + + HeadersExchange headers("headers"); + FieldTable args1; + args1.setString("x-match", "all"); + args1.setString("a", "A"); + args1.setInt("b", 1); + FieldTable args2; + args2.setString("x-match", "any"); + args2.setString("a", "A"); + args2.setInt("b", 1); + FieldTable args3; + args3.setString("x-match", "any"); + args3.setString("c", "C"); + args3.setInt("b", 6); + + headers.bind(a, "", &args1); + headers.bind(a, "", &args3); + headers.bind(b, "", &args2); + headers.bind(c, "", &args1); + + BOOST_CHECK(headers.isBound(a, 0, 0)); + BOOST_CHECK(headers.isBound(a, 0, &args1)); + BOOST_CHECK(headers.isBound(a, 0, &args3)); + BOOST_CHECK(!headers.isBound(a, 0, &args2)); + BOOST_CHECK(headers.isBound(b, 0, 0)); + BOOST_CHECK(headers.isBound(b, 0, &args2)); + BOOST_CHECK(headers.isBound(c, 0, &args1)); + BOOST_CHECK(!headers.isBound(d, 0, 0)); + BOOST_CHECK(!headers.isBound(d, 0, &args1)); + BOOST_CHECK(!headers.isBound(d, 0, &args2)); + BOOST_CHECK(!headers.isBound(d, 0, &args3)); +} + +QPID_AUTO_TEST_CASE(testDeleteGetAndRedeclare) +{ + ExchangeRegistry exchanges; + exchanges.declare("my-exchange", "direct", false, FieldTable()); + exchanges.destroy("my-exchange"); + try { + exchanges.get("my-exchange"); + } catch (const NotFoundException&) {} + std::pair<Exchange::shared_ptr, bool> response = exchanges.declare("my-exchange", "direct", false, FieldTable()); + BOOST_CHECK_EQUAL(string("direct"), response.first->getType()); +} + +intrusive_ptr<Message> cmessage(std::string exchange, std::string routingKey) { + intrusive_ptr<Message> msg(new Message()); + AMQFrame method(in_place<MessageTransferBody>(ProtocolVersion(), exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + msg->getFrames().append(method); + msg->getFrames().append(header); + msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey); + return msg; +} + +QPID_AUTO_TEST_CASE(testSequenceOptions) +{ + FieldTable args; + args.setInt("qpid.msg_sequence",1); + char* buff = new char[10000]; + framing::Buffer buffer(buff,10000); + { + DirectExchange direct("direct1", false, args); + + intrusive_ptr<Message> msg1 = cmessage("e", "A"); + intrusive_ptr<Message> msg2 = cmessage("e", "B"); + intrusive_ptr<Message> msg3 = cmessage("e", "C"); + + DeliverableMessage dmsg1(msg1); + DeliverableMessage dmsg2(msg2); + DeliverableMessage dmsg3(msg3); + + direct.route(dmsg1, "abc", 0); + direct.route(dmsg2, "abc", 0); + direct.route(dmsg3, "abc", 0); + + BOOST_CHECK_EQUAL(1, msg1->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); + BOOST_CHECK_EQUAL(2, msg2->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); + BOOST_CHECK_EQUAL(3, msg3->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); + + FanOutExchange fanout("fanout1", false, args); + HeadersExchange header("headers1", false, args); + TopicExchange topic ("topic1", false, args); + + // check other exchanges, that they preroute + intrusive_ptr<Message> msg4 = cmessage("e", "A"); + intrusive_ptr<Message> msg5 = cmessage("e", "B"); + intrusive_ptr<Message> msg6 = cmessage("e", "C"); + + DeliverableMessage dmsg4(msg4); + DeliverableMessage dmsg5(msg5); + DeliverableMessage dmsg6(msg6); + + fanout.route(dmsg4, "abc", 0); + BOOST_CHECK_EQUAL(1, msg4->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); + + FieldTable headers; + header.route(dmsg5, "abc", &headers); + BOOST_CHECK_EQUAL(1, msg5->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); + + topic.route(dmsg6, "abc", 0); + BOOST_CHECK_EQUAL(1, msg6->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); + direct.encode(buffer); + } + { + + ExchangeRegistry exchanges; + buffer.reset(); + DirectExchange::shared_ptr exch_dec = Exchange::decode(exchanges, buffer); + + intrusive_ptr<Message> msg1 = cmessage("e", "A"); + DeliverableMessage dmsg1(msg1); + exch_dec->route(dmsg1, "abc", 0); + + BOOST_CHECK_EQUAL(4, msg1->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); + + } + delete [] buff; +} + +QPID_AUTO_TEST_CASE(testIVEOption) +{ + FieldTable args; + args.setInt("qpid.ive",1); + DirectExchange direct("direct1", false, args); + FanOutExchange fanout("fanout1", false, args); + HeadersExchange header("headers1", false, args); + TopicExchange topic ("topic1", false, args); + + intrusive_ptr<Message> msg1 = cmessage("direct1", "abc"); + msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString("a", "abc"); + DeliverableMessage dmsg1(msg1); + + FieldTable args2; + args2.setString("x-match", "any"); + args2.setString("a", "abc"); + + direct.route(dmsg1, "abc", 0); + fanout.route(dmsg1, "abc", 0); + header.route(dmsg1, "abc", &args2); + topic.route(dmsg1, "abc", 0); + Queue::shared_ptr queue(new Queue("queue", true)); + Queue::shared_ptr queue1(new Queue("queue1", true)); + Queue::shared_ptr queue2(new Queue("queue2", true)); + Queue::shared_ptr queue3(new Queue("queue3", true)); + + BOOST_CHECK(HeadersExchange::match(args2, msg1->getProperties<MessageProperties>()->getApplicationHeaders())); + + BOOST_CHECK(direct.bind(queue, "abc", 0)); + BOOST_CHECK(fanout.bind(queue1, "abc", 0)); + BOOST_CHECK(header.bind(queue2, "", &args2)); + BOOST_CHECK(topic.bind(queue3, "abc", 0)); + + BOOST_CHECK_EQUAL(1u,queue->getMessageCount()); + BOOST_CHECK_EQUAL(1u,queue1->getMessageCount()); + BOOST_CHECK_EQUAL(1u,queue2->getMessageCount()); + BOOST_CHECK_EQUAL(1u,queue3->getMessageCount()); + +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/FieldTable.cpp b/RC9/qpid/cpp/src/tests/FieldTable.cpp new file mode 100644 index 0000000000..6b364723cf --- /dev/null +++ b/RC9/qpid/cpp/src/tests/FieldTable.cpp @@ -0,0 +1,178 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include "qpid/framing/Array.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" +#include <alloca.h> + +#include "unit_test.h" + +using namespace qpid::framing; + +QPID_AUTO_TEST_SUITE(FieldTableTestSuite) + +QPID_AUTO_TEST_CASE(testMe) +{ + FieldTable ft; + ft.setString("A", "BCDE"); + BOOST_CHECK(string("BCDE") == ft.getAsString("A")); + + char buff[100]; + Buffer wbuffer(buff, 100); + wbuffer.put(ft); + + Buffer rbuffer(buff, 100); + FieldTable ft2; + rbuffer.get(ft2); + BOOST_CHECK(string("BCDE") == ft2.getAsString("A")); + +} + +QPID_AUTO_TEST_CASE(testAssignment) +{ + FieldTable a; + FieldTable b; + + a.setString("A", "BBBB"); + a.setInt("B", 1234); + b = a; + a.setString("A", "CCCC"); + + BOOST_CHECK(string("CCCC") == a.getAsString("A")); + BOOST_CHECK(string("BBBB") == b.getAsString("A")); + BOOST_CHECK_EQUAL(1234, a.getAsInt("B")); + BOOST_CHECK_EQUAL(1234, b.getAsInt("B")); + BOOST_CHECK(IntegerValue(1234) == *a.get("B")); + BOOST_CHECK(IntegerValue(1234) == *b.get("B")); + + FieldTable d; + { + FieldTable c; + c = a; + + char* buff = static_cast<char*>(::alloca(c.encodedSize())); + Buffer wbuffer(buff, c.encodedSize()); + wbuffer.put(c); + + Buffer rbuffer(buff, c.encodedSize()); + rbuffer.get(d); + BOOST_CHECK_EQUAL(c, d); + BOOST_CHECK(string("CCCC") == c.getAsString("A")); + BOOST_CHECK(IntegerValue(1234) == *c.get("B")); + } + BOOST_CHECK(string("CCCC") == d.getAsString("A")); + BOOST_CHECK(IntegerValue(1234) == *d.get("B")); +} + + +QPID_AUTO_TEST_CASE(testNestedValues) +{ + char buff[100]; + { + FieldTable a; + FieldTable b; + std::vector<std::string> items; + items.push_back("one"); + items.push_back("two"); + Array c(items); + + a.setString("id", "A"); + b.setString("id", "B"); + a.setTable("B", b); + a.setArray("C", c); + + + Buffer wbuffer(buff, 100); + wbuffer.put(a); + } + { + Buffer rbuffer(buff, 100); + FieldTable a; + FieldTable b; + Array c; + rbuffer.get(a); + BOOST_CHECK(string("A") == a.getAsString("id")); + a.getTable("B", b); + BOOST_CHECK(string("B") == b.getAsString("id")); + a.getArray("C", c); + std::vector<std::string> items; + c.collect(items); + BOOST_CHECK((uint) 2 == items.size()); + BOOST_CHECK(string("one") == items[0]); + BOOST_CHECK(string("two") == items[1]); + } +} + +QPID_AUTO_TEST_CASE(testFloatAndDouble) +{ + char buff[100]; + float f = 5.672; + double d = 56.720001; + { + FieldTable a; + a.setString("string", "abc"); + a.setInt("int", 5672); + a.setFloat("float", f); + a.setDouble("double", d); + + Buffer wbuffer(buff, 100); + wbuffer.put(a); + } + { + Buffer rbuffer(buff, 100); + FieldTable a; + rbuffer.get(a); + BOOST_CHECK(string("abc") == a.getAsString("string")); + BOOST_CHECK(5672 == a.getAsInt("int")); + float f2; + BOOST_CHECK(!a.getFloat("string", f2)); + BOOST_CHECK(!a.getFloat("int", f2)); + BOOST_CHECK(a.getFloat("float", f2)); + BOOST_CHECK(f2 == f); + + double d2; + BOOST_CHECK(!a.getDouble("string", d2)); + BOOST_CHECK(!a.getDouble("int", d2)); + BOOST_CHECK(a.getDouble("double", d2)); + BOOST_CHECK(d2 == d); + } +} + +QPID_AUTO_TEST_CASE(test64GetAndSetConverts) +{ + FieldTable args; + args.setInt64("a",100); + args.setInt64("b",-(int64_t) ((int64_t) 1<<34)); + + args.setUInt64("c",1u); + args.setUInt64("d",(uint64_t) ((uint64_t) 1<<34)); + BOOST_CHECK_EQUAL(1u, args.getAsUInt64("c")); + BOOST_CHECK_EQUAL(100u, args.getAsUInt64("a")); + BOOST_CHECK_EQUAL(1, args.getAsInt64("c")); + BOOST_CHECK_EQUAL(100, args.getAsInt64("a")); + BOOST_CHECK_EQUAL(-(int64_t) ((int64_t) 1<<34), args.getAsInt64("b")); + BOOST_CHECK_EQUAL((uint64_t) ((uint64_t) 1<<34), args.getAsUInt64("d")); + BOOST_CHECK_EQUAL((int64_t) ((int64_t) 1<<34), args.getAsInt64("d")); + +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/FieldValue.cpp b/RC9/qpid/cpp/src/tests/FieldValue.cpp new file mode 100644 index 0000000000..448f068107 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/FieldValue.cpp @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "qpid/framing/FieldValue.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(FieldValueTestSuite) + +using namespace qpid::framing; + +Str16Value s("abc"); +IntegerValue i(42); +//DecimalValue d(1234,2); +//FieldTableValue ft; +//EmptyValue e; + +QPID_AUTO_TEST_CASE(testStr16ValueEquals) +{ + + BOOST_CHECK(Str16Value("abc") == s); + BOOST_CHECK(Str16Value("foo") != s); + BOOST_CHECK(s != i); + BOOST_CHECK(s.convertsTo<std::string>() == true); + BOOST_CHECK(s.convertsTo<int>() == false); + BOOST_CHECK(s.get<std::string>() == "abc"); + BOOST_CHECK_THROW(s.get<int>(), InvalidConversionException); +// BOOST_CHECK(s != ft); + +} + +QPID_AUTO_TEST_CASE(testIntegerValueEquals) +{ + BOOST_CHECK(IntegerValue(42) == i); + BOOST_CHECK(IntegerValue(5) != i); + BOOST_CHECK(i != s); + BOOST_CHECK(i.convertsTo<std::string>() == false); + BOOST_CHECK(i.convertsTo<int>() == true); + BOOST_CHECK_THROW(i.get<std::string>(), InvalidConversionException); + BOOST_CHECK(i.get<int>() == 42); +// BOOST_CHECK(i != ft); +} + +#if 0 +QPID_AUTO_TEST_CASE(testDecimalValueEquals) +{ + BOOST_CHECK(DecimalValue(1234, 2) == d); + BOOST_CHECK(DecimalValue(12345, 2) != d); + BOOST_CHECK(DecimalValue(1234, 3) != d); + BOOST_CHECK(d != s); +} + +QPID_AUTO_TEST_CASE(testFieldTableValueEquals) +{ + ft.getValue().setString("foo", "FOO"); + ft.getValue().setInt("magic", 7); + + BOOST_CHECK_EQUAL(std::string("FOO"), + ft.getValue().getString("foo")); + BOOST_CHECK_EQUAL(7, ft.getValue().getInt("magic")); + + FieldTableValue f2; + BOOST_CHECK(ft != f2); + f2.getValue().setString("foo", "FOO"); + BOOST_CHECK(ft != f2); + f2.getValue().setInt("magic", 7); + BOOST_CHECK_EQUAL(ft,f2); + BOOST_CHECK(ft == f2); + f2.getValue().setString("foo", "BAR"); + BOOST_CHECK(ft != f2); + BOOST_CHECK(ft != i); +} +#endif + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/ForkedBroker.h b/RC9/qpid/cpp/src/tests/ForkedBroker.h new file mode 100644 index 0000000000..bf9e9265c4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ForkedBroker.h @@ -0,0 +1,122 @@ +#ifndef 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> + +/** + * 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: + ForkedBroker(std::vector<const char*> argv) { init(argv); } + + ForkedBroker(int argc, const char* const argv[]) { + std::vector<const char*> args(argv, argv+argc); + init(args); + } + + ~ForkedBroker() { + try { kill(); } catch(const std::exception& e) { + QPID_LOG(error, QPID_MSG("Killing forked broker: " << e.what())); + } + } + + void kill(int sig=SIGINT) { + 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) + throw ErrnoException("wait for forked process failed"); + if (WEXITSTATUS(status) != 0) + throw qpid::Exception(QPID_MSG("Forked broker exited with: " << WEXITSTATUS(status))); + } + + uint16_t getPort() { return port; } + pid_t getPID() { return pid; } + + private: + + template <class F> struct OnExit { + F fn; + OnExit(F f) : fn(f) {} + ~OnExit() { fn(); } + }; + + void init(const std::vector<const char*>& args) { + using qpid::ErrnoException; + port = 0; + int pipeFds[2]; + if(::pipe(pipeFds) < 0) throw ErrnoException("Can't create pipe"); + 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."); + } + ::close(pipeFds[0]); + } + 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 = "../qpidd"; + std::vector<const char*> args2(args); + args2.push_back("--port=0"); + args2.push_back("--mgmt-enable=no"); // TODO aconway 2008-07-16: why does mgmt cause problems? + args2.push_back("--log-enable=error+"); // Keep quiet except for errors. + args2.push_back(0); + execv(prog, const_cast<char* const*>(&args2[0])); + throw ErrnoException("execv failed"); + } + } + + pid_t pid; + int port; +}; + +#endif /*!TESTS_FORKEDBROKER_H*/ diff --git a/RC9/qpid/cpp/src/tests/Frame.cpp b/RC9/qpid/cpp/src/tests/Frame.cpp new file mode 100644 index 0000000000..11905911fa --- /dev/null +++ b/RC9/qpid/cpp/src/tests/Frame.cpp @@ -0,0 +1,80 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/framing/Frame.h" + +#include <boost/lexical_cast.hpp> +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(FrameTestSuite) + +using namespace std; +using namespace qpid::framing; +using namespace boost; + +QPID_AUTO_TEST_CASE(testContentBody) { + Frame f(42, AMQContentBody("foobar")); + AMQBody* body=f.getBody(); + BOOST_CHECK(dynamic_cast<AMQContentBody*>(body)); + Buffer b(f.encodedSize(); + f.encode(b); + b.flip(); + Frame g; + g.decode(b); + AMQContentBody* content=dynamic_cast<AMQContentBody*>(g.getBody()); + BOOST_REQUIRE(content); + BOOST_CHECK_EQUAL(content->getData(), "foobar"); +} + +QPID_AUTO_TEST_CASE(testMethodBody) { + FieldTable args; + args.setString("foo", "bar"); + Frame f( + 42, QueueDeclareBody(ProtocolVersion(), 1, "q", "altex", + true, false, true, false, true, args)); + BOOST_CHECK_EQUAL(f.getChannel(), 42); + Buffer b(f.encodedSize(); + f.encode(b); + b.flip(); + Frame g; + g.decode(b); + BOOST_CHECK_EQUAL(f.getChannel(), g.getChannel()); + QueueDeclareBody* declare=dynamic_cast<QueueDeclareBody*>(g.getBody()); + BOOST_REQUIRE(declare); + BOOST_CHECK_EQUAL(declare->getAlternateExchange(), "altex"); + BOOST_CHECK_EQUAL(lexical_cast<string>(*f.getBody()), lexical_cast<string>(*g.getBody())); +} + +QPID_AUTO_TEST_CASE(testLoop) { + // Run in a loop so heap profiler can spot any allocations. + Buffer b(1024); + for (int i = 0; i < 100; ++i) { + Frame ctor(2, AccessRequestOkBody(ProtocolVersion(), 42)); + Frame assign(3); + assign.body = AccessRequestOkBody(ProtocolVersion(), 42); + assign.encode(b); + b.flip(); + Frame g; + g.decode(b); + BOOST_REQUIRE(dynamic_cast<AccessRequestOkBody*>(g.getBody())->getTicket() == 42); + } +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/FramingTest.cpp b/RC9/qpid/cpp/src/tests/FramingTest.cpp new file mode 100644 index 0000000000..f82507c0a7 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/FramingTest.cpp @@ -0,0 +1,151 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/Connection.h" +#include "qpid/client/Connector.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/framing/all_method_bodies.h" +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/reply_exceptions.h" +#include "unit_test.h" + +#include <boost/bind.hpp> +#include <boost/lexical_cast.hpp> +#include <iostream> + +#include <memory> +#include <sstream> +#include <typeinfo> + +using namespace qpid; +using namespace qpid::framing; +using namespace std; + +template <class T> +std::string tostring(const T& x) +{ + std::ostringstream out; + out << x; + return out.str(); +} + +QPID_AUTO_TEST_SUITE(FramingTestSuite) + +QPID_AUTO_TEST_CASE(testMessageTransferBody) +{ + char buffer[1024]; + ProtocolVersion version(highestProtocolVersion); + Buffer wbuff(buffer, sizeof(buffer)); + MessageTransferBody in(version, "my-exchange", 1, 1); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + MessageTransferBody out(version); + out.decode(rbuff); + BOOST_CHECK_EQUAL(tostring(in), tostring(out)); +} + +QPID_AUTO_TEST_CASE(testConnectionSecureBody) +{ + char buffer[1024]; + ProtocolVersion version(highestProtocolVersion); + Buffer wbuff(buffer, sizeof(buffer)); + std::string s = "security credential"; + ConnectionSecureBody in(version, s); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + ConnectionSecureBody out(version); + out.decode(rbuff); + BOOST_CHECK_EQUAL(tostring(in), tostring(out)); +} + +QPID_AUTO_TEST_CASE(testConnectionRedirectBody) +{ + char buffer[1024]; + ProtocolVersion version(highestProtocolVersion); + Buffer wbuff(buffer, sizeof(buffer)); + std::string a = "hostA"; + std::string b = "hostB"; + Array hosts(0x95); + hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(a))); + hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(b))); + + ConnectionRedirectBody in(version, a, hosts); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + ConnectionRedirectBody out(version); + out.decode(rbuff); + BOOST_CHECK_EQUAL(tostring(in), tostring(out)); +} + +QPID_AUTO_TEST_CASE(testQueueDeclareBody) +{ + char buffer[1024]; + ProtocolVersion version(highestProtocolVersion); + Buffer wbuff(buffer, sizeof(buffer)); + QueueDeclareBody in(version, "name", "dlq", true, false, true, false, FieldTable()); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + QueueDeclareBody out(version); + out.decode(rbuff); + BOOST_CHECK_EQUAL(tostring(in), tostring(out)); +} + +QPID_AUTO_TEST_CASE(testConnectionRedirectBodyFrame) +{ + char buffer[1024]; + ProtocolVersion version(highestProtocolVersion); + Buffer wbuff(buffer, sizeof(buffer)); + std::string a = "hostA"; + std::string b = "hostB"; + Array hosts(0x95); + hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(a))); + hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(b))); + + AMQFrame in(in_place<ConnectionRedirectBody>(version, a, hosts)); + in.setChannel(999); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + AMQFrame out; + out.decode(rbuff); + BOOST_CHECK_EQUAL(tostring(in), tostring(out)); +} + +QPID_AUTO_TEST_CASE(testMessageCancelBodyFrame) +{ + char buffer[1024]; + ProtocolVersion version(highestProtocolVersion); + Buffer wbuff(buffer, sizeof(buffer)); + AMQFrame in(in_place<MessageCancelBody>(version, "tag")); + in.setChannel(999); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + AMQFrame out; + out.decode(rbuff); + BOOST_CHECK_EQUAL(tostring(in), tostring(out)); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/HeaderTest.cpp b/RC9/qpid/cpp/src/tests/HeaderTest.cpp new file mode 100644 index 0000000000..33bf705e65 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/HeaderTest.cpp @@ -0,0 +1,110 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/FieldValue.h" +#include "unit_test.h" + +using namespace qpid::framing; +using namespace std; + +QPID_AUTO_TEST_SUITE(HeaderTestSuite) + +QPID_AUTO_TEST_CASE(testGenericProperties) +{ + AMQHeaderBody body; + body.get<MessageProperties>(true)->getApplicationHeaders().setString( + "A", "BCDE"); + char buff[100]; + Buffer wbuffer(buff, 100); + body.encode(wbuffer); + + Buffer rbuffer(buff, 100); + AMQHeaderBody body2; + body2.decode(rbuffer, body.encodedSize()); + MessageProperties* props = + body2.get<MessageProperties>(true); + BOOST_CHECK_EQUAL( + string("BCDE"), + props->getApplicationHeaders().get("A")->get<string>()); +} + +QPID_AUTO_TEST_CASE(testMessageProperties) +{ + AMQFrame out(in_place<AMQHeaderBody>()); + MessageProperties* props1 = + out.castBody<AMQHeaderBody>()->get<MessageProperties>(true); + + props1->setContentLength(42); + props1->setMessageId(Uuid(true)); + props1->setCorrelationId("correlationId"); + props1->setReplyTo(ReplyTo("ex","key")); + props1->setContentType("contentType"); + props1->setContentEncoding("contentEncoding"); + props1->setUserId("userId"); + props1->setAppId("appId"); + + char buff[10000]; + Buffer wbuffer(buff, 10000); + out.encode(wbuffer); + + Buffer rbuffer(buff, 10000); + AMQFrame in; + in.decode(rbuffer); + MessageProperties* props2 = + in.castBody<AMQHeaderBody>()->get<MessageProperties>(true); + + BOOST_CHECK_EQUAL(props1->getContentLength(), props2->getContentLength()); + BOOST_CHECK_EQUAL(props1->getMessageId(), props2->getMessageId()); + BOOST_CHECK_EQUAL(props1->getCorrelationId(), props2->getCorrelationId()); + BOOST_CHECK_EQUAL(props1->getContentType(), props2->getContentType()); + BOOST_CHECK_EQUAL(props1->getContentEncoding(), props2->getContentEncoding()); + BOOST_CHECK_EQUAL(props1->getUserId(), props2->getUserId()); + BOOST_CHECK_EQUAL(props1->getAppId(), props2->getAppId()); + +} + +QPID_AUTO_TEST_CASE(testDeliveryProperies) +{ + AMQFrame out(in_place<AMQHeaderBody>()); + DeliveryProperties* props1 = + out.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true); + + props1->setDiscardUnroutable(true); + props1->setExchange("foo"); + + char buff[10000]; + Buffer wbuffer(buff, 10000); + out.encode(wbuffer); + + Buffer rbuffer(buff, 10000); + AMQFrame in; + in.decode(rbuffer); + DeliveryProperties* props2 = + in.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true); + + BOOST_CHECK(props2->getDiscardUnroutable()); + BOOST_CHECK_EQUAL(string("foo"), props2->getExchange()); + BOOST_CHECK(!props2->hasTimestamp()); +} + +QPID_AUTO_TEST_SUITE_END() + diff --git a/RC9/qpid/cpp/src/tests/HeadersExchangeTest.cpp b/RC9/qpid/cpp/src/tests/HeadersExchangeTest.cpp new file mode 100644 index 0000000000..46933f955a --- /dev/null +++ b/RC9/qpid/cpp/src/tests/HeadersExchangeTest.cpp @@ -0,0 +1,115 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Exception.h" +#include "qpid/broker/HeadersExchange.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" +#include "unit_test.h" + +using namespace qpid::broker; +using namespace qpid::framing; + +QPID_AUTO_TEST_SUITE(HeadersExchangeTestSuite) + +QPID_AUTO_TEST_CASE(testMatchAll) +{ + FieldTable b, m, n; + b.setString("x-match", "all"); + b.setString("foo", "FOO"); + b.setInt("n", 42); + m.setString("foo", "FOO"); + m.setInt("n", 42); + BOOST_CHECK(HeadersExchange::match(b, m)); + + // Ignore extras. + m.setString("extra", "x"); + BOOST_CHECK(HeadersExchange::match(b, m)); + + // Fail mismatch, wrong value. + m.setString("foo", "NotFoo"); + BOOST_CHECK(!HeadersExchange::match(b, m)); + + // Fail mismatch, missing value + n.setInt("n", 42); + n.setString("extra", "x"); + BOOST_CHECK(!HeadersExchange::match(b, n)); +} + +QPID_AUTO_TEST_CASE(testMatchAny) +{ + FieldTable b, m, n; + b.setString("x-match", "any"); + b.setString("foo", "FOO"); + b.setInt("n", 42); + m.setString("foo", "FOO"); + BOOST_CHECK(!HeadersExchange::match(b, n)); + BOOST_CHECK(HeadersExchange::match(b, m)); + m.setInt("n", 42); + BOOST_CHECK(HeadersExchange::match(b, m)); +} + +QPID_AUTO_TEST_CASE(testMatchEmptyValue) +{ + FieldTable b, m; + b.setString("x-match", "all"); + b.set("foo", FieldTable::ValuePtr()); + b.set("n", FieldTable::ValuePtr()); + BOOST_CHECK(!HeadersExchange::match(b, m)); + m.setString("foo", "blah"); + m.setInt("n", 123); +} + +QPID_AUTO_TEST_CASE(testMatchEmptyArgs) +{ + FieldTable b, m; + m.setString("foo", "FOO"); + + b.setString("x-match", "all"); + BOOST_CHECK(HeadersExchange::match(b, m)); + b.setString("x-match", "any"); + BOOST_CHECK(!HeadersExchange::match(b, m)); +} + + +QPID_AUTO_TEST_CASE(testMatchNoXMatch) +{ + FieldTable b, m; + b.setString("foo", "FOO"); + m.setString("foo", "FOO"); + BOOST_CHECK(!HeadersExchange::match(b, m)); +} + +QPID_AUTO_TEST_CASE(testBindNoXMatch) +{ + HeadersExchange exchange("test"); + Queue::shared_ptr queue; + std::string key; + FieldTable args; + try { + //just checking this doesn't cause assertion etc + exchange.bind(queue, key, &args); + } catch(qpid::Exception&) { + //expected + } +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/IncompleteMessageList.cpp b/RC9/qpid/cpp/src/tests/IncompleteMessageList.cpp new file mode 100644 index 0000000000..925cdbf43e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/IncompleteMessageList.cpp @@ -0,0 +1,128 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include <sstream> +#include "qpid/broker/Message.h" +#include "qpid/broker/NullMessageStore.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/IncompleteMessageList.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(IncompleteMessageListTestSuite) + +using namespace qpid::broker; +using namespace qpid::framing; + +struct Checker +{ + std::list<SequenceNumber> ids; + + Checker() { } + + Checker(uint start, uint end) { + for (uint i = start; i <= end; i++) { + ids.push_back(i); + } + } + + Checker& expect(const SequenceNumber& id) { + ids.push_back(id); + return *this; + } + + void operator()(boost::intrusive_ptr<Message> msg) { + BOOST_CHECK(!ids.empty()); + BOOST_CHECK_EQUAL(msg->getCommandId(), ids.front()); + ids.pop_front(); + } +}; + +QPID_AUTO_TEST_CASE(testProcessSimple) +{ + IncompleteMessageList list; + SequenceNumber counter(1); + //fill up list with messages + for (int i = 0; i < 5; i++) { + boost::intrusive_ptr<Message> msg(new Message(counter++)); + list.add(msg); + } + //process and ensure they are all passed to completion listener + list.process(Checker(1, 5), false); + //process again and ensure none are resent to listener + list.process(Checker(), false); +} + +QPID_AUTO_TEST_CASE(testProcessWithIncomplete) +{ + IncompleteMessageList list; + SequenceNumber counter(1); + boost::intrusive_ptr<Message> middle; + //fill up list with messages + for (int i = 0; i < 5; i++) { + boost::intrusive_ptr<Message> msg(new Message(counter++)); + list.add(msg); + if (i == 2) { + //mark a message in the middle as incomplete + msg->enqueueAsync(); + middle = msg; + } + } + //process and ensure only message upto incomplete message are passed to listener + list.process(Checker(1, 2), false); + //mark message complete and re-process to get remaining messages sent to listener + middle->enqueueComplete(); + list.process(Checker(3, 5), false); +} + + +struct MockStore : public NullMessageStore +{ + Queue::shared_ptr queue; + boost::intrusive_ptr<Message> msg; + + void flush(const qpid::broker::PersistableQueue& q) { + BOOST_CHECK_EQUAL(queue.get(), &q); + msg->enqueueComplete(); + } +}; + +QPID_AUTO_TEST_CASE(testSyncProcessWithIncomplete) +{ + IncompleteMessageList list; + SequenceNumber counter(1); + MockStore store; + store.queue = Queue::shared_ptr(new Queue("mock-queue")); + //fill up list with messages + for (int i = 0; i < 5; i++) { + boost::intrusive_ptr<Message> msg(new Message(counter++)); + list.add(msg); + if (i == 2) { + //mark a message in the middle as incomplete + msg->enqueueAsync(store.queue, &store); + store.msg = msg; + } + } + //process with sync bit specified and ensure that all messages are passed to listener + list.process(Checker(1, 5), true); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/InlineAllocator.cpp b/RC9/qpid/cpp/src/tests/InlineAllocator.cpp new file mode 100644 index 0000000000..fe6eaefaf4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/InlineAllocator.cpp @@ -0,0 +1,63 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/InlineAllocator.h" +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(InlineAllocatorTestSuite) + +using namespace qpid; +using namespace std; + +QPID_AUTO_TEST_CASE(testAllocate) { + InlineAllocator<std::allocator<char>, 2> alloc; + + char* p = alloc.allocate(1); + BOOST_CHECK(p == (char*)&alloc); + alloc.deallocate(p,1); + + p = alloc.allocate(2); + BOOST_CHECK(p == (char*)&alloc); + alloc.deallocate(p,2); + + p = alloc.allocate(3); + BOOST_CHECK(p != (char*)&alloc); + alloc.deallocate(p,3); +} + +QPID_AUTO_TEST_CASE(testAllocateFull) { + InlineAllocator<std::allocator<char>, 1> alloc; + + char* p = alloc.allocate(1); + BOOST_CHECK(p == (char*)&alloc); + + char* q = alloc.allocate(1); + BOOST_CHECK(q != (char*)&alloc); + + alloc.deallocate(p,1); + p = alloc.allocate(1); + BOOST_CHECK(p == (char*)&alloc); + + alloc.deallocate(p,1); + alloc.deallocate(q,1); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/InlineVector.cpp b/RC9/qpid/cpp/src/tests/InlineVector.cpp new file mode 100644 index 0000000000..bcd36e47b4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/InlineVector.cpp @@ -0,0 +1,119 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/InlineVector.h" +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(InlineVectorTestSuite) + +using namespace qpid; +using namespace std; + +typedef InlineVector<int, 3> Vec; + +bool isInline(const Vec& v) { + return (const char*)&v <= (const char*)(&v[0]) && + (const char*)(&v[0]) < (const char*)&v+sizeof(v); +} + +QPID_AUTO_TEST_CASE(testCtor) { + { + Vec v; + BOOST_CHECK(isInline(v)); + BOOST_CHECK(v.empty()); + } + { + Vec v(3, 42); + BOOST_CHECK(isInline(v)); + BOOST_CHECK_EQUAL(3u, v.size()); + BOOST_CHECK_EQUAL(v[0], 42); + BOOST_CHECK_EQUAL(v[2], 42); + + Vec u(v); + BOOST_CHECK(isInline(u)); + BOOST_CHECK_EQUAL(3u, u.size()); + BOOST_CHECK_EQUAL(u[0], 42); + BOOST_CHECK_EQUAL(u[2], 42); + } + + { + Vec v(4, 42); + + BOOST_CHECK_EQUAL(v.size(), 4u); + BOOST_CHECK(!isInline(v)); + Vec u(v); + BOOST_CHECK_EQUAL(u.size(), 4u); + BOOST_CHECK(!isInline(u)); + } +} + +QPID_AUTO_TEST_CASE(testInsert) { + { + Vec v; + v.push_back(1); + BOOST_CHECK_EQUAL(v.size(), 1u); + BOOST_CHECK_EQUAL(v.back(), 1); + BOOST_CHECK(isInline(v)); + + v.insert(v.begin(), 2); + BOOST_CHECK_EQUAL(v.size(), 2u); + BOOST_CHECK_EQUAL(v.back(), 1); + BOOST_CHECK(isInline(v)); + + v.push_back(3); + BOOST_CHECK(isInline(v)); + + v.push_back(4); + + BOOST_CHECK(!isInline(v)); + } + { + Vec v(3,42); + v.insert(v.begin(), 9); + BOOST_CHECK_EQUAL(v.size(), 4u); + BOOST_CHECK(!isInline(v)); + } + { + Vec v(3,42); + v.insert(v.begin()+1, 9); + BOOST_CHECK(!isInline(v)); + BOOST_CHECK_EQUAL(v.size(), 4u); + } +} + +QPID_AUTO_TEST_CASE(testAssign) { + Vec v(3,42); + Vec u; + u = v; + BOOST_CHECK(isInline(u)); + u.push_back(4); + BOOST_CHECK(!isInline(u)); + v = u; + BOOST_CHECK(!isInline(v)); +} + +QPID_AUTO_TEST_CASE(testResize) { + Vec v; + v.resize(5); + BOOST_CHECK(!isInline(v)); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/Makefile.am b/RC9/qpid/cpp/src/tests/Makefile.am new file mode 100644 index 0000000000..3a608b2bae --- /dev/null +++ b/RC9/qpid/cpp/src/tests/Makefile.am @@ -0,0 +1,245 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +AM_CXXFLAGS = $(WARNING_CFLAGS) -DBOOST_TEST_DYN_LINK +INCLUDES = -I$(srcdir)/.. -I$(srcdir)/../gen -I$(top_builddir)/src/gen + +abs_builddir=@abs_builddir@ +extra_libs = +lib_client = $(abs_builddir)/../libqpidclient.la +lib_common = $(abs_builddir)/../libqpidcommon.la +lib_broker = $(abs_builddir)/../libqpidbroker.la +lib_console = $(abs_builddir)/../libqmfconsole.la +# lib_amqp_0_10 = $(abs_builddir)/../libqpidamqp_0_10.la + +# +# Initialize variables that are incremented with += +# +check_PROGRAMS= +check_LTLIBRARIES= +TESTS= +EXTRA_DIST= +CLEANFILES= + +# +# Unit test program +# +# Unit tests are built as a single program to reduce valgrind overhead +# when running the tests. If you want to build a subset of the tests do +# rm -f unit_test; make unit_test unit_test_OBJECTS="unit_test.o SelectedTest.o" +# + +TESTS+=unit_test +check_PROGRAMS+=unit_test +unit_test_LDADD=-lboost_unit_test_framework -lboost_regex \ + $(lib_client) $(lib_broker) $(lib_console) + +unit_test_SOURCES= unit_test.cpp unit_test.h \ + BrokerFixture.h SocketProxy.h \ + exception_test.cpp \ + RefCounted.cpp \ + SessionState.cpp Blob.cpp logging.cpp \ + AsyncCompletion.cpp \ + Url.cpp Uuid.cpp \ + Shlib.cpp FieldValue.cpp FieldTable.cpp Array.cpp \ + QueueOptionsTest.cpp \ + InlineAllocator.cpp \ + InlineVector.cpp \ + ClientSessionTest.cpp \ + SequenceSet.cpp \ + StringUtils.cpp \ + IncompleteMessageList.cpp \ + RangeSet.cpp \ + AtomicValue.cpp \ + QueueTest.cpp \ + AccumulatedAckTest.cpp \ + DtxWorkRecordTest.cpp \ + DeliveryRecordTest.cpp \ + ExchangeTest.cpp \ + HeadersExchangeTest.cpp \ + MessageTest.cpp \ + QueueRegistryTest.cpp \ + QueuePolicyTest.cpp \ + FramingTest.cpp \ + HeaderTest.cpp \ + SequenceNumberTest.cpp \ + TimerTest.cpp \ + TopicExchangeTest.cpp \ + TxBufferTest.cpp \ + TxPublishTest.cpp \ + MessageBuilderTest.cpp \ + ConnectionOptions.h \ + ForkedBroker.h \ + ManagementTest.cpp \ + MessageReplayTracker.cpp \ + ConsoleTest.cpp + +if HAVE_XML +unit_test_SOURCES+= XmlClientSessionTest.cpp +endif + + +# Disabled till we move to amqp_0_10 codec. +# amqp_0_10/serialize.cpp allSegmentTypes.h \ +# amqp_0_10/ProxyTemplate.cpp \ +# amqp_0_10/apply.cpp \ +# amqp_0_10/Map.cpp \ +# amqp_0_10/handlers.cpp + + +check_LTLIBRARIES += libshlibtest.la +libshlibtest_la_LDFLAGS = -module -rpath $(abs_builddir) +libshlibtest_la_SOURCES = shlibtest.cpp + +include cluster.mk +if SSL +include ssl.mk +endif + +# +# Other test programs +# +check_PROGRAMS+=perftest +perftest_SOURCES=perftest.cpp test_tools.h TestOptions.h ConnectionOptions.h +perftest_LDADD=$(lib_client) + +check_PROGRAMS+=txtest +txtest_SOURCES=txtest.cpp TestOptions.h ConnectionOptions.h +txtest_LDADD=$(lib_client) + +check_PROGRAMS+=latencytest +latencytest_SOURCES=latencytest.cpp TestOptions.h ConnectionOptions.h +latencytest_LDADD=$(lib_client) + +check_PROGRAMS+=echotest +echotest_SOURCES=echotest.cpp TestOptions.h ConnectionOptions.h +echotest_LDADD=$(lib_client) + +check_PROGRAMS+=client_test +client_test_SOURCES=client_test.cpp TestOptions.h ConnectionOptions.h +client_test_LDADD=$(lib_client) + +check_PROGRAMS+=topic_listener +topic_listener_SOURCES=topic_listener.cpp TestOptions.h ConnectionOptions.h +topic_listener_LDADD=$(lib_client) + +check_PROGRAMS+=topic_publisher +topic_publisher_SOURCES=topic_publisher.cpp TestOptions.h ConnectionOptions.h +topic_publisher_LDADD=$(lib_client) + +check_PROGRAMS+=publish +publish_SOURCES=publish.cpp TestOptions.h ConnectionOptions.h +publish_LDADD=$(lib_client) + +check_PROGRAMS+=consume +consume_SOURCES=consume.cpp TestOptions.h ConnectionOptions.h +consume_LDADD=$(lib_client) + +check_PROGRAMS+=header_test +header_test_SOURCES=header_test.cpp TestOptions.h ConnectionOptions.h +header_test_LDADD=$(lib_client) + +check_PROGRAMS+=failover_soak +failover_soak_SOURCES=failover_soak.cpp ForkedBroker.h +failover_soak_LDADD=$(lib_client) + +check_PROGRAMS+=declare_queues +declare_queues_SOURCES=declare_queues.cpp +declare_queues_LDADD=$(lib_client) + +check_PROGRAMS+=replaying_sender +replaying_sender_SOURCES=replaying_sender.cpp +replaying_sender_LDADD=$(lib_client) + +check_PROGRAMS+=resuming_receiver +resuming_receiver_SOURCES=resuming_receiver.cpp +resuming_receiver_LDADD=$(lib_client) + +check_PROGRAMS+=txshift +txshift_SOURCES=txshift.cpp TestOptions.h ConnectionOptions.h +txshift_LDADD=$(lib_client) + +check_PROGRAMS+=txjob +txjob_SOURCES=txjob.cpp TestOptions.h ConnectionOptions.h +txjob_LDADD=$(lib_client) + +check_PROGRAMS+=receiver +receiver_SOURCES=receiver.cpp TestOptions.h ConnectionOptions.h +receiver_LDADD=$(lib_client) + +check_PROGRAMS+=sender +sender_SOURCES=sender.cpp TestOptions.h ConnectionOptions.h +sender_LDADD=$(lib_client) + + +TESTS_ENVIRONMENT = VALGRIND=$(VALGRIND) srcdir=$(srcdir) QPID_DATA_DIR= BOOST_TEST_SHOW_PROGRESS=yes $(srcdir)/run_test + +system_tests = client_test quick_perftest quick_topictest run_header_test quick_txtest +TESTS += start_broker $(system_tests) python_tests stop_broker run_federation_tests run_acl_tests + +EXTRA_DIST += \ + run_test vg_check \ + run-unit-tests start_broker python_tests stop_broker \ + quick_topictest \ + quick_perftest \ + quick_txtest \ + topictest \ + run_header_test \ + header_test.py \ + ssl_test \ + config.null \ + ais_check \ + run_federation_tests \ + run_acl_tests \ + .valgrind.supp \ + MessageUtils.h \ + TestMessageStore.h \ + TxMocks.h \ + start_cluster stop_cluster restart_cluster + +check_LTLIBRARIES += libdlclose_noop.la +libdlclose_noop_la_LDFLAGS = -module -rpath $(abs_builddir) +libdlclose_noop_la_SOURCES = dlclose_noop.c + +CLEANFILES+=valgrind.out *.log *.vglog* dummy_test $(unit_wrappers) + +# FIXME aconway 2008-05-23: Disabled interop_runner because it uses +# the obsolete Channel class. Convert to Session and re-enable. +# +# check_PROGRAMS += interop_runner + +# interop_runner_SOURCES = \ +# interop_runner.cpp \ +# SimpleTestCaseBase.cpp \ +# BasicP2PTest.cpp \ +# BasicPubSubTest.cpp \ +# SimpleTestCaseBase.h \ +# BasicP2PTest.h \ +# BasicPubSubTest.h \ +# TestCase.h \ +# TestOptions.h ConnectionOptions.h +# interop_runner_LDADD = $(lib_client) $(lib_common) $(extra_libs) + + +# Longer running stability tests, not run by default check: target. +# Not run under valgrind, too slow +LONG_TESTS=fanout_perftest shared_perftest multiq_perftest topic_perftest run_failover_soak +EXTRA_DIST+=$(LONG_TESTS) run_perftest +check-long: + $(MAKE) check TESTS="start_broker $(LONG_TESTS) stop_broker" VALGRIND= diff --git a/RC9/qpid/cpp/src/tests/ManagementTest.cpp b/RC9/qpid/cpp/src/tests/ManagementTest.cpp new file mode 100644 index 0000000000..a3d29ec22c --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ManagementTest.cpp @@ -0,0 +1,85 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/management/ManagementObject.h" +#include "qpid/framing/Buffer.h" +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(ManagementTestSuite) + +using namespace qpid::framing; +using namespace qpid::management; + +QPID_AUTO_TEST_CASE(testObjectIdSerializeStream) { + std::string text("0-10-4-2500-80000000000"); + std::stringstream input(text); + + ObjectId oid(input); + + std::stringstream output; + output << oid; + + BOOST_CHECK_EQUAL(text, output.str()); +} + +QPID_AUTO_TEST_CASE(testObjectIdSerializeString) { + std::string text("0-10-4-2500-80000000000"); + + ObjectId oid(text); + + std::stringstream output; + output << oid; + + BOOST_CHECK_EQUAL(text, output.str()); +} + +QPID_AUTO_TEST_CASE(testObjectIdEncode) { + char buffer[100]; + Buffer msgBuf(buffer, 100); + msgBuf.putLongLong(0x1002000030000004LL); + msgBuf.putLongLong(0x0000000000000005LL); + msgBuf.reset(); + + ObjectId oid(msgBuf); + + std::stringstream out1; + out1 << oid; + + BOOST_CHECK_EQUAL(out1.str(), "1-2-3-4-5"); +} + +QPID_AUTO_TEST_CASE(testObjectIdAttach) { + AgentAttachment agent; + ObjectId oid(&agent, 10, 20, 50); + + std::stringstream out1; + out1 << oid; + BOOST_CHECK_EQUAL(out1.str(), "10-20-0-0-50"); + + agent.setBanks(30, 40); + std::stringstream out2; + out2 << oid; + BOOST_CHECK_EQUAL(out2.str(), "10-20-30-40-50"); +} + +QPID_AUTO_TEST_SUITE_END() + + diff --git a/RC9/qpid/cpp/src/tests/MessageBuilderTest.cpp b/RC9/qpid/cpp/src/tests/MessageBuilderTest.cpp new file mode 100644 index 0000000000..313a91c053 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/MessageBuilderTest.cpp @@ -0,0 +1,224 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/Message.h" +#include "qpid/broker/MessageBuilder.h" +#include "qpid/broker/NullMessageStore.h" +#include "qpid/framing/frame_functors.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/TypeFilter.h" +#include "unit_test.h" +#include <list> + +using namespace boost; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +class MockMessageStore : public NullMessageStore +{ + enum Op {STAGE=1, APPEND=2}; + + uint64_t id; + intrusive_ptr<PersistableMessage> expectedMsg; + string expectedData; + std::list<Op> ops; + + void checkExpectation(Op actual) + { + BOOST_CHECK_EQUAL(ops.front(), actual); + ops.pop_front(); + } + + public: + MockMessageStore() : id(0), expectedMsg(0) {} + + void expectStage(PersistableMessage& msg) + { + expectedMsg = &msg; + ops.push_back(STAGE); + } + + void expectAppendContent(PersistableMessage& msg, const string& data) + { + expectedMsg = &msg; + expectedData = data; + ops.push_back(APPEND); + } + + void stage(const intrusive_ptr<PersistableMessage>& msg) + { + checkExpectation(STAGE); + BOOST_CHECK_EQUAL(expectedMsg, msg); + msg->setPersistenceId(++id); + } + + void appendContent(const intrusive_ptr<const PersistableMessage>& msg, + const string& data) + { + checkExpectation(APPEND); + BOOST_CHECK_EQUAL(static_pointer_cast<const PersistableMessage>(expectedMsg), msg); + BOOST_CHECK_EQUAL(expectedData, data); + } + + bool expectationsMet() + { + return ops.empty(); + } + + //don't treat this store as a null impl + bool isNull() const + { + return false; + } + +}; + +QPID_AUTO_TEST_SUITE(MessageBuilderTestSuite) + +QPID_AUTO_TEST_CASE(testHeaderOnly) +{ + MessageBuilder builder(0, 0); + builder.start(SequenceNumber()); + + std::string exchange("builder-exchange"); + std::string key("builder-exchange"); + + AMQFrame method(in_place<MessageTransferBody>( + ProtocolVersion(), exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(0); + header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); + + builder.handle(method); + builder.handle(header); + + BOOST_CHECK(builder.getMessage()); + BOOST_CHECK_EQUAL(exchange, builder.getMessage()->getExchangeName()); + BOOST_CHECK_EQUAL(key, builder.getMessage()->getRoutingKey()); + BOOST_CHECK(builder.getMessage()->getFrames().isComplete()); +} + +QPID_AUTO_TEST_CASE(test1ContentFrame) +{ + MessageBuilder builder(0, 0); + builder.start(SequenceNumber()); + + std::string data("abcdefg"); + std::string exchange("builder-exchange"); + std::string key("builder-exchange"); + + AMQFrame method(in_place<MessageTransferBody>(ProtocolVersion(), exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + AMQFrame content(in_place<AMQContentBody>(data)); + method.setEof(false); + header.setBof(false); + header.setEof(false); + content.setBof(false); + + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data.size()); + header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); + + builder.handle(method); + BOOST_CHECK(builder.getMessage()); + BOOST_CHECK(!builder.getMessage()->getFrames().isComplete()); + + builder.handle(header); + BOOST_CHECK(builder.getMessage()); + BOOST_CHECK(!builder.getMessage()->getFrames().isComplete()); + + builder.handle(content); + BOOST_CHECK(builder.getMessage()); + BOOST_CHECK(builder.getMessage()->getFrames().isComplete()); +} + +QPID_AUTO_TEST_CASE(test2ContentFrames) +{ + MessageBuilder builder(0, 0); + builder.start(SequenceNumber()); + + std::string data1("abcdefg"); + std::string data2("hijklmn"); + std::string exchange("builder-exchange"); + std::string key("builder-exchange"); + + AMQFrame method(in_place<MessageTransferBody>( + ProtocolVersion(), exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + AMQFrame content1(in_place<AMQContentBody>(data1)); + AMQFrame content2(in_place<AMQContentBody>(data2)); + method.setEof(false); + header.setBof(false); + header.setEof(false); + content1.setBof(false); + content1.setEof(false); + content2.setBof(false); + + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size()); + header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); + + builder.handle(method); + builder.handle(header); + builder.handle(content1); + BOOST_CHECK(builder.getMessage()); + BOOST_CHECK(!builder.getMessage()->getFrames().isComplete()); + + builder.handle(content2); + BOOST_CHECK(builder.getMessage()); + BOOST_CHECK(builder.getMessage()->getFrames().isComplete()); +} + +QPID_AUTO_TEST_CASE(testStaging) +{ + MockMessageStore store; + MessageBuilder builder(&store, 5); + builder.start(SequenceNumber()); + + std::string data1("abcdefg"); + std::string data2("hijklmn"); + std::string exchange("builder-exchange"); + std::string key("builder-exchange"); + + AMQFrame method(in_place<MessageTransferBody>( + ProtocolVersion(), exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + AMQFrame content1(in_place<AMQContentBody>(data1)); + AMQFrame content2(in_place<AMQContentBody>(data2)); + + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size()); + header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); + + builder.handle(method); + builder.handle(header); + + store.expectStage(*builder.getMessage()); + builder.handle(content1); + BOOST_CHECK(store.expectationsMet()); + BOOST_CHECK_EQUAL((uint64_t) 1, builder.getMessage()->getPersistenceId()); + + store.expectAppendContent(*builder.getMessage(), data2); + builder.handle(content2); + BOOST_CHECK(store.expectationsMet()); + //were the content frames dropped? + BOOST_CHECK(!builder.getMessage()->isContentLoaded()); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/MessageReplayTracker.cpp b/RC9/qpid/cpp/src/tests/MessageReplayTracker.cpp new file mode 100644 index 0000000000..a5121cdeb7 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/MessageReplayTracker.cpp @@ -0,0 +1,99 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "unit_test.h" +#include "BrokerFixture.h" +#include "qpid/client/MessageReplayTracker.h" +#include "qpid/sys/Time.h" + +QPID_AUTO_TEST_SUITE(MessageReplayTrackerTests) + +using namespace qpid::client; +using namespace qpid::sys; +using std::string; + +class ReplayBufferChecker +{ + public: + + ReplayBufferChecker(uint from, uint to) : end(to), i(from) {} + + void operator()(const Message& m) + { + if (i > end) BOOST_FAIL("Extra message found: " + m.getData()); + BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i++)).str(), m.getData()); + } + private: + const uint end; + uint i; + +}; + +QPID_AUTO_TEST_CASE(testReplay) +{ + ProxySessionFixture fix; + fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true); + + MessageReplayTracker tracker(10); + tracker.init(fix.session); + for (uint i = 0; i < 5; i++) { + Message message((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + tracker.send(message); + } + ReplayBufferChecker checker(1, 10); + tracker.foreach(checker); + + tracker.replay(fix.session); + for (uint j = 0; j < 2; j++) {//each message should have been sent twice + for (uint i = 0; i < 5; i++) { + Message m; + BOOST_CHECK(fix.subs.get(m, "my-queue", TIME_SEC)); + BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData()); + } + } + Message m; + BOOST_CHECK(!fix.subs.get(m, "my-queue")); +} + +QPID_AUTO_TEST_CASE(testCheckCompletion) +{ + ProxySessionFixture fix; + fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true); + + MessageReplayTracker tracker(10); + tracker.init(fix.session); + for (uint i = 0; i < 5; i++) { + Message message((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + tracker.send(message); + } + fix.session.sync();//ensures all messages are complete + tracker.checkCompletion(); + tracker.replay(fix.session); + Message received; + for (uint i = 0; i < 5; i++) { + BOOST_CHECK(fix.subs.get(received, "my-queue")); + BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), received.getData()); + } + BOOST_CHECK(!fix.subs.get(received, "my-queue")); +} + +QPID_AUTO_TEST_SUITE_END() + + diff --git a/RC9/qpid/cpp/src/tests/MessageTest.cpp b/RC9/qpid/cpp/src/tests/MessageTest.cpp new file mode 100644 index 0000000000..f9292ee53e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/MessageTest.cpp @@ -0,0 +1,90 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/Message.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/FieldValue.h" +#include "qpid/framing/Uuid.h" + +#include "unit_test.h" + +#include <iostream> +#include <alloca.h> + +using namespace boost; +using namespace qpid::broker; +using namespace qpid::framing; + +QPID_AUTO_TEST_SUITE(MessageTestSuite) + +QPID_AUTO_TEST_CASE(testEncodeDecode) +{ + string exchange = "MyExchange"; + string routingKey = "MyRoutingKey"; + Uuid messageId(true); + string data1("abcdefg"); + string data2("hijklmn"); + + intrusive_ptr<Message> msg(new Message()); + + AMQFrame method(in_place<MessageTransferBody>( + ProtocolVersion(), exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + AMQFrame content1(in_place<AMQContentBody>(data1)); + AMQFrame content2(in_place<AMQContentBody>(data2)); + + msg->getFrames().append(method); + msg->getFrames().append(header); + msg->getFrames().append(content1); + msg->getFrames().append(content2); + + MessageProperties* mProps = msg->getFrames().getHeaders()->get<MessageProperties>(true); + mProps->setContentLength(data1.size() + data2.size()); + mProps->setMessageId(messageId); + FieldTable applicationHeaders; + applicationHeaders.setString("abc", "xyz"); + mProps->setApplicationHeaders(applicationHeaders); + DeliveryProperties* dProps = msg->getFrames().getHeaders()->get<DeliveryProperties>(true); + dProps->setRoutingKey(routingKey); + dProps->setDeliveryMode(PERSISTENT); + BOOST_CHECK(msg->isPersistent()); + + char* buff = static_cast<char*>(::alloca(msg->encodedSize())); + Buffer wbuffer(buff, msg->encodedSize()); + msg->encode(wbuffer); + + Buffer rbuffer(buff, msg->encodedSize()); + msg = new Message(); + msg->decodeHeader(rbuffer); + msg->decodeContent(rbuffer); + BOOST_CHECK_EQUAL(exchange, msg->getExchangeName()); + BOOST_CHECK_EQUAL(routingKey, msg->getRoutingKey()); + BOOST_CHECK_EQUAL((uint64_t) data1.size() + data2.size(), msg->contentSize()); + BOOST_CHECK_EQUAL((uint64_t) data1.size() + data2.size(), msg->getProperties<MessageProperties>()->getContentLength()); + BOOST_CHECK_EQUAL(messageId, msg->getProperties<MessageProperties>()->getMessageId()); + BOOST_CHECK_EQUAL(string("xyz"), msg->getProperties<MessageProperties>()->getApplicationHeaders().getAsString("abc")); + BOOST_CHECK_EQUAL((uint8_t) PERSISTENT, msg->getProperties<DeliveryProperties>()->getDeliveryMode()); + BOOST_CHECK(msg->isPersistent()); +} + +QPID_AUTO_TEST_SUITE_END() + diff --git a/RC9/qpid/cpp/src/tests/MessageUtils.h b/RC9/qpid/cpp/src/tests/MessageUtils.h new file mode 100644 index 0000000000..81508e534e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/MessageUtils.h @@ -0,0 +1,55 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/broker/Message.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/Uuid.h" + +using namespace qpid; +using namespace broker; +using namespace framing; + +struct MessageUtils +{ + static boost::intrusive_ptr<Message> createMessage(const string& exchange="", const string& routingKey="", + const Uuid& messageId=Uuid(true), uint64_t contentSize = 0) + { + boost::intrusive_ptr<Message> msg(new Message()); + + AMQFrame method(in_place<MessageTransferBody>(ProtocolVersion(), exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + + msg->getFrames().append(method); + msg->getFrames().append(header); + MessageProperties* props = msg->getFrames().getHeaders()->get<MessageProperties>(true); + props->setContentLength(contentSize); + props->setMessageId(messageId); + msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey); + return msg; + } + + static void addContent(boost::intrusive_ptr<Message> msg, const string& data) + { + AMQFrame content(in_place<AMQContentBody>(data)); + msg->getFrames().append(content); + } +}; diff --git a/RC9/qpid/cpp/src/tests/PollerTest.cpp b/RC9/qpid/cpp/src/tests/PollerTest.cpp new file mode 100644 index 0000000000..fcb1d0dadf --- /dev/null +++ b/RC9/qpid/cpp/src/tests/PollerTest.cpp @@ -0,0 +1,164 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/** + * Use socketpair to test the poller + */ + +#include "qpid/sys/Poller.h" + +#include <string> +#include <iostream> +#include <memory> +#include <exception> + +#include <assert.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +using namespace std; +using namespace qpid::sys; + +int writeALot(int fd, const string& s) { + int bytesWritten = 0; + do { + errno = 0; + int lastWrite = ::write(fd, s.c_str(), s.size()); + if ( lastWrite >= 0) { + bytesWritten += lastWrite; + } + } while (errno != EAGAIN); + return bytesWritten; +} + +int readALot(int fd) { + int bytesRead = 0; + char buf[1024]; + + do { + errno = 0; + int lastRead = ::read(fd, buf, sizeof(buf)); + if ( lastRead >= 0) { + bytesRead += lastRead; + } + } while (errno != EAGAIN); + return bytesRead; +} + +int main(int argc, char** argv) +{ + try + { + int sv[2]; + int rc = ::socketpair(AF_LOCAL, SOCK_STREAM, 0, sv); + assert(rc >= 0); + + // Set non-blocking + rc = ::fcntl(sv[0], F_SETFL, O_NONBLOCK); + assert(rc >= 0); + + rc = ::fcntl(sv[1], F_SETFL, O_NONBLOCK); + assert(rc >= 0); + + // Make up a large string + string testString = "This is only a test ... 1,2,3,4,5,6,7,8,9,10;"; + for (int i = 0; i < 6; i++) + testString += testString; + + // Read as much as we can from socket 0 + int bytesRead = readALot(sv[0]); + assert(bytesRead == 0); + cout << "Read(0): " << bytesRead << " bytes\n"; + + // Write as much as we can to socket 0 + int bytesWritten = writeALot(sv[0], testString); + cout << "Wrote(0): " << bytesWritten << " bytes\n"; + + // Read as much as we can from socket 1 + bytesRead = readALot(sv[1]); + assert(bytesRead == bytesWritten); + cout << "Read(1): " << bytesRead << " bytes\n"; + + auto_ptr<Poller> poller(new Poller); + + PollerHandle h0(sv[0]); + PollerHandle h1(sv[1]); + + poller->addFd(h0, Poller::INOUT); + + // Wait for 500ms - h0 should be writable + Poller::Event event = poller->wait(); + assert(event.handle == &h0); + assert(event.dir == Poller::OUT); + + // Write as much as we can to socket 0 + bytesWritten = writeALot(sv[0], testString); + cout << "Wrote(0): " << bytesWritten << " bytes\n"; + + // Wait for 500ms - h0 no longer writable + poller->rearmFd(h0); + event = poller->wait(500000000); + assert(event.handle == 0); + + // Test we can read it all now + poller->addFd(h1, Poller::INOUT); + event = poller->wait(); + assert(event.handle == &h1); + assert(event.dir == Poller::INOUT); + + bytesRead = readALot(sv[1]); + assert(bytesRead == bytesWritten); + cout << "Read(1): " << bytesRead << " bytes\n"; + + // At this point h1 should have been disabled from the poller + // (as it was just returned) and h0 can write again + event = poller->wait(); + assert(event.handle == &h0); + assert(event.dir == Poller::OUT); + + // Now both the handles should be disabled + event = poller->wait(500000000); + assert(event.handle == 0); + + // Test shutdown + poller->shutdown(); + event = poller->wait(); + assert(event.handle == 0); + assert(event.dir == Poller::SHUTDOWN); + + event = poller->wait(); + assert(event.handle == 0); + assert(event.dir == Poller::SHUTDOWN); + + poller->delFd(h1); + poller->delFd(h0); + + return 0; + } catch (exception& e) { + cout << "Caught exception " << e.what() << "\n"; + } +} + + diff --git a/RC9/qpid/cpp/src/tests/QueueOptionsTest.cpp b/RC9/qpid/cpp/src/tests/QueueOptionsTest.cpp new file mode 100644 index 0000000000..bac369bfec --- /dev/null +++ b/RC9/qpid/cpp/src/tests/QueueOptionsTest.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 <iostream> +#include "qpid/framing/Array.h" +#include "qpid/client/QueueOptions.h" +#include <alloca.h> + +#include "unit_test.h" + +using namespace qpid::client; + + +QPID_AUTO_TEST_SUITE(QueueOptionsTestSuite) + +QPID_AUTO_TEST_CASE(testSizePolicy) +{ + QueueOptions ft; + + ft.setSizePolicy(REJECT,1,2); + + BOOST_CHECK(QueueOptions::strREJECT == ft.getAsString(QueueOptions::strTypeKey)); + BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strMaxSizeKey)); + BOOST_CHECK(2 == ft.getAsInt(QueueOptions::strMaxCountKey)); + + ft.setSizePolicy(FLOW_TO_DISK,0,2); + BOOST_CHECK(QueueOptions::strFLOW_TO_DISK == ft.getAsString(QueueOptions::strTypeKey)); + BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strMaxSizeKey)); + BOOST_CHECK(2 == ft.getAsInt(QueueOptions::strMaxCountKey)); + + ft.setSizePolicy(RING,1,0); + BOOST_CHECK(QueueOptions::strRING == ft.getAsString(QueueOptions::strTypeKey)); + + ft.setSizePolicy(RING_STRICT,1,0); + BOOST_CHECK(QueueOptions::strRING_STRICT == ft.getAsString(QueueOptions::strTypeKey)); + + ft.clearSizePolicy(); + BOOST_CHECK(!ft.isSet(QueueOptions::strTypeKey)); + BOOST_CHECK(!ft.isSet(QueueOptions::strMaxSizeKey)); + BOOST_CHECK(!ft.isSet(QueueOptions::strMaxCountKey)); +} + +QPID_AUTO_TEST_CASE(testFlags) +{ + QueueOptions ft; + + ft.setPersistLastNode(); + ft.setOrdering(LVQ); + + BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strPersistLastNode)); + BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strLastValueQueue)); + + ft.clearPersistLastNode(); + ft.setOrdering(FIFO); + + BOOST_CHECK(!ft.isSet(QueueOptions::strPersistLastNode)); + BOOST_CHECK(!ft.isSet(QueueOptions::strLastValueQueue)); + +} + +QPID_AUTO_TEST_CASE(testSetOrdering) +{ + //ensure setOrdering(FIFO) works even if not preceded by a call to + //setOrdering(LVQ) + QueueOptions ft; + ft.setOrdering(FIFO); + BOOST_CHECK(!ft.isSet(QueueOptions::strLastValueQueue)); + +} + +QPID_AUTO_TEST_CASE(testClearPersistLastNode) +{ + //ensure clear works even if not preceded by the setting on the + //option + QueueOptions ft; + ft.clearPersistLastNode(); + BOOST_CHECK(!ft.isSet(QueueOptions::strPersistLastNode)); +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/QueuePolicyTest.cpp b/RC9/qpid/cpp/src/tests/QueuePolicyTest.cpp new file mode 100644 index 0000000000..6c650169c7 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/QueuePolicyTest.cpp @@ -0,0 +1,274 @@ + /* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "unit_test.h" +#include "test_tools.h" + +#include "qpid/broker/QueuePolicy.h" +#include "qpid/client/QueueOptions.h" +#include "qpid/sys/Time.h" +#include "qpid/framing/reply_exceptions.h" +#include "MessageUtils.h" +#include "BrokerFixture.h" + +using namespace qpid::broker; +using namespace qpid::client; +using namespace qpid::framing; + +QPID_AUTO_TEST_SUITE(QueuePolicyTestSuite) + +QueuedMessage createMessage(uint32_t size) +{ + QueuedMessage msg; + msg.payload = MessageUtils::createMessage(); + MessageUtils::addContent(msg.payload, std::string (size, 'x')); + return msg; +} + + +QPID_AUTO_TEST_CASE(testCount) +{ + std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy(5, 0)); + BOOST_CHECK_EQUAL((uint64_t) 0, policy->getMaxSize()); + BOOST_CHECK_EQUAL((uint32_t) 5, policy->getMaxCount()); + + QueuedMessage msg = createMessage(10); + for (size_t i = 0; i < 5; i++) { + policy->tryEnqueue(msg); + } + try { + policy->tryEnqueue(msg); + BOOST_FAIL("Policy did not fail on enqueuing sixth message"); + } catch (const ResourceLimitExceededException&) {} + + policy->dequeued(msg); + policy->tryEnqueue(msg); + + try { + policy->tryEnqueue(msg); + BOOST_FAIL("Policy did not fail on enqueuing sixth message (after dequeue)"); + } catch (const ResourceLimitExceededException&) {} +} + +QPID_AUTO_TEST_CASE(testSize) +{ + std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy(0, 50)); + QueuedMessage msg = createMessage(10); + + for (size_t i = 0; i < 5; i++) { + policy->tryEnqueue(msg); + } + try { + policy->tryEnqueue(msg); + BOOST_FAIL("Policy did not fail on aggregate size exceeding 50. " << *policy); + } catch (const ResourceLimitExceededException&) {} + + policy->dequeued(msg); + policy->tryEnqueue(msg); + + try { + policy->tryEnqueue(msg); + BOOST_FAIL("Policy did not fail on aggregate size exceeding 50 (after dequeue). " << *policy); + } catch (const ResourceLimitExceededException&) {} +} + +QPID_AUTO_TEST_CASE(testBoth) +{ + std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy(5, 50)); + try { + QueuedMessage msg = createMessage(51); + policy->tryEnqueue(msg); + BOOST_FAIL("Policy did not fail on single message exceeding 50. " << *policy); + } catch (const ResourceLimitExceededException&) {} + + std::vector<QueuedMessage> messages; + messages.push_back(createMessage(15)); + messages.push_back(createMessage(10)); + messages.push_back(createMessage(11)); + messages.push_back(createMessage(2)); + messages.push_back(createMessage(7)); + for (size_t i = 0; i < messages.size(); i++) { + policy->tryEnqueue(messages[i]); + } + //size = 45 at this point, count = 5 + try { + QueuedMessage msg = createMessage(5); + policy->tryEnqueue(msg); + BOOST_FAIL("Policy did not fail on count exceeding 6. " << *policy); + } catch (const ResourceLimitExceededException&) {} + try { + QueuedMessage msg = createMessage(10); + policy->tryEnqueue(msg); + BOOST_FAIL("Policy did not fail on aggregate size exceeding 50. " << *policy); + } catch (const ResourceLimitExceededException&) {} + + + policy->dequeued(messages[0]); + try { + QueuedMessage msg = createMessage(20); + policy->tryEnqueue(msg); + } catch (const ResourceLimitExceededException&) { + BOOST_FAIL("Policy failed incorrectly after dequeue. " << *policy); + } +} + +QPID_AUTO_TEST_CASE(testSettings) +{ + //test reading and writing the policy from/to field table + std::auto_ptr<QueuePolicy> a(QueuePolicy::createQueuePolicy(101, 303)); + FieldTable settings; + a->update(settings); + std::auto_ptr<QueuePolicy> b(QueuePolicy::createQueuePolicy(settings)); + BOOST_CHECK_EQUAL(a->getMaxCount(), b->getMaxCount()); + BOOST_CHECK_EQUAL(a->getMaxSize(), b->getMaxSize()); +} + +QPID_AUTO_TEST_CASE(testRingPolicy) +{ + FieldTable args; + std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy(5, 0, QueuePolicy::RING); + policy->update(args); + + ProxySessionFixture f; + std::string q("my-ring-queue"); + f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args); + for (int i = 0; i < 10; i++) { + f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); + } + client::Message msg; + for (int i = 5; i < 10; i++) { + BOOST_CHECK(f.subs.get(msg, q, qpid::sys::TIME_SEC)); + BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData()); + } + BOOST_CHECK(!f.subs.get(msg, q)); +} + +QPID_AUTO_TEST_CASE(testStrictRingPolicy) +{ + FieldTable args; + std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy(5, 0, QueuePolicy::RING_STRICT); + policy->update(args); + + ProxySessionFixture f; + std::string q("my-ring-queue"); + f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args); + LocalQueue incoming; + SubscriptionSettings settings(FlowControl::unlimited()); + settings.autoAck = 0; // no auto ack. + Subscription sub = f.subs.subscribe(incoming, q, settings); + for (int i = 0; i < 5; i++) { + f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); + } + for (int i = 0; i < 5; i++) { + BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str()); + } + try { + ScopedSuppressLogging sl; // Suppress messages for expected errors. + f.session.messageTransfer(arg::content=client::Message("Message_6", q)); + BOOST_FAIL("expecting ResourceLimitExceededException."); + } catch (const ResourceLimitExceededException&) {} +} + +QPID_AUTO_TEST_CASE(testPolicyWithDtx) +{ + FieldTable args; + std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy(5, 0, QueuePolicy::REJECT); + policy->update(args); + + ProxySessionFixture f; + std::string q("my-policy-queue"); + f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args); + LocalQueue incoming; + SubscriptionSettings settings(FlowControl::unlimited()); + settings.autoAck = 0; // no auto ack. + Subscription sub = f.subs.subscribe(incoming, q, settings); + f.session.dtxSelect(); + Xid tx1(1, "test-dtx-mgr", "tx1"); + f.session.dtxStart(arg::xid=tx1); + for (int i = 0; i < 5; i++) { + f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); + } + f.session.dtxEnd(arg::xid=tx1); + f.session.dtxCommit(arg::xid=tx1, arg::onePhase=true); + + Xid tx2(1, "test-dtx-mgr", "tx2"); + f.session.dtxStart(arg::xid=tx2); + for (int i = 0; i < 5; i++) { + BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str()); + } + SequenceSet accepting=sub.getUnaccepted(); + f.session.messageAccept(accepting); + f.session.dtxEnd(arg::xid=tx2); + f.session.dtxPrepare(arg::xid=tx2); + f.session.dtxRollback(arg::xid=tx2); + f.session.messageRelease(accepting); + + Xid tx3(1, "test-dtx-mgr", "tx3"); + f.session.dtxStart(arg::xid=tx3); + for (int i = 0; i < 5; i++) { + incoming.pop(); + } + accepting=sub.getUnaccepted(); + f.session.messageAccept(accepting); + f.session.dtxEnd(arg::xid=tx3); + f.session.dtxPrepare(arg::xid=tx3); + + Session other = f.connection.newSession(); + try { + ScopedSuppressLogging sl; // Suppress messages for expected errors. + other.messageTransfer(arg::content=client::Message("Message_6", q)); + BOOST_FAIL("expecting ResourceLimitExceededException."); + } catch (const ResourceLimitExceededException&) {} + + f.session.dtxCommit(arg::xid=tx3); + //now retry and this time should succeed + other = f.connection.newSession(); + other.messageTransfer(arg::content=client::Message("Message_6", q)); +} + +QPID_AUTO_TEST_CASE(testFlowToDiskWithNoStore) +{ + //Ensure that with no store loaded, we don't flow to disk but + //fallback to rejecting messages + QueueOptions args; + args.setSizePolicy(FLOW_TO_DISK, 0, 5); + + ProxySessionFixture f; + std::string q("my-queue"); + f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args); + LocalQueue incoming; + SubscriptionSettings settings(FlowControl::unlimited()); + settings.autoAck = 0; // no auto ack. + Subscription sub = f.subs.subscribe(incoming, q, settings); + for (int i = 0; i < 5; i++) { + f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); + } + for (int i = 0; i < 5; i++) { + BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str()); + } + try { + ScopedSuppressLogging sl; // Suppress messages for expected errors. + f.session.messageTransfer(arg::content=client::Message("Message_6", q)); + BOOST_FAIL("expecting ResourceLimitExceededException."); + } catch (const ResourceLimitExceededException&) {} +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/QueueRegistryTest.cpp b/RC9/qpid/cpp/src/tests/QueueRegistryTest.cpp new file mode 100644 index 0000000000..7ad4e0b89d --- /dev/null +++ b/RC9/qpid/cpp/src/tests/QueueRegistryTest.cpp @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qpid/broker/QueueRegistry.h" +#include "unit_test.h" +#include <string> + +using namespace qpid::broker; + +QPID_AUTO_TEST_SUITE(QueueRegistryTest) + +QPID_AUTO_TEST_CASE(testDeclare) +{ + std::string foo("foo"); + std::string bar("bar"); + QueueRegistry reg; + std::pair<Queue::shared_ptr, bool> qc; + + qc = reg.declare(foo, false, 0, 0); + Queue::shared_ptr q = qc.first; + BOOST_CHECK(q); + BOOST_CHECK(qc.second); // New queue + BOOST_CHECK_EQUAL(foo, q->getName()); + + qc = reg.declare(foo, false, 0, 0); + BOOST_CHECK_EQUAL(q, qc.first); + BOOST_CHECK(!qc.second); + + qc = reg.declare(bar, false, 0, 0); + q = qc.first; + BOOST_CHECK(q); + BOOST_CHECK_EQUAL(true, qc.second); + BOOST_CHECK_EQUAL(bar, q->getName()); +} + +QPID_AUTO_TEST_CASE(testDeclareTmp) +{ + QueueRegistry reg; + std::pair<Queue::shared_ptr, bool> qc; + + qc = reg.declare(std::string(), false, 0, 0); + BOOST_CHECK(qc.second); + BOOST_CHECK_EQUAL(std::string("tmp_1"), qc.first->getName()); +} + +QPID_AUTO_TEST_CASE(testFind) +{ + std::string foo("foo"); + std::string bar("bar"); + QueueRegistry reg; + std::pair<Queue::shared_ptr, bool> qc; + + BOOST_CHECK(reg.find(foo) == 0); + + reg.declare(foo, false, 0, 0); + reg.declare(bar, false, 0, 0); + Queue::shared_ptr q = reg.find(bar); + BOOST_CHECK(q); + BOOST_CHECK_EQUAL(bar, q->getName()); +} + +QPID_AUTO_TEST_CASE(testDestroy) +{ + std::string foo("foo"); + QueueRegistry reg; + std::pair<Queue::shared_ptr, bool> qc; + + qc = reg.declare(foo, false, 0, 0); + reg.destroy(foo); + // Queue is gone from the registry. + BOOST_CHECK(reg.find(foo) == 0); + // Queue is not actually destroyed till we drop our reference. + BOOST_CHECK_EQUAL(foo, qc.first->getName()); + // We shoud be the only reference. + BOOST_CHECK_EQUAL(1L, qc.first.use_count()); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/QueueTest.cpp b/RC9/qpid/cpp/src/tests/QueueTest.cpp new file mode 100644 index 0000000000..f1771e26cd --- /dev/null +++ b/RC9/qpid/cpp/src/tests/QueueTest.cpp @@ -0,0 +1,501 @@ + /* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES 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/Exception.h" +#include "qpid/broker/Broker.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/Deliverable.h" +#include "qpid/broker/ExchangeRegistry.h" +#include "qpid/broker/QueueRegistry.h" +#include "qpid/broker/NullMessageStore.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/client/QueueOptions.h" +#include <iostream> +#include "boost/format.hpp" + +using boost::intrusive_ptr; +using namespace qpid; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +class TestConsumer : public virtual Consumer{ +public: + typedef boost::shared_ptr<TestConsumer> shared_ptr; + + intrusive_ptr<Message> last; + bool received; + TestConsumer(bool acquire = true):Consumer(acquire), received(false) {}; + + virtual bool deliver(QueuedMessage& msg){ + last = msg.payload; + received = true; + return true; + }; + void notify() {} + OwnershipToken* getSession() { return 0; } +}; + +class FailOnDeliver : public Deliverable +{ + Message msg; +public: + void deliverTo(const boost::shared_ptr<Queue>& queue) + { + throw Exception(QPID_MSG("Invalid delivery to " << queue->getName())); + } + Message& getMessage() { return msg; } +}; + +intrusive_ptr<Message> create_message(std::string exchange, std::string routingKey) { + intrusive_ptr<Message> msg(new Message()); + AMQFrame method(in_place<MessageTransferBody>(ProtocolVersion(), exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + msg->getFrames().append(method); + msg->getFrames().append(header); + msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey); + return msg; +} + +QPID_AUTO_TEST_SUITE(QueueTestSuite) + +QPID_AUTO_TEST_CASE(testAsyncMessage) { + Queue::shared_ptr queue(new Queue("my_test_queue", true)); + intrusive_ptr<Message> received; + + TestConsumer::shared_ptr c1(new TestConsumer()); + queue->consume(c1); + + + //Test basic delivery: + intrusive_ptr<Message> msg1 = create_message("e", "A"); + msg1->enqueueAsync();//this is done on enqueue which is not called from process + queue->process(msg1); + sleep(2); + + BOOST_CHECK(!c1->received); + msg1->enqueueComplete(); + + received = queue->get().payload; + BOOST_CHECK_EQUAL(msg1.get(), received.get()); +} + + +QPID_AUTO_TEST_CASE(testAsyncMessageCount){ + Queue::shared_ptr queue(new Queue("my_test_queue", true)); + intrusive_ptr<Message> msg1 = create_message("e", "A"); + msg1->enqueueAsync();//this is done on enqueue which is not called from process + + queue->process(msg1); + sleep(2); + uint32_t compval=0; + BOOST_CHECK_EQUAL(compval, queue->getMessageCount()); + msg1->enqueueComplete(); + compval=1; + BOOST_CHECK_EQUAL(compval, queue->getMessageCount()); +} + +QPID_AUTO_TEST_CASE(testConsumers){ + Queue::shared_ptr queue(new Queue("my_queue", true)); + + //Test adding consumers: + TestConsumer::shared_ptr c1(new TestConsumer()); + TestConsumer::shared_ptr c2(new TestConsumer()); + queue->consume(c1); + queue->consume(c2); + + BOOST_CHECK_EQUAL(uint32_t(2), queue->getConsumerCount()); + + //Test basic delivery: + intrusive_ptr<Message> msg1 = create_message("e", "A"); + intrusive_ptr<Message> msg2 = create_message("e", "B"); + intrusive_ptr<Message> msg3 = create_message("e", "C"); + + queue->deliver(msg1); + BOOST_CHECK(queue->dispatch(c1)); + BOOST_CHECK_EQUAL(msg1.get(), c1->last.get()); + + queue->deliver(msg2); + BOOST_CHECK(queue->dispatch(c2)); + BOOST_CHECK_EQUAL(msg2.get(), c2->last.get()); + + c1->received = false; + queue->deliver(msg3); + BOOST_CHECK(queue->dispatch(c1)); + BOOST_CHECK_EQUAL(msg3.get(), c1->last.get()); + + //Test cancellation: + queue->cancel(c1); + BOOST_CHECK_EQUAL(uint32_t(1), queue->getConsumerCount()); + queue->cancel(c2); + BOOST_CHECK_EQUAL(uint32_t(0), queue->getConsumerCount()); +} + +QPID_AUTO_TEST_CASE(testRegistry){ + //Test use of queues in registry: + QueueRegistry registry; + registry.declare("queue1", true, true); + registry.declare("queue2", true, true); + registry.declare("queue3", true, true); + + BOOST_CHECK(registry.find("queue1")); + BOOST_CHECK(registry.find("queue2")); + BOOST_CHECK(registry.find("queue3")); + + registry.destroy("queue1"); + registry.destroy("queue2"); + registry.destroy("queue3"); + + BOOST_CHECK(!registry.find("queue1")); + BOOST_CHECK(!registry.find("queue2")); + BOOST_CHECK(!registry.find("queue3")); +} + +QPID_AUTO_TEST_CASE(testDequeue){ + Queue::shared_ptr queue(new Queue("my_queue", true)); + intrusive_ptr<Message> msg1 = create_message("e", "A"); + intrusive_ptr<Message> msg2 = create_message("e", "B"); + intrusive_ptr<Message> msg3 = create_message("e", "C"); + intrusive_ptr<Message> received; + + queue->deliver(msg1); + queue->deliver(msg2); + queue->deliver(msg3); + + BOOST_CHECK_EQUAL(uint32_t(3), queue->getMessageCount()); + + received = queue->get().payload; + BOOST_CHECK_EQUAL(msg1.get(), received.get()); + BOOST_CHECK_EQUAL(uint32_t(2), queue->getMessageCount()); + + received = queue->get().payload; + BOOST_CHECK_EQUAL(msg2.get(), received.get()); + BOOST_CHECK_EQUAL(uint32_t(1), queue->getMessageCount()); + + TestConsumer::shared_ptr consumer(new TestConsumer()); + queue->consume(consumer); + queue->dispatch(consumer); + if (!consumer->received) + sleep(2); + + BOOST_CHECK_EQUAL(msg3.get(), consumer->last.get()); + BOOST_CHECK_EQUAL(uint32_t(0), queue->getMessageCount()); + + received = queue->get().payload; + BOOST_CHECK(!received); + BOOST_CHECK_EQUAL(uint32_t(0), queue->getMessageCount()); + +} + +QPID_AUTO_TEST_CASE(testBound) +{ + //test the recording of bindings, and use of those to allow a queue to be unbound + string key("my-key"); + FieldTable args; + + Queue::shared_ptr queue(new Queue("my-queue", true)); + ExchangeRegistry exchanges; + //establish bindings from exchange->queue and notify the queue as it is bound: + Exchange::shared_ptr exchange1 = exchanges.declare("my-exchange-1", "direct").first; + exchange1->bind(queue, key, &args); + queue->bound(exchange1->getName(), key, args); + + Exchange::shared_ptr exchange2 = exchanges.declare("my-exchange-2", "fanout").first; + exchange2->bind(queue, key, &args); + queue->bound(exchange2->getName(), key, args); + + Exchange::shared_ptr exchange3 = exchanges.declare("my-exchange-3", "topic").first; + exchange3->bind(queue, key, &args); + queue->bound(exchange3->getName(), key, args); + + //delete one of the exchanges: + exchanges.destroy(exchange2->getName()); + exchange2.reset(); + + //unbind the queue from all exchanges it knows it has been bound to: + queue->unbind(exchanges, queue); + + //ensure the remaining exchanges don't still have the queue bound to them: + FailOnDeliver deliverable; + exchange1->route(deliverable, key, &args); + exchange3->route(deliverable, key, &args); +} + +QPID_AUTO_TEST_CASE(testPersistLastNodeStanding){ + + client::QueueOptions args; + args.setPersistLastNode(); + + Queue::shared_ptr queue(new Queue("my-queue", true)); + queue->configure(args); + + intrusive_ptr<Message> msg1 = create_message("e", "A"); + intrusive_ptr<Message> msg2 = create_message("e", "B"); + intrusive_ptr<Message> msg3 = create_message("e", "C"); + + //enqueue 2 messages + queue->deliver(msg1); + queue->deliver(msg2); + + //change mode + queue->setLastNodeFailure(); + + //enqueue 1 message + queue->deliver(msg3); + + //check all have persistent ids. + BOOST_CHECK(msg1->isPersistent()); + BOOST_CHECK(msg2->isPersistent()); + BOOST_CHECK(msg3->isPersistent()); + +} + +class TestMessageStoreOC : public NullMessageStore +{ + public: + + virtual void dequeue(TransactionContext*, + const boost::intrusive_ptr<PersistableMessage>& /*msg*/, + const PersistableQueue& /*queue*/) + { + } + + virtual void enqueue(TransactionContext*, + const boost::intrusive_ptr<PersistableMessage>& /*msg*/, + const PersistableQueue& /* queue */) + { + } + + TestMessageStoreOC() : NullMessageStore() {} + ~TestMessageStoreOC(){} +}; + + +QPID_AUTO_TEST_CASE(testLVQOrdering){ + + client::QueueOptions args; + // set queue mode + args.setOrdering(client::LVQ); + + Queue::shared_ptr queue(new Queue("my-queue", true )); + queue->configure(args); + + intrusive_ptr<Message> msg1 = create_message("e", "A"); + intrusive_ptr<Message> msg2 = create_message("e", "B"); + intrusive_ptr<Message> msg3 = create_message("e", "C"); + intrusive_ptr<Message> msg4 = create_message("e", "D"); + intrusive_ptr<Message> received; + + //set deliever match for LVQ a,b,c,a + + string key; + args.getLVQKey(key); + BOOST_CHECK_EQUAL(key, "qpid.LVQ_key"); + + + msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a"); + msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b"); + msg3->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c"); + msg4->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a"); + + //enqueue 4 message + queue->deliver(msg1); + queue->deliver(msg2); + queue->deliver(msg3); + queue->deliver(msg4); + + BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); + + received = queue->get().payload; + BOOST_CHECK_EQUAL(msg4.get(), received.get()); + + received = queue->get().payload; + BOOST_CHECK_EQUAL(msg2.get(), received.get()); + + received = queue->get().payload; + BOOST_CHECK_EQUAL(msg3.get(), received.get()); + + intrusive_ptr<Message> msg5 = create_message("e", "A"); + intrusive_ptr<Message> msg6 = create_message("e", "B"); + intrusive_ptr<Message> msg7 = create_message("e", "C"); + msg5->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a"); + msg6->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b"); + msg7->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c"); + queue->deliver(msg5); + queue->deliver(msg6); + queue->deliver(msg7); + + BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); + + received = queue->get().payload; + BOOST_CHECK_EQUAL(msg5.get(), received.get()); + + received = queue->get().payload; + BOOST_CHECK_EQUAL(msg6.get(), received.get()); + + received = queue->get().payload; + BOOST_CHECK_EQUAL(msg7.get(), received.get()); + +} + +QPID_AUTO_TEST_CASE(testLVQAcquire){ + + client::QueueOptions args; + // set queue mode + args.setOrdering(client::LVQ); + + Queue::shared_ptr queue(new Queue("my-queue", true )); + queue->configure(args); + + intrusive_ptr<Message> msg1 = create_message("e", "A"); + intrusive_ptr<Message> msg2 = create_message("e", "B"); + intrusive_ptr<Message> msg3 = create_message("e", "C"); + intrusive_ptr<Message> msg4 = create_message("e", "D"); + intrusive_ptr<Message> msg5 = create_message("e", "F"); + intrusive_ptr<Message> msg6 = create_message("e", "G"); + + //set deliever match for LVQ a,b,c,a + + string key; + args.getLVQKey(key); + BOOST_CHECK_EQUAL(key, "qpid.LVQ_key"); + + + msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a"); + msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b"); + msg3->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c"); + msg4->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a"); + msg5->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b"); + msg6->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c"); + + //enqueue 4 message + queue->deliver(msg1); + queue->deliver(msg2); + queue->deliver(msg3); + queue->deliver(msg4); + + BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); + + framing::SequenceNumber sequence(1); + QueuedMessage qmsg(queue.get(), msg1, sequence); + QueuedMessage qmsg2(queue.get(), msg2, ++sequence); + + BOOST_CHECK(!queue->acquire(qmsg)); + BOOST_CHECK(queue->acquire(qmsg2)); + + BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u); + + queue->deliver(msg5); + BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); + + // set mode to no browse and check + args.setOrdering(client::LVQ_NO_BROWSE); + queue->configure(args); + TestConsumer::shared_ptr c1(new TestConsumer(false)); + + queue->dispatch(c1); + queue->dispatch(c1); + queue->dispatch(c1); + + queue->deliver(msg6); + BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); + + intrusive_ptr<Message> received; + received = queue->get().payload; + BOOST_CHECK_EQUAL(msg4.get(), received.get()); + +} + +QPID_AUTO_TEST_CASE(testLVQMultiQueue){ + + client::QueueOptions args; + // set queue mode + args.setOrdering(client::LVQ); + + Queue::shared_ptr queue1(new Queue("my-queue", true )); + Queue::shared_ptr queue2(new Queue("my-queue", true )); + intrusive_ptr<Message> received; + queue1->configure(args); + queue2->configure(args); + + intrusive_ptr<Message> msg1 = create_message("e", "A"); + intrusive_ptr<Message> msg2 = create_message("e", "A"); + + string key; + args.getLVQKey(key); + BOOST_CHECK_EQUAL(key, "qpid.LVQ_key"); + + msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a"); + msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a"); + + queue1->deliver(msg1); + queue2->deliver(msg1); + queue1->deliver(msg2); + + received = queue1->get().payload; + BOOST_CHECK_EQUAL(msg2.get(), received.get()); + + received = queue2->get().payload; + BOOST_CHECK_EQUAL(msg1.get(), received.get()); + +} + +void addMessagesToQueue(uint count, Queue& queue, uint oddTtl = 200, uint evenTtl = 0) +{ + for (uint i = 0; i < count; i++) { + intrusive_ptr<Message> m = create_message("exchange", "key"); + if (i % 2) { + if (oddTtl) m->getProperties<DeliveryProperties>()->setTtl(oddTtl); + } else { + if (evenTtl) m->getProperties<DeliveryProperties>()->setTtl(evenTtl); + } + m->setTimestamp(); + queue.deliver(m); + } +} + +QPID_AUTO_TEST_CASE(testPurgeExpired) { + Queue queue("my-queue"); + addMessagesToQueue(10, queue); + BOOST_CHECK_EQUAL(queue.getMessageCount(), 10u); + ::usleep(300*1000); + queue.purgeExpired(); + BOOST_CHECK_EQUAL(queue.getMessageCount(), 5u); +} + +QPID_AUTO_TEST_CASE(testQueueCleaner) { + Timer timer; + QueueRegistry queues; + Queue::shared_ptr queue = queues.declare("my-queue").first; + addMessagesToQueue(10, *queue, 200, 400); + BOOST_CHECK_EQUAL(queue->getMessageCount(), 10u); + + QueueCleaner cleaner(queues, timer); + cleaner.start(100 * qpid::sys::TIME_MSEC); + ::usleep(300*1000); + BOOST_CHECK_EQUAL(queue->getMessageCount(), 5u); + ::usleep(300*1000); + BOOST_CHECK_EQUAL(queue->getMessageCount(), 0u); +} + +QPID_AUTO_TEST_SUITE_END() + + diff --git a/RC9/qpid/cpp/src/tests/README b/RC9/qpid/cpp/src/tests/README new file mode 100644 index 0000000000..0f4edee493 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/README @@ -0,0 +1,54 @@ += Running Qpid C++ tests = + +General philosophy is that "make check" run all tests by default, but +developers can run tests selectively as explained below. + +== Valgrind == + +By default we run tests under valgrind to detect memory errors if valgrind +is present. ./configure --disable-valgrind will disable it. + +Default valgrind options are specified in .valgrindrc-default, which a +checked-in file. The actual options used are in .valgrindrc which is a +local file. Normally it is a copy of valgrindrc-default but you can +modify at will. + +Supressed errors are listed in .valgrind.supp. If you want to change +suppressions for local testing, just modify .valgrindrc to point to a +different file. Do NOT add suppressions to .valgrindrc.supp unless +they are known problems outside of Qpid that can't reasonably be +worked around in Qpid. + + +== Unit Tests == +Unit tests use the boost test framework, and are compiled to rogram + unit_test +There are several options to control how test results are displayed, +see + http://www.boost.org/doc/libs/1_35_0/libs/test/doc/components/utf/parameters/index.html + +NOTE: some unit tests are written as CppUnit plug-ins, we are moving away +from CppUnit so new tests should use the boost framework. + +CppUnit tests are run by the script run-unit-tests. + +== System Tests == + +System tests are self contained AMQP client executables or scripts. +They are listed in the TESTS make variable, which can be over-ridden. + +The ./start_broker "test" launches the broker, ./stop_broker" stops it. +Tests in between assume the broker is running. + +./python_tests: runs ../python/run_tests. This is the main set of +system testss for the broker. + +Other C++ client test executables and scripts under client/test are +system tests for the client. + +By setting TESTS in a make command you can run a different subset of tests +against an already-running broker. + + + + diff --git a/RC9/qpid/cpp/src/tests/RangeSet.cpp b/RC9/qpid/cpp/src/tests/RangeSet.cpp new file mode 100644 index 0000000000..9c602de78d --- /dev/null +++ b/RC9/qpid/cpp/src/tests/RangeSet.cpp @@ -0,0 +1,141 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#include "unit_test.h" +#include "test_tools.h" +#include "qpid/RangeSet.h" + +using namespace std; +using namespace qpid; + +QPID_AUTO_TEST_SUITE(RangeSetTestSuite) + +typedef qpid::Range<int> TestRange; +typedef qpid::RangeSet<int> TestRangeSet; + +QPID_AUTO_TEST_CASE(testEmptyRange) { + TestRange r; + BOOST_CHECK(r.empty()); + BOOST_CHECK(!r.contains(0)); + // BOOST_CHECK(r.contiguous(0)); +} + +QPID_AUTO_TEST_CASE(testRangeSetAddPoint) { + TestRangeSet r; + BOOST_CHECK(r.empty()); + r += 3; + BOOST_CHECK_MESSAGE(r.contains(3), r); + BOOST_CHECK_MESSAGE(r.contains(TestRange(3,4)), r); + BOOST_CHECK(!r.empty()); + r += 5; + BOOST_CHECK_MESSAGE(r.contains(5), r); + BOOST_CHECK_MESSAGE(r.contains(TestRange(5,6)), r); + BOOST_CHECK_MESSAGE(!r.contains(TestRange(3,6)), r); + r += 4; + BOOST_CHECK_MESSAGE(r.contains(TestRange(3,6)), r); +} + +QPID_AUTO_TEST_CASE(testRangeSetAddRange) { + TestRangeSet r; + r += TestRange(0,3); + BOOST_CHECK(r.contains(TestRange(0,3))); + r += TestRange(4,6); + BOOST_CHECK_MESSAGE(r.contains(TestRange(4,6)), r); + r += 3; + BOOST_CHECK_MESSAGE(r.contains(TestRange(0,6)), r); + BOOST_CHECK(r.front() == 0); + BOOST_CHECK(r.back() == 6); +} + +QPID_AUTO_TEST_CASE(testRangeSetAddSet) { + TestRangeSet r; + TestRangeSet s = TestRangeSet(0,3)+TestRange(5,10); + r += s; + BOOST_CHECK_EQUAL(r,s); + r += TestRangeSet(3,5) + TestRange(7,12) + 15; + BOOST_CHECK_EQUAL(r, TestRangeSet(0,12) + 15); + + r.clear(); + BOOST_CHECK(r.empty()); + r += TestRange::makeClosed(6,10); + BOOST_CHECK_EQUAL(r, TestRangeSet(6,11)); + r += TestRangeSet(2,6)+8; + BOOST_CHECK_EQUAL(r, TestRangeSet(2,11)); +} + +QPID_AUTO_TEST_CASE(testRangeSetIterate) { + TestRangeSet r; + (((r += 1) += 10) += TestRange(4,7)) += 2; + BOOST_MESSAGE(r); + std::vector<int> actual; + std::copy(r.begin(), r.end(), std::back_inserter(actual)); + std::vector<int> expect = boost::assign::list_of(1)(2)(4)(5)(6)(10); + BOOST_CHECK_EQUAL(expect, actual); +} + +QPID_AUTO_TEST_CASE(testRangeSetRemove) { + // points + BOOST_CHECK_EQUAL(TestRangeSet(0,5)-3, TestRangeSet(0,3)+TestRange(4,5)); + BOOST_CHECK_EQUAL(TestRangeSet(1,5)-5, TestRangeSet(1,5)); + BOOST_CHECK_EQUAL(TestRangeSet(1,5)-0, TestRangeSet(1,5)); + + TestRangeSet r(TestRangeSet(0,5)+TestRange(10,15)+TestRange(20,25)); + + // TestRanges + BOOST_CHECK_EQUAL(r-TestRange(0,5), TestRangeSet(10,15)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(10,15), TestRangeSet(0,5)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(20,25), TestRangeSet(0,5)+TestRange(10,15)); + + BOOST_CHECK_EQUAL(r-TestRange(-5, 30), TestRangeSet()); + + BOOST_CHECK_EQUAL(r-TestRange(-5, 7), TestRangeSet(10,15)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(8,19), TestRangeSet(0,5)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(17,30), TestRangeSet(0,5)+TestRange(10,15)); + BOOST_CHECK_EQUAL(r-TestRange(17,30), TestRangeSet(0,5)+TestRange(10,15)); + + BOOST_CHECK_EQUAL(r-TestRange(-5, 5), TestRangeSet(10,15)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(10,19), TestRangeSet(0,5)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(18,25), TestRangeSet(0,5)+TestRange(10,15)); + BOOST_CHECK_EQUAL(r-TestRange(23,25), TestRangeSet(0,5)+TestRange(10,15)+TestRange(20,23)); + + BOOST_CHECK_EQUAL(r-TestRange(-3, 3), TestRangeSet(3,5)+TestRange(10,15)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(3, 7), TestRangeSet(0,2)+TestRange(10,15)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(3, 12), TestRangeSet(0,3)+TestRange(12,15)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(3, 22), TestRangeSet(12,15)+TestRange(22,25)); + BOOST_CHECK_EQUAL(r-TestRange(12, 22), TestRangeSet(0,5)+TestRange(10,11)+TestRange(22,25)); + + // Sets + BOOST_CHECK_EQUAL(r-(TestRangeSet(-1,6)+TestRange(11,14)+TestRange(23,25)), + TestRangeSet(10,11)+TestRange(14,15)+TestRange(20,23)); +} + +QPID_AUTO_TEST_CASE(testRangeContaining) { + TestRangeSet r; + (((r += 1) += TestRange(3,5)) += 7); + BOOST_CHECK_EQUAL(r.rangeContaining(0), TestRange(0,0)); + BOOST_CHECK_EQUAL(r.rangeContaining(1), TestRange(1,2)); + BOOST_CHECK_EQUAL(r.rangeContaining(2), TestRange(2,2)); + BOOST_CHECK_EQUAL(r.rangeContaining(3), TestRange(3,5)); + BOOST_CHECK_EQUAL(r.rangeContaining(4), TestRange(3,5)); + BOOST_CHECK_EQUAL(r.rangeContaining(5), TestRange(5,5)); + BOOST_CHECK_EQUAL(r.rangeContaining(6), TestRange(6,6)); + BOOST_CHECK_EQUAL(r.rangeContaining(7), TestRange(7,8)); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/RefCounted.cpp b/RC9/qpid/cpp/src/tests/RefCounted.cpp new file mode 100644 index 0000000000..8c679a3d2e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/RefCounted.cpp @@ -0,0 +1,50 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/RefCounted.h" +#include <boost/intrusive_ptr.hpp> + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(RefCountedTestSuiteTestSuite) + +using boost::intrusive_ptr; +using namespace std; +using namespace qpid; + +struct CountMe : public RefCounted { + static int instances; + CountMe() { ++instances; } + ~CountMe() { --instances; } +}; + +int CountMe::instances=0; + +QPID_AUTO_TEST_CASE(testRefCounted) { + BOOST_CHECK_EQUAL(0, CountMe::instances); + intrusive_ptr<CountMe> p(new CountMe()); + BOOST_CHECK_EQUAL(1, CountMe::instances); + intrusive_ptr<CountMe> q(p); + BOOST_CHECK_EQUAL(1, CountMe::instances); + q=0; + BOOST_CHECK_EQUAL(1, CountMe::instances); + p=0; + BOOST_CHECK_EQUAL(0, CountMe::instances); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/SequenceNumberTest.cpp b/RC9/qpid/cpp/src/tests/SequenceNumberTest.cpp new file mode 100644 index 0000000000..e4c6d066ef --- /dev/null +++ b/RC9/qpid/cpp/src/tests/SequenceNumberTest.cpp @@ -0,0 +1,205 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "unit_test.h" +#include <iostream> +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/SequenceNumberSet.h" + +using namespace qpid::framing; + + +void checkDifference(SequenceNumber& a, SequenceNumber& b, int gap) +{ + BOOST_CHECK_EQUAL(gap, a - b); + BOOST_CHECK_EQUAL(-gap, b - a); + + //increment until b wraps around + for (int i = 0; i < (gap + 2); i++, ++a, ++b) { + BOOST_CHECK_EQUAL(gap, a - b); + } + //keep incrementing until a also wraps around + for (int i = 0; i < (gap + 2); i++, ++a, ++b) { + BOOST_CHECK_EQUAL(gap, a - b); + } + //let b catch up and overtake + for (int i = 0; i < (gap*2); i++, ++b) { + BOOST_CHECK_EQUAL(gap - i, a - b); + BOOST_CHECK_EQUAL(i - gap, b - a); + } +} + +void checkComparison(SequenceNumber& a, SequenceNumber& b, int gap) +{ + //increment until b wraps around + for (int i = 0; i < (gap + 2); i++) { + BOOST_CHECK(++a < ++b);//test prefix + } + //keep incrementing until a also wraps around + for (int i = 0; i < (gap + 2); i++) { + BOOST_CHECK(a++ < b++);//test postfix + } + //let a 'catch up' + for (int i = 0; i < gap; i++) { + a++; + } + BOOST_CHECK(a == b); + BOOST_CHECK(++a > b); +} + + +QPID_AUTO_TEST_SUITE(SequenceNumberTestSuite) + +QPID_AUTO_TEST_CASE(testIncrementPostfix) +{ + SequenceNumber a; + SequenceNumber b; + BOOST_CHECK(!(a > b)); + BOOST_CHECK(!(b < a)); + BOOST_CHECK(a == b); + + SequenceNumber c = a++; + BOOST_CHECK(a > b); + BOOST_CHECK(b < a); + BOOST_CHECK(a != b); + BOOST_CHECK(c < a); + BOOST_CHECK(a != c); + + b++; + BOOST_CHECK(!(a > b)); + BOOST_CHECK(!(b < a)); + BOOST_CHECK(a == b); + BOOST_CHECK(c < b); + BOOST_CHECK(b != c); +} + +QPID_AUTO_TEST_CASE(testIncrementPrefix) +{ + SequenceNumber a; + SequenceNumber b; + BOOST_CHECK(!(a > b)); + BOOST_CHECK(!(b < a)); + BOOST_CHECK(a == b); + + SequenceNumber c = ++a; + BOOST_CHECK(a > b); + BOOST_CHECK(b < a); + BOOST_CHECK(a != b); + BOOST_CHECK(a == c); + + ++b; + BOOST_CHECK(!(a > b)); + BOOST_CHECK(!(b < a)); + BOOST_CHECK(a == b); +} + +QPID_AUTO_TEST_CASE(testWrapAround) +{ + const uint32_t max = 0xFFFFFFFF; + SequenceNumber a(max - 10); + SequenceNumber b(max - 5); + checkComparison(a, b, 5); + + const uint32_t max_signed = 0x7FFFFFFF; + SequenceNumber c(max_signed - 10); + SequenceNumber d(max_signed - 5); + checkComparison(c, d, 5); +} + +QPID_AUTO_TEST_CASE(testCondense) +{ + SequenceNumberSet set; + for (uint i = 0; i < 6; i++) { + set.push_back(SequenceNumber(i)); + } + set.push_back(SequenceNumber(7)); + for (uint i = 9; i < 13; i++) { + set.push_back(SequenceNumber(i)); + } + set.push_back(SequenceNumber(13)); + SequenceNumberSet actual = set.condense(); + + SequenceNumberSet expected; + expected.addRange(SequenceNumber(0), SequenceNumber(5)); + expected.addRange(SequenceNumber(7), SequenceNumber(7)); + expected.addRange(SequenceNumber(9), SequenceNumber(13)); + BOOST_CHECK_EQUAL(expected, actual); +} + +QPID_AUTO_TEST_CASE(testCondenseSingleRange) +{ + SequenceNumberSet set; + for (uint i = 0; i < 6; i++) { + set.push_back(SequenceNumber(i)); + } + SequenceNumberSet actual = set.condense(); + + SequenceNumberSet expected; + expected.addRange(SequenceNumber(0), SequenceNumber(5)); + BOOST_CHECK_EQUAL(expected, actual); +} + +QPID_AUTO_TEST_CASE(testCondenseSingleItem) +{ + SequenceNumberSet set; + set.push_back(SequenceNumber(1)); + SequenceNumberSet actual = set.condense(); + + SequenceNumberSet expected; + expected.addRange(SequenceNumber(1), SequenceNumber(1)); + BOOST_CHECK_EQUAL(expected, actual); +} + +QPID_AUTO_TEST_CASE(testDifference) +{ + SequenceNumber a; + SequenceNumber b; + + for (int i = 0; i < 10; i++, ++a) { + BOOST_CHECK_EQUAL(i, a - b); + BOOST_CHECK_EQUAL(-i, b - a); + } + + b = a; + + for (int i = 0; i < 10; i++, ++b) { + BOOST_CHECK_EQUAL(-i, a - b); + BOOST_CHECK_EQUAL(i, b - a); + } +} + +QPID_AUTO_TEST_CASE(testDifferenceWithWrapAround1) +{ + const uint32_t max = 0xFFFFFFFF; + SequenceNumber a(max - 5); + SequenceNumber b(max - 10); + checkDifference(a, b, 5); +} + +QPID_AUTO_TEST_CASE(testDifferenceWithWrapAround2) +{ + const uint32_t max_signed = 0x7FFFFFFF; + SequenceNumber c(max_signed - 5); + SequenceNumber d(max_signed - 10); + checkDifference(c, d, 5); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/SequenceSet.cpp b/RC9/qpid/cpp/src/tests/SequenceSet.cpp new file mode 100644 index 0000000000..ba2f1391a1 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/SequenceSet.cpp @@ -0,0 +1,140 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/framing/SequenceSet.h" +#include "unit_test.h" +#include <list> + +QPID_AUTO_TEST_SUITE(SequenceSetTestSuite) + +using namespace qpid::framing; + +struct RangeExpectations +{ + typedef std::pair<SequenceNumber, SequenceNumber> Range; + typedef std::list<Range> Ranges; + + Ranges ranges; + + RangeExpectations& expect(const SequenceNumber& start, const SequenceNumber& end) { + ranges.push_back(Range(start, end)); + return *this; + } + + void operator()(const SequenceNumber& start, const SequenceNumber& end) { + BOOST_CHECK(!ranges.empty()); + if (!ranges.empty()) { + BOOST_CHECK_EQUAL(start, ranges.front().first); + BOOST_CHECK_EQUAL(end, ranges.front().second); + ranges.pop_front(); + } + } + + void check(SequenceSet& set) { + set.for_each(*this); + BOOST_CHECK(ranges.empty()); + } +}; + +QPID_AUTO_TEST_CASE(testAdd) { + SequenceSet s; + s.add(2); + s.add(8,8); + s.add(3,5); + + for (uint32_t i = 0; i <= 1; i++) + BOOST_CHECK(!s.contains(i)); + + for (uint32_t i = 2; i <= 5; i++) + BOOST_CHECK(s.contains(i)); + + for (uint32_t i = 6; i <= 7; i++) + BOOST_CHECK(!s.contains(i)); + + BOOST_CHECK(s.contains(8)); + + for (uint32_t i = 9; i <= 10; i++) + BOOST_CHECK(!s.contains(i)); + + RangeExpectations().expect(2, 5).expect(8, 8).check(s); + + SequenceSet t; + t.add(6, 10); + t.add(s); + + for (uint32_t i = 0; i <= 1; i++) + BOOST_CHECK(!t.contains(i)); + + for (uint32_t i = 2; i <= 10; i++) + BOOST_CHECK_MESSAGE(t.contains(i), t << " contains " << i); + + RangeExpectations().expect(2, 10).check(t); +} + +QPID_AUTO_TEST_CASE(testAdd2) { + SequenceSet s; + s.add(7,6); + s.add(4,4); + s.add(3,10); + s.add(2); + RangeExpectations().expect(2, 10).check(s); +} + +QPID_AUTO_TEST_CASE(testRemove) { + SequenceSet s; + SequenceSet t; + s.add(0, 10); + t.add(0, 10); + + s.remove(7); + s.remove(3, 5); + s.remove(9, 10); + + t.remove(s); + + for (uint32_t i = 0; i <= 2; i++) { + BOOST_CHECK(s.contains(i)); + BOOST_CHECK(!t.contains(i)); + } + + for (uint32_t i = 3; i <= 5; i++) { + BOOST_CHECK(!s.contains(i)); + BOOST_CHECK(t.contains(i)); + } + + BOOST_CHECK(s.contains(6)); + BOOST_CHECK(!t.contains(6)); + + BOOST_CHECK(!s.contains(7)); + BOOST_CHECK(t.contains(7)); + + BOOST_CHECK(s.contains(8)); + BOOST_CHECK(!t.contains(8)); + + for (uint32_t i = 9; i <= 10; i++) { + BOOST_CHECK(!s.contains(i)); + BOOST_CHECK(t.contains(i)); + } + + RangeExpectations().expect(0, 2).expect(6, 6).expect(8, 8).check(s); + RangeExpectations().expect(3, 5).expect(7, 7).expect(9, 10).check(t); +} + +QPID_AUTO_TEST_SUITE_END() + + diff --git a/RC9/qpid/cpp/src/tests/SessionState.cpp b/RC9/qpid/cpp/src/tests/SessionState.cpp new file mode 100644 index 0000000000..2db25f9fe8 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/SessionState.cpp @@ -0,0 +1,300 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "unit_test.h" + +#include "qpid/SessionState.h" +#include "qpid/Exception.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/SessionFlushBody.h" + +#include <boost/bind.hpp> +#include <algorithm> +#include <functional> +#include <numeric> + +QPID_AUTO_TEST_SUITE(SessionStateTestSuite) + +using namespace std; +using namespace boost; +using namespace qpid::framing; + +// ================================================================ +// Utility functions. + +// Apply f to [begin, end) and accumulate the result +template <class Iter, class T, class F> +T applyAccumulate(Iter begin, Iter end, T seed, const F& f) { + return std::accumulate(begin, end, seed, bind(std::plus<T>(), _1, bind(f, _2))); +} + +// Create a frame with a one-char string. +AMQFrame& frame(char s) { + static AMQFrame frame; + frame.setBody(AMQContentBody(string(&s, 1))); + return frame; +} + +// Simple string representation of a frame. +string str(const AMQFrame& f) { + if (f.getMethod()) return "C"; // Command or Control + const AMQContentBody* c = dynamic_cast<const AMQContentBody*>(f.getBody()); + if (c) return c->getData(); // Return data for content frames. + return "H"; // Must be a header. +} +// Make a string from a range of frames. +string str(const boost::iterator_range<vector<AMQFrame>::const_iterator>& frames) { + string (*strFrame)(const AMQFrame&) = str; + return applyAccumulate(frames.begin(), frames.end(), string(), ptr_fun(strFrame)); +} +// Make a transfer command frame. +AMQFrame transferFrame(bool hasContent) { + AMQFrame t(in_place<MessageTransferBody>()); + t.setFirstFrame(true); + t.setLastFrame(true); + t.setFirstSegment(true); + t.setLastSegment(!hasContent); + return t; +} +// Make a content frame +AMQFrame contentFrame(string content, bool isLast=true) { + AMQFrame f(in_place<AMQContentBody>(content)); + f.setFirstFrame(true); + f.setLastFrame(true); + f.setFirstSegment(false); + f.setLastSegment(isLast); + return f; +} +AMQFrame contentFrameChar(char content, bool isLast=true) { + return contentFrame(string(1, content), isLast); +} + +// Send frame & return size of frame. +size_t send(qpid::SessionState& s, const AMQFrame& f) { s.senderRecord(f); return f.encodedSize(); } +// Send transfer command with no content. +size_t transfer0(qpid::SessionState& s) { return send(s, transferFrame(false)); } +// Send transfer frame with single content frame. +size_t transfer1(qpid::SessionState& s, string content) { + return send(s,transferFrame(true)) + send(s,contentFrame(content)); +} +size_t transfer1Char(qpid::SessionState& s, char content) { + return transfer1(s, string(1,content)); +} + +// Send transfer frame with multiple single-byte content frames. +size_t transferN(qpid::SessionState& s, string content) { + size_t size=send(s, transferFrame(!content.empty())); + if (!content.empty()) { + char last = content[content.size()-1]; + content.resize(content.size()-1); + size += applyAccumulate(content.begin(), content.end(), 0, + bind(&send, ref(s), + bind(contentFrameChar, _1, false))); + size += send(s, contentFrameChar(last, true)); + } + return size; +} + +// Send multiple transfers with single-byte content. +size_t transfers(qpid::SessionState& s, string content) { + return applyAccumulate(content.begin(), content.end(), 0, + bind(transfer1Char, ref(s), _1)); +} + +size_t contentFrameSize(size_t n=1) { return AMQFrame(in_place<AMQContentBody>()).encodedSize() + n; } +size_t transferFrameSize() { return AMQFrame(in_place<MessageTransferBody>()).encodedSize(); } + +// ==== qpid::SessionState test classes + +using qpid::SessionId; +using qpid::SessionPoint; + + +QPID_AUTO_TEST_CASE(testSendGetReplyList) { + qpid::SessionState s; + s.setTimeout(1); + s.senderGetCommandPoint(); + transfer1(s, "abc"); + transfers(s, "def"); + transferN(s, "xyz"); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(0,0))),"CabcCdCeCfCxyz"); + // Ignore controls. + s.senderRecord(AMQFrame(in_place<SessionFlushBody>())); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(2,0))),"CeCfCxyz"); +} + +QPID_AUTO_TEST_CASE(testNeedFlush) { + qpid::SessionState::Configuration c; + // sync after 2 1-byte transfers or equivalent bytes. + c.replayFlushLimit = 2*(transferFrameSize()+contentFrameSize()); + qpid::SessionState s(SessionId(), c); + s.setTimeout(1); + s.senderGetCommandPoint(); + transfers(s, "a"); + BOOST_CHECK(!s.senderNeedFlush()); + transfers(s, "b"); + BOOST_CHECK(s.senderNeedFlush()); + s.senderRecordFlush(); + BOOST_CHECK(!s.senderNeedFlush()); + transfers(s, "c"); + BOOST_CHECK(!s.senderNeedFlush()); + transfers(s, "d"); + BOOST_CHECK(s.senderNeedFlush()); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint())), "CaCbCcCd"); +} + +QPID_AUTO_TEST_CASE(testPeerConfirmed) { + qpid::SessionState::Configuration c; + // sync after 2 1-byte transfers or equivalent bytes. + c.replayFlushLimit = 2*(transferFrameSize()+contentFrameSize()); + qpid::SessionState s(SessionId(), c); + s.setTimeout(1); + s.senderGetCommandPoint(); + transfers(s, "ab"); + BOOST_CHECK(s.senderNeedFlush()); + transfers(s, "cd"); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(0,0))), "CaCbCcCd"); + s.senderConfirmed(SessionPoint(3)); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(3,0))), "Cd"); + BOOST_CHECK(!s.senderNeedFlush()); + + // Multi-frame transfer. + transfer1(s, "efg"); + transfers(s, "xy"); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(3,0))), "CdCefgCxCy"); + BOOST_CHECK(s.senderNeedFlush()); + + s.senderConfirmed(SessionPoint(4)); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(4,0))), "CefgCxCy"); + BOOST_CHECK(s.senderNeedFlush()); + + s.senderConfirmed(SessionPoint(5)); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(5,0))), "CxCy"); + BOOST_CHECK(s.senderNeedFlush()); + + s.senderConfirmed(SessionPoint(6)); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(6,0))), "Cy"); + BOOST_CHECK(!s.senderNeedFlush()); +} + +QPID_AUTO_TEST_CASE(testPeerCompleted) { + qpid::SessionState s; + s.setTimeout(1); + s.senderGetCommandPoint(); + // Completion implies confirmation + transfers(s, "abc"); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(0,0))), "CaCbCc"); + SequenceSet set(SequenceSet() + 0 + 1); + s.senderCompleted(set); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(2,0))), "Cc"); + + transfers(s, "def"); + // We dont do out-of-order confirmation, so this will only confirm up to 3: + set = SequenceSet(SequenceSet() + 2 + 3 + 5); + s.senderCompleted(set); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(4,0))), "CeCf"); +} + +QPID_AUTO_TEST_CASE(testReceive) { + // Advance expected/received correctly + qpid::SessionState s; + s.receiverSetCommandPoint(SessionPoint()); + BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(0)); + BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(0)); + + BOOST_CHECK(s.receiverRecord(transferFrame(false))); + BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(1)); + BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(1)); + + BOOST_CHECK(s.receiverRecord(transferFrame(true))); + SessionPoint point = SessionPoint(1, transferFrameSize()); + BOOST_CHECK_EQUAL(s.receiverGetExpected(), point); + BOOST_CHECK_EQUAL(s.receiverGetReceived(), point); + BOOST_CHECK(s.receiverRecord(contentFrame("", false))); + point.offset += contentFrameSize(0); + BOOST_CHECK_EQUAL(s.receiverGetExpected(), point); + BOOST_CHECK_EQUAL(s.receiverGetReceived(), point); + BOOST_CHECK(s.receiverRecord(contentFrame("", true))); + BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(2)); + BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(2)); + + // Idempotence barrier, rewind expected & receive some duplicates. + s.receiverSetCommandPoint(SessionPoint(1)); + BOOST_CHECK(!s.receiverRecord(transferFrame(false))); + BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(2)); + BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(2)); + BOOST_CHECK(s.receiverRecord(transferFrame(false))); + BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(3)); + BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(3)); +} + +QPID_AUTO_TEST_CASE(testCompleted) { + // completed & unknownCompleted + qpid::SessionState s; + s.receiverSetCommandPoint(SessionPoint()); + s.receiverRecord(transferFrame(false)); + s.receiverRecord(transferFrame(false)); + s.receiverRecord(transferFrame(false)); + s.receiverCompleted(1); + BOOST_CHECK_EQUAL(s.receiverGetUnknownComplete(), SequenceSet(SequenceSet()+1)); + s.receiverCompleted(0); + BOOST_CHECK_EQUAL(s.receiverGetUnknownComplete(), + SequenceSet(SequenceSet() + qpid::Range<SequenceNumber>(0,2))); + s.receiverKnownCompleted(SequenceSet(SequenceSet()+1)); + BOOST_CHECK_EQUAL(s.receiverGetUnknownComplete(), SequenceSet(SequenceSet()+2)); + // TODO aconway 2008-04-30: missing tests for known-completed. +} + +QPID_AUTO_TEST_CASE(testNeedKnownCompleted) { + size_t flushInterval= 2*(transferFrameSize()+contentFrameSize())+1; + qpid::SessionState::Configuration c(flushInterval); + qpid::SessionState s(qpid::SessionId(), c); + s.senderGetCommandPoint(); + transfers(s, "a"); + SequenceSet set(SequenceSet() + 0); + s.senderCompleted(set); + BOOST_CHECK(!s.senderNeedKnownCompleted()); + + transfers(s, "b"); + set += 1; + s.senderCompleted(set); + BOOST_CHECK(!s.senderNeedKnownCompleted()); + + transfers(s, "c"); + set += 2; + s.senderCompleted(set); + BOOST_CHECK(s.senderNeedKnownCompleted()); + s.senderRecordKnownCompleted(); + BOOST_CHECK(!s.senderNeedKnownCompleted()); + + transfers(s, "de"); + set += 3; + set += 4; + s.senderCompleted(set); + BOOST_CHECK(!s.senderNeedKnownCompleted()); + + transfers(s, "f"); + set += 2; + s.senderCompleted(set); + BOOST_CHECK(s.senderNeedKnownCompleted()); + s.senderRecordKnownCompleted(); + BOOST_CHECK(!s.senderNeedKnownCompleted()); +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/Shlib.cpp b/RC9/qpid/cpp/src/tests/Shlib.cpp new file mode 100644 index 0000000000..426a052c9f --- /dev/null +++ b/RC9/qpid/cpp/src/tests/Shlib.cpp @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "test_tools.h" +#include "qpid/sys/Shlib.h" +#include "qpid/Exception.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(ShlibTestSuite) + +using namespace qpid::sys; +typedef void (*CallMe)(int*); + +QPID_AUTO_TEST_CASE(testShlib) { + Shlib sh(".libs/libshlibtest.so"); + // Double cast to avoid ISO warning. + CallMe callMe=sh.getSymbol<CallMe>("callMe"); + BOOST_REQUIRE(callMe != 0); + int unloaded=0; + callMe(&unloaded); + sh.unload(); + BOOST_CHECK_EQUAL(42, unloaded); + try { + sh.getSymbol("callMe"); + BOOST_FAIL("Expected exception"); + } + catch (const qpid::Exception&) {} +} + +QPID_AUTO_TEST_CASE(testAutoShlib) { + int unloaded = 0; + { + AutoShlib sh(".libs/libshlibtest.so"); + CallMe callMe=sh.getSymbol<CallMe>("callMe"); + BOOST_REQUIRE(callMe != 0); + callMe(&unloaded); + } + BOOST_CHECK_EQUAL(42, unloaded); +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/SimpleTestCaseBase.cpp b/RC9/qpid/cpp/src/tests/SimpleTestCaseBase.cpp new file mode 100644 index 0000000000..2739734731 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/SimpleTestCaseBase.cpp @@ -0,0 +1,87 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "SimpleTestCaseBase.h" + +using namespace qpid; + +void SimpleTestCaseBase::start() +{ + if (worker.get()) { + worker->start(); + } +} + +void SimpleTestCaseBase::stop() +{ + if (worker.get()) { + worker->stop(); + } +} + +void SimpleTestCaseBase::report(client::Message& report) +{ + if (worker.get()) { + report.getHeaders().setInt("MESSAGE_COUNT", worker->getCount()); + //add number of messages sent or received + std::stringstream reportstr; + reportstr << worker->getCount(); + report.setData(reportstr.str()); + } +} + +SimpleTestCaseBase::Sender::Sender(ConnectionOptions& options, + const Exchange& _exchange, + const std::string& _key, + const int _messages) + : Worker(options, _messages), exchange(_exchange), key(_key) {} + +void SimpleTestCaseBase::Sender::init() +{ + channel.start(); +} + +void SimpleTestCaseBase::Sender::start(){ + Message msg; + while (count < messages) { + channel.publish(msg, exchange, key); + count++; + } + stop(); +} + +SimpleTestCaseBase::Worker::Worker(ConnectionOptions& options, const int _messages) : + messages(_messages), count(0) +{ + connection.open(options.host, options.port); + connection.openChannel(channel); +} + +void SimpleTestCaseBase::Worker::stop() +{ + channel.close(); + connection.close(); +} + +int SimpleTestCaseBase::Worker::getCount() +{ + return count; +} + diff --git a/RC9/qpid/cpp/src/tests/SimpleTestCaseBase.h b/RC9/qpid/cpp/src/tests/SimpleTestCaseBase.h new file mode 100644 index 0000000000..0c1052d0c2 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/SimpleTestCaseBase.h @@ -0,0 +1,89 @@ +#ifndef _SimpleTestCaseBase_ +#define _SimpleTestCaseBase_ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <memory> +#include <sstream> + +#include "qpid/Exception.h" +#include "qpid/client/Channel.h" +#include "qpid/client/Message.h" +#include "qpid/client/Connection.h" +#include "ConnectionOptions.h" +#include "qpid/client/MessageListener.h" +#include "TestCase.h" + + +namespace qpid { + +using namespace qpid::client; + +class SimpleTestCaseBase : public TestCase +{ +protected: + class Worker + { + protected: + client::Connection connection; + client::Channel channel; + const int messages; + int count; + + public: + + Worker(ConnectionOptions& options, const int messages); + virtual ~Worker(){} + + virtual void stop(); + virtual int getCount(); + virtual void init() = 0; + virtual void start() = 0; + }; + + class Sender : public Worker + { + const Exchange& exchange; + const std::string key; + public: + Sender(ConnectionOptions& options, + const Exchange& exchange, + const std::string& key, + const int messages); + void init(); + void start(); + }; + + std::auto_ptr<Worker> worker; + +public: + virtual void assign(const std::string& role, framing::FieldTable& params, ConnectionOptions& options) = 0; + + virtual ~SimpleTestCaseBase() {} + + void start(); + void stop(); + void report(client::Message& report); +}; + +} + +#endif diff --git a/RC9/qpid/cpp/src/tests/SocketProxy.h b/RC9/qpid/cpp/src/tests/SocketProxy.h new file mode 100644 index 0000000000..9722359d82 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/SocketProxy.h @@ -0,0 +1,143 @@ +#ifndef SOCKETPROXY_H +#define SOCKETPROXY_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Socket.h" +#include "qpid/sys/Poller.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Mutex.h" +#include "qpid/client/Connection.h" +#include "qpid/log/Statement.h" + +#include <algorithm> + +/** + * A simple socket proxy that forwards to another socket. + * Used between client & local broker to simulate network failures. + */ +class SocketProxy : private qpid::sys::Runnable +{ + public: + /** Connect to connectPort on host, start a forwarding thread. + * Listen for connection on getPort(). + */ + SocketProxy(int connectPort, const std::string host="localhost") + : closed(false), port(listener.listen()), dropClient(), dropServer() + { + client.connect(host, connectPort); + thread = qpid::sys::Thread(static_cast<qpid::sys::Runnable*>(this)); + } + + ~SocketProxy() { close(); } + + /** Simulate a network disconnect. */ + void close() { + { + qpid::sys::Mutex::ScopedLock l(lock); + if (closed) return; + closed=true; + } + poller.shutdown(); + if (thread.id() != qpid::sys::Thread::current().id()) + thread.join(); + client.close(); + } + + /** Simulate lost packets, drop data from client */ + void dropClientData(bool drop=true) { dropClient=drop; } + + /** Simulate lost packets, drop data from server */ + void dropServerData(bool drop=true) { dropServer=drop; } + + bool isClosed() const { + qpid::sys::Mutex::ScopedLock l(lock); + return closed; + } + + uint16_t getPort() const { return port; } + + private: + static void throwErrno(const std::string& msg) { + throw qpid::Exception(msg+":"+qpid::sys::strError(errno)); + } + static void throwIf(bool condition, const std::string& msg) { + if (condition) throw qpid::Exception(msg); + } + + void run() { + std::auto_ptr<qpid::sys::Socket> server; + try { + qpid::sys::PollerHandle listenerHandle(listener); + poller.addFd(listenerHandle, qpid::sys::Poller::INPUT); + qpid::sys::Poller::Event event = poller.wait(); + throwIf(event.type == qpid::sys::Poller::SHUTDOWN, "SocketProxy: Closed by close()"); + throwIf(!(event.type == qpid::sys::Poller::READABLE && event.handle == &listenerHandle), "SocketProxy: Accept failed"); + + poller.delFd(listenerHandle); + server.reset(listener.accept(0, 0)); + + // Pump data between client & server sockets + qpid::sys::PollerHandle clientHandle(client); + qpid::sys::PollerHandle serverHandle(*server); + poller.addFd(clientHandle, qpid::sys::Poller::INPUT); + poller.addFd(serverHandle, qpid::sys::Poller::INPUT); + char buffer[1024]; + for (;;) { + qpid::sys::Poller::Event event = poller.wait(); + throwIf(event.type == qpid::sys::Poller::SHUTDOWN, "SocketProxy: Closed by close()"); + throwIf(event.type == qpid::sys::Poller::DISCONNECTED, "SocketProxy: client/server disconnected"); + if (event.handle == &serverHandle) { + ssize_t n = server->read(buffer, sizeof(buffer)); + if (!dropServer) client.write(buffer, n); + poller.rearmFd(serverHandle); + } else if (event.handle == &clientHandle) { + ssize_t n = client.read(buffer, sizeof(buffer)); + if (!dropClient) server->write(buffer, n); + poller.rearmFd(clientHandle); + } else { + throwIf(true, "SocketProxy: No handle ready"); + } + } + } + catch (const std::exception& e) { + QPID_LOG(debug, "SocketProxy::run exception: " << e.what()); + } + try { + if (server.get()) server->close(); + close(); + } + catch (const std::exception& e) { + QPID_LOG(debug, "SocketProxy::run exception in client/server close()" << e.what()); + } + } + + mutable qpid::sys::Mutex lock; + bool closed; + qpid::sys::Poller poller; + qpid::sys::Socket client, listener; + uint16_t port; + qpid::sys::Thread thread; + bool dropClient, dropServer; +}; + +#endif diff --git a/RC9/qpid/cpp/src/tests/StringUtils.cpp b/RC9/qpid/cpp/src/tests/StringUtils.cpp new file mode 100644 index 0000000000..6a19119288 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/StringUtils.cpp @@ -0,0 +1,77 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include "qpid/StringUtils.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(StringUtilsTestSuite) + +using namespace qpid; +using std::string; + +QPID_AUTO_TEST_CASE(testSplit_general) +{ + std::vector<std::string> results = split("a bbbb, car,d123,,,e", ", "); + BOOST_CHECK_EQUAL(5u, results.size()); + BOOST_CHECK_EQUAL(string("a"), results[0]); + BOOST_CHECK_EQUAL(string("bbbb"), results[1]); + BOOST_CHECK_EQUAL(string("car"), results[2]); + BOOST_CHECK_EQUAL(string("d123"), results[3]); + BOOST_CHECK_EQUAL(string("e"), results[4]); +} + +QPID_AUTO_TEST_CASE(testSplit_noDelims) +{ + std::vector<std::string> results = split("abc", ", "); + BOOST_CHECK_EQUAL(1u, results.size()); + BOOST_CHECK_EQUAL(string("abc"), results[0]); +} + +QPID_AUTO_TEST_CASE(testSplit_delimAtEnd) +{ + std::vector<std::string> results = split("abc def,,", ", "); + BOOST_CHECK_EQUAL(2u, results.size()); + BOOST_CHECK_EQUAL(string("abc"), results[0]); + BOOST_CHECK_EQUAL(string("def"), results[1]); +} + +QPID_AUTO_TEST_CASE(testSplit_delimAtStart) +{ + std::vector<std::string> results = split(",,abc def", ", "); + BOOST_CHECK_EQUAL(2u, results.size()); + BOOST_CHECK_EQUAL(string("abc"), results[0]); + BOOST_CHECK_EQUAL(string("def"), results[1]); +} + +QPID_AUTO_TEST_CASE(testSplit_onlyDelims) +{ + std::vector<std::string> results = split(",, , ", ", "); + BOOST_CHECK_EQUAL(0u, results.size()); +} + +QPID_AUTO_TEST_CASE(testSplit_empty) +{ + std::vector<std::string> results = split("", ", "); + BOOST_CHECK_EQUAL(0u, results.size()); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/TestCase.h b/RC9/qpid/cpp/src/tests/TestCase.h new file mode 100644 index 0000000000..ba3330c951 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TestCase.h @@ -0,0 +1,64 @@ +#ifndef _TestCase_ +#define _TestCase_ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ConnectionOptions.h" +#include "qpid/client/Message.h" + + +namespace qpid { + +/** + * Interface to be implemented by test cases for use with the test + * runner. + */ +class TestCase +{ +public: + /** + * Directs the test case to act in a particular role. Some roles + * may be 'activated' at this stage others may require an explicit + * start request. + */ + virtual void assign(const std::string& role, framing::FieldTable& params, client::ConnectionOptions& options) = 0; + /** + * Each test will be started on its own thread, which should block + * until the test completes (this may or may not require an + * explicit stop() request). + */ + virtual void start() = 0; + /** + * Requests that the test be stopped if still running. + */ + virtual void stop() = 0; + /** + * Allows the test to fill in details on the final report + * message. Will be called only after start has returned. + */ + virtual void report(client::Message& report) = 0; + + virtual ~TestCase() {} +}; + +} + +#endif diff --git a/RC9/qpid/cpp/src/tests/TestMessageStore.h b/RC9/qpid/cpp/src/tests/TestMessageStore.h new file mode 100644 index 0000000000..be1ed57349 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TestMessageStore.h @@ -0,0 +1,58 @@ +#ifndef _tests_TestMessageStore_h +#define _tests_TestMessageStore_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/NullMessageStore.h" +#include <vector> + +using namespace qpid; +using namespace qpid::broker; +using namespace qpid::framing; + +typedef std::pair<string, boost::intrusive_ptr<PersistableMessage> > msg_queue_pair; + +class TestMessageStore : public NullMessageStore +{ + public: + std::vector<boost::intrusive_ptr<PersistableMessage> > dequeued; + std::vector<msg_queue_pair> enqueued; + + void dequeue(TransactionContext*, + const boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& /*queue*/) + { + dequeued.push_back(msg); + } + + void enqueue(TransactionContext*, + const boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue) + { + msg->enqueueComplete(); + enqueued.push_back(msg_queue_pair(queue.getName(), msg)); + } + + TestMessageStore() : NullMessageStore() {} + ~TestMessageStore(){} +}; + +#endif diff --git a/RC9/qpid/cpp/src/tests/TestOptions.h b/RC9/qpid/cpp/src/tests/TestOptions.h new file mode 100644 index 0000000000..a400fe5ecb --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TestOptions.h @@ -0,0 +1,79 @@ +#ifndef _TestOptions_ +#define _TestOptions_ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Options.h" +#include "qpid/log/Options.h" +#include "qpid/Url.h" +#include "qpid/log/Logger.h" +#include "qpid/client/Connection.h" +#include "ConnectionOptions.h" + +#include <iostream> +#include <exception> + +namespace qpid { + +struct TestOptions : public qpid::Options +{ + TestOptions(const std::string& helpText_=std::string(), + const std::string& argv0=std::string()) + : Options("Test Options"), help(false), log(argv0), helpText(helpText_) + { + addOptions() + ("help", optValue(help), "print this usage statement"); + add(con); + add(log); + } + + /** As well as parsing, throw help message if requested. */ + void parse(int argc, char** argv) { + try { + qpid::Options::parse(argc, argv); + } catch (const std::exception& e) { + std::ostringstream msg; + msg << *this << std::endl << std::endl << e.what() << std::endl; + throw qpid::Options::Exception(msg.str()); + } + qpid::log::Logger::instance().configure(log); + if (help) { + std::ostringstream msg; + msg << *this << std::endl << std::endl << helpText << std::endl; + throw qpid::Options::Exception(msg.str()); + } + } + + /** Open a connection using option values */ + void open(qpid::client::Connection& connection) { + connection.open(con); + } + + + bool help; + ConnectionOptions con; + qpid::log::Options log; + std::string helpText; +}; + +} + +#endif diff --git a/RC9/qpid/cpp/src/tests/TimerTest.cpp b/RC9/qpid/cpp/src/tests/TimerTest.cpp new file mode 100644 index 0000000000..50712ff79c --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TimerTest.cpp @@ -0,0 +1,120 @@ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/Timer.h" +#include "qpid/sys/Monitor.h" +#include "unit_test.h" +#include <math.h> +#include <iostream> +#include <memory> +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> + +using namespace qpid::broker; +using namespace qpid::sys; +using boost::intrusive_ptr; +using boost::dynamic_pointer_cast; + +class Counter +{ + Mutex lock; + uint counter; + public: + Counter() : counter(0) {} + uint next() + { + Mutex::ScopedLock l(lock); + return ++counter; + } +}; + +class TestTask : public TimerTask +{ + const AbsTime start; + const Duration expected; + AbsTime end; + bool fired; + uint position; + Monitor monitor; + Counter& counter; + + public: + TestTask(Duration timeout, Counter& _counter) + : TimerTask(timeout), start(now()), expected(timeout), end(start), fired(false), counter(_counter) {} + + void fire() + { + Monitor::ScopedLock l(monitor); + fired = true; + position = counter.next(); + end = now(); + monitor.notify(); + } + + void check(uint expected_position, uint64_t tolerance = 500 * TIME_MSEC) + { + Monitor::ScopedLock l(monitor); + BOOST_CHECK(fired); + BOOST_CHECK_EQUAL(expected_position, position); + Duration actual(start, end); + uint64_t difference = abs(expected - actual); + std::string msg(boost::lexical_cast<std::string>(boost::format("tolerance = %1%, difference = %2%") % tolerance % difference)); + BOOST_CHECK_MESSAGE(difference < tolerance, msg); + } + + void wait(Duration d) + { + Monitor::ScopedLock l(monitor); + monitor.wait(AbsTime(now(), d)); + } +}; + +class DummyRunner : public Runnable +{ + public: + void run() {} +}; + +QPID_AUTO_TEST_SUITE(TimerTestSuite) + +QPID_AUTO_TEST_CASE(testGeneral) +{ + Counter counter; + Timer timer; + intrusive_ptr<TestTask> task1(new TestTask(Duration(3 * TIME_SEC), counter)); + intrusive_ptr<TestTask> task2(new TestTask(Duration(1 * TIME_SEC), counter)); + intrusive_ptr<TestTask> task3(new TestTask(Duration(4 * TIME_SEC), counter)); + intrusive_ptr<TestTask> task4(new TestTask(Duration(2 * TIME_SEC), counter)); + + timer.add(task1); + timer.add(task2); + timer.add(task3); + timer.add(task4); + + dynamic_pointer_cast<TestTask>(task3)->wait(Duration(6 * TIME_SEC)); + + dynamic_pointer_cast<TestTask>(task1)->check(3); + dynamic_pointer_cast<TestTask>(task2)->check(1); + dynamic_pointer_cast<TestTask>(task3)->check(4); + dynamic_pointer_cast<TestTask>(task4)->check(2); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/TopicExchangeTest.cpp b/RC9/qpid/cpp/src/tests/TopicExchangeTest.cpp new file mode 100644 index 0000000000..af4263de34 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TopicExchangeTest.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/TopicExchange.h" +#include "unit_test.h" +#include "test_tools.h" + +using namespace qpid::broker; + +Tokens makeTokens(const char** begin, const char** end) +{ + Tokens t; + t.insert(t.end(), begin, end); + return t; +} + +// Calculate size of an array. +#define LEN(a) (sizeof(a)/sizeof(a[0])) + +// Convert array to token vector +#define TOKENS(a) makeTokens(a, a + LEN(a)) + +#define ASSERT_NORMALIZED(expect, pattern) \ + BOOST_CHECK_EQUAL(Tokens(expect), static_cast<Tokens>(TopicPattern(pattern))) + + +QPID_AUTO_TEST_SUITE(TopicExchangeTestSuite) + +QPID_AUTO_TEST_CASE(testTokens) +{ + Tokens tokens("hello.world"); + const char* expect[] = {"hello", "world"}; + BOOST_CHECK_EQUAL(TOKENS(expect), tokens); + + tokens = "a.b.c"; + const char* expect2[] = { "a", "b", "c" }; + BOOST_CHECK_EQUAL(TOKENS(expect2), tokens); + + tokens = ""; + BOOST_CHECK(tokens.empty()); + + tokens = "x"; + const char* expect3[] = { "x" }; + BOOST_CHECK_EQUAL(TOKENS(expect3), tokens); + + tokens = (".x"); + const char* expect4[] = { "", "x" }; + BOOST_CHECK_EQUAL(TOKENS(expect4), tokens); + + tokens = ("x."); + const char* expect5[] = { "x", "" }; + BOOST_CHECK_EQUAL(TOKENS(expect5), tokens); + + tokens = ("."); + const char* expect6[] = { "", "" }; + BOOST_CHECK_EQUAL(TOKENS(expect6), tokens); + + tokens = (".."); + const char* expect7[] = { "", "", "" }; + BOOST_CHECK_EQUAL(TOKENS(expect7), tokens); +} + +QPID_AUTO_TEST_CASE(testNormalize) +{ + BOOST_CHECK(TopicPattern("").empty()); + ASSERT_NORMALIZED("a.b.c", "a.b.c"); + ASSERT_NORMALIZED("a.*.c", "a.*.c"); + ASSERT_NORMALIZED("#", "#"); + ASSERT_NORMALIZED("#", "#.#.#.#"); + ASSERT_NORMALIZED("*.*.*.#", "#.*.#.*.#.#.*"); + ASSERT_NORMALIZED("a.*.*.*.#", "a.*.#.*.#.*.#"); + ASSERT_NORMALIZED("a.*.*.*.#", "a.*.#.*.#.*"); +} + +QPID_AUTO_TEST_CASE(testPlain) +{ + TopicPattern p("ab.cd.e"); + BOOST_CHECK(p.match("ab.cd.e")); + BOOST_CHECK(!p.match("abx.cd.e")); + BOOST_CHECK(!p.match("ab.cd")); + BOOST_CHECK(!p.match("ab.cd..e.")); + BOOST_CHECK(!p.match("ab.cd.e.")); + BOOST_CHECK(!p.match(".ab.cd.e")); + + p = ""; + BOOST_CHECK(p.match("")); + + p = "."; + BOOST_CHECK(p.match(".")); +} + + +QPID_AUTO_TEST_CASE(testStar) +{ + TopicPattern p("a.*.b"); + BOOST_CHECK(p.match("a.xx.b")); + BOOST_CHECK(!p.match("a.b")); + + p = "*.x"; + BOOST_CHECK(p.match("y.x")); + BOOST_CHECK(p.match(".x")); + BOOST_CHECK(!p.match("x")); + + p = "x.x.*"; + BOOST_CHECK(p.match("x.x.y")); + BOOST_CHECK(p.match("x.x.")); + BOOST_CHECK(!p.match("x.x")); + BOOST_CHECK(!p.match("q.x.y")); +} + +QPID_AUTO_TEST_CASE(testHash) +{ + TopicPattern p("a.#.b"); + BOOST_CHECK(p.match("a.b")); + BOOST_CHECK(p.match("a.x.b")); + BOOST_CHECK(p.match("a..x.y.zz.b")); + BOOST_CHECK(!p.match("a.b.")); + BOOST_CHECK(!p.match("q.x.b")); + + p = "a.#"; + BOOST_CHECK(p.match("a")); + BOOST_CHECK(p.match("a.b")); + BOOST_CHECK(p.match("a.b.c")); + + p = "#.a"; + BOOST_CHECK(p.match("a")); + BOOST_CHECK(p.match("x.y.a")); +} + +QPID_AUTO_TEST_CASE(testMixed) +{ + TopicPattern p("*.x.#.y"); + BOOST_CHECK(p.match("a.x.y")); + BOOST_CHECK(p.match("a.x.p.qq.y")); + BOOST_CHECK(!p.match("a.a.x.y")); + BOOST_CHECK(!p.match("aa.x.b.c")); + + p = "a.#.b.*"; + BOOST_CHECK(p.match("a.b.x")); + BOOST_CHECK(p.match("a.x.x.x.b.x")); +} + +QPID_AUTO_TEST_CASE(testCombo) +{ + TopicPattern p("*.#.#.*.*.#"); + BOOST_CHECK(p.match("x.y.z")); + BOOST_CHECK(p.match("x.y.z.a.b.c")); + BOOST_CHECK(!p.match("x.y")); + BOOST_CHECK(!p.match("x")); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/TxBufferTest.cpp b/RC9/qpid/cpp/src/tests/TxBufferTest.cpp new file mode 100644 index 0000000000..3d6a12cacc --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TxBufferTest.cpp @@ -0,0 +1,176 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/TxBuffer.h" +#include "unit_test.h" +#include <iostream> +#include <vector> +#include "TxMocks.h" + +using namespace qpid::broker; +using boost::static_pointer_cast; + +QPID_AUTO_TEST_SUITE(TxBufferTestSuite) + +QPID_AUTO_TEST_CASE(testCommitLocal) +{ + MockTransactionalStore store; + store.expectBegin().expectCommit(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectCommit(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectPrepare().expectPrepare().expectCommit().expectCommit();//opB enlisted twice to test relative order + MockTxOp::shared_ptr opC(new MockTxOp()); + opC->expectPrepare().expectCommit(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + buffer.enlist(static_pointer_cast<TxOp>(opB));//opB enlisted twice + buffer.enlist(static_pointer_cast<TxOp>(opC)); + + BOOST_CHECK(buffer.commitLocal(&store)); + store.check(); + BOOST_CHECK(store.isCommitted()); + opA->check(); + opB->check(); + opC->check(); +} + +QPID_AUTO_TEST_CASE(testFailOnCommitLocal) +{ + MockTransactionalStore store; + store.expectBegin().expectAbort(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp(true)); + opB->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opC(new MockTxOp());//will never get prepare as b will fail + opC->expectRollback(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + buffer.enlist(static_pointer_cast<TxOp>(opC)); + + BOOST_CHECK(!buffer.commitLocal(&store)); + BOOST_CHECK(store.isAborted()); + store.check(); + opA->check(); + opB->check(); + opC->check(); +} + +QPID_AUTO_TEST_CASE(testPrepare) +{ + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectPrepare(); + MockTxOp::shared_ptr opC(new MockTxOp()); + opC->expectPrepare(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + buffer.enlist(static_pointer_cast<TxOp>(opC)); + + BOOST_CHECK(buffer.prepare(0)); + opA->check(); + opB->check(); + opC->check(); +} + +QPID_AUTO_TEST_CASE(testFailOnPrepare) +{ + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare(); + MockTxOp::shared_ptr opB(new MockTxOp(true)); + opB->expectPrepare(); + MockTxOp::shared_ptr opC(new MockTxOp());//will never get prepare as b will fail + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + buffer.enlist(static_pointer_cast<TxOp>(opC)); + + BOOST_CHECK(!buffer.prepare(0)); + opA->check(); + opB->check(); + opC->check(); +} + +QPID_AUTO_TEST_CASE(testRollback) +{ + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp(true)); + opB->expectRollback(); + MockTxOp::shared_ptr opC(new MockTxOp()); + opC->expectRollback(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + buffer.enlist(static_pointer_cast<TxOp>(opC)); + + buffer.rollback(); + opA->check(); + opB->check(); + opC->check(); +} + +QPID_AUTO_TEST_CASE(testBufferIsClearedAfterRollback) +{ + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectRollback(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + + buffer.rollback(); + buffer.commit();//second call should not reach ops + opA->check(); + opB->check(); +} + +QPID_AUTO_TEST_CASE(testBufferIsClearedAfterCommit) +{ + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectCommit(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectCommit(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + + buffer.commit(); + buffer.rollback();//second call should not reach ops + opA->check(); + opB->check(); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/TxMocks.h b/RC9/qpid/cpp/src/tests/TxMocks.h new file mode 100644 index 0000000000..fe103c5fe5 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TxMocks.h @@ -0,0 +1,229 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _tests_TxMocks_h +#define _tests_TxMocks_h + + +#include "qpid/Exception.h" +#include "qpid/broker/TransactionalStore.h" +#include "qpid/broker/TxOp.h" +#include <iostream> +#include <vector> + +using namespace qpid::broker; +using boost::static_pointer_cast; +using std::string; + +template <class T> void assertEqualVector(std::vector<T>& expected, std::vector<T>& actual){ + unsigned int i = 0; + while(i < expected.size() && i < actual.size()){ + BOOST_CHECK_EQUAL(expected[i], actual[i]); + i++; + } + if (i < expected.size()) { + throw qpid::Exception(QPID_MSG("Missing " << expected[i])); + } else if (i < actual.size()) { + throw qpid::Exception(QPID_MSG("Extra " << actual[i])); + } + BOOST_CHECK_EQUAL(expected.size(), actual.size()); +} + +class TxOpConstants{ +protected: + const string PREPARE; + const string COMMIT; + const string ROLLBACK; + + TxOpConstants() : PREPARE("PREPARE"), COMMIT("COMMIT"), ROLLBACK("ROLLBACK") {} +}; + +class MockTxOp : public TxOp, public TxOpConstants{ + std::vector<string> expected; + std::vector<string> actual; + bool failOnPrepare; + string debugName; +public: + typedef boost::shared_ptr<MockTxOp> shared_ptr; + + MockTxOp() : failOnPrepare(false) {} + MockTxOp(bool _failOnPrepare) : failOnPrepare(_failOnPrepare) {} + + void setDebugName(string name){ + debugName = name; + } + + void printExpected(){ + std::cout << std::endl << "MockTxOp[" << debugName << "] expects: "; + for (std::vector<string>::iterator i = expected.begin(); i < expected.end(); i++) { + if(i != expected.begin()) std::cout << ", "; + std::cout << *i; + } + std::cout << std::endl; + } + + void printActual(){ + std::cout << std::endl << "MockTxOp[" << debugName << "] actual: "; + for (std::vector<string>::iterator i = actual.begin(); i < actual.end(); i++) { + if(i != actual.begin()) std::cout << ", "; + std::cout << *i; + } + std::cout << std::endl; + } + + bool prepare(TransactionContext*) throw(){ + actual.push_back(PREPARE); + return !failOnPrepare; + } + void commit() throw(){ + actual.push_back(COMMIT); + } + void rollback() throw(){ + if(!debugName.empty()) std::cout << std::endl << "MockTxOp[" << debugName << "]::rollback()" << std::endl; + actual.push_back(ROLLBACK); + } + MockTxOp& expectPrepare(){ + expected.push_back(PREPARE); + return *this; + } + MockTxOp& expectCommit(){ + expected.push_back(COMMIT); + return *this; + } + MockTxOp& expectRollback(){ + expected.push_back(ROLLBACK); + return *this; + } + void check(){ + assertEqualVector(expected, actual); + } + + void accept(TxOpConstVisitor&) const {} + + ~MockTxOp(){} +}; + +class MockTransactionalStore : public TransactionalStore{ + const string BEGIN; + const string BEGIN2PC; + const string PREPARE; + const string COMMIT; + const string ABORT; + std::vector<string> expected; + std::vector<string> actual; + + enum states {OPEN = 1, PREPARED = 2, COMMITTED = 3, ABORTED = 4}; + int state; + + class TestTransactionContext : public TPCTransactionContext{ + MockTransactionalStore* store; + public: + TestTransactionContext(MockTransactionalStore* _store) : store(_store) {} + void prepare(){ + if(!store->isOpen()) throw "txn already completed"; + store->state = PREPARED; + } + + void commit(){ + if(!store->isOpen() && !store->isPrepared()) throw "txn already completed"; + store->state = COMMITTED; + } + + void abort(){ + if(!store->isOpen() && !store->isPrepared()) throw "txn already completed"; + store->state = ABORTED; + } + ~TestTransactionContext(){} + }; + +public: + MockTransactionalStore() : + BEGIN("BEGIN"), BEGIN2PC("BEGIN2PC"), PREPARE("PREPARE"), COMMIT("COMMIT"), ABORT("ABORT"), state(OPEN){} + + void collectPreparedXids(std::set<std::string>&) + { + throw "Operation not supported"; + } + + std::auto_ptr<TPCTransactionContext> begin(const std::string&){ + actual.push_back(BEGIN2PC); + std::auto_ptr<TPCTransactionContext> txn(new TestTransactionContext(this)); + return txn; + } + std::auto_ptr<TransactionContext> begin(){ + actual.push_back(BEGIN); + std::auto_ptr<TransactionContext> txn(new TestTransactionContext(this)); + return txn; + } + void prepare(TPCTransactionContext& ctxt){ + actual.push_back(PREPARE); + dynamic_cast<TestTransactionContext&>(ctxt).prepare(); + } + void commit(TransactionContext& ctxt){ + actual.push_back(COMMIT); + dynamic_cast<TestTransactionContext&>(ctxt).commit(); + } + void abort(TransactionContext& ctxt){ + actual.push_back(ABORT); + dynamic_cast<TestTransactionContext&>(ctxt).abort(); + } + MockTransactionalStore& expectBegin(){ + expected.push_back(BEGIN); + return *this; + } + MockTransactionalStore& expectBegin2PC(){ + expected.push_back(BEGIN2PC); + return *this; + } + MockTransactionalStore& expectPrepare(){ + expected.push_back(PREPARE); + return *this; + } + MockTransactionalStore& expectCommit(){ + expected.push_back(COMMIT); + return *this; + } + MockTransactionalStore& expectAbort(){ + expected.push_back(ABORT); + return *this; + } + void check(){ + assertEqualVector(expected, actual); + } + + bool isPrepared(){ + return state == PREPARED; + } + + bool isCommitted(){ + return state == COMMITTED; + } + + bool isAborted(){ + return state == ABORTED; + } + + bool isOpen() const{ + return state == OPEN; + } + ~MockTransactionalStore(){} +}; + +#endif diff --git a/RC9/qpid/cpp/src/tests/TxPublishTest.cpp b/RC9/qpid/cpp/src/tests/TxPublishTest.cpp new file mode 100644 index 0000000000..9e9715c987 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TxPublishTest.cpp @@ -0,0 +1,94 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/NullMessageStore.h" +#include "qpid/broker/RecoveryManager.h" +#include "qpid/broker/TxPublish.h" +#include "unit_test.h" +#include <iostream> +#include <list> +#include <vector> +#include "MessageUtils.h" +#include "TestMessageStore.h" + +using std::list; +using std::pair; +using std::vector; +using boost::intrusive_ptr; +using namespace qpid::broker; +using namespace qpid::framing; + +struct TxPublishTest +{ + + TestMessageStore store; + Queue::shared_ptr queue1; + Queue::shared_ptr queue2; + intrusive_ptr<Message> msg; + TxPublish op; + + TxPublishTest() : + queue1(new Queue("queue1", false, &store, 0)), + queue2(new Queue("queue2", false, &store, 0)), + msg(MessageUtils::createMessage("exchange", "routing_key", "id")), + op(msg) + { + msg->getProperties<DeliveryProperties>()->setDeliveryMode(PERSISTENT); + op.deliverTo(queue1); + op.deliverTo(queue2); + } +}; + + +QPID_AUTO_TEST_SUITE(TxPublishTestSuite) + +QPID_AUTO_TEST_CASE(testPrepare) +{ + TxPublishTest t; + + intrusive_ptr<PersistableMessage> pmsg = static_pointer_cast<PersistableMessage>(t.msg); + //ensure messages are enqueued in store + t.op.prepare(0); + BOOST_CHECK_EQUAL((size_t) 2, t.store.enqueued.size()); + BOOST_CHECK_EQUAL(string("queue1"), t.store.enqueued[0].first); + BOOST_CHECK_EQUAL(pmsg, t.store.enqueued[0].second); + BOOST_CHECK_EQUAL(string("queue2"), t.store.enqueued[1].first); + BOOST_CHECK_EQUAL(pmsg, t.store.enqueued[1].second); + BOOST_CHECK_EQUAL( true, ( static_pointer_cast<PersistableMessage>(t.msg))->isEnqueueComplete()); +} + +QPID_AUTO_TEST_CASE(testCommit) +{ + TxPublishTest t; + + //ensure messages are delivered to queue + t.op.prepare(0); + t.op.commit(); + BOOST_CHECK_EQUAL((uint32_t) 1, t.queue1->getMessageCount()); + intrusive_ptr<Message> msg_dequeue = t.queue1->get().payload; + + BOOST_CHECK_EQUAL( true, (static_pointer_cast<PersistableMessage>(msg_dequeue))->isEnqueueComplete()); + BOOST_CHECK_EQUAL(t.msg, msg_dequeue); + + BOOST_CHECK_EQUAL((uint32_t) 1, t.queue2->getMessageCount()); + BOOST_CHECK_EQUAL(t.msg, t.queue2->get().payload); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/Url.cpp b/RC9/qpid/cpp/src/tests/Url.cpp new file mode 100644 index 0000000000..f3b42a7208 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/Url.cpp @@ -0,0 +1,67 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#include "unit_test.h" +#include "test_tools.h" +#include "qpid/Url.h" +#include <boost/assign.hpp> + +using namespace std; +using namespace qpid; +using namespace boost::assign; + +QPID_AUTO_TEST_SUITE(UrlTestSuite) + +#define URL_CHECK_STR(STR) BOOST_CHECK_EQUAL(Url(STR).str(), STR) +#define URL_CHECK_INVALID(STR) BOOST_CHECK_THROW(Url(STR), Url::Invalid) + +QPID_AUTO_TEST_CASE(TestParseTcp) { + URL_CHECK_STR("amqp:tcp:host:42"); + URL_CHECK_STR("amqp:tcp:host-._~%ff%23:42"); // unreserved chars and pct encoded hex. + + // Check defaults + BOOST_CHECK_EQUAL(Url("amqp:host:42").str(), "amqp:tcp:host:42"); + BOOST_CHECK_EQUAL(Url("amqp:tcp:host").str(), "amqp:tcp:host:5672"); + BOOST_CHECK_EQUAL(Url("amqp:tcp:").str(), "amqp:tcp:127.0.0.1:5672"); + BOOST_CHECK_EQUAL(Url("amqp:").str(), "amqp:tcp:127.0.0.1:5672"); + BOOST_CHECK_EQUAL(Url("amqp::42").str(), "amqp:tcp:127.0.0.1:42"); + + URL_CHECK_INVALID("amqp::badHost!#$#"); + URL_CHECK_INVALID("amqp::host:badPort"); +} + +QPID_AUTO_TEST_CASE(TestParseExample) { + URL_CHECK_STR("amqp:example:x"); + URL_CHECK_INVALID("amqp:example:badExample"); +} + +QPID_AUTO_TEST_CASE(TestParseMultiAddress) { + URL_CHECK_STR("amqp:tcp:host:0,example:y,tcp:foo:0,example:1"); + URL_CHECK_STR("amqp:example:z,tcp:foo:0"); + URL_CHECK_INVALID("amqp:tcp:h:0,"); + URL_CHECK_INVALID(",amqp:tcp:h"); +} + + +QPID_AUTO_TEST_CASE(TestInvalidAddress) { + URL_CHECK_INVALID("xxxx"); + URL_CHECK_INVALID(""); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/Uuid.cpp b/RC9/qpid/cpp/src/tests/Uuid.cpp new file mode 100644 index 0000000000..ee86d75a26 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/Uuid.cpp @@ -0,0 +1,79 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/framing/Uuid.h" +#include "qpid/framing/Buffer.h" + +#include "unit_test.h" + +#include <set> +#include <alloca.h> + +QPID_AUTO_TEST_SUITE(UuidTestSuite) + +using namespace std; +using namespace qpid::framing; + +struct UniqueSet : public std::set<Uuid> { + void operator()(const Uuid& uuid) { + BOOST_REQUIRE(find(uuid) == end()); + insert(uuid); + } +}; + +QPID_AUTO_TEST_CASE(testUuidCtor) { + // Uniqueness + boost::array<Uuid,1000> uuids; + for_each(uuids.begin(), uuids.end(), mem_fun_ref(&Uuid::generate)); + UniqueSet unique; + for_each(uuids.begin(), uuids.end(), unique); +} + +boost::array<uint8_t, 16> sample = {{'\x1b', '\x4e', '\x28', '\xba', '\x2f', '\xa1', '\x11', '\xd2', '\x88', '\x3f', '\xb9', '\xa7', '\x61', '\xbd', '\xe3', '\xfb'}}; +const string sampleStr("1b4e28ba-2fa1-11d2-883f-b9a761bde3fb"); + +QPID_AUTO_TEST_CASE(testUuidIstream) { + Uuid uuid; + istringstream in(sampleStr); + in >> uuid; + BOOST_CHECK(!in.fail()); + BOOST_CHECK(uuid == sample); +} + +QPID_AUTO_TEST_CASE(testUuidOstream) { + Uuid uuid(sample.c_array()); + ostringstream out; + out << uuid; + BOOST_CHECK(out.good()); + BOOST_CHECK_EQUAL(out.str(), sampleStr); +} + +QPID_AUTO_TEST_CASE(testUuidEncodeDecode) { + char* buff = static_cast<char*>(::alloca(Uuid::size())); + Buffer wbuf(buff, Uuid::size()); + Uuid uuid(sample.c_array()); + uuid.encode(wbuf); + + Buffer rbuf(buff, Uuid::size()); + Uuid decoded; + decoded.decode(rbuf); + BOOST_CHECK_EQUAL(string(sample.begin(), sample.end()), + string(decoded.begin(), decoded.end())); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/XmlClientSessionTest.cpp b/RC9/qpid/cpp/src/tests/XmlClientSessionTest.cpp new file mode 100644 index 0000000000..98558f0a76 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/XmlClientSessionTest.cpp @@ -0,0 +1,221 @@ +/* + * + * Licensed to the Apachef Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "unit_test.h" +#include "test_tools.h" +#include "BrokerFixture.h" +#include "qpid/sys/Shlib.h" +#include "qpid/sys/Monitor.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Runnable.h" +#include "qpid/framing/TransferContent.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/LocalQueue.h" +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionManager.h" + +#include <boost/optional.hpp> +#include <boost/lexical_cast.hpp> + +#include <vector> + +QPID_AUTO_TEST_SUITE(XmlClientSessionTest) + +using namespace qpid::client; + +using namespace qpid::client::arg; +using namespace qpid::framing; +using namespace qpid; +using qpid::sys::Shlib; +using qpid::sys::Monitor; +using std::string; +using std::cout; +using std::endl; + +Shlib shlib("../.libs/xml.so"); + +class SubscribedLocalQueue : public LocalQueue { + private: + SubscriptionManager& subscriptions; + public: + SubscribedLocalQueue(SubscriptionManager& subs) : subscriptions(subs) {} + Message get () { return pop(); } + virtual ~SubscribedLocalQueue() {} +}; + + +struct SimpleListener : public MessageListener +{ + Monitor lock; + std::vector<Message> messages; + + void received(Message& msg) + { + Monitor::ScopedLock l(lock); + messages.push_back(msg); + lock.notifyAll(); + } + + void waitFor(const uint n) + { + Monitor::ScopedLock l(lock); + while (messages.size() < n) { + lock.wait(); + } + } +}; + +struct ClientSessionFixture : public ProxySessionFixture +{ + void declareSubscribe(const string& q="odd_blue", + const string& dest="xml") + { + session.queueDeclare(queue=q); + session.messageSubscribe(queue=q, destination=dest, acquireMode=1); + session.messageFlow(destination=dest, unit=0, value=0xFFFFFFFF);//messages + session.messageFlow(destination=dest, unit=1, value=0xFFFFFFFF);//bytes + } +}; + +// ########### START HERE #################################### + + + +QPID_AUTO_TEST_CASE(testXmlBinding) { + ClientSessionFixture f; + + SubscriptionManager subscriptions(f.session); + SubscribedLocalQueue localQueue(subscriptions); + + f.session.exchangeDeclare(qpid::client::arg::exchange="xml", qpid::client::arg::type="xml"); + f.session.queueDeclare(qpid::client::arg::queue="odd_blue"); + subscriptions.subscribe(localQueue, "odd_blue"); + + FieldTable binding; + binding.setString("xquery", "declare variable $color external;" + "(./message/id mod 2 = 1) and ($color = 'blue')"); + f.session.exchangeBind(qpid::client::arg::exchange="xml", qpid::client::arg::queue="odd_blue", qpid::client::arg::bindingKey="query_name", qpid::client::arg::arguments=binding); + + Message message; + message.getDeliveryProperties().setRoutingKey("query_name"); + + message.getHeaders().setString("color", "blue"); + string m = "<message><id>1</id></message>"; + message.setData(m); + + f.session.messageTransfer(qpid::client::arg::content=message, qpid::client::arg::destination="xml"); + + Message m2 = localQueue.get(); + BOOST_CHECK_EQUAL(m, m2.getData()); +} + +/** + * Ensure that multiple queues can be bound using the same routing key + */ +QPID_AUTO_TEST_CASE(testXMLBindMultipleQueues) { + ClientSessionFixture f; + + + f.session.exchangeDeclare(arg::exchange="xml", arg::type="xml"); + f.session.queueDeclare(arg::queue="blue", arg::exclusive=true, arg::autoDelete=true); + f.session.queueDeclare(arg::queue="red", arg::exclusive=true, arg::autoDelete=true); + + FieldTable blue; + blue.setString("xquery", "./colour = 'blue'"); + f.session.exchangeBind(arg::exchange="xml", arg::queue="blue", arg::bindingKey="by-colour", arg::arguments=blue); + FieldTable red; + red.setString("xquery", "./colour = 'red'"); + f.session.exchangeBind(arg::exchange="xml", arg::queue="red", arg::bindingKey="by-colour", arg::arguments=red); + + Message sent1("<colour>blue</colour>", "by-colour"); + f.session.messageTransfer(arg::content=sent1, arg::destination="xml"); + + Message sent2("<colour>red</colour>", "by-colour"); + f.session.messageTransfer(arg::content=sent2, arg::destination="xml"); + + Message received; + BOOST_CHECK(f.subs.get(received, "blue")); + BOOST_CHECK_EQUAL(sent1.getData(), received.getData()); + BOOST_CHECK(f.subs.get(received, "red")); + BOOST_CHECK_EQUAL(sent2.getData(), received.getData()); +} + +//### Test: Bad XML does not kill the server - and does not even +// raise an exception, the content is not required to be XML. + +QPID_AUTO_TEST_CASE(testXMLSendBadXML) { + ClientSessionFixture f; + + f.session.exchangeDeclare(arg::exchange="xml", arg::type="xml"); + f.session.queueDeclare(arg::queue="blue", arg::exclusive=true, arg::autoDelete=true)\ + ; + f.session.queueDeclare(arg::queue="red", arg::exclusive=true, arg::autoDelete=true); + + FieldTable blue; + blue.setString("xquery", "./colour = 'blue'"); + f.session.exchangeBind(arg::exchange="xml", arg::queue="blue", arg::bindingKey="by-c\ +olour", arg::arguments=blue); + FieldTable red; + red.setString("xquery", "./colour = 'red'"); + f.session.exchangeBind(arg::exchange="xml", arg::queue="red", arg::bindingKey="by-co\ +lour", arg::arguments=red); + + Message sent1("<>colour>blue</colour>", "by-colour"); + f.session.messageTransfer(arg::content=sent1, arg::destination="xml"); + + BOOST_CHECK_EQUAL(1, 1); +} + + +//### Test: Bad XQuery does not kill the server, but does raise an exception + +QPID_AUTO_TEST_CASE(testXMLBadXQuery) { + ClientSessionFixture f; + + f.session.exchangeDeclare(arg::exchange="xml", arg::type="xml"); + f.session.queueDeclare(arg::queue="blue", arg::exclusive=true, arg::autoDelete=true)\ + ; + + try { + ScopedSuppressLogging sl; // Supress logging of error messages for expected error. + FieldTable blue; + blue.setString("xquery", "./colour $=! 'blue'"); + f.session.exchangeBind(arg::exchange="xml", arg::queue="blue", arg::bindingKey="by-c\ +olour", arg::arguments=blue); + } + catch (const InternalErrorException& e) { + return; + } + BOOST_ERROR("A bad XQuery must raise an exception when used in an XML Binding."); + +} + + +//### Test: Each session can provide its own definition for a query name + + + +//### Test: Bindings persist, surviving broker restart + +QPID_AUTO_TEST_SUITE_END() + diff --git a/RC9/qpid/cpp/src/tests/acl.py b/RC9/qpid/cpp/src/tests/acl.py new file mode 100755 index 0000000000..671b2fe247 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/acl.py @@ -0,0 +1,459 @@ +#!/usr/bin/env python +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import sys +import qpid +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import uuid4 +from qpid.testlib import TestBase010, testrunner +from qmf.console import Session +from qpid.datatypes import Message + +def scan_args(name, default=None, args=sys.argv[1:]): + if (name in args): + pos = args.index(name) + return args[pos + 1] + elif default: + return default + else: + print "Please specify extra argument: %s" % name + sys.exit(2) + +def extract_args(name, args): + if (name in args): + pos = args.index(name) + del args[pos:pos+2] + else: + return None + +def get_broker_port(): + return scan_args("--port", "5672") + +def get_session(user, passwd): + socket = connect('127.0.0.1', int(get_broker_port())) + connection = Connection (sock=socket, username=user, password=passwd) + connection.start() + return connection.session(str(uuid4())) + +class ACLFile: + def __init__(self): + self.f = open('data_dir/policy.acl','w'); + + def write(self,line): + self.f.write(line) + + def close(self): + self.f.close() + +class ACLTests(TestBase010): + + def reload_acl(self): + acl = self.qmf.getObjects(_class="acl")[0] + return acl.reloadACLFile() + + def setUp(self): + aclf = ACLFile() + aclf.write('acl allow all all\n') + aclf.close() + TestBase010.setUp(self) + self.startQmf() + self.reload_acl() + + #===================================== + # ACL general tests + #===================================== + + def test_deny_all(self): + """ + Test the deny all mode + """ + aclf = ACLFile() + aclf.write('acl allow guest@QPID all all\n') + aclf.write('acl allow bob@QPID create queue\n') + aclf.write('acl deny all all') + aclf.close() + + self.reload_acl() + + session = get_session('bob','bob') + try: + session.queue_declare(queue="deny_queue") + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue create request"); + self.fail("Error during queue create request"); + + try: + session.exchange_bind(exchange="amq.direct", queue="deny_queue", binding_key="routing_key") + self.fail("ACL should deny queue bind request"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + + def test_allow_all(self): + """ + Test the allow all mode + """ + aclf = ACLFile() + aclf.write('acl deny bob@QPID bind exchange\n') + aclf.write('acl allow all all') + aclf.close() + + self.reload_acl() + + session = get_session('bob','bob') + try: + session.queue_declare(queue="allow_queue") + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue create request"); + self.fail("Error during queue create request"); + + try: + session.exchange_bind(exchange="amq.direct", queue="allow_queue", binding_key="routing_key") + self.fail("ACL should deny queue bind request"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + + + #===================================== + # ACL file format tests + #===================================== + + def test_empty_groups(self): + """ + Test empty groups + """ + aclf = ACLFile() + aclf.write('acl group\n') + aclf.write('acl group admins bob@QPID joe@QPID\n') + aclf.write('acl allow all all') + aclf.close() + + result = self.reload_acl() + if (result.text.find("Insufficient tokens for acl definition",0,len(result.text)) == -1): + self.fail("ACL Reader should reject the acl file due to empty group name") + + def test_illegal_acl_formats(self): + """ + Test illegal acl formats + """ + aclf = ACLFile() + aclf.write('acl group admins bob@QPID joe@QPID\n') + aclf.write('acl allow all all') + aclf.close() + + result = self.reload_acl() + if (result.text.find("Unknown ACL permission",0,len(result.text)) == -1): + self.fail(result) + + def test_illegal_extension_lines(self): + """ + Test illegal extension lines + """ + + aclf = ACLFile() + aclf.write('group admins bob@QPID \ ') + aclf.write(' \ \n') + aclf.write('joe@QPID \n') + aclf.write('acl allow all all') + aclf.close() + + result = self.reload_acl() + if (result.text.find("contains illegal characters",0,len(result.text)) == -1): + self.fail(result) + + + + #===================================== + # ACL queue tests + #===================================== + + def test_queue_acl(self): + """ + Test various modes for queue acl + """ + aclf = ACLFile() + aclf.write('acl deny bob@QPID create queue name=q1 durable=true passive=true\n') + aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true\n') + aclf.write('acl deny bob@QPID access queue name=q3\n') + aclf.write('acl deny bob@QPID purge queue name=q3\n') + aclf.write('acl deny bob@QPID delete queue name=q4\n') + aclf.write('acl allow all all') + aclf.close() + + self.reload_acl() + + session = get_session('bob','bob') + + try: + session.queue_declare(queue="q1", durable='true', passive='true') + self.fail("ACL should deny queue create request with name=q1 durable=true passive=true"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.queue_declare(queue="q2", exclusive='true') + self.fail("ACL should deny queue create request with name=q2 exclusive=true"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.queue_declare(queue="q3", exclusive='true') + session.queue_declare(queue="q4", durable='true') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue create request for q3 and q4 with any parameter"); + + try: + session.queue_query(queue="q3") + self.fail("ACL should deny queue query request for q3"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.queue_purge(queue="q3") + self.fail("ACL should deny queue purge request for q3"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.queue_purge(queue="q4") + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue purge request for q4"); + + try: + session.queue_delete(queue="q4") + self.fail("ACL should deny queue delete request for q4"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.queue_delete(queue="q3") + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue delete request for q3"); + + #===================================== + # ACL exchange tests + #===================================== + + def test_exchange_acl(self): + """ + Test various modes for exchange acl + """ + aclf = ACLFile() + aclf.write('acl deny bob@QPID create exchange name=testEx durable=true passive=true\n') + aclf.write('acl deny bob@QPID create exchange name=ex1 type=direct\n') + aclf.write('acl deny bob@QPID access exchange name=myEx\n') + aclf.write('acl deny bob@QPID bind exchange name=myEx queuename=q1 routingkey=rk1\n') + aclf.write('acl deny bob@QPID unbind exchange name=myEx queuename=q1 routingkey=rk1\n') + aclf.write('acl deny bob@QPID delete exchange name=myEx\n') + aclf.write('acl allow all all') + aclf.close() + + self.reload_acl() + + session = get_session('bob','bob') + + try: + session.exchange_declare(exchange='testEx', durable='true', passive='true') + self.fail("ACL should deny exchange create request with name=testEx durable=true passive=true"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.exchange_declare(exchange='ex1', type='direct') + self.fail("ACL should deny exchange create request with name=ex1 type=direct"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.exchange_declare(exchange='myXml', type='direct') + session.queue_declare(queue='q1') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange create request for myXml with any parameter"); + + try: + session.exchange_query(name='myEx') + self.fail("ACL should deny queue query request for q3"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.exchange_bind(exchange='myEx', queue='q1', binding_key='rk1') + self.fail("ACL should deny exchange bind request with exchange='myEx' queuename='q1' bindingkey='rk1'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.exchange_bind(exchange='myXml', queue='q1', binding_key='x') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange bind request for exchange='myXml', queue='q1', binding_key='x'"); + try: + session.exchange_unbind(exchange='myEx', queue='q1', binding_key='rk1') + self.fail("ACL should deny exchange unbind request with exchange='myEx' queuename='q1' bindingkey='rk1'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.exchange_unbind(exchange='myXml', queue='q1', binding_key='x') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange unbind request for exchange='myXml', queue='q1', binding_key='x'"); + + try: + session.exchange_delete(exchange='myEx') + self.fail("ACL should deny exchange delete request for myEx"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.exchange_delete(exchange='myXml') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange delete request for myXml"); + + + #===================================== + # ACL consume tests + #===================================== + + def test_consume_acl(self): + """ + Test various consume acl + """ + aclf = ACLFile() + aclf.write('acl deny bob@QPID consume queue name=q1 durable=true\n') + aclf.write('acl deny bob@QPID consume queue name=q2 exclusive=true\n') + aclf.write('acl allow all all') + aclf.close() + + self.reload_acl() + + session = get_session('bob','bob') + + + try: + session.queue_declare(queue='q1', durable='true') + session.queue_declare(queue='q2', exclusive='true') + session.queue_declare(queue='q3', durable='true') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow create queue request"); + + try: + session.message_subscribe(queue='q1', destination='myq1') + self.fail("ACL should deny message subscriber request for queue='q1'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.message_subscribe(queue='q2', destination='myq1') + self.fail("ACL should deny message subscriber request for queue='q2'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.message_subscribe(queue='q3', destination='myq1') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow create message subscribe"); + + + #===================================== + # ACL publish tests + #===================================== + + def test_publish_acl(self): + """ + Test various publish acl + """ + aclf = ACLFile() + aclf.write('acl deny bob@QPID publish exchange name=amq.direct routingkey=rk1\n') + aclf.write('acl deny bob@QPID publish exchange name=amq.topic\n') + aclf.write('acl deny bob@QPID publish exchange name=myEx routingkey=rk2\n') + aclf.write('acl allow all all') + aclf.close() + + self.reload_acl() + + session = get_session('bob','bob') + + try: + session.exchange_declare(exchange='myEx', type='topic') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange create request for myEx with any parameter"); + + props = session.delivery_properties(routing_key="rk1") + + try: + session.message_transfer(destination="amq.direct", message=Message(props,"Test")) + self.fail("ACL should deny message transfer to name=amq.direct routingkey=rk1"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.message_transfer(destination="amq.topic", message=Message(props,"Test")) + self.fail("ACL should deny message transfer to name=amq.topic"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.message_transfer(destination="myEx", message=Message(props,"Test")) + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow message transfer to exchange myEx with routing key rk1"); + + + props = session.delivery_properties(routing_key="rk2") + try: + session.message_transfer(destination="amq.direct", message=Message(props,"Test")) + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow message transfer to exchange amq.direct"); + + +if __name__ == '__main__': + args = sys.argv[1:] + #need to remove the extra options from args as test runner doesn't recognize them + extract_args("--port", args) + args.append("acl") + + if not testrunner.run(args): sys.exit(1) diff --git a/RC9/qpid/cpp/src/tests/ais_check b/RC9/qpid/cpp/src/tests/ais_check new file mode 100755 index 0000000000..5687110165 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ais_check @@ -0,0 +1,56 @@ +#!/bin/sh +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +srcdir=`dirname $0` + +# Check AIS requirements and run tests if found. +id -nG | grep '\<ais\>' >/dev/null || \ + NOGROUP="You are not a member of the ais group." +ps -u root | grep 'aisexec\|corosync' >/dev/null || \ + NOAISEXEC="The aisexec or corosync daemon is not running as root" + +if test -n "$NOGROUP" -o -n "$NOAISEXEC"; then + cat <<EOF + + =========== WARNING: NOT RUNNING AIS TESTS ============== + + Tests that depend on the openais library (used for clustering) + will not be run because: + + $NOGROUP + $NOAISEXEC + + ========================================================== + +EOF + exit 0; # A warning, not a failure. +fi + +# Execute command with the ais group set. +with_ais_group() { + id -nG | grep '\<ais\>' >/dev/null || { echo "You are not a member of the ais group."; exit 1; } + echo $* | newgrp ais +} + +# Run the tests +srcdir=`dirname $0` +with_ais_group $srcdir/run_test ./cluster_test || ERROR=1 +exit $ERROR + diff --git a/RC9/qpid/cpp/src/tests/ais_test.cpp b/RC9/qpid/cpp/src/tests/ais_test.cpp new file mode 100644 index 0000000000..00c61242e4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ais_test.cpp @@ -0,0 +1,23 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Defines test_main function to link with actual unit test code. +#define BOOST_AUTO_TEST_MAIN // Boost 1.33 +#define BOOST_TEST_MAIN +#include "unit_test.h" + diff --git a/RC9/qpid/cpp/src/tests/allSegmentTypes.h b/RC9/qpid/cpp/src/tests/allSegmentTypes.h new file mode 100644 index 0000000000..e942250c89 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/allSegmentTypes.h @@ -0,0 +1,128 @@ +#ifndef TESTS_ALLSEGMENTTYPES_H +#define TESTS_ALLSEGMENTTYPES_H +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/// +/// This file was automatically generated from the AMQP specification. +/// Do not edit. +/// + + +#include "qpid/amqp_0_10/specification.h" +#include "qpid/amqp_0_10/Header.h" +#include "qpid/amqp_0_10/Body.h" + +using namespace qpid::amqp_0_10; + +template <class Op> size_t allSegmentTypes(Op& op) { + op(Header()); + op(Body()); + op(ControlHolder(connection::Start())); + op(ControlHolder(connection::StartOk())); + op(ControlHolder(connection::Secure())); + op(ControlHolder(connection::SecureOk())); + op(ControlHolder(connection::Tune())); + op(ControlHolder(connection::TuneOk())); + op(ControlHolder(connection::Open())); + op(ControlHolder(connection::OpenOk())); + // op(ControlHolder(connection::Redirect())); // known-hosts array + op(ControlHolder(connection::Heartbeat())); + // op(ControlHolder(connection::Close())); // class/method dropped + op(ControlHolder(connection::CloseOk())); + op(ControlHolder(session::Attach())); + op(ControlHolder(session::Attached())); + op(ControlHolder(session::Detach())); + op(ControlHolder(session::Detached())); + op(ControlHolder(session::RequestTimeout())); + op(ControlHolder(session::Timeout())); + op(ControlHolder(session::CommandPoint())); + // op(ControlHolder(session::Expected())); // fragments array encoding problem + // op(ControlHolder(session::Confirmed())); // fragments array encoding problem + op(ControlHolder(session::Completed())); + op(ControlHolder(session::KnownCompleted())); + op(ControlHolder(session::Flush())); + op(ControlHolder(session::Gap())); + // FIXME aconway 2008-04-15: command encoding, fix headers, fix sized structs. + op(CommandHolder(execution::Sync())); + op(CommandHolder(execution::Result())); + + // FIXME aconway 2008-04-16: investigate remaining failures. + // op(CommandHolder(execution::Exception())); + op(CommandHolder(message::Transfer())); + op(CommandHolder(message::Accept())); + // op(CommandHolder(message::Reject())); + op(CommandHolder(message::Release())); + op(CommandHolder(message::Acquire())); + // op(CommandHolder(message::Resume())); + op(CommandHolder(message::Subscribe())); + op(CommandHolder(message::Cancel())); + op(CommandHolder(message::SetFlowMode())); + op(CommandHolder(message::Flow())); + op(CommandHolder(message::Flush())); + op(CommandHolder(message::Stop())); + op(CommandHolder(tx::Select())); + op(CommandHolder(tx::Commit())); + op(CommandHolder(tx::Rollback())); + op(CommandHolder(dtx::Select())); + // op(CommandHolder(dtx::Start())); + // op(CommandHolder(dtx::End())); + // op(CommandHolder(dtx::Commit())); + // op(CommandHolder(dtx::Forget())); + // op(CommandHolder(dtx::GetTimeout())); + // op(CommandHolder(dtx::Prepare())); + // op(CommandHolder(dtx::Recover())); + // op(CommandHolder(dtx::Rollback())); + // op(CommandHolder(dtx::SetTimeout())); + op(CommandHolder(exchange::Declare())); + op(CommandHolder(exchange::Delete())); + op(CommandHolder(exchange::Query())); + op(CommandHolder(exchange::Bind())); + op(CommandHolder(exchange::Unbind())); + op(CommandHolder(exchange::Bound())); + op(CommandHolder(queue::Declare())); + op(CommandHolder(queue::Delete())); + op(CommandHolder(queue::Purge())); + op(CommandHolder(queue::Query())); + // op(CommandHolder(file::Qos())); + // op(CommandHolder(file::QosOk())); +// op(CommandHolder(file::Consume())); +// op(CommandHolder(file::ConsumeOk())); +// op(CommandHolder(file::Cancel())); +// op(CommandHolder(file::Open())); +// op(CommandHolder(file::OpenOk())); +// op(CommandHolder(file::Stage())); +// op(CommandHolder(file::Publish())); +// op(CommandHolder(file::Return())); +// op(CommandHolder(file::Deliver())); +// op(CommandHolder(file::Ack())); +// op(CommandHolder(file::Reject())); +// op(CommandHolder(stream::Qos())); +// op(CommandHolder(stream::QosOk())); +// op(CommandHolder(stream::Consume())); +// op(CommandHolder(stream::ConsumeOk())); +// op(CommandHolder(stream::Cancel())); +// op(CommandHolder(stream::Publish())); +// op(CommandHolder(stream::Return())); +// op(CommandHolder(stream::Deliver())); + return 0; +} +#endif /*!TESTS_ALLSEGMENTTYPES_H*/ diff --git a/RC9/qpid/cpp/src/tests/amqp_0_10/Map.cpp b/RC9/qpid/cpp/src/tests/amqp_0_10/Map.cpp new file mode 100644 index 0000000000..efde967050 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/amqp_0_10/Map.cpp @@ -0,0 +1,98 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "unit_test.h" +#include "qpid/amqp_0_10/Map.h" +#include "qpid/amqp_0_10/Array.h" +#include "qpid/amqp_0_10/Struct32.h" +#include "qpid/amqp_0_10/UnknownType.h" +#include "qpid/amqp_0_10/Codec.h" +#include <iostream> + +using namespace qpid::amqp_0_10; +using namespace std; + +QPID_AUTO_TEST_SUITE(MapTestSuite) + + QPID_AUTO_TEST_CASE(testGetSet) { + MapValue v; + v = Str8("foo"); + BOOST_CHECK(v.get<Str8>()); + BOOST_CHECK(!v.get<uint8_t>()); + BOOST_CHECK_EQUAL(*v.get<Str8>(), "foo"); + + v = uint8_t(42); + BOOST_CHECK(!v.get<Str8>()); + BOOST_CHECK(v.get<uint8_t>()); + BOOST_CHECK_EQUAL(*v.get<uint8_t>(), 42); + + v = uint16_t(12); + BOOST_CHECK(v.get<uint16_t>()); + BOOST_CHECK_EQUAL(*v.get<uint16_t>(), 12); +} + +template <class R> struct TestVisitor : public MapValue::Visitor<R> { + template <class T> R operator()(const T&) const { throw MapValue::BadTypeException(); } + R operator()(const R& r) const { return r; } +}; + +QPID_AUTO_TEST_CASE(testVisit) { + MapValue v; + v = Str8("foo"); + BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor<Str8>()), "foo"); + v = Uint16(42); + BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor<Uint16>()), 42); + try { + v.apply_visitor(TestVisitor<bool>()); + BOOST_FAIL("Expecting exception"); + } + catch(const MapValue::BadTypeException&) {} +} + + +QPID_AUTO_TEST_CASE(testEncodeMapValue) { + MapValue mv; + std::string data; + mv = Str8("hello"); + Codec::encode(back_inserter(data))(mv); + BOOST_CHECK_EQUAL(data.size(), Codec::size(mv)); + MapValue mv2; + Codec::decode(data.begin())(mv2); + BOOST_CHECK_EQUAL(mv2.getCode(), 0x85); + BOOST_REQUIRE(mv2.get<Str8>()); + BOOST_CHECK_EQUAL(*mv2.get<Str8>(), "hello"); +} + +QPID_AUTO_TEST_CASE(testEncode) { + Map map; + std::string data; + map["A"] = true; + map["b"] = Str8("hello"); + Codec::encode(back_inserter(data))(map); + BOOST_CHECK_EQUAL(Codec::size(map), data.size()); + Map map2; + Codec::decode(data.begin())(map2); + BOOST_CHECK_EQUAL(map.size(), 2u); + BOOST_CHECK(map["A"].get<bool>()); + BOOST_CHECK_EQUAL(*map["b"].get<Str8>(), "hello"); +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp b/RC9/qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp new file mode 100644 index 0000000000..9d4fcb8935 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp @@ -0,0 +1,49 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "unit_test.h" +#include "qpid/amqp_0_10/ProxyTemplate.h" +#include <boost/any.hpp> + +QPID_AUTO_TEST_SUITE(ProxyTemplateTestSuite) + +using namespace qpid::amqp_0_10; + +struct ToAny { + template <class T> + boost::any operator()(const T& t) { return boost::any(t); } +}; + +struct AnyProxy : public ProxyTemplate<ToAny, boost::any> {}; + +QPID_AUTO_TEST_CASE(testAnyProxy) { + AnyProxy p; + boost::any a=p.connectionTune(1,2,3,4); + BOOST_CHECK_EQUAL(a.type().name(), typeid(connection::Tune).name()); + connection::Tune* tune=boost::any_cast<connection::Tune>(&a); + BOOST_REQUIRE(tune); + BOOST_CHECK_EQUAL(tune->channelMax, 1u); + BOOST_CHECK_EQUAL(tune->maxFrameSize, 2u); + BOOST_CHECK_EQUAL(tune->heartbeatMin, 3u); + BOOST_CHECK_EQUAL(tune->heartbeatMax, 4u); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/amqp_0_10/apply.cpp b/RC9/qpid/cpp/src/tests/amqp_0_10/apply.cpp new file mode 100644 index 0000000000..5a67c28c79 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/amqp_0_10/apply.cpp @@ -0,0 +1,99 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "unit_test.h" +#include "qpid/amqp_0_10/specification.h" +#include "qpid/amqp_0_10/ApplyControl.h" + +QPID_AUTO_TEST_SUITE(VisitorTestSuite) + +using namespace qpid::amqp_0_10; + +struct GetCode : public ApplyFunctor<uint8_t> { + template <class T> uint8_t operator()(const T&) const { return T::CODE; } +}; + +struct SetChannelMax : ApplyFunctor<void> { + template <class T> void operator()(T&) const { BOOST_FAIL(""); } + void operator()(connection::Tune& t) const { t.channelMax=42; } +}; + +struct TestFunctor { + typedef bool result_type; + bool operator()(const connection::Tune& tune) { + BOOST_CHECK_EQUAL(tune.channelMax, 1u); + BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u); + BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u); + BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u); + return true; + } + template <class T> + bool operator()(const T&) { return false; } +}; + +QPID_AUTO_TEST_CASE(testApply) { + connection::Tune tune(1,2,3,4); + Control* p = &tune; + + // boost oddity - without the cast we get undefined symbol errors. + BOOST_CHECK_EQUAL(apply(GetCode(), *p), (uint8_t)connection::Tune::CODE); + + TestFunctor tf; + BOOST_CHECK(apply(tf, *p)); + + connection::Start start; + p = &start; + BOOST_CHECK(!apply(tf, *p)); + + apply(SetChannelMax(), tune); + BOOST_CHECK_EQUAL(tune.channelMax, 42); +} + +struct VoidTestFunctor { + typedef void result_type; + + int code; + VoidTestFunctor() : code() {} + + void operator()(const connection::Tune& tune) { + BOOST_CHECK_EQUAL(tune.channelMax, 1u); + BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u); + BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u); + BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u); + code=connection::Tune::CODE; + } + template <class T> + void operator()(const T&) { code=0xFF; } +}; + +QPID_AUTO_TEST_CASE(testApplyVoid) { + connection::Tune tune(1,2,3,4); + Control* p = &tune; + VoidTestFunctor tf; + apply(tf, *p); + BOOST_CHECK_EQUAL(uint8_t(connection::Tune::CODE), tf.code); + + connection::Start start; + p = &start; + apply(tf, *p); + BOOST_CHECK_EQUAL(0xFF, tf.code); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/amqp_0_10/handlers.cpp b/RC9/qpid/cpp/src/tests/amqp_0_10/handlers.cpp new file mode 100644 index 0000000000..428643c802 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/amqp_0_10/handlers.cpp @@ -0,0 +1,125 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "unit_test.h" +#include "qpid/Exception.h" +#include "qpid/amqp_0_10/Unit.h" +#include "qpid/amqp_0_10/ControlHolder.h" +#include "qpid/amqp_0_10/CommandHolder.h" +#include "qpid/amqp_0_10/handlers.h" +#include "qpid/amqp_0_10/specification.h" + +QPID_AUTO_TEST_SUITE(handler_tests) + +using namespace qpid::amqp_0_10; +using namespace std; + +string called; // Set by called handler function + +// Note on handlers: +// +// Control and Command handlers are separate, both behave the same way, +// so substitute "control or command" for command in the following. +// +// Command handlers derive from CommandHandler and implement functions +// for all the commands they handle. Handling an unimplemented command +// will raise NotImplementedException. +// +// Using virtual inheritance from CommandHandler allows multiple +// handlers to be aggregated into one with multiple inheritance, +// See test code for example. +// +// E.g. the existing broker model would have two control handlers: +// - ConnectionHandler: ControlHandler for connection controls. +// - SessionHandler: ControlHandler for session controls. +// It would have class-command handlers for each AMQP class: +// - QueueHandler, MessageHandler etc.. handle each class. +// And an aggregate handler in place of BrokerAdapter +// - BrokerCommandHandler: public QueueHandler, MessageHandler ... +// +// In other applications (e.g. cluster) any combination of commands +// can be handled by a given handler. It _might_ simplify the code +// to collaps ConnectionHandler and SessionHandler into a single +// ControlHandler (or it might not.) + +struct TestExecutionHandler : public virtual CommandHandler { + void executionSync() { called = "executionSync"; } + // ... etc. for all execution commands +}; + +struct TestMessageHandler : public virtual CommandHandler { + void messageCancel(const Str8&) { called="messageCancel"; } + // ... etc. +}; + +// Aggregate handler for all recognised commands. +struct TestCommandHandler : + public TestExecutionHandler, + public TestMessageHandler + // ... etc. handlers for all command classes. +{}; // Nothing to do. + + +// Sample unit handler, written as a static_visitor. +// Note it could equally be written with if/else statements +// in handle. +// +struct TestUnitHandler : public boost::static_visitor<void> { + TestCommandHandler handler; + void handle(const Unit& u) { u.applyVisitor(*this); } + + void operator()(const Body&) { called="Body"; } + void operator()(const Header&) { called="Header"; } + void operator()(const ControlHolder&) { throw qpid::Exception("I don't do controls."); } + void operator()(const CommandHolder& c) { c.invoke(handler); } +}; + +QPID_AUTO_TEST_CASE(testHandlers) { + TestUnitHandler handler; + Unit u; + + u = Body(); + handler.handle(u); + BOOST_CHECK_EQUAL("Body", called); + + u = Header(); + handler.handle(u); + BOOST_CHECK_EQUAL("Header", called); + + // in_place<Foo>(...) is equivalent to Foo(...) but + // constructs Foo directly in the holder, avoiding + // a copy. + + u = CommandHolder(in_place<execution::Sync>()); + handler.handle(u); + BOOST_CHECK_EQUAL("executionSync", called); + + u = ControlHolder(in_place<connection::Start>(Map(), Str16Array(), Str16Array())); + try { + handler.handle(u); + } catch (const qpid::Exception&) {} + + u = CommandHolder(in_place<message::Cancel>(Str8())); + handler.handle(u); + BOOST_CHECK_EQUAL("messageCancel", called); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/amqp_0_10/serialize.cpp b/RC9/qpid/cpp/src/tests/amqp_0_10/serialize.cpp new file mode 100644 index 0000000000..0cfeb8d28d --- /dev/null +++ b/RC9/qpid/cpp/src/tests/amqp_0_10/serialize.cpp @@ -0,0 +1,429 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "unit_test.h" +#include "allSegmentTypes.h" + +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/Buffer.h" + +#include "qpid/amqp_0_10/Packer.h" +#include "qpid/amqp_0_10/built_in_types.h" +#include "qpid/amqp_0_10/Codec.h" +#include "qpid/amqp_0_10/specification.h" +#include "qpid/amqp_0_10/ControlHolder.h" +#include "qpid/amqp_0_10/Struct32.h" +#include "qpid/amqp_0_10/FrameHeader.h" +#include "qpid/amqp_0_10/Map.h" +#include "qpid/amqp_0_10/Unit.h" +#include "tests/allSegmentTypes.h" + +#include <boost/test/test_case_template.hpp> +#include <boost/type_traits/is_arithmetic.hpp> +#include <boost/utility/enable_if.hpp> +#include <boost/optional.hpp> +#include <boost/mpl/vector.hpp> +#include <boost/mpl/back_inserter.hpp> +#include <boost/mpl/copy.hpp> +#include <boost/mpl/empty_sequence.hpp> +#include <boost/current_function.hpp> +#include <iterator> +#include <string> +#include <sstream> +#include <iostream> +#include <netinet/in.h> + +// Missing operators needed for tests. +namespace boost { +template <class T, size_t N> +std::ostream& operator<<(std::ostream& out, const array<T,N>& a) { + std::ostream_iterator<T> o(out, " "); + std::copy(a.begin(), a.end(), o); + return out; +} +} // boost + +QPID_AUTO_TEST_SUITE(SerializeTestSuite) + +using namespace std; +namespace mpl=boost::mpl; +using namespace qpid::amqp_0_10; +using qpid::framing::in_place; + +template <class A, class B> struct concat2 { typedef typename mpl::copy<B, typename mpl::back_inserter<A> >::type type; }; +template <class A, class B, class C> struct concat3 { typedef typename concat2<A, typename concat2<B, C>::type>::type type; }; +template <class A, class B, class C, class D> struct concat4 { typedef typename concat2<A, typename concat3<B, C, D>::type>::type type; }; + +typedef mpl::vector<Boolean, Char, Int32, Int64, Int8, Uint16, CharUtf32, Uint32, Uint64, Bin8, Uint8>::type IntegralTypes; +typedef mpl::vector<Bin1024, Bin128, Bin16, Bin256, Bin32, Bin40, Bin512, Bin64, Bin72>::type BinTypes; +typedef mpl::vector<Double, Float>::type FloatTypes; +typedef mpl::vector<SequenceNo, Uuid, Datetime, Dec32, Dec64> FixedSizeClassTypes; +typedef mpl::vector<Map, Vbin8, Str8Latin, Str8, Str8Utf16, Vbin16, Str16Latin, Str16, Str16Utf16, Vbin32> VariableSizeTypes; + +typedef concat4<IntegralTypes, BinTypes, FloatTypes, FixedSizeClassTypes>::type FixedSizeTypes; +typedef concat2<FixedSizeTypes, VariableSizeTypes>::type AllTypes; + +// TODO aconway 2008-02-20: should test 64 bit integrals for order also. +QPID_AUTO_TEST_CASE(testNetworkByteOrder) { + string data; + + uint32_t l = 0x11223344; + Codec::encode(std::back_inserter(data))(l); + uint32_t enc=reinterpret_cast<const uint32_t&>(*data.data()); + uint32_t l2 = ntohl(enc); + BOOST_CHECK_EQUAL(l, l2); + + data.clear(); + uint16_t s = 0x1122; + Codec::encode(std::back_inserter(data))(s); + uint32_t s2 = ntohs(*reinterpret_cast<const uint32_t*>(data.data())); + BOOST_CHECK_EQUAL(s, s2); +} + +QPID_AUTO_TEST_CASE(testSetLimit) { + typedef Codec::Encoder<back_insert_iterator<string> > Encoder; + string data; + Encoder encode(back_inserter(data), 3); + encode('1')('2')('3'); + try { + encode('4'); + BOOST_FAIL("Expected exception"); + } catch (...) {} // FIXME aconway 2008-04-03: catch proper exception + BOOST_CHECK_EQUAL(data, "123"); +} + +QPID_AUTO_TEST_CASE(testScopedLimit) { + typedef Codec::Encoder<back_insert_iterator<string> > Encoder; + string data; + Encoder encode(back_inserter(data), 10); + encode(Str8("123")); // 4 bytes + { + Encoder::ScopedLimit l(encode, 3); + encode('a')('b')('c'); + try { + encode('d'); + BOOST_FAIL("Expected exception"); + } catch(...) {} // FIXME aconway 2008-04-03: catch proper exception + } + BOOST_CHECK_EQUAL(data, "\003123abc"); + encode('x')('y')('z'); + try { + encode('!'); + BOOST_FAIL("Expected exception"); + } catch(...) {} // FIXME aconway 2008-04-03: catch proper exception + BOOST_CHECK_EQUAL(data.size(), 10u); +} + +// Assign test values to the various types. +void testValue(bool& b) { b = true; } +void testValue(Bit&) { } +template <class T> typename boost::enable_if<boost::is_arithmetic<T> >::type testValue(T& n) { n=42; } +void testValue(CharUtf32& c) { c = 43; } +void testValue(long long& l) { l = 0x012345; } +void testValue(Datetime& dt) { dt = qpid::sys::now(); } +void testValue(Uuid& uuid) { uuid=Uuid(true); } +template <class E, class M> void testValue(Decimal<E,M>& d) { d.exponent=2; d.mantissa=0x1122; } +void testValue(SequenceNo& s) { s = 42; } +template <size_t N> void testValue(Bin<N>& a) { a.assign(42); } +template <class T, class S, int Unique> void testValue(SerializableString<T, S, Unique>& s) { + char msg[]="foobar"; + s.assign(msg, msg+sizeof(msg)); +} +void testValue(Str16& s) { s = "the quick brown fox jumped over the lazy dog"; } +void testValue(Str8& s) { s = "foobar"; } +void testValue(Map& m) { m["s"] = Str8("foobar"); m["b"] = true; m["c"] = uint16_t(42); } + +//typedef mpl::vector<Str8, Str16>::type TestTypes; +/*BOOST_AUTO_TEST_CASE_TEMPLATE(testEncodeDecode, T, AllTypes) +{ + string data; + T t; + testValue(t); + Codec::encode(std::back_inserter(data))(t); + + BOOST_CHECK_EQUAL(Codec::size(t), data.size()); + + T t2; + Codec::decode(data.begin())(t2); + BOOST_CHECK_EQUAL(t,t2); +} +*/ + +struct TestMe { + bool encoded, decoded; + char value; + TestMe(char v) : encoded(), decoded(), value(v) {} + template <class S> void encode(S& s) const { + const_cast<TestMe*>(this)->encoded=true; s(value); + } + template <class S> void decode(S& s) { decoded=true; s(value); } + template <class S> void serialize(S& s) { s.split(*this); } +}; + +QPID_AUTO_TEST_CASE(testSplit) { + string data; + TestMe t1('x'); + Codec::encode(std::back_inserter(data))(t1); + BOOST_CHECK(t1.encoded); + BOOST_CHECK(!t1.decoded); + BOOST_CHECK_EQUAL(data, "x"); + + TestMe t2('y'); + Codec::decode(data.begin())(t2); + BOOST_CHECK(!t2.encoded); + BOOST_CHECK(t2.decoded); + BOOST_CHECK_EQUAL(t2.value, 'x'); +} + +QPID_AUTO_TEST_CASE(testControlEncodeDecode) { + string data; + Control::Holder h(in_place<connection::Tune>(1,2,3,4)); + Codec::encode(std::back_inserter(data))(h); + + BOOST_CHECK_EQUAL(data.size(), Codec::size(h)); + + Codec::Decoder<string::iterator> decode(data.begin()); + Control::Holder h2; + decode(h2); + + BOOST_REQUIRE(h2.get()); + BOOST_CHECK_EQUAL(h2.get()->getClassCode(), connection::CODE); + BOOST_CHECK_EQUAL(h2.get()->getCode(), uint8_t(connection::Tune::CODE)); + connection::Tune& tune=static_cast<connection::Tune&>(*h2.get()); + BOOST_CHECK_EQUAL(tune.channelMax, 1u); + BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u); + BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u); + BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u); +} + +QPID_AUTO_TEST_CASE(testStruct32) { + message::DeliveryProperties dp; + dp.priority=message::MEDIUM; + dp.routingKey="foo"; + Struct32 s(dp); + string data; + Codec::encode(back_inserter(data))(s); + + uint32_t structSize; // Starts with size + Codec::decode(data.begin())(structSize); + BOOST_CHECK_EQUAL(structSize, Codec::size(dp) + 2); // +2 for code + BOOST_CHECK_EQUAL(structSize, data.size()-4); // encoded body + + BOOST_CHECK_EQUAL(data.size(), Codec::size(s)); + Struct32 s2; + Codec::decode(data.begin())(s2); + message::DeliveryProperties* dp2 = s2.getIf<message::DeliveryProperties>(); + BOOST_REQUIRE(dp2); + BOOST_CHECK_EQUAL(dp2->priority, message::MEDIUM); + BOOST_CHECK_EQUAL(dp2->routingKey, "foo"); +} + +QPID_AUTO_TEST_CASE(testStruct32Unknown) { + // Verify we can recode an unknown struct unchanged. + Struct32 s; + string data; + Codec::encode(back_inserter(data))(uint32_t(10)); + data.append(10, 'X'); + Codec::decode(data.begin())(s); + string data2; + Codec::encode(back_inserter(data2))(s); + BOOST_CHECK_EQUAL(data.size(), data2.size()); + BOOST_CHECK_EQUAL(data, data2); +} + +struct DummyPacked { + static const uint8_t PACK=1; + boost::optional<char> i, j; + char k; + Bit l,m; + DummyPacked(char a=0, char b=0, char c=0) : i(a), j(b), k(c), l(), m() {} + template <class S> void serialize(S& s) { s(i)(j)(k)(l)(m); } +}; + +Packer<DummyPacked> serializable(DummyPacked& d) { return Packer<DummyPacked>(d); } + +QPID_AUTO_TEST_CASE(testPackBits) { + DummyPacked d('a','b','c'); + BOOST_CHECK_EQUAL(packBits(d), 7u); + d.j = boost::none; + BOOST_CHECK_EQUAL(packBits(d), 5u); + d.m = true; + BOOST_CHECK_EQUAL(packBits(d), 0x15u); +} + + +QPID_AUTO_TEST_CASE(testPacked) { + string data; + + Codec::encode(back_inserter(data))('a')(boost::optional<char>('b'))(boost::optional<char>())('c'); + BOOST_CHECK_EQUAL(data, "abc"); + data.clear(); + + DummyPacked dummy('a','b','c'); + + Codec::encode(back_inserter(data))(dummy); + BOOST_CHECK_EQUAL(data.size(), 4u); + BOOST_CHECK_EQUAL(data, string("\007abc")); + data.clear(); + + dummy.i = boost::none; + Codec::encode(back_inserter(data))(dummy); + BOOST_CHECK_EQUAL(data, string("\6bc")); + data.clear(); + + const char* missing = "\5xy"; + Codec::decode(missing)(dummy); + BOOST_CHECK(dummy.i); + BOOST_CHECK_EQUAL(*dummy.i, 'x'); + BOOST_CHECK(!dummy.j); + BOOST_CHECK_EQUAL(dummy.k, 'y'); +} + +QPID_AUTO_TEST_CASE(testUnitControl) { + string data; + Control::Holder h(in_place<connection::Tune>(1,2,3,4)); + Codec::encode(std::back_inserter(data))(h); + + Unit unit(FrameHeader(FIRST_FRAME|LAST_FRAME, CONTROL)); + Codec::decode(data.begin())(unit); + + BOOST_REQUIRE(unit.get<ControlHolder>()); + + string data2; + Codec::encode(back_inserter(data2))(unit); + + BOOST_CHECK_EQUAL(data, data2); +} + +QPID_AUTO_TEST_CASE(testArray) { + ArrayDomain<char> a; + a.resize(3, 'x'); + string data; + Codec::encode(back_inserter(data))(a); + + ArrayDomain<char> b; + Codec::decode(data.begin())(b); + BOOST_CHECK_EQUAL(b.size(), 3u); + string data3; + Codec::encode(back_inserter(data3))(a); + BOOST_CHECK_EQUAL(data, data3); + + Array x; + Codec::decode(data.begin())(x); + BOOST_CHECK_EQUAL(x.size(), 3u); + BOOST_CHECK_EQUAL(x[0].size(), 1u); + BOOST_CHECK_EQUAL(*x[0].begin(), 'x'); + BOOST_CHECK_EQUAL(*x[2].begin(), 'x'); + + string data2; + Codec::encode(back_inserter(data2))(x); + BOOST_CHECK_EQUAL(data,data2); +} + +QPID_AUTO_TEST_CASE(testStruct) { + string data; + + message::DeliveryProperties dp; + BOOST_CHECK(!dp.discardUnroutable); + dp.immediate = true; + dp.redelivered = false; + dp.priority = message::MEDIUM; + dp.exchange = "foo"; + + Codec::encode(back_inserter(data))(dp); + // Skip 4 bytes size, little-endian decode for pack bits. + uint16_t encodedBits=uint8_t(data[5]); + encodedBits <<= 8; + encodedBits += uint8_t(data[4]); + BOOST_CHECK_EQUAL(encodedBits, packBits(dp)); + + data.clear(); + Struct32 h(dp); + Codec::encode(back_inserter(data))(h); + + Struct32 h2; + Codec::decode(data.begin())(h2); + BOOST_CHECK_EQUAL(h2.getClassCode(), Uint8(message::DeliveryProperties::CLASS_CODE)); + BOOST_CHECK_EQUAL(h2.getCode(), Uint8(message::DeliveryProperties::CODE)); + message::DeliveryProperties* dp2 = + dynamic_cast<message::DeliveryProperties*>(h2.get()); + BOOST_CHECK(dp2); + BOOST_CHECK(!dp2->discardUnroutable); + BOOST_CHECK(dp2->immediate); + BOOST_CHECK(!dp2->redelivered); + BOOST_CHECK_EQUAL(dp2->priority, message::MEDIUM); + BOOST_CHECK_EQUAL(dp2->exchange, "foo"); +} + +struct RecodeUnit { + template <class T> + void operator() (const T& t) { + BOOST_MESSAGE(BOOST_CURRENT_FUNCTION << " called with: " << t); + using qpid::framing::Buffer; + using qpid::framing::AMQFrame; + + session::Header sh; + BOOST_CHECK_EQUAL(Codec::size(sh), 2u); + + // Encode unit. + Unit u(t); + string data; + Codec::encode(back_inserter(data))(u.getHeader())(u); + data.push_back(char(0xCE)); // Preview end-of-frame + + // Decode AMQFrame + Buffer buf(&data[0], data.size()); + AMQFrame f; + f.decode(buf); + BOOST_MESSAGE("AMQFrame decoded: " << f); + // Encode AMQFrame + string data2(f.size(), ' '); + Buffer buf2(&data2[0], data.size()); + f.encode(buf2); + + // Verify encoded by unit == encoded by AMQFrame + BOOST_CHECK_MESSAGE(data == data2, BOOST_CURRENT_FUNCTION); + + // Decode unit + // FIXME aconway 2008-04-15: must set limit to decode a header. + Codec::Decoder<string::iterator> decode(data2.begin(), data2.size()-1); + + FrameHeader h; + decode(h); + BOOST_CHECK_EQUAL(u.getHeader(), h); + Unit u2(h); + decode(u2); + + // Re-encode unit + string data3; + Codec::encode(back_inserter(data3))(u2.getHeader())(u2); + data3.push_back(char(0xCE)); // Preview end-of-frame + + BOOST_CHECK_MESSAGE(data3 == data2, BOOST_CURRENT_FUNCTION); + } +}; + +QPID_AUTO_TEST_CASE(testSerializeAllSegmentTypes) { + RecodeUnit recode; + allSegmentTypes(recode); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/benchmark b/RC9/qpid/cpp/src/tests/benchmark new file mode 100755 index 0000000000..c075837847 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/benchmark @@ -0,0 +1,95 @@ +#!/bin/sh +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# A basic "benchmark" to generate performacne samples of throughput +# and latency against a single cluster member while they are replicating. +# +# Must be run in the qpid src/tests build directory. +# + +usage() { +cat <<EOF +Usage: $0 [options] -- client hosts --- broker hosts +Read the script for options. +EOF +} +# Defaults +TESTDIR=${TESTDIR:-$PWD} # Absolute path to test exes on all hosts. +SCRIPTDIR=${SCRIPTDIR:-`dirname $0`} # Path to local test scripts directory. +SAMPLES=10 # Runs of each test. +COUNT=${COUNT:-10000} # Count for pub/sub tests. +SIZE=${SIZE:-600} # Size of messages +ECHO=${ECHO:-1000} # Count for echo test. +NSUBS=${NSUBS:-4} +NPUBS=${NPUBS:-4} + +collect() { eval $COLLECT=\""\$$COLLECT $*"\"; } +COLLECT=ARGS +while test $# -gt 0; do + case $1 in + --testdir) TESTDIR=$2 ; shift 2 ;; + --samples) SAMPLES=$2 ; shift 2 ;; + --count) COUNT=$2 ; shift 2 ;; + --echos) ECHO=$2 ; shift 2 ;; + --size) SIZE=$2 ; shift 2 ;; + --nsubs) NSUBS=$2 ; shift 2 ;; + --npubs) NPUBS=$2 ; shift 2 ;; + --) COLLECT=CLIENTARG; shift ;; + ---) COLLECT=BROKERARG; shift;; + *) collect $1; shift ;; + esac +done + +CLIENTS=${CLIENTARG:-$CLIENTS} +BROKERS=${BROKERARG:-$BROKERS} +test -z "$CLIENTS" && { echo "Must specify at least one client host."; exit 1; } +test -z "$BROKERS" && { echo "Must specify at least one broker host."; exit 1; } + +export TESTDIR # For perfdist +CLIENTS=($CLIENTS) # Convert to array +BROKERS=($BROKERS) +trap "rm -f $FILES" EXIT + +dosamples() { + FILE=`mktemp` + FILES="$FILES $FILE" + TABS=`echo "$HEADING" | sed s'/[^ ]//g'` + { + echo "\"$*\"$TABS" + echo "$HEADING" + for (( i=0; i<$SAMPLES; ++i)) ; do echo "`$*`" ; done + echo + } | tee $FILE +} + +HEADING="pub sub total Mb" +dosamples $SCRIPTDIR/perfdist --size $SIZE --count $COUNT --nsubs $NSUBS --npubs $NPUBS -s -- ${CLIENTS[*]} --- ${BROKERS[*]} +HEADING="pub" +dosamples ssh -A ${CLIENTS[0]} $TESTDIR/publish --routing-key perftest0 --size $SIZE --count $COUNT -s -b ${BROKERS[0]} +HEADING="sub" +dosamples ssh -A ${CLIENTS[0]} $TESTDIR/consume --queue perftest0 -s --count $COUNT -b ${BROKERS[0]} +HEADING="min max avg" +dosamples ssh -A ${CLIENTS[0]} $TESTDIR/echotest --count $ECHO -s -b ${BROKERS[0]} + +echo +echo "Tab separated spreadsheet (also saved as benchmark.tab):" +echo + +echo "benchmark -- ${CLIENTS[*]} --- ${BROKERS[*]} " | tee benchmark.tab +paste $FILES | tee -a benchmark.tab diff --git a/RC9/qpid/cpp/src/tests/client_test.cpp b/RC9/qpid/cpp/src/tests/client_test.cpp new file mode 100644 index 0000000000..204c2c4b71 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/client_test.cpp @@ -0,0 +1,149 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/** + * This file provides a simple test (and example) of basic + * functionality including declaring an exchange and a queue, binding + * these together, publishing a message and receiving that message + * asynchronously. + */ + +#include <iostream> + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/Session.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/all_method_bodies.h" + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::framing; +using std::string; + +struct Args : public TestOptions { + uint msgSize; + bool verbose; + + Args() : TestOptions("Simple test of Qpid c++ client; sends and receives a single message."), msgSize(26) + { + addOptions() + ("size", optValue(msgSize, "N"), "message size") + ("verbose", optValue(verbose), "print out some status messages"); + } +}; + +const std::string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + +std::string generateData(uint size) +{ + if (size < chars.length()) { + return chars.substr(0, size); + } + std::string data; + for (uint i = 0; i < (size / chars.length()); i++) { + data += chars; + } + data += chars.substr(0, size % chars.length()); + return data; +} + +void print(const std::string& text, const Message& msg) +{ + std::cout << text; + if (msg.getData().size() > 16) { + std::cout << msg.getData().substr(0, 16) << "..."; + } else { + std::cout << msg.getData(); + } + std::cout << std::endl; +} + +int main(int argc, char** argv) +{ + try { + Args opts; + opts.parse(argc, argv); + + //Connect to the broker: + Connection connection; + opts.open(connection); + if (opts.verbose) std::cout << "Opened connection." << std::endl; + + //Create and open a session on the connection through which + //most functionality is exposed: + Session session = connection.newSession(); + if (opts.verbose) std::cout << "Opened session." << std::endl; + + + //'declare' the exchange and the queue, which will create them + //as they don't exist + session.exchangeDeclare(arg::exchange="MyExchange", arg::type="direct"); + if (opts.verbose) std::cout << "Declared exchange." << std::endl; + session.queueDeclare(arg::queue="MyQueue", arg::autoDelete=true, arg::exclusive=true); + if (opts.verbose) std::cout << "Declared queue." << std::endl; + + //now bind the queue to the exchange + session.exchangeBind(arg::exchange="MyExchange", arg::queue="MyQueue", arg::bindingKey="MyKey"); + if (opts.verbose) std::cout << "Bound queue to exchange." << std::endl; + + //create and send a message to the exchange using the routing + //key we bound our queue with: + Message msgOut(generateData(opts.msgSize)); + msgOut.getDeliveryProperties().setRoutingKey("MyKey"); + session.messageTransfer(arg::destination="MyExchange", arg::content=msgOut, arg::acceptMode=1); + if (opts.verbose) print("Published message: ", msgOut); + + //subscribe to the queue, add sufficient credit and then get + //incoming 'frameset', check that its a message transfer and + //then convert it to a message (see Dispatcher and + //SubscriptionManager utilties for common reusable patterns at + //a higher level) + session.messageSubscribe(arg::queue="MyQueue", arg::destination="MyId"); + session.messageFlow(arg::destination="MyId", arg::unit=0, arg::value=1); //credit for one message + session.messageFlow(arg::destination="MyId", arg::unit=1, arg::value=0xFFFFFFFF); //credit for infinite bytes + if (opts.verbose) std::cout << "Subscribed to queue." << std::endl; + FrameSet::shared_ptr incoming = session.get(); + if (incoming->isA<MessageTransferBody>()) { + Message msgIn(*incoming); + if (msgIn.getData() == msgOut.getData()) { + if (opts.verbose) std::cout << "Received the exepected message." << std::endl; + session.messageAccept(SequenceSet(msgIn.getId())); + session.markCompleted(msgIn.getId(), true, true); + } else { + print("Received an unexepected message: ", msgIn); + } + } else { + throw Exception("Unexpected command received"); + } + + //close the session & connection + session.close(); + if (opts.verbose) std::cout << "Closed session." << std::endl; + connection.close(); + if (opts.verbose) std::cout << "Closed connection." << std::endl; + return 0; + } catch(const std::exception& e) { + std::cout << e.what() << std::endl; + } + return 1; +} diff --git a/RC9/qpid/cpp/src/tests/cluster.mk b/RC9/qpid/cpp/src/tests/cluster.mk new file mode 100644 index 0000000000..1f027ccb4c --- /dev/null +++ b/RC9/qpid/cpp/src/tests/cluster.mk @@ -0,0 +1,41 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +if HAVE_LIBCPG + +# +# Cluster tests makefile fragment, to be included in Makefile.am +# + +# NOTE: Programs using the openais library must be run with gid=ais +# You should do "newgrp ais" before running the tests to run these. +# + + +# ais_check checks pre-requisites for cluster tests and runs them if ok. +TESTS+=ais_check +EXTRA_DIST+=ais_check start_cluster stop_cluster + +check_PROGRAMS+=cluster_test +cluster_test_SOURCES=unit_test.cpp cluster_test.cpp +cluster_test_LDADD=$(lib_client) ../cluster.la -lboost_unit_test_framework + +unit_test_LDADD+=../cluster.la + +endif diff --git a/RC9/qpid/cpp/src/tests/cluster_test.cpp b/RC9/qpid/cpp/src/tests/cluster_test.cpp new file mode 100644 index 0000000000..f4a38ae861 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/cluster_test.cpp @@ -0,0 +1,648 @@ +/* + * + * 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/DumpClient.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 <string> +#include <iostream> +#include <iterator> +#include <vector> +#include <set> +#include <algorithm> +#include <iterator> + +namespace std { // ostream operators in std:: namespace +template <class T> +ostream& operator<<(ostream& o, const std::set<T>& s) { return seqPrint(o, s); } +} + + +QPID_AUTO_TEST_SUITE(cluster) + +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; + +/** 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; +} + +/** 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> { + string name; + std::auto_ptr<BrokerFixture> localBroker; + int localIndex; + std::vector<shared_ptr<ForkedBroker> > forkedBrokers; + + public: + /** @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, int localIndex=0); + void add(size_t n) { for (size_t i=0; i < n; ++i) add(); } + void add(); // Add a broker. + void addLocal(); // Add a local broker. + void setup(); + + bool hasLocal() const { return localIndex >= 0 && size_t(localIndex) < size(); } + + /** Kill a forked broker with sig, or shutdown localBroker if n==0. */ + void kill(size_t n, int sig=SIGINT) { + if (n == size_t(localIndex)) + localBroker->broker->shutdown(); + else + forkedBrokers[n]->kill(sig); + } + + /** Kill a broker and suppress errors from connection. */ + void killWithSilencer(size_t n, client::Connection& c, int sig=SIGINT) { + ScopedSuppressLogging sl; + kill(n,sig); + try { c.close(); } catch(...) {} + } +}; + +ClusterFixture::ClusterFixture(size_t n, int localIndex_) : name(Uuid(true).str()), localIndex(localIndex_) { + add(n); +} + +void ClusterFixture::add() { + if (size() != size_t(localIndex)) { // fork a broker process. + std::ostringstream os; os << "fork" << size(); + std::string prefix = os.str(); + const char* argv[] = { + "qpidd " __FILE__ , + "--no-module-dir", + "--load-module=../.libs/cluster.so", + "--cluster-name", name.c_str(), + "--auth=no", "--no-data-dir", + "--log-prefix", prefix.c_str(), + }; + size_t argc = sizeof(argv)/sizeof(argv[0]); + forkedBrokers.push_back(shared_ptr<ForkedBroker>(new ForkedBroker(argc, argv))); + push_back(forkedBrokers.back()->getPort()); + } + else { // Run in this process + addLocal(); + } +} + +void ClusterFixture::addLocal() { + assert(int(size()) == localIndex || localIndex == -1); + localIndex = size(); + const char* argv[] = { + "qpidd " __FILE__ , + "--load-module=../.libs/cluster.so", + "--cluster-name", name.c_str(), + "--auth=no", "--no-data-dir" + }; + size_t argc = sizeof(argv)/sizeof(argv[0]); + ostringstream os; os << "local" << localIndex; + qpid::log::Logger::instance().setPrefix(os.str()); + localBroker.reset(new BrokerFixture(parseOpts(argc, argv))); + push_back(localBroker->getPort()); + forkedBrokers.push_back(shared_ptr<ForkedBroker>()); +} + +ostream& operator<<(ostream& o, const cpg_name* n) { + return o << qpid::cluster::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<uint16_t> makeSet(const C& c) { + set<uint16_t> s; + std::copy(c.begin(), c.end(), std::inserter(s, s.begin())); + return s; +} + +template <class T> std::set<uint16_t> knownBrokerPorts(T& source, int n=-1) { + vector<Url> urls = source.getKnownBrokers(); + if (n >= 0 && unsigned(n) != urls.size()) { + BOOST_MESSAGE("knownBrokerPorts waiting for " << n << ": " << urls); + // Retry up to 10 secs in .1 second intervals. + for (size_t retry=100; urls.size() != unsigned(n) && retry != 0; --retry) { + ::usleep(1000*100); // 0.1 secs + urls = source.getKnownBrokers(); + } + } + BOOST_MESSAGE("knownBrokerPorts expecting " << n << ": " << urls); + set<uint16_t> s; + for (vector<Url>::const_iterator i = urls.begin(); i != urls.end(); ++i) + s.insert((*i)[0].get<TcpAddress>()->port); + 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->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"); +} + +QPID_AUTO_TEST_CASE(testSequenceOptions) { + // Make sure the exchange qpid.msg_sequence property is properly replicated. + ClusterFixture cluster(1); + Client c0(cluster[0], "c0"); + FieldTable args; + args.setInt("qpid.msg_sequence", 1); // FIXME aconway 2008-11-11: works with "qpid.sequence_counter"?? + c0.session.queueDeclare(arg::queue="q"); + c0.session.exchangeDeclare(arg::exchange="ex", arg::type="direct", arg::arguments=args); + c0.session.exchangeBind(arg::exchange="ex", arg::queue="q", arg::bindingKey="k"); + c0.session.messageTransfer(arg::content=Message("1", "k"), arg::destination="ex"); + c0.session.messageTransfer(arg::content=Message("2", "k"), arg::destination="ex"); + BOOST_CHECK_EQUAL(1, getMsgSequence(c0.subs.get("q", TIME_SEC))); + BOOST_CHECK_EQUAL(2, getMsgSequence(c0.subs.get("q", TIME_SEC))); + + cluster.add(); + Client c1(cluster[1]); + c1.session.messageTransfer(arg::content=Message("3", "k"), arg::destination="ex"); + BOOST_CHECK_EQUAL(3, getMsgSequence(c1.subs.get("q", TIME_SEC))); +} + +QPID_AUTO_TEST_CASE(testUnsupported) { + ScopedSuppressLogging sl; + ClusterFixture cluster(1); + Client c1(cluster[0], "c1"); + BOOST_CHECK_THROW(c1.session.dtxSelect(), FramingErrorException); + Client c2(cluster[0], "c2"); + Message m; + m.getDeliveryProperties().setTtl(1); + BOOST_CHECK_THROW(c2.session.messageTransfer(arg::content=m), Exception); +} + +QPID_AUTO_TEST_CASE(testTxTransaction) { + ClusterFixture cluster(1); + Client c0(cluster[0], "c0"); + c0.session.queueDeclare(arg::queue="q"); + c0.session.messageTransfer(arg::content=Message("A", "q")); + c0.session.messageTransfer(arg::content=Message("B", "q")); + + // Start a transaction that will commit. + Session commitSession = c0.connection.newSession("commit"); + SubscriptionManager commitSubs(commitSession); + commitSession.txSelect(); + commitSession.messageTransfer(arg::content=Message("a", "q")); + commitSession.messageTransfer(arg::content=Message("b", "q")); + BOOST_CHECK_EQUAL(commitSubs.get("q", TIME_SEC).getData(), "A"); + + // Start a transaction that will roll back. + Session rollbackSession = c0.connection.newSession("rollback"); + SubscriptionManager rollbackSubs(rollbackSession); + rollbackSession.txSelect(); + rollbackSession.messageTransfer(arg::content=Message("1", "q")); + Message rollbackMessage = rollbackSubs.get("q", TIME_SEC); + 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=Message("2", "q")); + commitSession.messageTransfer(arg::content=Message("c", "q")); + rollbackSession.messageTransfer(arg::content=Message("3", "q")); + + 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", TIME_SEC).getData(), "B"); + BOOST_CHECK_EQUAL(c1.subs.get("q", TIME_SEC).getData(), "a"); + BOOST_CHECK_EQUAL(c1.subs.get("q", TIME_SEC).getData(), "b"); + BOOST_CHECK_EQUAL(c1.subs.get("q", TIME_SEC).getData(), "c"); +} + +QPID_AUTO_TEST_CASE(testUnacked) { + // Verify replication of unacknowledged messages. + ClusterFixture cluster(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"); + c0.session.messageTransfer(arg::content=Message("11","q1")); + LocalQueue q1; + c0.subs.subscribe(q1, "q1", manualAccept); + BOOST_CHECK_EQUAL(q1.get(TIME_SEC).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"); + c0.session.messageTransfer(arg::content=Message("21","q2")); + c0.session.messageTransfer(arg::content=Message("22","q2")); + LocalQueue q2; + c0.subs.subscribe(q2, "q2", manualAcquire); + m = q2.get(TIME_SEC); // 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(TIME_SEC).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"); + c0.session.messageTransfer(arg::content=Message("31", "q3")); + c0.session.messageTransfer(arg::content=Message("32", "q3")); + LocalQueue q3; + c0.subs.subscribe(q3, "q3", manualComplete); + Message m31=q3.get(TIME_SEC); + 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(TIME_SEC).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", TIME_SEC).getData(), "11"); + BOOST_CHECK_EQUAL(c1.subs.get("q2", TIME_SEC).getData(), "21"); + BOOST_CHECK_EQUAL(c1.subs.get("q2", TIME_SEC).getData(), "22"); +} + +QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(testDumpTxState, 1) { + // Verify that we dump transaction state correctly to new members. + ClusterFixture cluster(1); + Client c0(cluster[0], "c0"); + + // Do work in a transaction. + c0.session.txSelect(); + c0.session.queueDeclare("q"); + c0.session.messageTransfer(arg::content=Message("1","q")); + c0.session.messageTransfer(arg::content=Message("2","q")); + Message m; + BOOST_CHECK(c0.subs.get(m, "q", TIME_SEC)); + 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", TIME_SEC)); + BOOST_CHECK_EQUAL(m.getData(), "2"); + + // Another transaction with both members active. + c0.session.messageTransfer(arg::content=Message("3","q")); + 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", TIME_SEC)); + BOOST_CHECK_EQUAL(m.getData(), "3"); +} + +QPID_AUTO_TEST_CASE(testDumpMessageBuilder) { + // Verify that we dump a partially recieved message to a new member. + ClusterFixture cluster(1); + Client c0(cluster[0], "c0"); + c0.session.queueDeclare("q"); + Sender sender(ConnectionAccess::getImpl(c0.connection), c0.session.getChannel()); + + // Send first 2 frames of message. + MessageTransferBody transfer( + ProtocolVersion(), std::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"); + 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. + ::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", TIME_SEC)); + BOOST_CHECK_EQUAL(m.getData(), "abcd"); + BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection).size()); +} + +QPID_AUTO_TEST_CASE(testConnectionKnownHosts) { + ClusterFixture cluster(1); + Client c0(cluster[0], "c0"); + set<uint16_t> kb0 = knownBrokerPorts(c0.connection); + BOOST_CHECK_EQUAL(kb0.size(), 1u); + BOOST_CHECK_EQUAL(kb0, makeSet(cluster)); + + cluster.add(); + Client c1(cluster[1], "c1"); + set<uint16_t> kb1 = knownBrokerPorts(c1.connection); + 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<uint16_t> kb2 = knownBrokerPorts(c2.connection); + 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(DumpConsumers) { + ClusterFixture cluster(1, 1); + + Client c0(cluster[0], "c0"); + c0.session.queueDeclare("p"); + c0.session.queueDeclare("q"); + 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=Message("aaa", "q")); + + c0.session.messageTransfer(arg::content=Message("bbb", "p")); + c0.session.messageTransfer(arg::content=Message("ccc", "p")); + + // Activate the subscription, ensure message removed on all queues. + c0.subs.setFlowControl("q", FlowControl::unlimited()); + Message m; + BOOST_CHECK(c0.lq.get(m, TIME_SEC)); + 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, TIME_SEC)); + 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", TIME_SEC)); + 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=Message("xxx", "q")); + BOOST_REQUIRE(c1.subs.get(m, "q", TIME_SEC)); + BOOST_REQUIRE_EQUAL(m.getData(), "xxx"); + } +} + +QPID_AUTO_TEST_CASE(testCatchupSharedState) { + ClusterFixture cluster(1); + Client c0(cluster[0], "c0"); + + // Create some shared state. + c0.session.queueDeclare("q"); + c0.session.messageTransfer(arg::content=Message("foo","q")); + c0.session.messageTransfer(arg::content=Message("bar","q")); + while (c0.session.queueQuery("q").getMessageCount() != 2) + ::usleep(1000); // Wait for message to show up on broker 0. + + // Add a new broker, it should catch up. + cluster.add(); + + // Do some work post-add + c0.session.queueDeclare("p"); + c0.session.messageTransfer(arg::content=Message("pfoo","p")); + + // Do some work post-join + BOOST_REQUIRE_EQUAL(knownBrokerPorts(c0.connection, 2).size(), 2u); + c0.session.messageTransfer(arg::content=Message("pbar","p")); + + // Verify new brokers have state. + Message m; + + Client c1(cluster[1], "c1"); + + BOOST_CHECK(c1.subs.get(m, "q", TIME_SEC)); + BOOST_CHECK_EQUAL(m.getData(), "foo"); + BOOST_CHECK(c1.subs.get(m, "q", TIME_SEC)); + BOOST_CHECK_EQUAL(m.getData(), "bar"); + 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", TIME_SEC)); + BOOST_CHECK_EQUAL(m.getData(), "pfoo"); + BOOST_CHECK(c2.subs.get(m, "p", TIME_SEC)); + BOOST_CHECK_EQUAL(m.getData(), "pbar"); + BOOST_CHECK_EQUAL(c2.session.queueQuery("p").getMessageCount(), 0u); +} + +QPID_AUTO_TEST_CASE(testWiringReplication) { + ClusterFixture cluster(3); + Client c0(cluster[0]); + BOOST_CHECK(c0.session.queueQuery("q").getQueue().empty()); + BOOST_CHECK(c0.session.exchangeQuery("ex").getType().empty()); + c0.session.queueDeclare("q"); + 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 cluster(2); + Client c0(cluster[0]); + c0.session.queueDeclare("q"); + c0.session.messageTransfer(arg::content=Message("foo", "q")); + c0.session.messageTransfer(arg::content=Message("bar", "q")); + c0.session.close(); + Client c1(cluster[1]); + Message msg; + BOOST_CHECK(c1.subs.get(msg, "q", qpid::sys::TIME_SEC)); + BOOST_CHECK_EQUAL(string("foo"), msg.getData()); + BOOST_CHECK(c1.subs.get(msg, "q", qpid::sys::TIME_SEC)); + BOOST_CHECK_EQUAL(string("bar"), msg.getData()); +} + +QPID_AUTO_TEST_CASE(testMessageDequeue) { + // Enqueue on one broker, dequeue on two others. + ClusterFixture cluster(3); + Client c0(cluster[0], "c0"); + c0.session.queueDeclare("q"); + c0.session.messageTransfer(arg::content=Message("foo", "q")); + c0.session.messageTransfer(arg::content=Message("bar", "q")); + + 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 cluster(3); + Client c0(cluster[0]); + BOOST_REQUIRE_EQUAL(knownBrokerPorts(c0.connection, 3).size(), 3u); // Wait for brokers. + + // First start a subscription. + c0.session.queueDeclare("q"); + c0.subs.subscribe(c0.lq, "q", FlowControl::messageCredit(2)); + + // Now send messages + Client c1(cluster[1]); + c1.session.messageTransfer(arg::content=Message("foo", "q")); + c1.session.messageTransfer(arg::content=Message("bar", "q")); + + // Check they arrived + Message m; + BOOST_CHECK(c0.lq.get(m, sys::TIME_SEC)); + BOOST_CHECK_EQUAL("foo", m.getData()); + BOOST_CHECK(c0.lq.get(m, sys::TIME_SEC)); + 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_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/config.null b/RC9/qpid/cpp/src/tests/config.null new file mode 100644 index 0000000000..565c7da435 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/config.null @@ -0,0 +1 @@ +# empty config diff --git a/RC9/qpid/cpp/src/tests/consume.cpp b/RC9/qpid/cpp/src/tests/consume.cpp new file mode 100644 index 0000000000..4d74b8ae57 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/consume.cpp @@ -0,0 +1,119 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <algorithm> +#include <iostream> +#include <memory> +#include <sstream> +#include <vector> + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionManager.h" + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::sys; +using namespace std; + +typedef vector<string> StringSet; + +struct Args : public qpid::TestOptions { + uint count; + uint ack; + string queue; + bool declare; + bool summary; + + Args() : count(1000), ack(0), queue("publish-consume"), + declare(false), summary(false) + { + addOptions() + ("count", optValue(count, "N"), "number of messages to publish") + ("ack-frequency", optValue(ack, "N"), "ack every N messages (0 means use no-ack mode)") + ("queue", optValue(queue, "<queue name>"), "queue to consume from") + ("declare", optValue(declare), "declare the queue") + ("s,summary", optValue(summary), "Print undecorated rate."); + } +}; + +Args opts; + +struct Client +{ + Connection connection; + Session session; + + Client() + { + opts.open(connection); + session = connection.newSession(); + } + + void consume() + { + if (opts.declare) + session.queueDeclare(opts.queue); + SubscriptionManager subs(session); + LocalQueue lq; + SubscriptionSettings settings; + settings.acceptMode = opts.ack > 0 ? ACCEPT_MODE_EXPLICIT : ACCEPT_MODE_NONE; + settings.flowControl = FlowControl(opts.count, SubscriptionManager::UNLIMITED,false); + Subscription sub = subs.subscribe(lq, opts.queue, settings); + Message msg; + AbsTime begin=now(); + for (size_t i = 0; i < opts.count; ++i) { + msg=lq.pop(); + QPID_LOG(info, "Received: " << msg.getMessageProperties().getCorrelationId()); + } + if (opts.ack != 0) + sub.accept(sub.getUnaccepted()); // Cumulative ack for final batch. + AbsTime end=now(); + double secs(double(Duration(begin,end))/TIME_SEC); + if (opts.summary) cout << opts.count/secs << endl; + else cout << "Time: " << secs << "s Rate: " << opts.count/secs << endl; + } + + ~Client() + { + try{ + session.close(); + connection.close(); + } catch(const exception& e) { + cout << e.what() << endl; + } + } +}; + +int main(int argc, char** argv) +{ + try { + opts.parse(argc, argv); + Client client; + client.consume(); + return 0; + } catch(const exception& e) { + cout << e.what() << endl; + } + return 1; +} diff --git a/RC9/qpid/cpp/src/tests/declare_queues.cpp b/RC9/qpid/cpp/src/tests/declare_queues.cpp new file mode 100644 index 0000000000..7f61bde12a --- /dev/null +++ b/RC9/qpid/cpp/src/tests/declare_queues.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/client/FailoverManager.h> +#include <qpid/client/Session.h> +#include <qpid/Exception.h> + +#include <cstdlib> +#include <iostream> + +using namespace qpid::client; + +using namespace std; + +int main(int argc, char ** argv) +{ + ConnectionSettings settings; + if ( argc != 3 ) + { + cerr << "Usage: declare_queues host port\n"; + return 1; + } + + settings.host = argv[1]; + settings.port = atoi(argv[2]); + + FailoverManager connection(settings); + try { + bool complete = false; + while (!complete) { + Session session = connection.connect().newSession(); + try { + session.queueDeclare(arg::queue="message_queue"); + complete = true; + } catch (const qpid::TransportFailure&) {} + } + connection.close(); + return 0; + } catch (const exception& error) { + cerr << "declare_queues failed:" << error.what() << endl; + cerr << " host: " << settings.host + << " port: " << settings.port << endl; + return 1; + } + +} + + + + + diff --git a/RC9/qpid/cpp/src/tests/dlclose_noop.c b/RC9/qpid/cpp/src/tests/dlclose_noop.c new file mode 100644 index 0000000000..ba2fa75891 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/dlclose_noop.c @@ -0,0 +1,30 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * Loaded via LD_PRELOAD this will turn dlclose into a no-op. + * + * Allows valgrind to generate useful reports from programs that + * dynamically unload libraries before exit, such as CppUnit's + * DllPlugInTester. + * + */ + +#include <stdio.h> +void* dlclose(void* handle) {} + diff --git a/RC9/qpid/cpp/src/tests/echotest.cpp b/RC9/qpid/cpp/src/tests/echotest.cpp new file mode 100644 index 0000000000..7cbf3e7df4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/echotest.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/client/Connection.h> +#include <qpid/client/SubscriptionManager.h> +#include <qpid/client/Session.h> +#include <qpid/client/Message.h> +#include <qpid/client/MessageListener.h> +#include <qpid/sys/Time.h> + +#include <iostream> + +using namespace qpid::client; +using namespace qpid::framing; +using namespace qpid::sys; +using namespace std; + +struct Args : public qpid::Options, + public qpid::client::ConnectionSettings +{ + bool help; + uint count; + uint size; + bool summary; + + Args() : qpid::Options("Simple latency test optins"), help(false), count(20), size(0), summary() + { + using namespace qpid; + addOptions() + ("help", optValue(help), "Print this usage statement") + ("count", optValue(count, "N"), "Number of messages to send") + ("size", optValue(count, "N"), "Size of messages") + ("broker,b", optValue(host, "HOST"), "Broker host to connect to") + ("port,p", optValue(port, "PORT"), "Broker port to connect to") + ("username", optValue(username, "USER"), "user name for broker log in.") + ("password", optValue(password, "PASSWORD"), "password for broker log in.") + ("mechanism", optValue(mechanism, "MECH"), "SASL mechanism to use when authenticating.") + ("tcp-nodelay", optValue(tcpNoDelay), "Turn on tcp-nodelay") + ("s,summary", optValue(summary), "Print only average latency."); + } +}; + +uint64_t current_time() +{ + Duration t(now()); + return t; +} + +class Listener : public MessageListener +{ + private: + Session session; + SubscriptionManager subscriptions; + uint counter; + const uint limit; + std::string queue; + Message request; + double total, min, max; + bool summary; + + public: + Listener(Session& session, uint limit, bool summary); + void start(uint size); + void received(Message& message); +}; + +Listener::Listener(Session& s, uint l, bool summary_) : + session(s), subscriptions(s), counter(0), limit(l), + queue(session.getId().getName()), total(), + min(std::numeric_limits<double>::max()), max(), summary(summary_) +{} + +void Listener::start(uint size) +{ + session.queueDeclare(arg::queue=queue, arg::exclusive=true, arg::autoDelete=true); + request.getDeliveryProperties().setRoutingKey(queue); + subscriptions.subscribe(*this, queue, SubscriptionSettings(FlowControl::unlimited(), ACCEPT_MODE_NONE)); + + request.getDeliveryProperties().setTimestamp(current_time()); + if (size) request.setData(std::string(size, 'X')); + async(session).messageTransfer(arg::content=request); + subscriptions.run(); +} + +void Listener::received(Message& response) +{ + //extract timestamp and compute latency: + uint64_t sentAt = response.getDeliveryProperties().getTimestamp(); + uint64_t receivedAt = current_time(); + + double latency = ((double) (receivedAt - sentAt)) / TIME_MSEC; + if (!summary) cout << "Latency: " << latency << "ms" << endl; + min = std::min(latency, min); + max = std::max(latency, max); + total += latency; + + if (++counter < limit) { + request.getDeliveryProperties().setTimestamp(current_time()); + async(session).messageTransfer(arg::content=request); + } else { + subscriptions.cancel(queue); + if (summary) cout << min << "\t" << max << "\t" << total/limit << endl; + else cout << "min: " << min << " max: " << max << " average: " << total/limit << endl; + } +} + +int main(int argc, char** argv) +{ + Args opts; + opts.parse(argc, argv); + + if (opts.help) { + std::cout << opts << std::endl; + return 0; + } + + Connection connection; + try { + connection.open(opts); + Session session = connection.newSession(); + Listener listener(session, opts.count, opts.summary); + listener.start(opts.size); + + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/RC9/qpid/cpp/src/tests/exception_test.cpp b/RC9/qpid/cpp/src/tests/exception_test.cpp new file mode 100644 index 0000000000..e420bf2f0b --- /dev/null +++ b/RC9/qpid/cpp/src/tests/exception_test.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 "unit_test.h" +#include "test_tools.h" +#include "BrokerFixture.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Thread.h" +#include "qpid/framing/reply_exceptions.h" + +QPID_AUTO_TEST_SUITE(exception_test) + +// FIXME aconway 2008-06-12: need to update our exception handling to +// 0-10 handling and extend this test to provoke all the exceptional +// conditions we know of and verify the correct exception is thrown. + +using namespace std; +using namespace qpid; +using namespace sys; +using namespace client; +using namespace framing; + +using qpid::broker::Broker; +using boost::bind; +using boost::function; + +template <class Ex> +struct Catcher : public Runnable { + function<void ()> f; + bool caught; + Thread thread; + + Catcher(function<void ()> f_) : f(f_), caught(false), thread(this) {} + ~Catcher() { join(); } + + void run() { + try { + ScopedSuppressLogging sl; // Suppress messages for expected errors. + f(); + } + catch(const Ex& e) { + caught=true; + BOOST_MESSAGE(string("Caught expected exception: ")+e.what()+"["+typeid(e).name()+"]"); + } + catch(const std::exception& e) { + BOOST_ERROR(string("Bad exception: ")+e.what()+"["+typeid(e).name()+"] expected: "+typeid(Ex).name()); + } + catch(...) { + BOOST_ERROR(string("Bad exception: unknown")); + } + } + + bool join() { + if (thread.id()) { + thread.join(); + thread=Thread(); + } + return caught; + } +}; + +QPID_AUTO_TEST_CASE(TestSessionBusy) { + SessionFixture f; + try { + ScopedSuppressLogging sl; // Suppress messages for expected errors. + f.connection.newSession(f.session.getId().getName()); + BOOST_FAIL("Expected SessionBusyException for " << f.session.getId().getName()); + } catch (const SessionBusyException&) {} // FIXME aconway 2008-09-22: client is not throwing correct exception. +} + +QPID_AUTO_TEST_CASE(DisconnectedPop) { + ProxySessionFixture fix; + ProxyConnection c(fix.broker->getPort(Broker::TCP_TRANSPORT)); + fix.session.queueDeclare(arg::queue="q"); + fix.subs.subscribe(fix.lq, "q"); + Catcher<TransportFailure> pop(bind(&LocalQueue::pop, &fix.lq, sys::TIME_SEC)); + fix.connection.proxy.close(); + BOOST_CHECK(pop.join()); +} + +QPID_AUTO_TEST_CASE(DisconnectedListen) { + ProxySessionFixture fix; + struct NullListener : public MessageListener { + void received(Message&) { BOOST_FAIL("Unexpected message"); } + } l; + ProxyConnection c(fix.broker->getPort(Broker::TCP_TRANSPORT)); + fix.session.queueDeclare(arg::queue="q"); + fix.subs.subscribe(l, "q"); + + Catcher<TransportFailure> runner(bind(&SubscriptionManager::run, boost::ref(fix.subs))); + fix.connection.proxy.close(); + runner.join(); + BOOST_CHECK_THROW(fix.session.close(), TransportFailure); +} + +QPID_AUTO_TEST_CASE(NoSuchQueueTest) { + ProxySessionFixture fix; + ScopedSuppressLogging sl; // Suppress messages for expected errors. + BOOST_CHECK_THROW(fix.subs.subscribe(fix.lq, "no such queue"), NotFoundException); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/failover_soak.cpp b/RC9/qpid/cpp/src/tests/failover_soak.cpp new file mode 100644 index 0000000000..6149e845e4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/failover_soak.cpp @@ -0,0 +1,654 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES 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 <string> +#include <iostream> +#include <sstream> +#include <vector> + +#include <ForkedBroker.h> + + + + +using namespace std; + + +typedef vector<ForkedBroker *> brokerVector; + +typedef enum +{ + NO_STATUS, + RUNNING, + COMPLETED +} +childStatus; + + + +struct child +{ + child ( string & name, pid_t pid ) + : name(name), pid(pid), retval(-999), status(RUNNING) + { + gettimeofday ( & startTime, 0 ); + } + + + void + done ( int _retval ) + { + retval = _retval; + status = COMPLETED; + gettimeofday ( & stopTime, 0 ); + } + + + string name; + pid_t pid; + int retval; + childStatus status; + struct timeval startTime, + stopTime; +}; + + + + +struct children : public vector<child *> +{ + void + add ( string & name, pid_t pid ) + { + push_back(new child ( name, pid )); + } + + + 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 > 0 ) + { + 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 ( ) + { + vector<child *>::iterator i; + for ( i = begin(); i != end(); ++ i ) + if ( (COMPLETED == (*i)->status) && (0 != (*i)->retval) ) + return (*i)->retval; + + 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"; + } + + + /* + Only call this if you already know there is at least + one child still running. Supply a time in seconds. + If it has been at least that long since a shild stopped + running, we judge the system to have hung. + */ + bool + hanging ( int hangTime ) + { + struct timeval now, + duration; + gettimeofday ( &now, 0 ); + + vector<child *>::iterator i; + for ( i = begin(); i != end(); ++ i ) + { + timersub ( & now, &((*i)->startTime), & duration ); + if ( duration.tv_sec >= hangTime ) + return true; + } + + return false; + } + + + int verbosity; +}; + + + +children allMyChildren; + + + + +void +childExit ( int signalNumber ) +{ + signalNumber ++; // Now maybe the compiler willleave me alone? + 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, int & num ) { + num = mrand(1000); + stringstream ss; + ss << "soakTestCluster_" << num; + 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"; +} + + + + + +void +startNewBroker ( brokerVector & brokers, + char const * srcRoot, + char const * moduleDir, + string const clusterName ) +{ + static int brokerId = 0; + stringstream path, prefix, module; + module << moduleDir << "/cluster.so"; + path << srcRoot << "/qpidd"; + prefix << "soak-" << brokerId++; + + const char * const argv[] = + { + "qpidd", + "-p0", + "--load-module=cluster.so", + "--cluster-name", + clusterName.c_str(), + "--auth=no", + "--no-data-dir", + "--no-module-dir", + "--mgmt-enable=no", + "--log-prefix", prefix.str().c_str(), + 0 + }; + + size_t argc = sizeof(argv)/sizeof(argv[0]); + brokers.push_back ( new ForkedBroker ( argc, argv ) ); +} + + + + + +void +killFrontBroker ( brokerVector & brokers, int verbosity ) +{ + 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; + } + delete brokers[0]; + brokers.erase ( brokers.begin() ); +} + + + + + +void +killAllBrokers ( brokerVector & brokers ) +{ + for ( uint i = 0; i < brokers.size(); ++ i ) + try { brokers[i]->kill(9); } + catch ( ... ) { } +} + + + + + +pid_t +runDeclareQueuesClient ( brokerVector brokers, + char const * host, + char const * path, + int verbosity + ) +{ + string name("declareQueues"); + int port = brokers[0]->getPort ( ); + + if ( verbosity > 0 ) + cout << "startDeclareQueuesClient: host: " + << host + << " port: " + << port + << endl; + stringstream portSs; + portSs << port; + + vector<const char*> argv; + argv.push_back ( "declareQueues" ); + argv.push_back ( host ); + argv.push_back ( portSs.str().c_str() ); + argv.push_back ( 0 ); + pid_t pid = fork(); + + if ( ! pid ) { + execv ( path, const_cast<char * const *>(&argv[0]) ); + perror ( "error executing dq: " ); + return 0; + } + + allMyChildren.add ( name, pid ); + return pid; +} + + + + + +pid_t +startReceivingClient ( brokerVector brokers, + char const * host, + char const * receiverPath, + char const * reportFrequency, + int verbosity + ) +{ + string name("receiver"); + int port = brokers[0]->getPort ( ); + + if ( verbosity > 0 ) + cout << "startReceivingClient: port " << port << endl; + char portStr[100]; + char verbosityStr[100]; + sprintf(portStr, "%d", port); + sprintf(verbosityStr, "%d", 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 ( 0 ); + + pid_t pid = fork(); + + if ( ! pid ) { + execv ( receiverPath, const_cast<char * const *>(&argv[0]) ); + perror ( "error executing receiver: " ); + return 0; + } + + allMyChildren.add ( name, pid ); + return pid; +} + + + + + +pid_t +startSendingClient ( brokerVector brokers, + char const * host, + char const * senderPath, + char const * nMessages, + char const * reportFrequency, + int verbosity + ) +{ + string name("sender"); + int port = brokers[0]->getPort ( ); + + if ( verbosity ) + cout << "startSenderClient: port " << port << endl; + char portStr[100]; + char verbosityStr[100]; + + sprintf ( portStr, "%d", port); + sprintf ( verbosityStr, "%d", 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 ); + argv.push_back ( 0 ); + + pid_t pid = fork(); + + if ( ! pid ) { + execv ( senderPath, const_cast<char * const *>(&argv[0]) ); + perror ( "error executing sender: " ); + return 0; + } + + allMyChildren.add ( name, pid ); + return pid; +} + + + +#define HUNKY_DORY 0 +#define BAD_ARGS 1 +#define CANT_FORK_DQ 2 +#define CANT_FORK_RECEIVER 3 +#define DQ_FAILED 4 +#define ERROR_ON_CHILD 5 +#define HANGING 6 + + +int +main ( int argc, char const ** argv ) +{ + if ( argc < 9 ) { + cerr << "Usage: failoverSoak srcRoot moduleDir host senderPath receiverPath nMessages verbosity\n"; + cerr << " ( argc was " << argc << " )\n"; + return BAD_ARGS; + } + + signal ( SIGCHLD, childExit ); + + char const * srcRoot = argv[1]; + char const * moduleDir = argv[2]; + char const * host = argv[3]; + char const * declareQueuesPath = argv[4]; + char const * senderPath = argv[5]; + char const * receiverPath = argv[6]; + char const * nMessages = argv[7]; + char const * reportFrequency = argv[8]; + int verbosity = atoi(argv[9]); + + int maxBrokers = 50; + + allMyChildren.verbosity = verbosity; + + int clusterNum; + string clusterName; + + srand ( getpid() ); + + makeClusterName ( clusterName, clusterNum ); + + brokerVector brokers; + + if ( verbosity > 0 ) + cout << "Starting initial cluster...\n"; + + int nBrokers = 3; + for ( int i = 0; i < nBrokers; ++ i ) { + startNewBroker ( brokers, + srcRoot, + moduleDir, + clusterName ); + } + + + if ( verbosity > 0 ) + printBrokers ( brokers ); + + // Run the declareQueues child. + int childStatus; + pid_t dqClientPid = + runDeclareQueuesClient ( brokers, host, declareQueuesPath, verbosity ); + if ( -1 == dqClientPid ) { + cerr << "failoverSoak error: Couldn't fork declareQueues.\n"; + return CANT_FORK_DQ; + } + + // Don't continue until declareQueues is finished. + pid_t retval = waitpid ( dqClientPid, & childStatus, 0); + if ( retval != dqClientPid) { + cerr << "failoverSoak error: waitpid on declareQueues returned value " << retval << endl; + return DQ_FAILED; + } + allMyChildren.exited ( dqClientPid, childStatus ); + + + + // Start the receiving client. + pid_t receivingClientPid = + startReceivingClient ( brokers, + host, + receiverPath, + reportFrequency, + verbosity ); + if ( -1 == receivingClientPid ) { + cerr << "failoverSoak error: Couldn't fork receiver.\n"; + return CANT_FORK_RECEIVER; + } + + + // Start the sending client. + pid_t sendingClientPid = + startSendingClient ( brokers, + host, + senderPath, + nMessages, + reportFrequency, + verbosity ); + if ( -1 == sendingClientPid ) { + cerr << "failoverSoak error: Couldn't fork sender.\n"; + return CANT_FORK_RECEIVER; + } + + + int minSleep = 3, + maxSleep = 6; + + + for ( int totalBrokers = 3; + totalBrokers < maxBrokers; + ++ totalBrokers + ) + { + if ( verbosity > 0 ) + cout << totalBrokers << " brokers have been added to the cluster.\n\n\n"; + + // Sleep for a while. ------------------------- + int sleepyTime = mrand ( minSleep, maxSleep ); + if ( verbosity > 0 ) + cout << "Sleeping for " << sleepyTime << " seconds.\n"; + sleep ( sleepyTime ); + + // Kill the oldest broker. -------------------------- + killFrontBroker ( brokers, verbosity ); + + // Sleep for a while. ------------------------- + sleepyTime = mrand ( minSleep, maxSleep ); + if ( verbosity > 0 ) + cerr << "Sleeping for " << sleepyTime << " seconds.\n"; + sleep ( sleepyTime ); + + // Start a new broker. -------------------------- + if ( verbosity > 0 ) + cout << "Starting new broker.\n\n"; + + startNewBroker ( brokers, + srcRoot, + moduleDir, + clusterName ); + + if ( verbosity > 0 ) + printBrokers ( brokers ); + + // If all children have exited, quit. + int unfinished = allMyChildren.unfinished(); + if ( ! unfinished ) { + killAllBrokers ( brokers ); + + if ( verbosity > 0 ) + cout << "failoverSoak: all children have exited.\n"; + int retval = allMyChildren.checkChildren(); + if ( verbosity > 0 ) + std::cerr << "failoverSoak: checkChildren: " << retval << endl; + return retval ? ERROR_ON_CHILD : HUNKY_DORY; + } + + // Even if some are still running, if there's an error, quit. + if ( allMyChildren.checkChildren() ) + { + if ( verbosity > 0 ) + cout << "failoverSoak: error on child.\n"; + allMyChildren.killEverybody(); + killAllBrokers ( brokers ); + return ERROR_ON_CHILD; + } + + // If one is hanging, quit. + if ( allMyChildren.hanging ( 120 ) ) + { + if ( verbosity > 0 ) + cout << "failoverSoak: child hanging.\n"; + allMyChildren.killEverybody(); + killAllBrokers ( brokers ); + return HANGING; + } + + if ( verbosity > 0 ) { + std::cerr << "------- next kill-broker loop --------\n"; + allMyChildren.print(); + } + } + + retval = allMyChildren.checkChildren(); + if ( verbosity > 0 ) + std::cerr << "failoverSoak: checkChildren: " << retval << endl; + + if ( verbosity > 0 ) + cout << "failoverSoak: maxBrokers reached.\n"; + + allMyChildren.killEverybody(); + killAllBrokers ( brokers ); + + return retval ? ERROR_ON_CHILD : HUNKY_DORY; +} + + + diff --git a/RC9/qpid/cpp/src/tests/fanout_perftest b/RC9/qpid/cpp/src/tests/fanout_perftest new file mode 100755 index 0000000000..d8a7661f49 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/fanout_perftest @@ -0,0 +1,22 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +exec `dirname $0`/run_perftest 10000 --mode fanout --npubs 16 --nsubs 16 --size 64 diff --git a/RC9/qpid/cpp/src/tests/federated_topic_test b/RC9/qpid/cpp/src/tests/federated_topic_test new file mode 100755 index 0000000000..21d8411eaf --- /dev/null +++ b/RC9/qpid/cpp/src/tests/federated_topic_test @@ -0,0 +1,130 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Run the topic test on a federated setup + +# Clean up old log files +rm -f subscriber_*.log + +# Defaults values +SUBSCRIBERS=2 +MESSAGES=1000 +BATCHES=1 +VERBOSE=1 + +while getopts "s:m:b:" opt ; do + case $opt in + s) SUBSCRIBERS=$OPTARG ;; + m) MESSAGES=$OPTARG ;; + b) BATCHES=$OPTARG ;; + ?) + echo "Usage: %0 [-s <subscribers>] [-m <messages.] [-b <batches>]" + exit 1 + ;; + esac +done + +MY_DIR=$(dirname $(which $0)) +PYTHON_DIR=${MY_DIR}/../../../python + +trap stop_brokers EXIT + +start_broker() { + ${MY_DIR}/../qpidd --daemon --port 0 --no-module-dir --no-data-dir --auth no > qpidd.port +} + +start_brokers() { + start_broker + PORT_A=`cat qpidd.port` + start_broker + PORT_B=`cat qpidd.port` + start_broker + PORT_C=`cat qpidd.port` +} + +stop_brokers() { + for p in $PORT_A $PORT_B $PORT_C; do + ${MY_DIR}/../qpidd -q --port $p + done +} + +subscribe() { + #which broker should we connect to? + if (( $1 % 2 )); then + MY_PORT=$PORT_C; + else + MY_PORT=$PORT_A; + fi + + echo Subscriber $1 connecting on $MY_PORT + LOG="subscriber_$1.log" + ${MY_DIR}/topic_listener -p $MY_PORT > $LOG 2>&1 && rm -f $LOG +} + +publish() { + ${MY_DIR}/topic_publisher --messages $MESSAGES --batches $BATCHES --subscribers $SUBSCRIBERS -p $PORT_A +} + +setup_routes() { + BROKER_A="localhost:$PORT_A" + BROKER_B="localhost:$PORT_B" + BROKER_C="localhost:$PORT_C" + export PYTHONPATH=$PYTHON_DIR + if (($VERBOSE)); then + echo "Establishing routes for topic..." + fi + $PYTHON_DIR/commands/qpid-route route add $BROKER_B $BROKER_A amq.topic topic_control B B + $PYTHON_DIR/commands/qpid-route route add $BROKER_C $BROKER_B amq.topic topic_control C C + if (($VERBOSE)); then + echo "linked A->B->C" + fi + $PYTHON_DIR/commands/qpid-route route add $BROKER_B $BROKER_C amq.topic topic_control B B + $PYTHON_DIR/commands/qpid-route route add $BROKER_A $BROKER_B amq.topic topic_control A A + if (($VERBOSE)); then + echo "linked C->B->A" + echo "Establishing routes for response queue..." + fi + + $PYTHON_DIR/commands/qpid-route route add $BROKER_B $BROKER_C amq.direct response B B + $PYTHON_DIR/commands/qpid-route route add $BROKER_A $BROKER_B amq.direct response A A + if (($VERBOSE)); then + echo "linked C->B->A" + for b in $BROKER_A $BROKER_B $BROKER_C; do + echo "Routes for $b" + $PYTHON_DIR/commands/qpid-route route list $b + done + fi +} + +if test -d ${PYTHON_DIR} ; then + start_brokers + if (($VERBOSE)); then + echo "Running federated topic test against brokers on ports $PORT_A $PORT_B $PORT_C" + fi + + for ((i=$SUBSCRIBERS ; i--; )); do + subscribe $i & + done + + setup_routes + + publish || exit 1 +fi diff --git a/RC9/qpid/cpp/src/tests/federation.py b/RC9/qpid/cpp/src/tests/federation.py new file mode 100755 index 0000000000..ad82964007 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/federation.py @@ -0,0 +1,505 @@ +#!/usr/bin/env python +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import sys +from qpid.testlib import TestBase010, testrunner +from qpid.datatypes import Message +from qpid.queue import Empty +from time import sleep + +def add_module(args=sys.argv[1:]): + for a in args: + if a.startswith("federation"): + return False + return True + +def scan_args(name, default=None, args=sys.argv[1:]): + if (name in args): + pos = args.index(name) + return args[pos + 1] + elif default: + return default + else: + print "Please specify extra argument: %s" % name + sys.exit(2) + +def extract_args(name, args): + if (name in args): + pos = args.index(name) + del args[pos:pos+2] + else: + return None + +def remote_host(): + return scan_args("--remote-host", "localhost") + +def remote_port(): + return int(scan_args("--remote-port")) + +class FederationTests(TestBase010): + + def test_bridge_create_and_close(self): + self.startQmf(); + qmf = self.qmf + + broker = qmf.getObjects(_class="broker")[0] + result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp") + self.assertEqual(result.status, 0) + + link = qmf.getObjects(_class="link")[0] + result = link.bridge(False, "amq.direct", "amq.direct", "my-key", "", "", False, False, False) + self.assertEqual(result.status, 0) + + bridge = qmf.getObjects(_class="bridge")[0] + result = bridge.close() + self.assertEqual(result.status, 0) + + result = link.close() + self.assertEqual(result.status, 0) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + def test_pull_from_exchange(self): + session = self.session + + self.startQmf() + qmf = self.qmf + broker = qmf.getObjects(_class="broker")[0] + result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp") + self.assertEqual(result.status, 0) + + link = qmf.getObjects(_class="link")[0] + result = link.bridge(False, "amq.direct", "amq.fanout", "my-key", "", "", False, False, False) + self.assertEqual(result.status, 0) + + bridge = qmf.getObjects(_class="bridge")[0] + + #setup queue to receive messages from local broker + session.queue_declare(queue="fed1", exclusive=True, auto_delete=True) + session.exchange_bind(queue="fed1", exchange="amq.fanout") + self.subscribe(queue="fed1", destination="f1") + queue = session.incoming("f1") + sleep(6) + + #send messages to remote broker and confirm it is routed to local broker + r_conn = self.connect(host=remote_host(), port=remote_port()) + r_session = r_conn.session("test_pull_from_exchange") + + for i in range(1, 11): + dp = r_session.delivery_properties(routing_key="my-key") + r_session.message_transfer(destination="amq.direct", message=Message(dp, "Message %d" % i)) + + for i in range(1, 11): + msg = queue.get(timeout=5) + self.assertEqual("Message %d" % i, msg.body) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in queue: " + extra.body) + except Empty: None + + result = bridge.close() + self.assertEqual(result.status, 0) + result = link.close() + self.assertEqual(result.status, 0) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + def test_push_to_exchange(self): + session = self.session + + self.startQmf() + qmf = self.qmf + broker = qmf.getObjects(_class="broker")[0] + result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp") + self.assertEqual(result.status, 0) + + link = qmf.getObjects(_class="link")[0] + result = link.bridge(False, "amq.direct", "amq.fanout", "my-key", "", "", False, True, False) + self.assertEqual(result.status, 0) + + bridge = qmf.getObjects(_class="bridge")[0] + + #setup queue to receive messages from remote broker + r_conn = self.connect(host=remote_host(), port=remote_port()) + r_session = r_conn.session("test_push_to_exchange") + r_session.queue_declare(queue="fed1", exclusive=True, auto_delete=True) + r_session.exchange_bind(queue="fed1", exchange="amq.fanout") + self.subscribe(session=r_session, queue="fed1", destination="f1") + queue = r_session.incoming("f1") + sleep(6) + + #send messages to local broker and confirm it is routed to remote broker + for i in range(1, 11): + dp = session.delivery_properties(routing_key="my-key") + session.message_transfer(destination="amq.direct", message=Message(dp, "Message %d" % i)) + + for i in range(1, 11): + msg = queue.get(timeout=5) + self.assertEqual("Message %d" % i, msg.body) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in queue: " + extra.body) + except Empty: None + + result = bridge.close() + self.assertEqual(result.status, 0) + result = link.close() + self.assertEqual(result.status, 0) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + def test_pull_from_queue(self): + session = self.session + + #setup queue on remote broker and add some messages + r_conn = self.connect(host=remote_host(), port=remote_port()) + r_session = r_conn.session("test_pull_from_queue") + r_session.queue_declare(queue="my-bridge-queue", auto_delete=True) + for i in range(1, 6): + dp = r_session.delivery_properties(routing_key="my-bridge-queue") + r_session.message_transfer(message=Message(dp, "Message %d" % i)) + + #setup queue to receive messages from local broker + session.queue_declare(queue="fed1", exclusive=True, auto_delete=True) + session.exchange_bind(queue="fed1", exchange="amq.fanout") + self.subscribe(queue="fed1", destination="f1") + queue = session.incoming("f1") + + self.startQmf() + qmf = self.qmf + broker = qmf.getObjects(_class="broker")[0] + result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp") + self.assertEqual(result.status, 0) + + link = qmf.getObjects(_class="link")[0] + result = link.bridge(False, "my-bridge-queue", "amq.fanout", "my-key", "", "", True, False, False) + self.assertEqual(result.status, 0) + + bridge = qmf.getObjects(_class="bridge")[0] + sleep(3) + + #add some more messages (i.e. after bridge was created) + for i in range(6, 11): + dp = r_session.delivery_properties(routing_key="my-bridge-queue") + r_session.message_transfer(message=Message(dp, "Message %d" % i)) + + for i in range(1, 11): + try: + msg = queue.get(timeout=5) + self.assertEqual("Message %d" % i, msg.body) + except Empty: + self.fail("Failed to find expected message containing 'Message %d'" % i) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in queue: " + extra.body) + except Empty: None + + result = bridge.close() + self.assertEqual(result.status, 0) + result = link.close() + self.assertEqual(result.status, 0) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + def test_tracing_automatic(self): + remoteUrl = "%s:%d" % (remote_host(), remote_port()) + self.startQmf() + l_broker = self.qmf_broker + r_broker = self.qmf.addBroker(remoteUrl) + + l_brokerObj = self.qmf.getObjects(_class="broker", _broker=l_broker)[0] + r_brokerObj = self.qmf.getObjects(_class="broker", _broker=r_broker)[0] + + l_res = l_brokerObj.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp") + r_res = r_brokerObj.connect(testrunner.host, testrunner.port, False, "PLAIN", "guest", "guest", "tcp") + + self.assertEqual(l_res.status, 0) + self.assertEqual(r_res.status, 0) + + l_link = self.qmf.getObjects(_class="link", _broker=l_broker)[0] + r_link = self.qmf.getObjects(_class="link", _broker=r_broker)[0] + + l_res = l_link.bridge(False, "amq.direct", "amq.direct", "key", "", "", False, False, False) + r_res = r_link.bridge(False, "amq.direct", "amq.direct", "key", "", "", False, False, False) + + self.assertEqual(l_res.status, 0) + self.assertEqual(r_res.status, 0) + + count = 0 + while l_link.state != "Operational" or r_link.state != "Operational": + count += 1 + if count > 10: + self.fail("Fed links didn't become operational after 10 seconds") + sleep(1) + l_link = self.qmf.getObjects(_class="link", _broker=l_broker)[0] + r_link = self.qmf.getObjects(_class="link", _broker=r_broker)[0] + sleep(3) + + #setup queue to receive messages from local broker + session = self.session + session.queue_declare(queue="fed1", exclusive=True, auto_delete=True) + session.exchange_bind(queue="fed1", exchange="amq.direct", binding_key="key") + self.subscribe(queue="fed1", destination="f1") + queue = session.incoming("f1") + + #setup queue on remote broker and add some messages + r_conn = self.connect(host=remote_host(), port=remote_port()) + r_session = r_conn.session("test_trace") + for i in range(1, 11): + dp = r_session.delivery_properties(routing_key="key") + r_session.message_transfer(destination="amq.direct", message=Message(dp, "Message %d" % i)) + + for i in range(1, 11): + try: + msg = queue.get(timeout=5) + self.assertEqual("Message %d" % i, msg.body) + except Empty: + self.fail("Failed to find expected message containing 'Message %d'" % i) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in queue: " + extra.body) + except Empty: None + + def test_tracing(self): + session = self.session + + self.startQmf() + qmf = self.qmf + broker = qmf.getObjects(_class="broker")[0] + result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp") + self.assertEqual(result.status, 0) + + link = qmf.getObjects(_class="link")[0] + result = link.bridge(False, "amq.direct", "amq.fanout", "my-key", "my-bridge-id", + "exclude-me,also-exclude-me", False, False, False) + self.assertEqual(result.status, 0) + bridge = qmf.getObjects(_class="bridge")[0] + + #setup queue to receive messages from local broker + session.queue_declare(queue="fed1", exclusive=True, auto_delete=True) + session.exchange_bind(queue="fed1", exchange="amq.fanout") + self.subscribe(queue="fed1", destination="f1") + queue = session.incoming("f1") + sleep(6) + + #send messages to remote broker and confirm it is routed to local broker + r_conn = self.connect(host=remote_host(), port=remote_port()) + r_session = r_conn.session("test_tracing") + + trace = [None, "exclude-me", "a,exclude-me,b", "also-exclude-me,c", "dont-exclude-me"] + body = ["yes", "first-bad", "second-bad", "third-bad", "yes"] + for b, t in zip(body, trace): + headers = {} + if (t): headers["x-qpid.trace"]=t + dp = r_session.delivery_properties(routing_key="my-key") + mp = r_session.message_properties(application_headers=headers) + r_session.message_transfer(destination="amq.direct", message=Message(dp, mp, b)) + + for e in ["my-bridge-id", "dont-exclude-me,my-bridge-id"]: + msg = queue.get(timeout=5) + self.assertEqual("yes", msg.body) + self.assertEqual(e, self.getAppHeader(msg, "x-qpid.trace")) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in queue: " + extra.body) + except Empty: None + + result = bridge.close() + self.assertEqual(result.status, 0) + result = link.close() + self.assertEqual(result.status, 0) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + def test_dynamic_fanout(self): + session = self.session + r_conn = self.connect(host=remote_host(), port=remote_port()) + r_session = r_conn.session("test_dynamic_fanout") + + session.exchange_declare(exchange="fed.fanout", type="fanout") + r_session.exchange_declare(exchange="fed.fanout", type="fanout") + + self.startQmf() + qmf = self.qmf + broker = qmf.getObjects(_class="broker")[0] + result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp") + self.assertEqual(result.status, 0) + + link = qmf.getObjects(_class="link")[0] + result = link.bridge(False, "fed.fanout", "fed.fanout", "", "", "", False, False, True) + self.assertEqual(result.status, 0) + bridge = qmf.getObjects(_class="bridge")[0] + sleep(5) + + session.queue_declare(queue="fed1", exclusive=True, auto_delete=True) + session.exchange_bind(queue="fed1", exchange="fed.fanout") + self.subscribe(queue="fed1", destination="f1") + queue = session.incoming("f1") + + for i in range(1, 11): + dp = r_session.delivery_properties() + r_session.message_transfer(destination="fed.fanout", message=Message(dp, "Message %d" % i)) + + for i in range(1, 11): + msg = queue.get(timeout=5) + self.assertEqual("Message %d" % i, msg.body) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in queue: " + extra.body) + except Empty: None + + result = bridge.close() + self.assertEqual(result.status, 0) + result = link.close() + self.assertEqual(result.status, 0) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + + def test_dynamic_direct(self): + session = self.session + r_conn = self.connect(host=remote_host(), port=remote_port()) + r_session = r_conn.session("test_dynamic_direct") + + session.exchange_declare(exchange="fed.direct", type="direct") + r_session.exchange_declare(exchange="fed.direct", type="direct") + + self.startQmf() + qmf = self.qmf + broker = qmf.getObjects(_class="broker")[0] + result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp") + self.assertEqual(result.status, 0) + + link = qmf.getObjects(_class="link")[0] + result = link.bridge(False, "fed.direct", "fed.direct", "", "", "", False, False, True) + self.assertEqual(result.status, 0) + bridge = qmf.getObjects(_class="bridge")[0] + sleep(5) + + session.queue_declare(queue="fed1", exclusive=True, auto_delete=True) + session.exchange_bind(queue="fed1", exchange="fed.direct", binding_key="fd-key") + self.subscribe(queue="fed1", destination="f1") + queue = session.incoming("f1") + + for i in range(1, 11): + dp = r_session.delivery_properties(routing_key="fd-key") + r_session.message_transfer(destination="fed.direct", message=Message(dp, "Message %d" % i)) + + for i in range(1, 11): + msg = queue.get(timeout=5) + self.assertEqual("Message %d" % i, msg.body) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in queue: " + extra.body) + except Empty: None + + result = bridge.close() + self.assertEqual(result.status, 0) + result = link.close() + self.assertEqual(result.status, 0) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + + def test_dynamic_topic(self): + session = self.session + r_conn = self.connect(host=remote_host(), port=remote_port()) + r_session = r_conn.session("test_dynamic_topic") + + session.exchange_declare(exchange="fed.topic", type="topic") + r_session.exchange_declare(exchange="fed.topic", type="topic") + + self.startQmf() + qmf = self.qmf + broker = qmf.getObjects(_class="broker")[0] + result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp") + self.assertEqual(result.status, 0) + + link = qmf.getObjects(_class="link")[0] + result = link.bridge(False, "fed.topic", "fed.topic", "", "", "", False, False, True) + self.assertEqual(result.status, 0) + bridge = qmf.getObjects(_class="bridge")[0] + sleep(5) + + session.queue_declare(queue="fed1", exclusive=True, auto_delete=True) + session.exchange_bind(queue="fed1", exchange="fed.topic", binding_key="ft-key.#") + self.subscribe(queue="fed1", destination="f1") + queue = session.incoming("f1") + + for i in range(1, 11): + dp = r_session.delivery_properties(routing_key="ft-key.one.two") + r_session.message_transfer(destination="fed.topic", message=Message(dp, "Message %d" % i)) + + for i in range(1, 11): + msg = queue.get(timeout=5) + self.assertEqual("Message %d" % i, msg.body) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in queue: " + extra.body) + except Empty: None + + result = bridge.close() + self.assertEqual(result.status, 0) + result = link.close() + self.assertEqual(result.status, 0) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + + def getProperty(self, msg, name): + for h in msg.headers: + if hasattr(h, name): return getattr(h, name) + return None + + def getAppHeader(self, msg, name): + headers = self.getProperty(msg, "application_headers") + if headers: + return headers[name] + return None + + +if __name__ == '__main__': + args = sys.argv[1:] + #need to remove the extra options from args as test runner doesn't recognise them + extract_args("--remote-port", args) + extract_args("--remote-host", args) + + if add_module(): + #add module(s) to run to testrunners args + args.append("federation") + + if not testrunner.run(args): sys.exit(1) diff --git a/RC9/qpid/cpp/src/tests/header_test.cpp b/RC9/qpid/cpp/src/tests/header_test.cpp new file mode 100644 index 0000000000..ba9ffacc9b --- /dev/null +++ b/RC9/qpid/cpp/src/tests/header_test.cpp @@ -0,0 +1,59 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <iostream> + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionManager.h" + +using namespace qpid; +using namespace qpid::client; +using namespace std; + +int main(int argc, char** argv) +{ + TestOptions opts; + try { + opts.parse(argc, argv); + Connection connection; + connection.open(opts.con); + Session session = connection.newSession(); + std::string q("header_interop_test_queue"); + session.queueDeclare(arg::queue=q); + double pi = 3.14159265; + float e = 2.71828; + Message msg("", q); + msg.getMessageProperties().getApplicationHeaders().setDouble("pi", pi); + msg.getMessageProperties().getApplicationHeaders().setFloat("e", e); + session.messageTransfer(arg::content=msg); + + session.close(); + connection.close(); + + return 0; + } catch(const exception& e) { + cout << e.what() << endl; + } + return 1; +} diff --git a/RC9/qpid/cpp/src/tests/header_test.py b/RC9/qpid/cpp/src/tests/header_test.py new file mode 100755 index 0000000000..d5a2c16c01 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/header_test.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import qpid +import sys +import os +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message, RangedSet, uuid4 +from qpid.queue import Empty +from math import fabs + +def getApplicationHeaders(msg): + for h in msg.headers: + if hasattr(h, 'application_headers'): return getattr(h, 'application_headers') + return None + +# Set parameters for login + +host="127.0.0.1" +port=5672 +user="guest" +password="guest" + +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) + +# Create a connection. +socket = connect(host, port) +connection = Connection (sock=socket) +connection.start() +session = connection.session(str(uuid4())) + +q = "header_interop_test_queue" +session.queue_declare(queue=q) + +session.message_subscribe(queue=q, destination="received") +queue = session.incoming("received") +queue.start() + +msg = queue.get(timeout=10) +pi = 3.14159265 +e = 2.71828 + +headers = getApplicationHeaders(msg) +pi_ = headers["pi"] +e_ = headers["e"] +session.close(timeout=10) + +failed = False + +if pi != pi_: + print "got incorrect value for pi: ", pi_, " expected:", pi + failed = True + +if fabs(e - e_) > 0.0001: + print "got incorrect value for e: ", e_, " expected:", e + failed = True + +if failed: + sys.exit(1) +else: + print "Correct header values received." + sys.exit(0) + + + diff --git a/RC9/qpid/cpp/src/tests/interop_runner.cpp b/RC9/qpid/cpp/src/tests/interop_runner.cpp new file mode 100644 index 0000000000..8c6e0a6991 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/interop_runner.cpp @@ -0,0 +1,251 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Options.h" +#include "qpid/ptr_map.h" +#include "qpid/Exception.h" +#include "qpid/client/Channel.h" +#include "qpid/client/Connection.h" +#include "qpid/client/ConnectionOptions.h" +#include "qpid/client/Exchange.h" +#include "qpid/client/MessageListener.h" +#include "qpid/client/Queue.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Time.h" +#include <iostream> +#include <memory> +#include "BasicP2PTest.h" +#include "BasicPubSubTest.h" +#include "TestCase.h" +#include <boost/ptr_container/ptr_map.hpp> + +/** + * Framework for interop tests. + * + * [see http://cwiki.apache.org/confluence/display/qpid/Interop+Testing+Specification for details]. + */ + +using namespace qpid::client; +using namespace qpid::sys; +using qpid::TestCase; +using qpid::framing::FieldTable; +using qpid::framing::ReplyTo; +using namespace std; + +class DummyRun : public TestCase +{ +public: + DummyRun() {} + void assign(const string&, FieldTable&, ConnectionOptions&) {} + void start() {} + void stop() {} + void report(qpid::client::Message&) {} +}; + +string parse_next_word(const string& input, const string& delims, string::size_type& position); + +/** + */ +class Listener : public MessageListener, private Runnable{ + typedef boost::ptr_map<string, TestCase> TestMap; + + Channel& channel; + ConnectionOptions& options; + TestMap tests; + const string name; + const string topic; + TestCase* test; + auto_ptr<Thread> runner; + ReplyTo reportTo; + string reportCorrelator; + + void shutdown(); + bool invite(const string& name); + void run(); + + void sendResponse(Message& response, ReplyTo replyTo); + void sendResponse(Message& response, Message& request); + void sendSimpleResponse(const string& type, Message& request); + void sendReport(); +public: + Listener(Channel& channel, ConnectionOptions& options); + void received(Message& msg); + void bindAndConsume(); + void registerTest(string name, TestCase* test); +}; + +struct TestSettings : ConnectionOptions +{ + bool help; + + TestSettings() : help(false) + { + addOptions() + ("help", qpid::optValue(help), "print this usage statement"); + } +}; + +int main(int argc, char** argv) { + try { + TestSettings options; + options.parse(argc, argv); + if (options.help) { + cout << options; + } else { + Connection connection; + connection.open(options.host, options.port, "guest", "guest", options.virtualhost); + + Channel channel; + connection.openChannel(channel); + + Listener listener(channel, options); + listener.registerTest("TC1_DummyRun", new DummyRun()); + listener.registerTest("TC2_BasicP2P", new qpid::BasicP2PTest()); + listener.registerTest("TC3_BasicPubSub", new qpid::BasicPubSubTest()); + + listener.bindAndConsume(); + + channel.run(); + connection.close(); + } + } catch(const exception& error) { + cout << error.what() << endl << "Type " << argv[0] << " --help for help" << endl; + } +} + +Listener::Listener(Channel& _channel, ConnectionOptions& _options) : channel(_channel), options(_options), name(options.clientid), topic("iop.control." + name) +{} + +void Listener::registerTest(string name, TestCase* test) +{ + tests.insert(name, test); +} + +void Listener::bindAndConsume() +{ + Queue control(name, true); + channel.declareQueue(control); + qpid::framing::FieldTable bindArgs; + //replace these separate binds with a wildcard once that is supported on java broker + channel.bind(Exchange::STANDARD_TOPIC_EXCHANGE, control, "iop.control", bindArgs); + channel.bind(Exchange::STANDARD_TOPIC_EXCHANGE, control, topic, bindArgs); + + string tag; + channel.consume(control, tag, this); +} + +void Listener::sendSimpleResponse(const string& type, Message& request) +{ + Message response; + response.getHeaders().setString("CONTROL_TYPE", type); + response.getHeaders().setString("CLIENT_NAME", name); + response.getHeaders().setString("CLIENT_PRIVATE_CONTROL_KEY", topic); + response.getMessageProperties().setCorrelationId(request.getMessageProperties().getCorrelationId()); + sendResponse(response, request); +} + +void Listener::sendResponse(Message& response, Message& request) +{ + sendResponse(response, request.getMessageProperties().getReplyTo()); +} + +void Listener::sendResponse(Message& response, ReplyTo replyTo) +{ + string exchange = replyTo.getExchange(); + string routingKey = replyTo.getRoutingKey(); + channel.publish(response, exchange, routingKey); +} + +void Listener::received(Message& message) +{ + string type(message.getHeaders().getString("CONTROL_TYPE")); + + if (type == "INVITE") { + string name(message.getHeaders().getString("TEST_NAME")); + if (name.empty() || invite(name)) { + sendSimpleResponse("ENLIST", message); + } else { + cout << "Can't take part in '" << name << "'" << endl; + } + } else if (type == "ASSIGN_ROLE") { + test->assign(message.getHeaders().getString("ROLE"), message.getHeaders(), options); + sendSimpleResponse("ACCEPT_ROLE", message); + } else if (type == "START") { + reportTo = message.getMessageProperties().getReplyTo(); + reportCorrelator = message.getMessageProperties().getCorrelationId(); + runner = auto_ptr<Thread>(new Thread(this)); + } else if (type == "STATUS_REQUEST") { + reportTo = message.getMessageProperties().getReplyTo(); + reportCorrelator = message.getMessageProperties().getCorrelationId(); + test->stop(); + sendReport(); + } else if (type == "TERMINATE") { + if (test) test->stop(); + shutdown(); + } else { + cerr <<"ERROR!: Received unknown control message: " << type << endl; + shutdown(); + } +} + +void Listener::shutdown() +{ + channel.close(); +} + +bool Listener::invite(const string& name) +{ + TestMap::iterator i = tests.find(name); + test = (i != tests.end()) ? qpid::ptr_map_ptr(i) : 0; + return test; +} + +void Listener::run() +{ + //NB: this method will be called in its own thread + //start test and when start returns... + test->start(); + sendReport(); +} + +void Listener::sendReport() +{ + Message report; + report.getHeaders().setString("CONTROL_TYPE", "REPORT"); + test->report(report); + report.getMessageProperties().setCorrelationId(reportCorrelator); + sendResponse(report, reportTo); +} + +string parse_next_word(const string& input, const string& delims, string::size_type& position) +{ + string::size_type start = input.find_first_not_of(delims, position); + if (start == string::npos) { + return ""; + } else { + string::size_type end = input.find_first_of(delims, start); + if (end == string::npos) { + end = input.length(); + } + position = end; + return input.substr(start, end - start); + } +} diff --git a/RC9/qpid/cpp/src/tests/latencytest.cpp b/RC9/qpid/cpp/src/tests/latencytest.cpp new file mode 100644 index 0000000000..6895964133 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/latencytest.cpp @@ -0,0 +1,432 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +#include <algorithm> +#include <limits> +#include <iostream> +#include <memory> +#include <sstream> +#include <vector> + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/sys/Time.h" + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::sys; +using std::string; + +typedef std::vector<std::string> StringSet; + +struct Args : public qpid::TestOptions { + uint size; + uint count; + uint rate; + bool sync; + uint reportFrequency; + uint timeLimit; + uint queues; + uint prefetch; + uint ack; + bool cumulative; + bool csv; + bool durable; + string base; + + Args() : size(256), count(1000), rate(0), reportFrequency(1000), + timeLimit(0), queues(1), + prefetch(100), ack(0), + durable(false), base("latency-test") + { + addOptions() + + ("size", optValue(size, "N"), "message size") + ("queues", optValue(queues, "N"), "number of queues") + ("count", optValue(count, "N"), "number of messages to send") + ("rate", optValue(rate, "N"), "target message rate (causes count to be ignored)") + ("sync", optValue(sync), "send messages synchronously") + ("report-frequency", optValue(reportFrequency, "N"), + "number of milliseconds to wait between reports (ignored unless rate specified)") + ("time-limit", optValue(timeLimit, "N"), + "test duration, in seconds") + ("prefetch", optValue(prefetch, "N"), "prefetch count (0 implies no flow control, and no acking)") + ("ack", optValue(ack, "N"), "Ack frequency in messages (defaults to half the prefetch value)") + ("durable", optValue(durable, "yes|no"), "use durable messages") + ("csv", optValue(csv), "print stats in csv format (rate,min,max,avg)") + ("cumulative", optValue(cumulative), "cumulative stats in csv format") + ("queue-base-name", optValue(base, "<name>"), "base name for queues"); + } +}; + +const std::string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + +Args opts; +double c_min, c_avg, c_max; + +uint64_t current_time() +{ + Duration t(now()); + return t; +} + +struct Stats +{ + Mutex lock; + uint count; + double minLatency; + double maxLatency; + double totalLatency; + + Stats(); + void update(double l); + void print(); + void reset(); +}; + +class Client : public Runnable +{ +protected: + Connection connection; + AsyncSession session; + Thread thread; + string queue; + +public: + Client(const string& q); + virtual ~Client() {} + + void start(); + void join(); + void run(); + virtual void test() = 0; +}; + +class Receiver : public Client, public MessageListener +{ + SubscriptionManager mgr; + uint count; + Stats& stats; + +public: + Receiver(const string& queue, Stats& stats); + void test(); + void received(Message& msg); + Stats getStats(); + uint getCount() { return count; } + void stop() { mgr.stop(); mgr.cancel(queue); } +}; + + +class Sender : public Client +{ + string generateData(uint size); + void sendByRate(); + void sendByCount(); + Receiver& receiver; + const string data; +public: + Sender(const string& queue, Receiver& receiver); + void test(); +}; + + +class Test +{ + const string queue; + Stats stats; + Receiver receiver; + Sender sender; + AbsTime begin; + +public: + Test(const string& q) : queue(q), receiver(queue, stats), sender(queue, receiver), begin(now()) {} + void start(); + void join(); + void report(); +}; + + +Client::Client(const string& q) : queue(q) +{ + opts.open(connection); + session = connection.newSession(); +} + +void Client::start() +{ + thread = Thread(this); +} + +void Client::join() +{ + thread.join(); +} + +void Client::run() +{ + try{ + test(); + session.close(); + connection.close(); + } catch(const std::exception& e) { + std::cout << "Error in receiver: " << e.what() << std::endl; + } +} + +Receiver::Receiver(const string& q, Stats& s) : Client(q), mgr(session), count(0), stats(s) +{ + session.queueDeclare(arg::queue=queue, arg::durable=opts.durable, arg::autoDelete=true); + uint msgCount = session.queueQuery(arg::queue=queue).get().getMessageCount(); + if (msgCount) { + std::cout << "Warning: found " << msgCount << " msgs on " << queue << ". Purging..." << std::endl; + session.queuePurge(arg::queue=queue); + } + SubscriptionSettings settings; + if (opts.prefetch) { + settings.autoAck = (opts.ack ? opts.ack : (opts.prefetch / 2)); + settings.flowControl = FlowControl::messageWindow(opts.prefetch); + } else { + settings.acceptMode = ACCEPT_MODE_NONE; + settings.flowControl = FlowControl::unlimited(); + } + mgr.subscribe(*this, queue, settings); +} + +void Receiver::test() +{ + mgr.run(); + mgr.cancel(queue); +} + +void Receiver::received(Message& msg) +{ + ++count; + uint64_t sentAt = msg.getDeliveryProperties().getTimestamp(); + //uint64_t sentAt = msg.getHeaders().getTimestamp("sent-at");// TODO: add support for uint64_t as a field table type + uint64_t receivedAt = current_time(); + + //std::cerr << "Latency: " << (receivedAt - sentAt) << std::endl; + stats.update(((double) (receivedAt - sentAt)) / TIME_MSEC); + + if (!opts.rate && count >= opts.count) { + mgr.stop(); + } +} + +void Stats::update(double latency) +{ + Mutex::ScopedLock l(lock); + count++; + minLatency = std::min(minLatency, latency); + maxLatency = std::max(maxLatency, latency); + totalLatency += latency; +} + +Stats::Stats() : count(0), minLatency(std::numeric_limits<double>::max()), maxLatency(0), totalLatency(0) {} + +void Stats::print() +{ + static bool already_have_stats = false; + uint value; + + if (opts.rate) + value = opts.rate; + else + value = opts.count; + Mutex::ScopedLock l(lock); + double aux_avg = (totalLatency / count); + if (!opts.cumulative) { + if (!opts.csv) { + std::cout << "Latency(ms): min=" << minLatency << ", max=" << + maxLatency << ", avg=" << aux_avg; + } else { + std::cout << value << "," << minLatency << "," << maxLatency << + "," << aux_avg; + } + } else { + if (already_have_stats) { + c_avg = (c_min + aux_avg) / 2; + if (c_min > minLatency) c_min = minLatency; + if (c_max < maxLatency) c_max = maxLatency; + } else { + c_avg = aux_avg; + c_min = minLatency; + c_max = maxLatency; + already_have_stats = true; + } + std::cout << value << "," << c_min << "," << c_max << + "," << c_avg; + } + +} + +void Stats::reset() +{ + Mutex::ScopedLock l(lock); + count = 0; + totalLatency = maxLatency = 0; + minLatency = std::numeric_limits<double>::max(); +} + +Sender::Sender(const string& q, Receiver& receiver) : Client(q), receiver(receiver), data(generateData(opts.size)) {} + +void Sender::test() +{ + if (opts.rate) sendByRate(); + else sendByCount(); +} + +void Sender::sendByCount() +{ + Message msg(data, queue); + if (opts.durable) { + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + } + + for (uint i = 0; i < opts.count; i++) { + uint64_t sentAt(current_time()); + msg.getDeliveryProperties().setTimestamp(sentAt); + async(session).messageTransfer(arg::content=msg, arg::acceptMode=1); + if (opts.sync) session.sync(); + } + session.sync(); +} + +void Sender::sendByRate() +{ + Message msg(data, queue); + if (opts.durable) { + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + } + + //calculate interval (in micro secs) between messages to achieve desired rate + uint64_t interval = (1000*1000)/opts.rate; + uint64_t timeLimit(opts.timeLimit * TIME_SEC); + uint64_t start(current_time()); + + while (true) { + uint64_t start_msg(current_time()); + msg.getDeliveryProperties().setTimestamp(start_msg); + async(session).messageTransfer(arg::content=msg, arg::acceptMode=1); + if (opts.sync) session.sync(); + + uint64_t now = current_time(); + + if (timeLimit != 0 && (now - start) > timeLimit) { + session.sync(); + receiver.stop(); + break; + } + + uint64_t timeTaken = (now - start_msg) / TIME_USEC; + if (timeTaken < interval) { + qpid::sys::usleep(interval - timeTaken); + } else if (timeTaken > interval && + !opts.csv && !opts.cumulative) { // Don't be so verbose in this case, we're piping the results to another program + std::cout << "Could not achieve desired rate! (Took " << timeTaken + << " microsecs to send message, aiming for " << interval << " microsecs)" << std::endl; + } + } +} + +string Sender::generateData(uint size) +{ + if (size < chars.length()) { + return chars.substr(0, size); + } + std::string data; + for (uint i = 0; i < (size / chars.length()); i++) { + data += chars; + } + data += chars.substr(0, size % chars.length()); + return data; +} + + +void Test::start() +{ + receiver.start(); + begin = AbsTime(now()); + sender.start(); +} + +void Test::join() +{ + sender.join(); + receiver.join(); + AbsTime end = now(); + Duration time(begin, end); + double msecs(time / TIME_MSEC); + if (!opts.csv) { + std::cout << "Sent " << receiver.getCount() << " msgs through " << queue + << " in " << msecs << "ms (" << (receiver.getCount() * 1000 / msecs) << " msgs/s) "; + } + stats.print(); + std::cout << std::endl; +} + +void Test::report() +{ + stats.print(); + std::cout << std::endl; + stats.reset(); +} + +int main(int argc, char** argv) +{ + try { + opts.parse(argc, argv); + if (opts.cumulative) + opts.csv = true; + boost::ptr_vector<Test> tests(opts.queues); + for (uint i = 0; i < opts.queues; i++) { + std::ostringstream out; + out << opts.base << "-" << (i+1); + tests.push_back(new Test(out.str())); + } + for (boost::ptr_vector<Test>::iterator i = tests.begin(); i != tests.end(); i++) { + i->start(); + } + if (opts.rate && !opts.timeLimit) { + while (true) { + qpid::sys::usleep(opts.reportFrequency * 1000); + //print latency report: + for (boost::ptr_vector<Test>::iterator i = tests.begin(); i != tests.end(); i++) { + i->report(); + } + } + } else { + for (boost::ptr_vector<Test>::iterator i = tests.begin(); i != tests.end(); i++) { + i->join(); + } + } + + return 0; + } catch(const std::exception& e) { + std::cout << e.what() << std::endl; + } + return 1; +} diff --git a/RC9/qpid/cpp/src/tests/logging.cpp b/RC9/qpid/cpp/src/tests/logging.cpp new file mode 100644 index 0000000000..051722e7c8 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/logging.cpp @@ -0,0 +1,366 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "test_tools.h" +#include "qpid/log/Logger.h" +#include "qpid/log/Options.h" +#include "qpid/log/OstreamOutput.h" +#include "qpid/memory.h" +#include "qpid/Options.h" +#if defined (_WIN32) +#else +# include "qpid/log/posix/SinkOptions.h" +#endif + +#include <boost/test/floating_point_comparison.hpp> +#include <boost/format.hpp> +#include "unit_test.h" + +#include <exception> +#include <fstream> +#include <time.h> + + +QPID_AUTO_TEST_SUITE(loggingTestSuite) + +using namespace std; +using namespace boost; +using namespace qpid::log; + +QPID_AUTO_TEST_CASE(testStatementInit) { + Statement s=QPID_LOG_STATEMENT_INIT(debug); int line=__LINE__; + BOOST_CHECK(!s.enabled); + BOOST_CHECK_EQUAL(string(__FILE__), s.file); + BOOST_CHECK_EQUAL(line, s.line); + BOOST_CHECK_EQUAL(debug, s.level); +} + + +QPID_AUTO_TEST_CASE(testSelector_enable) { + Selector s; + // Simple enable + s.enable(debug,"foo"); + BOOST_CHECK(s.isEnabled(debug,"foo")); + BOOST_CHECK(!s.isEnabled(error,"foo")); + BOOST_CHECK(!s.isEnabled(error,"bar")); + + // Substring match + BOOST_CHECK(s.isEnabled(debug, "bazfoobar")); + BOOST_CHECK(!s.isEnabled(debug, "bazbar")); + + // Different levels for different substrings. + s.enable(info, "bar"); + BOOST_CHECK(s.isEnabled(debug, "foobar")); + BOOST_CHECK(s.isEnabled(info, "foobar")); + BOOST_CHECK(!s.isEnabled(debug, "bar")); + BOOST_CHECK(!s.isEnabled(info, "foo")); + + // Enable-strings + s.enable("notice:blob"); + BOOST_CHECK(s.isEnabled(notice, "blob")); + s.enable("error+:oops"); + BOOST_CHECK(s.isEnabled(error, "oops")); + BOOST_CHECK(s.isEnabled(critical, "oops")); +} + +QPID_AUTO_TEST_CASE(testStatementEnabled) { + // Verify that the singleton enables and disables static + // log statements. + Logger& l = Logger::instance(); + ScopedSuppressLogging ls(l); + l.select(Selector(debug)); + static Statement s=QPID_LOG_STATEMENT_INIT(debug); + BOOST_CHECK(!s.enabled); + static Statement::Initializer init(s); + BOOST_CHECK(s.enabled); + + static Statement s2=QPID_LOG_STATEMENT_INIT(warning); + static Statement::Initializer init2(s2); + BOOST_CHECK(!s2.enabled); + + l.select(Selector(warning)); + BOOST_CHECK(!s.enabled); + BOOST_CHECK(s2.enabled); +} + +struct TestOutput : public Logger::Output { + vector<string> msg; + vector<Statement> stmt; + + TestOutput(Logger& l) { + l.output(std::auto_ptr<Logger::Output>(this)); + } + + void log(const Statement& s, const string& m) { + msg.push_back(m); + stmt.push_back(s); + } + string last() { return msg.back(); } +}; + +using boost::assign::list_of; + +QPID_AUTO_TEST_CASE(testLoggerOutput) { + Logger l; + l.clear(); + l.select(Selector(debug)); + Statement s=QPID_LOG_STATEMENT_INIT(debug); + + TestOutput* out=new TestOutput(l); + + // Verify message is output. + l.log(s, "foo"); + vector<string> expect=list_of("foo\n"); + BOOST_CHECK_EQUAL(expect, out->msg); + + // Verify multiple outputs + TestOutput* out2=new TestOutput(l); + l.log(Statement(), "baz"); + expect.push_back("baz\n"); + BOOST_CHECK_EQUAL(expect, out->msg); + expect.erase(expect.begin()); + BOOST_CHECK_EQUAL(expect, out2->msg); +} + +QPID_AUTO_TEST_CASE(testMacro) { + Logger& l=Logger::instance(); + ScopedSuppressLogging ls(l); + l.select(Selector(info)); + TestOutput* out=new TestOutput(l); + QPID_LOG(info, "foo"); + vector<string> expect=list_of("foo\n"); + BOOST_CHECK_EQUAL(expect, out->msg); + BOOST_CHECK_EQUAL(__FILE__, out->stmt.front().file); + + // Not enabled: + QPID_LOG(debug, "bar"); + BOOST_CHECK_EQUAL(expect, out->msg); + + QPID_LOG(info, 42 << " bingo"); + expect.push_back("42 bingo\n"); + BOOST_CHECK_EQUAL(expect, out->msg); +} + +QPID_AUTO_TEST_CASE(testLoggerFormat) { + Logger& l = Logger::instance(); + ScopedSuppressLogging ls(l); + l.select(Selector(critical)); + TestOutput* out=new TestOutput(l); + + l.format(Logger::FILE); + QPID_LOG(critical, "foo"); + BOOST_CHECK_EQUAL(out->last(), string(__FILE__)+": foo\n"); + + l.format(Logger::FILE|Logger::LINE); + QPID_LOG(critical, "foo"); + BOOST_CHECK_REGEX(string(__FILE__)+":\\d+: foo\n", out->last()); + + l.format(Logger::FUNCTION); + QPID_LOG(critical, "foo"); + BOOST_CHECK_REGEX("void .*testLoggerFormat.*\\(\\): foo\n", out->last()); + + l.format(Logger::LEVEL); + QPID_LOG(critical, "foo"); + BOOST_CHECK_EQUAL("critical foo\n", out->last()); +} + +QPID_AUTO_TEST_CASE(testOstreamOutput) { + Logger& l=Logger::instance(); + ScopedSuppressLogging ls(l); + l.select(Selector(error)); + ostringstream os; + l.output(qpid::make_auto_ptr<Logger::Output>(new OstreamOutput(os))); + QPID_LOG(error, "foo"); + QPID_LOG(error, "bar"); + QPID_LOG(error, "baz"); + BOOST_CHECK_EQUAL("foo\nbar\nbaz\n", os.str()); +} + +#if 0 // This test requires manual intervention. Normally disabled. +QPID_AUTO_TEST_CASE(testSyslogOutput) { + Logger& l=Logger::instance(); + Logger::StateSaver ls(l); + l.clear(); + l.select(Selector(info)); + l.syslog("qpid_test"); + QPID_LOG(info, "Testing QPID"); + BOOST_ERROR("Manually verify that /var/log/messages contains a recent line 'Testing QPID'"); +} +#endif // 0 + +int count() { + static int n = 0; + return n++; +} + +int loggedCount() { + static int n = 0; + QPID_LOG(debug, "counting: " << n); + return n++; +} + + +using namespace qpid::sys; + +// Measure CPU time. +clock_t timeLoop(int times, int (*fp)()) { + clock_t start=clock(); + while (times-- > 0) + (*fp)(); + return clock() - start; +} + +// Overhead test disabled because it consumes a ton of CPU and takes +// forever under valgrind. Not friendly for regular test runs. +// +#if 0 +QPID_AUTO_TEST_CASE(testOverhead) { + // Ensure that the ratio of CPU time for an incrementing loop + // with and without disabled log statements is in acceptable limits. + // + int times=100000000; + clock_t noLog=timeLoop(times, count); + clock_t withLog=timeLoop(times, loggedCount); + double ratio=double(withLog)/double(noLog); + + // NB: in initial tests the ratio was consistently below 1.5, + // 2.5 is reasonable and should avoid spurios failures + // due to machine load. + // + BOOST_CHECK_SMALL(ratio, 2.5); +} +#endif // 0 + +Statement statement( + Level level, const char* file="", int line=0, const char* fn=0) +{ + Statement s={0, file, line, fn, level}; + return s; +} + + +#define ARGC(argv) (sizeof(argv)/sizeof(char*)) + +QPID_AUTO_TEST_CASE(testOptionsParse) { + const char* argv[]={ + 0, + "--log-enable", "error+:foo", + "--log-enable", "debug:bar", + "--log-enable", "info", + "--log-to-stderr", "no", + "--log-to-file", "logout", + "--log-level", "yes", + "--log-source", "1", + "--log-thread", "true", + "--log-function", "YES" + }; + qpid::log::Options opts(""); + qpid::log::posix::SinkOptions sinks("test"); + opts.parse(ARGC(argv), const_cast<char**>(argv)); + sinks = *opts.sinkOptions; + vector<string> expect=list_of("error+:foo")("debug:bar")("info"); + BOOST_CHECK_EQUAL(expect, opts.selectors); + BOOST_CHECK(!sinks.logToStderr); + BOOST_CHECK(!sinks.logToStdout); + BOOST_CHECK(sinks.logFile == "logout"); + BOOST_CHECK(opts.level); + BOOST_CHECK(opts.source); + BOOST_CHECK(opts.function); + BOOST_CHECK(opts.thread); +} + +QPID_AUTO_TEST_CASE(testOptionsDefault) { + Options opts(""); + qpid::log::posix::SinkOptions sinks("test"); + sinks = *opts.sinkOptions; + BOOST_CHECK(sinks.logToStderr); + BOOST_CHECK(!sinks.logToStdout); + BOOST_CHECK(sinks.logFile.length() == 0); + vector<string> expect=list_of("notice+"); + BOOST_CHECK_EQUAL(expect, opts.selectors); + BOOST_CHECK(opts.time && opts.level); + BOOST_CHECK(!(opts.source || opts.function || opts.thread)); +} + +QPID_AUTO_TEST_CASE(testSelectorFromOptions) { + const char* argv[]={ + 0, + "--log-enable", "error+:foo", + "--log-enable", "debug:bar", + "--log-enable", "info" + }; + qpid::log::Options opts(""); + opts.parse(ARGC(argv), const_cast<char**>(argv)); + vector<string> expect=list_of("error+:foo")("debug:bar")("info"); + BOOST_CHECK_EQUAL(expect, opts.selectors); + Selector s(opts); + BOOST_CHECK(!s.isEnabled(warning, "x")); + BOOST_CHECK(!s.isEnabled(debug, "x")); + BOOST_CHECK(s.isEnabled(debug, "bar")); + BOOST_CHECK(s.isEnabled(error, "foo")); + BOOST_CHECK(s.isEnabled(critical, "foo")); +} + +QPID_AUTO_TEST_CASE(testLoggerStateure) { + Logger& l=Logger::instance(); + ScopedSuppressLogging ls(l); + Options opts("test"); + const char* argv[]={ + 0, + "--log-time", "no", + "--log-source", "yes", + "--log-to-stderr", "no", + "--log-to-file", "logging.tmp", + "--log-enable", "critical" + }; + opts.parse(ARGC(argv), const_cast<char**>(argv)); + l.configure(opts); + QPID_LOG(critical, "foo"); int srcline=__LINE__; + ifstream log("logging.tmp"); + string line; + getline(log, line); + string expect=(format("critical %s:%d: foo")%__FILE__%srcline).str(); + BOOST_CHECK_EQUAL(expect, line); + log.close(); + unlink("logging.tmp"); +} + +QPID_AUTO_TEST_CASE(testQuoteNonPrintable) { + Logger& l=Logger::instance(); + ScopedSuppressLogging ls(l); + Options opts("test"); + opts.time=false; + qpid::log::posix::SinkOptions *sinks = + dynamic_cast<qpid::log::posix::SinkOptions *>(opts.sinkOptions.get()); + sinks->logToStderr = false; + sinks->logFile = "logging.tmp"; + l.configure(opts); + + char s[] = "null\0tab\tspace newline\nret\r\x80\x99\xff"; + string str(s, sizeof(s)); + QPID_LOG(critical, str); + ifstream log("logging.tmp"); + string line; + getline(log, line, '\0'); + string expect="critical null\\x00tab\tspace newline\nret\r\\x80\\x99\\xFF\\x00\n"; + BOOST_CHECK_EQUAL(expect, line); + log.close(); + unlink("logging.tmp"); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/multiq_perftest b/RC9/qpid/cpp/src/tests/multiq_perftest new file mode 100755 index 0000000000..10f9edd2a6 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/multiq_perftest @@ -0,0 +1,22 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +exec `dirname $0`/run_perftest 10000 --mode shared --qt 16 diff --git a/RC9/qpid/cpp/src/tests/perfdist b/RC9/qpid/cpp/src/tests/perfdist new file mode 100755 index 0000000000..59548b23f7 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/perfdist @@ -0,0 +1,87 @@ +#!/bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +# +# Distributed perftest. +# Runs perftest clients on multiple hosts using ssh. +# + +set -e +usage() { +cat <<EOF +usage: $0 <perftest-args> -- <client-hosts ...> [ --- <broker hosts...> ] +Client & broker hosts can also be set in env vars CLIENTS and BROKERS. + +Run perftest clients on the client hosts against brokers on the broker +hosts Clients are assigned to client hosts round robin: publishers +first, then subscribers. If there are multiple brokers (for cluster +tests) clients connect to them round robin. + +Broker hosts can be listed with -b in perftest-args or after --- +at the end of the arguments. + +Error: $* +EOF +exit 1 +} + +TESTDIR=${TESTDIR:-$PWD} # Absolute path to test exes on all hosts. + +collect() { eval $COLLECT=\""\$$COLLECT $*"\"; } +NPUBS=1 +NSUBS=1 +COLLECT=ARGS +while test $# -gt 0; do + case $1 in + --publish|--subscribe|--setup|--control) usage "Don't pass perftest action flags: $1" ;; + --npubs) collect $1 $2; NPUBS=$2; shift 2 ;; + --nsubs) collect $1 $2; NSUBS=$2; shift 2 ;; + -s|--summary) collect $1; QUIET=yes; shift 1 ;; + -b|--broker) BROKERS="$BROKERS $2"; shift 2;; + --) COLLECT=CLIENTARG; shift ;; + ---) COLLECT=BROKERARG; shift;; + *) collect $1; shift ;; + esac +done + +CLIENTS=${CLIENTARG:-$CLIENTS} +if [ -z "$CLIENTS" ]; then usage "No client hosts listed after --"; fi +BROKERS=${BROKERARG:-$BROKERS} +if [ -z "$BROKERS" ]; then usage "No brokers specified"; fi + +PERFTEST="$TESTDIR/perftest $ARGS" + +CLIENTS=($CLIENTS) +BROKERS=($BROKERS) +start() { + CLIENT=${CLIENTS[i % ${#CLIENTS[*]}]} + BROKER=${BROKERS[i % ${#BROKERS[*]}]} + ARGS="$* --broker $BROKER" + cmd="ssh -n $CLIENT $PERFTEST $ARGS" + test -z "$QUIET" && echo "Client $i: $cmd" + $cmd & +} + +$PERFTEST --setup -b ${BROKERS[0]} +for (( i=0 ; i < $NPUBS ; ++i)); do start --publish; done +for (( ; i < $NPUBS+$NSUBS ; ++i)); do start --subscribe; done +$PERFTEST --control -b ${BROKERS[0]} diff --git a/RC9/qpid/cpp/src/tests/perftest.cpp b/RC9/qpid/cpp/src/tests/perftest.cpp new file mode 100644 index 0000000000..be9ffd846e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/perftest.cpp @@ -0,0 +1,701 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "TestOptions.h" + +#include "qpid/client/AsyncSession.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Completion.h" +#include "qpid/client/Message.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/Time.h" + +#include <boost/lexical_cast.hpp> +#include <boost/bind.hpp> +#include <boost/function.hpp> +#include <boost/ptr_container/ptr_vector.hpp> + +#include <iostream> +#include <sstream> +#include <numeric> +#include <algorithm> +#include <unistd.h> +#include <math.h> + + +using namespace std; +using namespace qpid; +using namespace client; +using namespace sys; +using boost::lexical_cast; +using boost::bind; + +enum Mode { SHARED, FANOUT, TOPIC }; +const char* modeNames[] = { "shared", "fanout", "topic" }; + +// istream/ostream ops so Options can read/display Mode. +istream& operator>>(istream& in, Mode& mode) { + string s; + in >> s; + int i = find(modeNames, modeNames+3, s) - modeNames; + if (i >= 3) throw Exception("Invalid mode: "+s); + mode = Mode(i); + return in; +} + +ostream& operator<<(ostream& out, Mode mode) { + return out << modeNames[mode]; +} + + +struct Opts : public TestOptions { + + // Actions + bool setup, control, publish, subscribe; + + // Queue policy + uint32_t queueMaxCount; + uint64_t queueMaxSize; + bool queueDurable; + + // Publisher + size_t pubs; + size_t count ; + size_t size; + bool confirm; + bool durable; + bool uniqueData; + bool syncPub; + + // Subscriber + size_t subs; + size_t ack; + + // General + size_t qt; + size_t iterations; + Mode mode; + bool summary; + uint32_t intervalSub; + uint32_t intervalPub; + size_t tx; + size_t txPub; + size_t txSub; + bool commitAsync; + + static const std::string helpText; + + Opts() : + TestOptions(helpText), + setup(false), control(false), publish(false), subscribe(false), + pubs(1), count(500000), size(1024), confirm(true), durable(false), uniqueData(false), syncPub(false), + subs(1), ack(0), + qt(1), iterations(1), mode(SHARED), summary(false), + intervalSub(0), intervalPub(0), tx(0), txPub(0), txSub(0), commitAsync(false) + { + addOptions() + ("setup", optValue(setup), "Create shared queues.") + ("control", optValue(control), "Run test, print report.") + ("publish", optValue(publish), "Publish messages.") + ("subscribe", optValue(subscribe), "Subscribe for messages.") + + ("mode", optValue(mode, "shared|fanout|topic"), "Test mode." + "\nshared: --qt queues, --npubs publishers and --nsubs subscribers per queue.\n" + "\nfanout: --npubs publishers, --nsubs subscribers, fanout exchange." + "\ntopic: --qt topics, --npubs publishers and --nsubs subscribers per topic.\n") + + ("npubs", optValue(pubs, "N"), "Create N publishers.") + ("count", optValue(count, "N"), "Each publisher sends N messages.") + ("size", optValue(size, "BYTES"), "Size of messages in bytes.") + ("pub-confirm", optValue(confirm, "yes|no"), "Publisher use confirm-mode.") + ("durable", optValue(durable, "yes|no"), "Publish messages as durable.") + ("unique-data", optValue(uniqueData, "yes|no"), "Make data for each message unique.") + ("sync-publish", optValue(syncPub, "yes|no"), "Wait for confirmation of each message before sending the next one.") + + ("nsubs", optValue(subs, "N"), "Create N subscribers.") + ("sub-ack", optValue(ack, "N"), "N>0: Subscriber acks batches of N.\n" + "N==0: Subscriber uses unconfirmed mode") + + ("qt", optValue(qt, "N"), "Create N queues or topics.") + ("iterations", optValue(iterations, "N"), "Desired number of iterations of the test.") + ("summary,s", optValue(summary), "Summary output: pubs/sec subs/sec transfers/sec Mbytes/sec") + + ("queue-max-count", optValue(queueMaxCount, "N"), "queue policy: count to trigger 'flow to disk'") + ("queue-max-size", optValue(queueMaxSize, "N"), "queue policy: accumulated size to trigger 'flow to disk'") + ("queue-durable", optValue(queueDurable, "N"), "Make queue durable (implied if durable set)") + + ("interval_sub", optValue(intervalSub, "ms"), ">=0 delay between msg consume") + ("interval_pub", optValue(intervalPub, "ms"), ">=0 delay between msg publish") + + ("tx", optValue(tx, "N"), "if non-zero, the transaction batch size for publishing and consuming") + ("pub-tx", optValue(txPub, "N"), "if non-zero, the transaction batch size for publishing") + ("async-commit", optValue(commitAsync, "yes|no"), "Don't wait for completion of commit") + ("sub-tx", optValue(txSub, "N"), "if non-zero, the transaction batch size for consuming"); + } + + // Computed values + size_t totalPubs; + size_t totalSubs; + size_t transfers; + size_t subQuota; + + void parse(int argc, char** argv) { + TestOptions::parse(argc, argv); + switch (mode) { + case SHARED: + if (count % subs) { + count += subs - (count % subs); + cout << "WARNING: Adjusted --count to " << count + << " the nearest multiple of --nsubs" << endl; + } + totalPubs = pubs*qt; + totalSubs = subs*qt; + subQuota = (pubs*count)/subs; + break; + case FANOUT: + if (qt != 1) cerr << "WARNING: Fanout mode, ignoring --qt=" + << qt << endl; + qt=1; + totalPubs = pubs; + totalSubs = subs; + subQuota = totalPubs*count; + break; + case TOPIC: + totalPubs = pubs*qt; + totalSubs = subs*qt; + subQuota = pubs*count; + break; + } + transfers=(totalPubs*count) + (totalSubs*subQuota); + if (tx) { + if (txPub) { + cerr << "WARNING: Using overriden tx value for publishers: " << txPub << std::endl; + } else { + txPub = tx; + } + if (txSub) { + cerr << "WARNING: Using overriden tx value for subscribers: " << txSub << std::endl; + } else { + txSub = tx; + } + } + } +}; + +const std::string Opts::helpText= +"There are two ways to use perftest: single process or multi-process.\n\n" +"If none of the --setup, --publish, --subscribe or --control options\n" +"are given perftest will run a single-process test.\n" +"For a multi-process test first run:\n" +" perftest --setup <other options>\n" +"and wait for it to complete. The remaining process should run concurrently::\n" +"Run --npubs times: perftest --publish <other options>\n" +"Run --nsubs times: perftest --subscribe <other options>\n" +"Run once: perftest --control <other options>\n" +"Note the <other options> must be identical for all processes.\n"; + +Opts opts; + +struct Client : public Runnable { + Connection connection; + AsyncSession session; + Thread thread; + + Client() { + opts.open(connection); + session = connection.newSession(); + } + + ~Client() { + try { + session.close(); + connection.close(); + } catch (const std::exception& e) { + std::cerr << "Error in shutdown: " << e.what() << std::endl; + } + } +}; + +struct Setup : public Client { + + void queueInit(string name, bool durable=false, const framing::FieldTable& settings=framing::FieldTable()) { + session.queueDeclare(arg::queue=name, arg::durable=durable, arg::arguments=settings); + session.queuePurge(arg::queue=name); + session.sync(); + } + + void run() { + queueInit("pub_start"); + queueInit("pub_done"); + queueInit("sub_ready"); + queueInit("sub_done"); + if (opts.mode==SHARED) { + framing::FieldTable settings;//queue policy settings + settings.setInt("qpid.max_count", opts.queueMaxCount); + settings.setInt("qpid.max_size", opts.queueMaxSize); + for (size_t i = 0; i < opts.qt; ++i) { + ostringstream qname; + qname << "perftest" << i; + queueInit(qname.str(), opts.durable || opts.queueDurable, settings); + } + } + } +}; + +void expect(string actual, string expect) { + if (expect != actual) + throw Exception("Expecting "+expect+" but received "+actual); + +} + +double secs(Duration d) { return double(d)/TIME_SEC; } +double secs(AbsTime start, AbsTime finish) { + return secs(Duration(start,finish)); +} + + +// Collect rates & print stats. +class Stats { + vector<double> values; + double sum; + + public: + Stats() : sum(0) {} + + // Functor to collect rates. + void operator()(const string& data) { + try { + double d=lexical_cast<double>(data); + values.push_back(d); + sum += d; + } catch (const std::exception&) { + throw Exception("Bad report: "+data); + } + } + + double mean() const { + return sum/values.size(); + } + + double stdev() const { + if (values.size() <= 1) return 0; + double avg = mean(); + double ssq = 0; + for (vector<double>::const_iterator i = values.begin(); + i != values.end(); ++i) { + double x=*i; + x -= avg; + ssq += x*x; + } + return sqrt(ssq/(values.size()-1)); + } + + ostream& print(ostream& out) { + ostream_iterator<double> o(out, "\n"); + copy(values.begin(), values.end(), o); + out << "Average: " << mean(); + if (values.size() > 1) + out << " (std.dev. " << stdev() << ")"; + return out << endl; + } +}; + + +// Manage control queues, collect and print reports. +struct Controller : public Client { + + SubscriptionManager subs; + + Controller() : subs(session) {} + + /** Process messages from queue by applying a functor. */ + void process(size_t n, string queue, + boost::function<void (const string&)> msgFn) + { + if (!opts.summary) + cout << "Processing " << n << " messages from " + << queue << " " << flush; + LocalQueue lq; + subs.setFlowControl(n, SubscriptionManager::UNLIMITED, false); + subs.subscribe(lq, queue); + for (size_t i = 0; i < n; ++i) { + if (!opts.summary) cout << "." << flush; + msgFn(lq.pop().getData()); + } + if (!opts.summary) cout << " done." << endl; + } + + void process(size_t n, LocalQueue lq, string queue, + boost::function<void (const string&)> msgFn) + { + session.messageFlow(queue, 0, n); + if (!opts.summary) + cout << "Processing " << n << " messages from " + << queue << " " << flush; + for (size_t i = 0; i < n; ++i) { + if (!opts.summary) cout << "." << flush; + msgFn(lq.pop().getData()); + } + if (!opts.summary) cout << " done." << endl; + } + + void send(size_t n, string queue, string data) { + if (!opts.summary) + cout << "Sending " << data << " " << n << " times to " << queue + << endl; + Message msg(data, queue); + for (size_t i = 0; i < n; ++i) + session.messageTransfer(arg::content=msg, arg::acceptMode=1); + } + + void run() { // Controller + try { + // Wait for subscribers to be ready. + process(opts.totalSubs, "sub_ready", bind(expect, _1, "ready")); + + LocalQueue pubDone; + LocalQueue subDone; + subs.setFlowControl(0, SubscriptionManager::UNLIMITED, false); + subs.subscribe(pubDone, "pub_done"); + subs.subscribe(subDone, "sub_done"); + + double txrateTotal(0); + double mbytesTotal(0); + double pubRateTotal(0); + double subRateTotal(0); + + for (size_t j = 0; j < opts.iterations; ++j) { + AbsTime start=now(); + send(opts.totalPubs, "pub_start", "start"); // Start publishers + + Stats pubRates; + Stats subRates; + + process(opts.totalPubs, pubDone, "pub_done", boost::ref(pubRates)); + process(opts.totalSubs, subDone, "sub_done", boost::ref(subRates)); + + AbsTime end=now(); + + double time=secs(start, end); + double txrate=opts.transfers/time; + double mbytes=(txrate*opts.size)/(1024*1024); + + if (!opts.summary) { + cout << endl << "Total " << opts.transfers << " transfers of " + << opts.size << " bytes in " + << time << " seconds." << endl; + cout << endl << "Publish transfers/sec: " << endl; + pubRates.print(cout); + cout << endl << "Subscribe transfers/sec: " << endl; + subRates.print(cout); + cout << endl + << "Total transfers/sec: " << txrate << endl + << "Total Mbytes/sec: " << mbytes << endl; + } + else { + cout << pubRates.mean() << "\t" + << subRates.mean() << "\t" + << txrate << "\t" + << mbytes << endl; + } + + txrateTotal += txrate; + mbytesTotal += mbytes; + pubRateTotal += pubRates.mean(); + subRateTotal += subRates.mean(); + } + if (opts.iterations > 1) { + cout << "Averages: "<< endl + << (pubRateTotal / opts.iterations) << "\t" + << (subRateTotal / opts.iterations) << "\t" + << (txrateTotal / opts.iterations) << "\t" + << (mbytesTotal / opts.iterations) << endl; + } + } + catch (const std::exception& e) { + cout << "Controller exception: " << e.what() << endl; + } + } +}; + + +struct PublishThread : public Client { + string destination; + string routingKey; + + PublishThread() {}; + + PublishThread(string key, string dest=string()) { + destination=dest; + routingKey=key; + } + + void run() { // Publisher + try { + string data; + size_t offset(0); + if (opts.uniqueData) { + offset = 5; + data += "data:";//marker (requested for latency testing tool scripts) + data += string(sizeof(size_t), 'X');//space for seq no + data += session.getId().str(); + if (opts.size > data.size()) { + data += string(opts.size - data.size(), 'X'); + } else if(opts.size < data.size()) { + cout << "WARNING: Increased --size to " << data.size() + << " to honour --unique-data" << endl; + } + } else { + size_t msgSize=max(opts.size, sizeof(size_t)); + data = string(msgSize, 'X'); + } + + Message msg(data, routingKey); + if (opts.durable) + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + + + if (opts.txPub){ + session.txSelect(); + } + SubscriptionManager subs(session); + LocalQueue lq; + subs.setFlowControl(1, SubscriptionManager::UNLIMITED, true); + subs.subscribe(lq, "pub_start"); + + for (size_t j = 0; j < opts.iterations; ++j) { + expect(lq.pop().getData(), "start"); + AbsTime start=now(); + for (size_t i=0; i<opts.count; i++) { + // Stamp the iteration into the message data, avoid + // any heap allocation. + const_cast<std::string&>(msg.getData()).replace(offset, sizeof(size_t), + reinterpret_cast<const char*>(&i), sizeof(size_t)); + if (opts.syncPub) { + sync(session).messageTransfer( + arg::destination=destination, + arg::content=msg, + arg::acceptMode=1); + } else { + session.messageTransfer( + arg::destination=destination, + arg::content=msg, + arg::acceptMode=1); + } + if (opts.txPub && ((i+1) % opts.txPub == 0)){ + if (opts.commitAsync){ + session.txCommit(); + } else { + sync(session).txCommit(); + } + } + if (opts.intervalPub) ::usleep(opts.intervalPub*1000); + } + if (opts.confirm) session.sync(); + AbsTime end=now(); + double time=secs(start,end); + + // Send result to controller. + Message report(lexical_cast<string>(opts.count/time), "pub_done"); + session.messageTransfer(arg::content=report, arg::acceptMode=1); + if (opts.txPub){ + sync(session).txCommit(); + } + } + session.close(); + } + catch (const std::exception& e) { + cout << "PublishThread exception: " << e.what() << endl; + } + } +}; + +struct SubscribeThread : public Client { + + string queue; + + SubscribeThread() {} + + SubscribeThread(string q) { queue = q; } + + SubscribeThread(string key, string ex) { + queue=session.getId().str(); // Unique name. + session.queueDeclare(arg::queue=queue, + arg::exclusive=true, + arg::autoDelete=true, + arg::durable=opts.durable); + session.exchangeBind(arg::queue=queue, + arg::exchange=ex, + arg::bindingKey=key); + } + + void verify(bool cond, const char* test, uint32_t expect, uint32_t actual) { + if (!cond) { + Message error( + QPID_MSG("Sequence error: expected n" << test << expect << " but got " << actual), + "sub_done"); + session.messageTransfer(arg::content=error, arg::acceptMode=1); + throw Exception(error.getData()); + } + } + + void run() { // Subscribe + try { + if (opts.txSub) sync(session).txSelect(); + SubscriptionManager subs(session); + SubscriptionSettings settings; + settings.autoAck = opts.txSub ? opts.txSub : opts.ack; + settings.acceptMode = (opts.txSub || opts.ack ? ACCEPT_MODE_EXPLICIT : ACCEPT_MODE_NONE); + settings.flowControl = FlowControl::messageCredit(opts.subQuota); + LocalQueue lq; + Subscription subscription = subs.subscribe(lq, queue, settings); + // Notify controller we are ready. + session.messageTransfer(arg::content=Message("ready", "sub_ready"), arg::acceptMode=1); + if (opts.txSub) { + if (opts.commitAsync) session.txCommit(); + else sync(session).txCommit(); + } + + for (size_t j = 0; j < opts.iterations; ++j) { + if (j > 0) { + //need to allocate some more credit + session.messageFlow(queue, 0, opts.subQuota); + } + Message msg; + AbsTime start=now(); + size_t expect=0; + for (size_t i = 0; i < opts.subQuota; ++i) { + msg=lq.pop(); + if (opts.txSub && ((i+1) % opts.txSub == 0)) { + if (opts.commitAsync) session.txCommit(); + else sync(session).txCommit(); + } + if (opts.intervalSub) ::usleep(opts.intervalSub*1000); + // TODO aconway 2007-11-23: check message order for. + // multiple publishers. Need an array of counters, + // one per publisher and a publisher ID in the + // message. Careful not to introduce a lot of overhead + // here, e.g. no std::map, std::string etc. + // + // For now verify order only for a single publisher. + size_t offset = opts.uniqueData ? 5 /*marker is 'data:'*/ : 0; + size_t n = *reinterpret_cast<const size_t*>(msg.getData().data() + offset); + if (opts.pubs == 1) { + if (opts.subs == 1 || opts.mode == FANOUT) verify(n==expect, "==", expect, n); + else verify(n>=expect, ">=", expect, n); + expect = n+1; + } + } + if (opts.txSub || opts.ack) + subscription.accept(subscription.getUnaccepted()); + if (opts.txSub) { + if (opts.commitAsync) session.txCommit(); + else sync(session).txCommit(); + } + AbsTime end=now(); + + // Report to publisher. + Message result(lexical_cast<string>(opts.subQuota/secs(start,end)), + "sub_done"); + session.messageTransfer(arg::content=result, arg::acceptMode=1); + if (opts.txSub) sync(session).txCommit(); + } + session.close(); + } + catch (const std::exception& e) { + cout << "SubscribeThread exception: " << e.what() << endl; + } + } +}; + +int main(int argc, char** argv) { + + try { + opts.parse(argc, argv); + + string exchange; + switch (opts.mode) { + case FANOUT: exchange="amq.fanout"; break; + case TOPIC: exchange="amq.topic"; break; + case SHARED: break; + } + + bool singleProcess= + (!opts.setup && !opts.control && !opts.publish && !opts.subscribe); + if (singleProcess) + opts.setup = opts.control = opts.publish = opts.subscribe = true; + + if (opts.setup) Setup().run(); // Set up queues + + boost::ptr_vector<Client> subs(opts.subs); + boost::ptr_vector<Client> pubs(opts.pubs); + + // Start pubs/subs for each queue/topic. + for (size_t i = 0; i < opts.qt; ++i) { + ostringstream key; + key << "perftest" << i; // Queue or topic name. + if (opts.publish) { + size_t n = singleProcess ? opts.pubs : 1; + for (size_t j = 0; j < n; ++j) { + pubs.push_back(new PublishThread(key.str(), exchange)); + pubs.back().thread=Thread(pubs.back()); + } + } + if (opts.subscribe) { + size_t n = singleProcess ? opts.subs : 1; + for (size_t j = 0; j < n; ++j) { + if (opts.mode==SHARED) + subs.push_back(new SubscribeThread(key.str())); + else + subs.push_back(new SubscribeThread(key.str(),exchange)); + subs.back().thread=Thread(subs.back()); + } + } + } + + if (opts.control) Controller().run(); + + + // Wait for started threads. + if (opts.publish) { + for (boost::ptr_vector<Client>::iterator i=pubs.begin(); + i != pubs.end(); + ++i) + i->thread.join(); + } + + + if (opts.subscribe) { + for (boost::ptr_vector<Client>::iterator i=subs.begin(); + i != subs.end(); + ++i) + i->thread.join(); + } + return 0; + } + catch (const std::exception& e) { + cout << endl << e.what() << endl; + } + return 1; +} + + diff --git a/RC9/qpid/cpp/src/tests/policy.acl b/RC9/qpid/cpp/src/tests/policy.acl new file mode 100644 index 0000000000..ef46026555 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/policy.acl @@ -0,0 +1 @@ +acl allow all all diff --git a/RC9/qpid/cpp/src/tests/publish.cpp b/RC9/qpid/cpp/src/tests/publish.cpp new file mode 100644 index 0000000000..34c2b8fefc --- /dev/null +++ b/RC9/qpid/cpp/src/tests/publish.cpp @@ -0,0 +1,128 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <algorithm> +#include <iostream> +#include <memory> +#include <sstream> +#include <vector> + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/client/SubscriptionManager.h" + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::sys; +using namespace std; + +typedef vector<string> StringSet; + +struct Args : public qpid::TestOptions { + uint size; + uint count; + bool durable; + string destination; + string routingKey; + bool summary; + bool id; + + Args() : size(256), count(1000), durable(true), routingKey("publish-consume"), summary(false), id(false) { + addOptions() + ("size", optValue(size, "N"), "message size") + ("count", optValue(count, "N"), "number of messages to publish") + ("durable", optValue(durable, "yes|no"), "use durable messages") + ("destination", optValue(destination, "<exchange name>"), "destination to publish to") + ("routing-key", optValue(routingKey, "<key>"), "routing key to publish with") + ("summary,s", optValue(summary), "Output only the rate.") + ("id", optValue(id), "Add unique correlation ID"); + } +}; + +Args opts; + +struct Client +{ + Connection connection; + AsyncSession session; + + Client() + { + opts.open(connection); + session = connection.newSession(); + } + + // Cheap hex calculation, avoid expensive ostrstream and string + // creation to generate correlation ids in message loop. + char hex(char i) { return i<10 ? '0'+i : 'A'+i-10; } + void hex(char i, string& s) { + s[0]=hex(i>>24); s[1]=hex(i>>16); s[2]=hex(i>>8); s[3]=i; + } + + void publish() + { + AbsTime begin=now(); + Message msg(string(opts.size, 'X'), opts.routingKey); + string correlationId = "0000"; + if (opts.durable) + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + + for (uint i = 0; i < opts.count; i++) { + if (opts.id) { + hex(i+1, correlationId); + msg.getMessageProperties().setCorrelationId(correlationId); + } + session.messageTransfer(arg::destination=opts.destination, + arg::content=msg, + arg::acceptMode=1); + } + session.sync(); + AbsTime end=now(); + double secs(double(Duration(begin,end))/TIME_SEC); + if (opts.summary) cout << opts.count/secs << endl; + else cout << "Time: " << secs << "s Rate: " << opts.count/secs << endl; + } + + ~Client() + { + try{ + session.close(); + connection.close(); + } catch(const exception& e) { + cout << e.what() << endl; + } + } +}; + +int main(int argc, char** argv) +{ + try { + opts.parse(argc, argv); + Client client; + client.publish(); + return 0; + } catch(const exception& e) { + cout << e.what() << endl; + } + return 1; +} diff --git a/RC9/qpid/cpp/src/tests/python_tests b/RC9/qpid/cpp/src/tests/python_tests new file mode 100755 index 0000000000..30bb8259a4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/python_tests @@ -0,0 +1,39 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Run the python tests. +QPID_PORT=${QPID_PORT:-5672} +PYTHON_TESTS=${PYTHON_TESTS:-$*} +MY_DIR=`dirname \`which $0\`` +QPID_PYTHON_DIR=${QPID_PYTHON_DIR:-${MY_DIR}/../../../python} + +run() { + SPEC=$1 + FAILING=$2 + ./run-tests --skip-self-test -v -s $SPEC -I $FAILING -b localhost:$QPID_PORT $PYTHON_TESTS || { echo "FAIL python tests for $SPEC"; exit 1; } +} + +if test -d ${QPID_PYTHON_DIR} ; then + cd ${QPID_PYTHON_DIR} + run 0-10-errata cpp_failing_0-10.txt +else + echo "WARNING: No python tests. $QPID_PYTHON_DIR not found." +fi diff --git a/RC9/qpid/cpp/src/tests/quick_perftest b/RC9/qpid/cpp/src/tests/quick_perftest new file mode 100755 index 0000000000..4f7cf3cb54 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/quick_perftest @@ -0,0 +1,22 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +exec `dirname $0`/run_test ./perftest --summary --count 100 diff --git a/RC9/qpid/cpp/src/tests/quick_topictest b/RC9/qpid/cpp/src/tests/quick_topictest new file mode 100755 index 0000000000..5e7d85849f --- /dev/null +++ b/RC9/qpid/cpp/src/tests/quick_topictest @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +# Quick and quiet topic test for make check. +test -z "$srcdir" && srcdir=. +$srcdir/topictest -s2 -m2 -b1 > topictest.log 2>&1 || { + echo $0 FAILED: + cat topictest.log + exit 1 +} +rm topictest.log diff --git a/RC9/qpid/cpp/src/tests/quick_txtest b/RC9/qpid/cpp/src/tests/quick_txtest new file mode 100755 index 0000000000..938e3805d8 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/quick_txtest @@ -0,0 +1,22 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +exec `dirname $0`/run_test ./txtest --queues 4 --tx-count 10 --quiet diff --git a/RC9/qpid/cpp/src/tests/receiver.cpp b/RC9/qpid/cpp/src/tests/receiver.cpp new file mode 100644 index 0000000000..1b0b6b2548 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/receiver.cpp @@ -0,0 +1,129 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <qpid/client/FailoverManager.h> +#include <qpid/client/Session.h> +#include <qpid/client/Message.h> +#include <qpid/client/SubscriptionManager.h> +#include <qpid/client/SubscriptionManager.h> +#include "TestOptions.h" + +#include <iostream> +#include <fstream> + + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::framing; + +using namespace std; + +struct Args : public qpid::TestOptions +{ + string queue; + uint messages; + bool ignoreDuplicates; + uint creditWindow; + uint ackFrequency; + + Args() : queue("test-queue"), messages(0), ignoreDuplicates(false), creditWindow(0), ackFrequency(1) + { + addOptions() + ("queue", qpid::optValue(queue, "QUEUE NAME"), "Queue from which to request messages") + ("messages", qpid::optValue(messages, "N"), "Number of messages to receive; 0 means receive indefinitely") + ("ignore-duplicates", qpid::optValue(ignoreDuplicates), "Detect and ignore duplicates (by checking 'sn' header)") + ("credit-window", qpid::optValue(creditWindow, "N"), "Credit window (0 implies infinite window)") + ("ack-frequency", qpid::optValue(ackFrequency, "N"), "Ack frequency (0 implies none of the messages will get accepted)"); + } +}; + +const string EOS("eos"); + +class Receiver : public MessageListener, public FailoverManager::Command +{ + public: + Receiver(const string& queue, uint messages, bool ignoreDuplicates, uint creditWindow, uint ackFrequency); + void received(Message& message); + void execute(AsyncSession& session, bool isRetry); + private: + const string queue; + const uint count; + const bool skipDups; + SubscriptionSettings settings; + Subscription subscription; + uint processed; + uint lastSn; + + bool isDuplicate(Message& message); +}; + +Receiver::Receiver(const string& q, uint messages, bool ignoreDuplicates, uint creditWindow, uint ackFrequency) : + queue(q), count(messages), skipDups(ignoreDuplicates), processed(0), lastSn(0) +{ + if (creditWindow) settings.flowControl = FlowControl::messageWindow(creditWindow); + settings.autoAck = ackFrequency; +} + +void Receiver::received(Message & message) +{ + if (!(skipDups && isDuplicate(message))) { + bool eos = message.getData() == EOS; + if (!eos) std::cout << message.getData() << std::endl; + if (eos || ++processed == count) subscription.cancel(); + } +} + +bool Receiver::isDuplicate(Message& message) +{ + uint sn = message.getHeaders().getAsInt("sn"); + if (lastSn < sn) { + lastSn = sn; + return false; + } else { + return true; + } +} + +void Receiver::execute(AsyncSession& session, bool /*isRetry*/) +{ + SubscriptionManager subs(session); + subscription = subs.subscribe(*this, queue, settings); + subs.run(); +} + +int main(int argc, char ** argv) +{ + Args opts; + try { + opts.parse(argc, argv); + FailoverManager connection(opts.con); + Receiver receiver(opts.queue, opts.messages, opts.ignoreDuplicates, opts.creditWindow, opts.ackFrequency); + connection.execute(receiver); + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cerr << "Failure: " << error.what() << std::endl; + } + return 1; +} + + + diff --git a/RC9/qpid/cpp/src/tests/replaying_sender.cpp b/RC9/qpid/cpp/src/tests/replaying_sender.cpp new file mode 100644 index 0000000000..7e148e277f --- /dev/null +++ b/RC9/qpid/cpp/src/tests/replaying_sender.cpp @@ -0,0 +1,131 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <qpid/client/FailoverManager.h> +#include <qpid/client/Session.h> +#include <qpid/client/AsyncSession.h> +#include <qpid/client/Message.h> +#include <qpid/client/MessageReplayTracker.h> +#include <qpid/Exception.h> + +#include <iostream> +#include <sstream> + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::framing; + +using namespace std; + +class Sender : public FailoverManager::Command +{ + public: + Sender(const std::string& queue, uint count, uint reportFreq); + void execute(AsyncSession& session, bool isRetry); + uint getSent(); + + int verbosity; + + private: + MessageReplayTracker sender; + const uint count; + uint sent; + const uint reportFrequency; + Message message; + +}; + +Sender::Sender(const std::string& queue, uint count_, uint reportFreq ) : sender(10), count(count_), sent(0), reportFrequency(reportFreq) +{ + message.getDeliveryProperties().setRoutingKey(queue); +} + +void Sender::execute(AsyncSession& session, bool isRetry) +{ + if (isRetry) sender.replay(session); + else sender.init(session); + while (sent < count) { + stringstream message_data; + message_data << ++sent; + message.setData(message_data.str()); + message.getHeaders().setInt("sn", sent); + sender.send(message); + if (count > reportFrequency && !(sent % reportFrequency)) { + if ( verbosity > 0 ) + std::cout << "sent " << sent << " of " << count << std::endl; + } + } + message.setData("That's all, folks!"); + sender.send(message); + + if ( verbosity > 0 ) + std::cout << "SENDER COMPLETED\n"; +} + +uint Sender::getSent() +{ + return sent; +} + +int main(int argc, char ** argv) +{ + ConnectionSettings settings; + + if ( argc != 6 ) + { + std::cerr << "Usage: replaying_sender host port n_messages report_frequency verbosity\n"; + return 1; + } + + settings.host = argv[1]; + settings.port = atoi(argv[2]); + int n_messages = atoi(argv[3]); + int reportFrequency = atoi(argv[4]); + int verbosity = atoi(argv[5]); + + FailoverManager connection(settings); + Sender sender("message_queue", n_messages, reportFrequency ); + sender.verbosity = verbosity; + try { + connection.execute ( sender ); + if ( verbosity > 0 ) + { + std::cout << "Sender finished. Sent " + << sender.getSent() + << " messages." + << endl; + } + connection.close(); + return 0; + } + catch(const std::exception& error) + { + cerr << "Sender (host: " + << settings.host + << " port: " + << settings.port + << " ) " + << " Failed: " + << error.what() + << std::endl; + } + return 1; +} diff --git a/RC9/qpid/cpp/src/tests/restart_cluster b/RC9/qpid/cpp/src/tests/restart_cluster new file mode 100755 index 0000000000..6a6abc8042 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/restart_cluster @@ -0,0 +1,38 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Re-start a cluster on the local host. + +srcdir=`dirname $0` +$srcdir/stop_cluster +exec $srcdir/start_cluster "$@" +#!/bin/sh +# Re-start a cluster on the local host. + +srcdir=`dirname $0` +$srcdir/stop_cluster +exec $srcdir/start_cluster "$@" +#!/bin/sh +# Re-start a cluster on the local host. + +srcdir=`dirname $0` +$srcdir/stop_cluster +exec $srcdir/start_cluster "$@" diff --git a/RC9/qpid/cpp/src/tests/resuming_receiver.cpp b/RC9/qpid/cpp/src/tests/resuming_receiver.cpp new file mode 100644 index 0000000000..f49a115e1e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/resuming_receiver.cpp @@ -0,0 +1,163 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <qpid/client/FailoverManager.h> +#include <qpid/client/Session.h> +#include <qpid/client/Message.h> +#include <qpid/client/SubscriptionManager.h> + +#include <iostream> +#include <fstream> + + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::framing; + +using namespace std; + + +class Listener : public MessageListener, + public FailoverManager::Command, + public FailoverManager::ReconnectionStrategy +{ + public: + Listener ( int report_frequency = 1000, int verbosity = 0 ); + void received(Message& message); + void execute(AsyncSession& session, bool isRetry); + void check(); + void editUrlList(std::vector<Url>& urls); + private: + Subscription subscription; + uint count; + uint received_twice; + uint lastSn; + bool gaps; + uint reportFrequency; + int verbosity; +}; + + +Listener::Listener(int freq, int verbosity) + : count(0), + received_twice(0), + lastSn(0), + gaps(false), + reportFrequency(freq), + verbosity(verbosity) +{} + + +void Listener::received(Message & message) +{ + if (message.getData() == "That's all, folks!") + { + if(verbosity > 0 ) + { + std::cout << "Shutting down listener for " + << message.getDestination() << std::endl; + + std::cout << "Listener received " + << count + << " messages (" + << received_twice + << " received_twice)" + << endl; + } + subscription.cancel(); + if ( verbosity > 0 ) + std::cout << "LISTENER COMPLETED\n"; + } else { + uint sn = message.getHeaders().getAsInt("sn"); + if (lastSn < sn) { + if (sn - lastSn > 1) { + std::cerr << "Error: gap in sequence between " << lastSn << " and " << sn << std::endl; + gaps = true; + } + lastSn = sn; + ++count; + if ( ! ( count % reportFrequency ) ) { + if ( verbosity > 0 ) + std::cout << "Listener has received " + << count + << " messages.\n"; + } + } else { + ++received_twice; + } + } +} + +void Listener::check() +{ + if (gaps) throw Exception("Detected gaps in sequence; messages appear to have been lost."); +} + +void Listener::execute(AsyncSession& session, bool isRetry) +{ + if (isRetry) { + // std::cout << "Resuming from " << count << std::endl; + } + SubscriptionManager subs(session); + subscription = subs.subscribe(*this, "message_queue"); + subs.run(); +} + +void Listener::editUrlList(std::vector<Url>& urls) +{ + /** + * A more realistic algorithm would be to search through the list + * for prefered hosts and ensure they come first in the list. + */ + if (urls.size() > 1) std::rotate(urls.begin(), urls.begin() + 1, urls.end()); +} + +int main(int argc, char ** argv) +{ + ConnectionSettings settings; + + if ( argc != 5 ) + { + std::cerr << "Usage: resuming_receiver host port report_frequency verbosity\n"; + return 1; + } + + settings.host = argv[1]; + settings.port = atoi(argv[2]); + int reportFrequency = atoi(argv[3]); + int verbosity = atoi(argv[4]); + + Listener listener(reportFrequency, verbosity); + FailoverManager connection(settings, &listener); + + try { + connection.execute(listener); + connection.close(); + listener.check(); + return 0; + } catch(const std::exception& error) { + std::cerr << "Receiver failed: " << error.what() << std::endl; + } + return 1; +} + + + diff --git a/RC9/qpid/cpp/src/tests/run-unit-tests b/RC9/qpid/cpp/src/tests/run-unit-tests new file mode 100755 index 0000000000..862a76c4f5 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/run-unit-tests @@ -0,0 +1,48 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# +# Library names (without path or .so) and CppUnit test paths can be +# specified on the command line or in env var UNIT_TESTS. For example: +# +# Selected test classes: +# ./run-unit-tests ValueTest ClientChannelTest +# +# Individual test method +# ./run-unit-tests ValueTest :ValueTest::testStringValueEquals +# +# Build and run selected tests: +# make check TESTS=run-unit-tests UNIT_TESTS=ClientChannelTest +# + +for u in $* $UNIT_TESTS ; do + case $u in + :*) TEST_ARGS="$TEST_ARGS $u" ;; # A test path. + *) TEST_ARGS="$TEST_ARGS .libs/$u.so" ;; # A test library. + esac +done +test -z "$TEST_ARGS" && TEST_ARGS=".libs/*Test.so" + +test -z "$srcdir" && srcdir=. + +# libdlclose_noop prevents unloading symbols needed for valgrind output. +export LD_PRELOAD=.libs/libdlclose_noop.so +source $srcdir/run_test DllPlugInTester -c -b $TEST_ARGS diff --git a/RC9/qpid/cpp/src/tests/run_acl_tests b/RC9/qpid/cpp/src/tests/run_acl_tests new file mode 100755 index 0000000000..8d583c1895 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/run_acl_tests @@ -0,0 +1,64 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Run the acl tests. $srcdir is set by the Makefile. +PYTHON_DIR=$srcdir/../../../python +DATA_DIR=`pwd`/data_dir + +trap stop_brokers INT TERM QUIT + +start_brokers() { + ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIR --load-module ../.libs/acl.so --acl-file policy.acl --auth no > qpidd.port + LOCAL_PORT=`cat qpidd.port` +} + +stop_brokers() { + ../qpidd -q --port $LOCAL_PORT +} + +test_loading_acl_from_absolute_path(){ + POLICY_FILE=$PWD/$srcdir/policy.acl + ../qpidd --daemon --port 0 --no-module-dir --no-data-dir --auth no --load-module ../.libs/acl.so --acl-file $POLICY_FILE -t --log-to-file temp.log 2>/dev/null + PORT=`grep "Listening on TCP port" temp.log | awk '{print $8}'` + ACL_FILE=`grep "notice Read ACL file" temp.log | awk '{print $7}'` + ../qpidd -q --port $PORT + if test "$ACL_FILE" != "\"$POLICY_FILE\""; then + echo "unable to load policy file from an absolute path"; + return 1; + fi + rm temp.log +} + +if test -d ${PYTHON_DIR} ; then + rm -rf $DATA_DIR + mkdir -p $DATA_DIR + cp $srcdir/policy.acl $DATA_DIR + start_brokers + echo "Running acl tests using brokers on ports $LOCAL_PORT" + PYTHONPATH=$PYTHON_DIR + export PYTHONPATH + $srcdir/acl.py -v -s $srcdir/../../../specs/amqp.0-10-qpid-errata.xml -b localhost:$LOCAL_PORT --port $LOCAL_PORT || EXITCODE=1 + stop_brokers || EXITCODE=1 + test_loading_acl_from_absolute_path || EXITCODE=1 + rm -rf $DATA_DIR + exit $EXITCODE +fi + diff --git a/RC9/qpid/cpp/src/tests/run_failover_soak b/RC9/qpid/cpp/src/tests/run_failover_soak new file mode 100755 index 0000000000..9dddf59cf1 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/run_failover_soak @@ -0,0 +1,56 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Check AIS requirements and run tests if found. +id -ng | grep '\<ais\>' >/dev/null || \ + NOGROUP="The ais group is not your primary group." +ps -u root | grep aisexec >/dev/null || \ + NOAISEXEC="The aisexec daemon is not running as root" + +if test -n "$NOGROUP" -o -n "$NOAISEXEC"; then + cat <<EOF + + =========== WARNING: NOT RUNNING AIS TESTS ============== + + Tests that depend on the openais library (used for clustering) + will not be run because: + + $NOGROUP + $NOAISEXEC + + ========================================================== + +EOF + exit 0; # A warning, not a failure. +fi + + +host=127.0.0.1 + +src_root=.. +module_dir=$src_root/.libs +n_messages=300000 +report_frequency=10000 +verbosity=1 + + +exec `dirname $0`/failover_soak $src_root $module_dir $host ./declare_queues ./replaying_sender ./resuming_receiver $n_messages $report_frequency $verbosity + diff --git a/RC9/qpid/cpp/src/tests/run_federation_tests b/RC9/qpid/cpp/src/tests/run_federation_tests new file mode 100755 index 0000000000..28bcc012cc --- /dev/null +++ b/RC9/qpid/cpp/src/tests/run_federation_tests @@ -0,0 +1,52 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Run the federation tests. +MY_DIR=`dirname \`which $0\`` +PYTHON_DIR=${MY_DIR}/../../../python + +trap stop_brokers INT TERM QUIT + +start_brokers() { + ../qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no > qpidd.port + LOCAL_PORT=`cat qpidd.port` + ../qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no > qpidd.port + REMOTE_PORT=`cat qpidd.port` +} + +stop_brokers() { + ../qpidd -q --port $LOCAL_PORT + ../qpidd -q --port $REMOTE_PORT +} + +if test -d ${PYTHON_DIR} ; then + start_brokers + echo "Running federation tests using brokers on ports $LOCAL_PORT $REMOTE_PORT" + PYTHONPATH=${PYTHON_DIR} + export PYTHONPATH + ${MY_DIR}/federation.py -v -s ${MY_DIR}/../../../specs/amqp.0-10-qpid-errata.xml -b localhost:$LOCAL_PORT --remote-port $REMOTE_PORT $@ + RETCODE=$? + stop_brokers + if test x$RETCODE != x0; then + echo "FAIL federation tests"; exit 1; + fi +fi + diff --git a/RC9/qpid/cpp/src/tests/run_header_test b/RC9/qpid/cpp/src/tests/run_header_test new file mode 100755 index 0000000000..39d4a24f84 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/run_header_test @@ -0,0 +1,37 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Simple test of encode/decode of a double in application headers +# TODO: this should be expanded to cover a wider set of types and go +# in both directions + +srcdir=`dirname $0` +PYTHON_DIR=$srcdir/../../../python +test -f qpidd.port && QPID_PORT=`cat qpidd.port` + +if test -d ${PYTHON_DIR} ; then + ./header_test -p $QPID_PORT + export PYTHONPATH=$PYTHON_DIR:$PYTHONPATH + $srcdir/header_test.py "localhost" $QPID_PORT +else + echo "Skipping header test as python libs not found" +fi + diff --git a/RC9/qpid/cpp/src/tests/run_perftest b/RC9/qpid/cpp/src/tests/run_perftest new file mode 100755 index 0000000000..1a9b934641 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/run_perftest @@ -0,0 +1,28 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Args: count [perftest options...] +# Run a perftest with count multiplied. +# +MULTIPLIER=3 +COUNT=`expr $1 \* $MULTIPLIER` +shift +exec `dirname $0`/run_test ./perftest --summary --count $COUNT "$@" diff --git a/RC9/qpid/cpp/src/tests/run_test b/RC9/qpid/cpp/src/tests/run_test new file mode 100755 index 0000000000..062e9e137e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/run_test @@ -0,0 +1,78 @@ +#!/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. +# + +# +# 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. +# + +srcdir=`dirname $0` +. $srcdir/vg_check + +# Export variables from makefile. +export VALGRIND srcdir + +# Set QPID_PORT if qpidd.port exists. +test -f qpidd.port && QPID_PORT=`cat qpidd.port` +export QPID_PORT + +# Avoid silly libtool error messages if these are not defined +test -z "$LC_ALL" && LC_ALL= +test -z "$LC_CTYPE" && LC_CTYPE= +test -z "$LC_COLLATE" && LC_COLLATE= +test -z "$LC_MESSAGES" && LC_MESSAGES= +export LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + +VG_LOG="`basename $1`.vglog" +rm -f $VG_LOG* + +VALGRIND_OPTS=" +--gen-suppressions=all +--leak-check=full +--demangle=yes +--suppressions=$srcdir/.valgrind.supp +--num-callers=25 +--log-file=$VG_LOG -- +" +# FIXME aconway 2008-07-16: removed --trace-children=yes, problems with cluster tests forking +# qpidd libtool script. Investigate & restore --trace-children if possible. + +ERROR=0 +if grep -l "^# Generated by .*libtool" "$1" >/dev/null 2>&1; then + # This is a libtool "executable". Valgrind it if VALGRIND specified. + test -n "$VALGRIND" && VALGRIND="$VALGRIND $VALGRIND_OPTS" + # Hide output unless there's an error. + libtool --mode=execute $VALGRIND "$@" 2>&1 || ERROR=1 + test -n "$VALGRIND" && { vg_check $VG_LOG* || ERROR=1 ; } +else + # This is a non-libtool shell script, just execute it. + exec "$@" +fi + +exit $ERROR + diff --git a/RC9/qpid/cpp/src/tests/sender.cpp b/RC9/qpid/cpp/src/tests/sender.cpp new file mode 100644 index 0000000000..a02b713d86 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/sender.cpp @@ -0,0 +1,100 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <qpid/client/FailoverManager.h> +#include <qpid/client/Session.h> +#include <qpid/client/AsyncSession.h> +#include <qpid/client/Message.h> +#include <qpid/client/MessageReplayTracker.h> +#include <qpid/Exception.h> +#include "TestOptions.h" + +#include <iostream> + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::framing; + +using namespace std; + +struct Args : public qpid::TestOptions +{ + string destination; + string key; + uint sendEos; + + Args() : key("test-queue"), sendEos(0) + { + addOptions() + ("exchange", qpid::optValue(destination, "EXCHANGE"), "Exchange to send messages to") + ("routing-key", qpid::optValue(key, "KEY"), "Routing key to add to messages") + ("send-eos", qpid::optValue(sendEos, "N"), "Send N EOS messages to mark end of input"); + } +}; + +const string EOS("eos"); + +class Sender : public FailoverManager::Command +{ + public: + Sender(const std::string& destination, const std::string& key, uint sendEos); + void execute(AsyncSession& session, bool isRetry); + private: + MessageReplayTracker sender; + Message message; + const uint sendEos; + uint sent; +}; + +Sender::Sender(const std::string& destination, const std::string& key, uint eos) : + sender(10), message(destination, key), sendEos(eos), sent(0) {} + +void Sender::execute(AsyncSession& session, bool isRetry) +{ + if (isRetry) sender.replay(session); + else sender.init(session); + string data; + while (std::cin >> data) { + message.setData(data); + message.getHeaders().setInt("sn", ++sent); + sender.send(message); + } + for (uint i = sendEos; i > 0; --i) { + message.setData(EOS); + sender.send(message); + } +} + +int main(int argc, char ** argv) +{ + Args opts; + try { + opts.parse(argc, argv); + FailoverManager connection(opts.con); + Sender sender(opts.destination, opts.key, opts.sendEos); + connection.execute(sender); + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << "Failed: " << error.what() << std::endl; + } + return 1; +} diff --git a/RC9/qpid/cpp/src/tests/shared_perftest b/RC9/qpid/cpp/src/tests/shared_perftest new file mode 100755 index 0000000000..cc192d25bd --- /dev/null +++ b/RC9/qpid/cpp/src/tests/shared_perftest @@ -0,0 +1,22 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +exec `dirname $0`/run_perftest 100000 --mode shared --npubs 16 --nsubs 16 diff --git a/RC9/qpid/cpp/src/tests/shlibtest.cpp b/RC9/qpid/cpp/src/tests/shlibtest.cpp new file mode 100644 index 0000000000..80320ea7be --- /dev/null +++ b/RC9/qpid/cpp/src/tests/shlibtest.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. + * + */ + +int* loaderData = 0; +extern "C" void callMe(int *i) { loaderData=i; } + +struct OnUnload { ~OnUnload() { *loaderData=42; } }; +OnUnload unloader; // For destructor. + + + diff --git a/RC9/qpid/cpp/src/tests/ssl.mk b/RC9/qpid/cpp/src/tests/ssl.mk new file mode 100644 index 0000000000..cb887c8fda --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ssl.mk @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +TESTS+=ssl_test +EXTRA_DIST+=ssl_test +clean-local: + rm -rf test_cert_db cert.password
\ No newline at end of file diff --git a/RC9/qpid/cpp/src/tests/ssl_test b/RC9/qpid/cpp/src/tests/ssl_test new file mode 100755 index 0000000000..047db93d20 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ssl_test @@ -0,0 +1,71 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Run a simple test over SSL + +CONFIG=$(dirname $0)/config.null +CERT_DIR=`pwd`/test_cert_db +CERT_PW_FILE=`pwd`/cert.password +HOSTNAME=`hostname` +COUNT=10000 + +trap stop_broker EXIT + +error() { echo $*; exit 1; } + +create_certs() { + #create certificate and key databases with single, simple, self-signed certificate in it + mkdir ${CERT_DIR} + certutil -N -d ${CERT_DIR} -f ${CERT_PW_FILE} + certutil -S -d ${CERT_DIR} -n ${HOSTNAME} -s "CN=${HOSTNAME}" -t "CT,," -x -f ${CERT_PW_FILE} -z /usr/bin/certutil +} + +start_broker() { + ../qpidd --daemon --transport ssl --port 0 --ssl-port 0 --no-data-dir --no-module-dir --auth no --config $CONFIG --load-module ../.libs/ssl.so --ssl-cert-db $CERT_DIR --ssl-cert-password-file $CERT_PW_FILE > qpidd.port + PORT=`cat qpidd.port` +} + +stop_broker() { + if [[ $PORT ]] ; then + ../qpidd -q --port $PORT + fi +} +CERTUTIL=$(type -p certutil) +if [[ !(-x $CERTUTIL) ]] ; then + echo "No certutil, skipping ssl test"; + exit 0; +fi + +if [[ !(-e ${CERT_PW_FILE}) ]] ; then + echo password > ${CERT_PW_FILE} +fi +if [[ !(-e ${CERT_DIR}) ]] ; then + create_certs || error "Could not create test certificate" +fi + +start_broker || error "Could not start broker" +echo "Running SSL test on port $PORT" +export QPID_NO_MODULE_DIR=1 +export QPID_LOAD_MODULE=../.libs/sslconnector.so +export QPID_SSL_CERT_DB=${CERT_DIR} +export QPID_SSL_CERT_PASSWORD_FILE=${CERT_PW_FILE} +./perftest --count ${COUNT} --port ${PORT} -P ssl -b $HOSTNAME --summary + diff --git a/RC9/qpid/cpp/src/tests/start_broker b/RC9/qpid/cpp/src/tests/start_broker new file mode 100755 index 0000000000..093c44051a --- /dev/null +++ b/RC9/qpid/cpp/src/tests/start_broker @@ -0,0 +1,24 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Start a test broker. +srcdir=`dirname $0` +exec $srcdir/run_test ../qpidd --auth=no --no-module-dir --daemon --port=0 --log-to-file qpidd.log "$@" > qpidd.port diff --git a/RC9/qpid/cpp/src/tests/start_cluster b/RC9/qpid/cpp/src/tests/start_cluster new file mode 100755 index 0000000000..ee306edf14 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/start_cluster @@ -0,0 +1,46 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Start a cluster of brokers on local host, put the list of ports for cluster members in cluster.ports +# + +# Execute command with the ais group set. +with_ais_group() { + id -nG | grep '\<ais\>' >/dev/null || { echo "You are not a member of the ais group." 1>&2; exit 1; } + echo $* | newgrp ais +} + +test -f cluster.ports && { echo "cluster.ports file already exists" ; exit 1; } +rm -f cluster*.log +SIZE=$1; shift +CLUSTER=`pwd` # Cluster name=pwd, avoid clashes. +OPTS="-d --load-module ../.libs/cluster.so --cluster-name=$CLUSTER --no-data-dir --auth=no $*" + +if test "$SIZE" = "one"; then # Special case of singleton cluster, use default port. + ../qpidd -q + with_ais_group ../qpidd $OPTS --log-to-file=cluster.log || exit 1 +else + for (( i=0; i<SIZE; ++i )); do + PORT=`with_ais_group ../qpidd -p0 --log-to-file=cluster$i.log $OPTS` || exit 1 + echo $PORT >> cluster.ports + done +fi + diff --git a/RC9/qpid/cpp/src/tests/start_cluster_hosts b/RC9/qpid/cpp/src/tests/start_cluster_hosts new file mode 100755 index 0000000000..7680da01ad --- /dev/null +++ b/RC9/qpid/cpp/src/tests/start_cluster_hosts @@ -0,0 +1,70 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# +# Start a cluster of brokers on local host, put the list of host port addresses +# in cluster.ports +# +# Arguments: [-k] [-p port] HOST [HOST...] +# -p port to start broker on, can be 0. Actual ports recorded in cluster.addr. +# -k kill any qpidd processes owned by this user before starting. +# +# Start a broker on each named host. Name a host twice to start multiple brokers. +# +# You must be able to ssh to each host and be in group ais. +# $QPIDD must be executable on each host. +# Logs go to syslog on each host, with a unique prefix per broker. +# + +QPIDD=${QPIDD:-$PWD/../qpidd} +LIBQPIDCLUSTER=${LIBQPIDCLUSTER:-$PWD/../.libs/cluster.so} +NAME=$USER # User name is default cluster name. +RESTART=NO + +while getopts "kp:n:q:r" ARG ; do + case $ARG in + k) KILL=yes ;; + p) PORT="$OPTARG" ;; + n) NAME=$OPTARG ;; + q) QPIDD=$OPTARG ;; + l) LIBQPIDCLUSTER=$OPTARG ;; + r) RESTART=yes ;; + *) echo "Error parsing options: $ARG"; exit 1 ;; + esac +done +shift `expr $OPTIND - 1` +test -n "$PORT" && PORTOPT="-p $PORT" +test "$KILL" = yes && KILL="$QPIDD -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/RC9/qpid/cpp/src/tests/stop_broker b/RC9/qpid/cpp/src/tests/stop_broker new file mode 100755 index 0000000000..2f45309a2b --- /dev/null +++ b/RC9/qpid/cpp/src/tests/stop_broker @@ -0,0 +1,41 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Stop the broker, check for errors. +# +QPID_PORT=`cat qpidd.port` +export QPID_PORT +rm -f qpidd.port + +../qpidd --quit || ERROR=1 + +# Check qpidd.log. +egrep 'warning\|error\|critical' qpidd.log && { + echo "WARNING: Suspicious broker log entries in qpidd.log, above." +} + +# Check valgrind log. +if test -n "$VALGRIND"; then + . `dirname $0`/vg_check $VG_LOG* + vg_check qpidd.vglog* || ERROR=1 +fi + +exit $ERROR diff --git a/RC9/qpid/cpp/src/tests/stop_cluster b/RC9/qpid/cpp/src/tests/stop_cluster new file mode 100755 index 0000000000..b3f0e7395e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/stop_cluster @@ -0,0 +1,33 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Stop brokers on ports listed in cluster.ports + +PORTS=`cat cluster.ports` +for PORT in $PORTS ; do + ../qpidd -qp $PORT || ERROR="$ERROR $PORT" +done +rm -f cluster.ports + +if [ -n "$ERROR" ]; then + echo "Errors stopping brokers on ports: $ERROR" + exit 1 +fi diff --git a/RC9/qpid/cpp/src/tests/test_tools.h b/RC9/qpid/cpp/src/tests/test_tools.h new file mode 100644 index 0000000000..37a6594f8a --- /dev/null +++ b/RC9/qpid/cpp/src/tests/test_tools.h @@ -0,0 +1,94 @@ +#ifndef TEST_TOOLS_H +#define TEST_TOOLS_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "qpid/log/Logger.h" + +#include <limits.h> // Include before boost/test headers. +#include <boost/test/test_tools.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/regex.hpp> +#include <boost/assign/list_of.hpp> +#include <vector> +#include <ostream> + +// Print a sequence +template <class T> std::ostream& seqPrint(std::ostream& o, const T& seq) { + std::copy(seq.begin(), seq.end(), std::ostream_iterator<typename T::value_type>(o, " ")); + return o; +} + +// Compare sequences +template <class T, class U> +bool seqEqual(const T& a, const U& b) { + typename T::const_iterator i = a.begin(); + typename U::const_iterator j = b.begin(); + while (i != a.end() && j != b.end() && *i == *j) { ++i; ++j; } + return (i == a.end()) && (j == b.end()); +} + +// ostream and == operators so we can compare vectors and boost::assign::list_of +// with BOOST_CHECK_EQUALS +namespace std { // In namespace std so boost can find them. + +template <class T> +ostream& operator<<(ostream& o, const vector<T>& v) { return seqPrint(o, v); } + +template <class T> +ostream& operator<<(ostream& o, const boost::assign_detail::generic_list<T>& l) { return seqPrint(o, l); } + +template <class T> +bool operator == (const vector<T>& a, const boost::assign_detail::generic_list<T>& b) { return seqEqual(a, b); } + +template <class T> +bool operator == (const boost::assign_detail::generic_list<T>& b, const vector<T>& a) { return seqEqual(a, b); } +} + +/** NB: order of parameters is regex first, in line with + * CHECK(expected, actual) convention. + */ +inline bool regexPredicate(const std::string& re, const std::string& text) { + return boost::regex_match(text, boost::regex(re)); +} + +/** Check for regular expression match. You must #include <boost/regex.hpp> */ +#if (BOOST_VERSION < 103300) + #define BOOST_CHECK_REGEX(re, text) +#else + #define BOOST_CHECK_REGEX(re, text) \ + BOOST_CHECK_PREDICATE(regexPredicate, (re)(text)) +#endif + +/** Check if types of two objects (as given by typeinfo::name()) match. */ +#define BOOST_CHECK_TYPEID_EQUAL(a,b) BOOST_CHECK_EQUAL(typeid(a).name(),typeid(b).name()) + +/** + * Supress all logging in a scope, restore to previous configuration in destructor. + */ +struct ScopedSuppressLogging { + typedef qpid::log::Logger Logger; + ScopedSuppressLogging(Logger& l=Logger::instance()) : logger(l), opts(l.getOptions()) { l.clear(); } + ~ScopedSuppressLogging() { logger.configure(opts); } + Logger& logger; + qpid::log::Options opts; +}; + + +#endif /*!TEST_TOOLS_H*/ + diff --git a/RC9/qpid/cpp/src/tests/topic_listener.cpp b/RC9/qpid/cpp/src/tests/topic_listener.cpp new file mode 100644 index 0000000000..7bdc2c32de --- /dev/null +++ b/RC9/qpid/cpp/src/tests/topic_listener.cpp @@ -0,0 +1,202 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/** + * This file provides one half of a test and example of a pub-sub + * style of interaction. See topic_publisher.cpp for the other half, + * in which the logic for publishing is defined. + * + * This file contains the listener logic. A listener will subscribe to + * a logical 'topic'. It will count the number of messages it receives + * and the time elapsed between the first one and the last one. It + * recognises two types of 'special' message that tell it to (a) send + * a report containing this information, (b) shutdown (i.e. stop + * listening). + */ + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/MessageListener.h" +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/sys/Time.h" +#include "qpid/framing/FieldValue.h" +#include <iostream> +#include <sstream> + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::sys; +using namespace qpid::framing; +using namespace std; + +/** + * A message listener implementation in which the runtime logic is + * defined. + */ +class Listener : public MessageListener{ + Session session; + SubscriptionManager& mgr; + const string responseQueue; + const bool transactional; + bool init; + int count; + AbsTime start; + + void shutdown(); + void report(); +public: + Listener(const Session& session, SubscriptionManager& mgr, const string& reponseQueue, bool tx); + virtual void received(Message& msg); + Subscription subscription; +}; + +/** + * A utility class for managing the options passed in. + */ +struct Args : public qpid::TestOptions { + int ack; + bool transactional; + bool durable; + int prefetch; + string statusqueue; + + Args() : ack(0), transactional(false), durable(false), prefetch(0) { + addOptions() + ("ack", optValue(ack, "MODE"), "Ack frequency in messages (defaults to half the prefetch value)") + ("transactional", optValue(transactional), "Use transactions") + ("durable", optValue(durable), "subscribers should use durable queues") + ("prefetch", optValue(prefetch, "N"), "prefetch count (0 implies no flow control, and no acking)") + ("status-queue", optValue(statusqueue, "QUEUE-NAME"), "Message queue to put status messages on"); + } +}; + + +/** + * The main routine creates a Listener instance and sets it up to + * consume from a private queue bound to the exchange with the + * appropriate topic name. + */ +int main(int argc, char** argv){ + try{ + Args args; + args.parse(argc, argv); + if(args.help) + cout << args << endl; + else { + Connection connection; + args.open(connection); + AsyncSession session = connection.newSession(); + + //declare exchange, queue and bind them: + session.queueDeclare(arg::queue="response"); + std::string control = "control_" + session.getId().str(); + if (args.durable) { + session.queueDeclare(arg::queue=control, arg::durable=true); + } else { + session.queueDeclare(arg::queue=control, arg::exclusive=true, arg::autoDelete=true); + } + session.exchangeBind(arg::exchange="amq.topic", arg::queue=control, arg::bindingKey="topic_control"); + + //set up listener + SubscriptionManager mgr(session); + Listener listener(session, mgr, "response", args.transactional); + SubscriptionSettings settings; + if (args.prefetch) { + settings.autoAck = (args.ack ? args.ack : (args.prefetch / 2)); + settings.flowControl = FlowControl::messageCredit(args.prefetch); + } else { + settings.acceptMode = ACCEPT_MODE_NONE; + settings.flowControl = FlowControl::unlimited(); + } + listener.subscription = mgr.subscribe(listener, control, settings); + session.sync(); + + if( args.statusqueue.length() > 0 ) { + stringstream msg_str; + msg_str << "topic_listener: " << (int)getpid(); + session.messageTransfer(arg::content=Message(msg_str.str(), args.statusqueue)); + cout << "Ready status put on queue '" << args.statusqueue << "'" << endl; + } + + if (args.transactional) { + session.txSelect(); + } + + cout << "topic_listener: listening..." << endl; + mgr.run(); + if (args.durable) { + session.queueDelete(arg::queue=control); + } + session.close(); + cout << "closing connection" << endl; + connection.close(); + } + return 0; + } catch (const std::exception& error) { + cout << "topic_listener: " << error.what() << endl; + } + return 1; +} + +Listener::Listener(const Session& s, SubscriptionManager& m, const string& _responseq, bool tx) : + session(s), mgr(m), responseQueue(_responseq), transactional(tx), init(false), count(0){} + +void Listener::received(Message& message){ + if(!init){ + start = now(); + count = 0; + init = true; + cout << "Batch started." << endl; + } + string type = message.getHeaders().getAsString("TYPE"); + + if(string("TERMINATION_REQUEST") == type){ + shutdown(); + }else if(string("REPORT_REQUEST") == type){ + subscription.accept(subscription.getUnaccepted()); // Accept everything upto this point + cout <<"Batch ended, sending report." << endl; + //send a report: + report(); + init = false; + }else if (++count % 1000 == 0){ + cout <<"Received " << count << " messages." << endl; + } +} + +void Listener::shutdown(){ + mgr.stop(); +} + +void Listener::report(){ + AbsTime finish = now(); + Duration time(start, finish); + stringstream reportstr; + reportstr << "Received " << count << " messages in " + << time/TIME_MSEC << " ms."; + Message msg(reportstr.str(), responseQueue); + msg.getHeaders().setString("TYPE", "REPORT"); + session.messageTransfer(arg::destination="amq.direct", arg::content=msg, arg::acceptMode=1); + if(transactional){ + sync(session).txCommit(); + } +} + diff --git a/RC9/qpid/cpp/src/tests/topic_perftest b/RC9/qpid/cpp/src/tests/topic_perftest new file mode 100755 index 0000000000..cd440b2458 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/topic_perftest @@ -0,0 +1,22 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +exec `dirname $0`/run_perftest 10000 --mode topic --qt 16 diff --git a/RC9/qpid/cpp/src/tests/topic_publisher.cpp b/RC9/qpid/cpp/src/tests/topic_publisher.cpp new file mode 100644 index 0000000000..f37ad2dc0e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/topic_publisher.cpp @@ -0,0 +1,224 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/** + * This file provides one half of a test and example of a pub-sub + * style of interaction. See topic_listener.cpp for the other half, in + * which the logic for subscribers is defined. + * + * This file contains the publisher logic. The publisher will send a + * number of messages to the exchange with the appropriate routing key + * for the logical 'topic'. Once it has done this it will then send a + * request that each subscriber report back with the number of message + * it has received and the time that elapsed between receiving the + * first one and receiving the report request. Once the expected + * number of reports are received, it sends out a request that each + * subscriber shutdown. + */ + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/MessageListener.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/sys/Monitor.h" +#include <unistd.h> +#include "qpid/sys/Time.h" +#include <cstdlib> +#include <iostream> + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::sys; +using namespace std; + +/** + * The publishing logic is defined in this class. It implements + * message listener and can therfore be used to receive messages sent + * back by the subscribers. + */ +class Publisher { + AsyncSession session; + SubscriptionManager mgr; + LocalQueue queue; + const string controlTopic; + const bool transactional; + const bool durable; + + string generateData(int size); + +public: + Publisher(const AsyncSession& session, const string& controlTopic, bool tx, bool durable); + int64_t publish(int msgs, int listeners, int size); + void terminate(); +}; + +/** + * A utility class for managing the options passed in to the test + */ +struct Args : public TestOptions { + int messages; + int subscribers; + bool transactional; + bool durable; + int batches; + int delay; + int size; + string statusqueue; + + Args() : messages(1000), subscribers(1), + transactional(false), durable(false), + batches(1), delay(0), size(256) + { + addOptions() + ("messages", optValue(messages, "N"), "how many messages to send") + ("subscribers", optValue(subscribers, "N"), "how many subscribers to expect reports from") + ("transactional", optValue(transactional), "client should use transactions") + ("durable", optValue(durable), "messages should be durable") + ("batches", optValue(batches, "N"), "how many batches to run") + ("delay", optValue(delay, "SECONDS"), "Causes a delay between each batch") + ("size", optValue(size, "BYTES"), "size of the published messages") + ("status-queue", optValue(statusqueue, "QUEUE-NAME"), "Message queue to read status messages from"); + } +}; + +int main(int argc, char** argv) { + try{ + Args args; + args.parse(argc, argv); + if(args.help) + cout << args << endl; + else { + Connection connection; + args.open(connection); + AsyncSession session = connection.newSession(); + + // If status-queue is defined, wait for all expected listeners to join in before we start + if( args.statusqueue.length() > 0 ) { + cout << "Waiting for " << args.subscribers << " listeners..." << endl; + SubscriptionManager statusSubs(session); + LocalQueue statusQ; + statusSubs.subscribe(statusQ, args.statusqueue); + for (int i = 0; i < args.subscribers; i++) { + Message m = statusQ.get(); + if( m.getData().find("topic_listener: ", 0) == 0 ) { + cout << "Listener " << (i+1) << " of " << args.subscribers + << " is ready (pid " << m.getData().substr(16, m.getData().length() - 16) + << ")" << endl; + } else { + throw Exception(QPID_MSG("Unexpected message received on status queue: " << m.getData())); + } + } + } + + if (args.transactional) { + session.txSelect(); + } + session.queueDeclare(arg::queue="response"); + session.exchangeBind(arg::exchange="amq.direct", arg::queue="response", arg::bindingKey="response"); + + Publisher publisher(session, "topic_control", args.transactional, args.durable); + + int batchSize(args.batches); + int64_t max(0); + int64_t min(0); + int64_t sum(0); + for(int i = 0; i < batchSize; i++){ + if(i > 0 && args.delay) sleep(args.delay); + int64_t msecs = + publisher.publish(args.messages, + args.subscribers, + args.size) / TIME_MSEC; + if(!max || msecs > max) max = msecs; + if(!min || msecs < min) min = msecs; + sum += msecs; + cout << "Completed " << (i+1) << " of " << batchSize + << " in " << msecs << "ms" << endl; + } + publisher.terminate(); + int64_t avg = sum / batchSize; + if(batchSize > 1){ + cout << batchSize << " batches completed. avg=" << avg << + ", max=" << max << ", min=" << min << endl; + } + session.close(); + connection.close(); + } + return 0; + }catch(exception& error) { + cout << error.what() << endl; + } + return 1; +} + +Publisher::Publisher(const AsyncSession& _session, const string& _controlTopic, bool tx, bool d) : + session(_session), mgr(session), controlTopic(_controlTopic), transactional(tx), durable(d) +{ + mgr.subscribe(queue, "response"); +} + +int64_t Publisher::publish(int msgs, int listeners, int size){ + Message msg(generateData(size), controlTopic); + if (durable) { + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + } + AbsTime start = now(); + + for(int i = 0; i < msgs; i++){ + session.messageTransfer(arg::content=msg, arg::destination="amq.topic", arg::acceptMode=1); + } + //send report request + Message reportRequest("", controlTopic); + reportRequest.getHeaders().setString("TYPE", "REPORT_REQUEST"); + session.messageTransfer(arg::content=reportRequest, arg::destination="amq.topic", arg::acceptMode=1); + if(transactional){ + sync(session).txCommit(); + } + //wait for a response from each listener (TODO, could log these) + for (int i = 0; i < listeners; i++) { + Message report = queue.pop(); + } + + if(transactional){ + sync(session).txCommit(); + } + + AbsTime finish = now(); + return Duration(start, finish); +} + +string Publisher::generateData(int size){ + string data; + for(int i = 0; i < size; i++){ + data += ('A' + (i / 26)); + } + return data; +} + +void Publisher::terminate(){ + //send termination request + Message terminationRequest("", controlTopic); + terminationRequest.getHeaders().setString("TYPE", "TERMINATION_REQUEST"); + session.messageTransfer(arg::content=terminationRequest, arg::destination="amq.topic", arg::acceptMode=1); + if(transactional){ + session.txCommit(); + } +} diff --git a/RC9/qpid/cpp/src/tests/topictest b/RC9/qpid/cpp/src/tests/topictest new file mode 100755 index 0000000000..8fd680ee35 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/topictest @@ -0,0 +1,61 @@ +#!/bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Run the C++ topic test + +# Clean up old log files +rm -f subscriber_*.log + +# Defaults values +SUBSCRIBERS=10 +MESSAGES=2000 +BATCHES=10 + +while getopts "s:m:b:h:t" opt ; do + case $opt in + s) SUBSCRIBERS=$OPTARG ;; + m) MESSAGES=$OPTARG ;; + b) BATCHES=$OPTARG ;; + h) HOST=-h$OPTARG ;; + t) TRANSACTIONAL="--transactional --durable" ;; + ?) + echo "Usage: %0 [-s <subscribers>] [-m <messages.] [-b <batches>]" + exit 1 + ;; + esac +done + +subscribe() { + echo Start subscriber $1 + LOG="subscriber_$1.log" + ./topic_listener $TRANSACTIONAL > $LOG 2>&1 && rm -f $LOG +} + +publish() { + ./topic_publisher --messages $MESSAGES --batches $BATCHES --subscribers $SUBSCRIBERS $HOST $TRANSACTIONAL +} + +for ((i=$SUBSCRIBERS ; i--; )); do + subscribe $i & +done +# FIXME aconway 2007-03-27: Hack around startup race. Fix topic test. +sleep 2 +publish 2>&1 || exit 1 diff --git a/RC9/qpid/cpp/src/tests/txjob.cpp b/RC9/qpid/cpp/src/tests/txjob.cpp new file mode 100644 index 0000000000..94db96a666 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/txjob.cpp @@ -0,0 +1,95 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <iostream> +#include <boost/bind.hpp> +#include <boost/ptr_container/ptr_vector.hpp> + +#include "TestOptions.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/client/FailoverManager.h" +#include "qpid/client/Message.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/sys/Thread.h" + +using namespace qpid::client; +using namespace qpid::sys; + +struct Args : public qpid::TestOptions +{ + string workQueue; + string source; + string dest; + uint messages; + uint jobs; + bool quit; + bool declareQueues; + + Args() : workQueue("txshift-control"), source("txshift-1"), dest("txshift-2"), messages(0), jobs(0), + quit(false), declareQueues(false) + { + addOptions() + ("messages", qpid::optValue(messages, "N"), "Number of messages to shift") + ("jobs", qpid::optValue(jobs, "N"), "Number of shift jobs to request") + ("source", qpid::optValue(source, "QUEUE NAME"), "source queue from which messages will be shifted") + ("dest", qpid::optValue(dest, "QUEUE NAME"), "dest queue to which messages will be shifted") + ("work-queue", qpid::optValue(workQueue, "QUEUE NAME"), "work queue from which to take instructions") + ("add-quit", qpid::optValue(quit), "add a 'quit' instruction to the queue (after any other jobs)") + ("declare-queues", qpid::optValue(declareQueues), "issue a declare for all queues"); + } +}; + +//TODO: might be nice to make this capable of failover as well at some +//point; for now its just for the setup phase. +int main(int argc, char** argv) +{ + Args opts; + try { + opts.parse(argc, argv); + Connection connection; + connection.open(opts.con); + Session session = connection.newSession(); + if (opts.declareQueues) { + session.queueDeclare(arg::queue=opts.workQueue); + session.queueDeclare(arg::queue=opts.source); + session.queueDeclare(arg::queue=opts.dest); + } + for (uint i = 0; i < opts.jobs; ++i) { + Message job("transfer", opts.workQueue); + job.getHeaders().setString("src", opts.source); + job.getHeaders().setString("dest", opts.dest); + job.getHeaders().setInt("count", opts.messages); + async(session).messageTransfer(arg::content=job); + } + + if (opts.quit) { + async(session).messageTransfer(arg::content=Message("quit", opts.workQueue)); + } + + session.sync(); + session.close(); + + return 0; + } catch(const std::exception& e) { + std::cout << e.what() << std::endl; + return 1; + } +} diff --git a/RC9/qpid/cpp/src/tests/txshift.cpp b/RC9/qpid/cpp/src/tests/txshift.cpp new file mode 100644 index 0000000000..5db08d7a53 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/txshift.cpp @@ -0,0 +1,185 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <iostream> +#include <boost/bind.hpp> +#include <boost/ptr_container/ptr_vector.hpp> + +#include "TestOptions.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/client/FailoverManager.h" +#include "qpid/client/Message.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/Thread.h" + +using namespace qpid::client; +using namespace qpid::sys; + +struct Args : public qpid::TestOptions +{ + string workQueue; + size_t workers; + + Args() : workQueue("txshift-control"), workers(1) + { + addOptions() + ("workers", qpid::optValue(workers, "N"), "Number of separate worker sessions to start") + ("work-queue", qpid::optValue(workQueue, "NAME"), "work queue from which to take instructions"); + } +}; + +struct Transfer : MessageListener +{ + std::string control; + std::string source; + std::string destination; + uint expected; + uint transfered; + SubscriptionSettings controlSettings; + Subscription controlSubscription; + SubscriptionSettings sourceSettings; + Subscription sourceSubscription; + + Transfer(const std::string control_) : control(control_), expected(0), transfered(0) {} + + void subscribeToSource(SubscriptionManager& manager) + { + sourceSettings.autoAck = 0;//will accept once at the end of the batch + sourceSettings.flowControl = FlowControl::messageCredit(expected); + sourceSubscription = manager.subscribe(*this, source, sourceSettings); + QPID_LOG(info, "Subscribed to source: " << source << " expecting: " << expected); + } + + void subscribeToControl(SubscriptionManager& manager) + { + controlSettings.flowControl = FlowControl::messageCredit(1); + controlSubscription = manager.subscribe(*this, control, controlSettings); + QPID_LOG(info, "Subscribed to job queue"); + } + + void received(Message& message) + { + QPID_LOG(debug, "received: " << message.getData() << " for " << message.getDestination()); + if (message.getDestination() == source) { + receivedFromSource(message); + } else if (message.getDestination() == control) { + receivedFromControl(message); + } else { + QPID_LOG(error, "Unexpected message: " << message.getData() << " to " << message.getDestination()); + } + } + + void receivedFromSource(Message& message) + { + QPID_LOG(debug, "transfering " << (transfered+1) << " of " << expected); + message.getDeliveryProperties().setRoutingKey(destination); + async(sourceSubscription.getSession()).messageTransfer(arg::content=message); + if (++transfered == expected) { + QPID_LOG(info, "completed job: " << transfered << " messages shifted from " << + source << " to " << destination); + sourceSubscription.accept(sourceSubscription.getUnaccepted()); + sourceSubscription.getSession().txCommit(); + sourceSubscription.cancel(); + //grant credit to allow broker to send us another control message + controlSubscription.grantMessageCredit(1); + } + } + + void receivedFromControl(Message& message) + { + if (message.getData() == "transfer") { + source = message.getHeaders().getAsString("src"); + destination = message.getHeaders().getAsString("dest"); + expected = message.getHeaders().getAsInt("count"); + transfered = 0; + QPID_LOG(info, "received transfer request: " << expected << " messages to be shifted from " << + source << " to " << destination); + subscribeToSource(controlSubscription.getSubscriptionManager()); + } else if (message.getData() == "quit") { + QPID_LOG(info, "received quit request"); + controlSubscription.cancel(); + } else { + std::cerr << "Rejecting invalid message: " << message.getData() << std::endl; + controlSubscription.getSession().messageReject(SequenceSet(message.getId())); + } + } + +}; + +struct Worker : FailoverManager::Command, Runnable +{ + FailoverManager& connection; + Transfer transfer; + Thread runner; + + Worker(FailoverManager& c, const std::string& controlQueue) : connection(c), transfer(controlQueue) {} + + void run() + { + connection.execute(*this); + } + + void start() + { + runner = Thread(this); + } + + void join() + { + runner.join(); + } + + void execute(AsyncSession& session, bool isRetry) + { + if (isRetry) QPID_LOG(info, "Retrying..."); + session.txSelect(); + SubscriptionManager subs(session); + transfer.subscribeToControl(subs); + subs.run(); + } +}; + +int main(int argc, char** argv) +{ + Args opts; + try { + opts.parse(argc, argv); + FailoverManager connection(opts.con); + connection.connect(); + if (opts.workers == 1) { + Worker worker(connection, opts.workQueue); + worker.run(); + } else { + boost::ptr_vector<Worker> workers; + for (size_t i = 0; i < opts.workers; i++) { + workers.push_back(new Worker(connection, opts.workQueue)); + } + for_each(workers.begin(), workers.end(), boost::bind(&Worker::start, _1)); + for_each(workers.begin(), workers.end(), boost::bind(&Worker::join, _1)); + } + + return 0; + } catch(const std::exception& e) { + std::cout << e.what() << std::endl; + return 1; + } +} diff --git a/RC9/qpid/cpp/src/tests/txtest.cpp b/RC9/qpid/cpp/src/tests/txtest.cpp new file mode 100644 index 0000000000..0c8ce90648 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/txtest.cpp @@ -0,0 +1,329 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <algorithm> +#include <iomanip> +#include <iostream> +#include <memory> +#include <sstream> +#include <vector> + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/framing/Array.h" +#include "qpid/framing/Buffer.h" +#include "qpid/sys/uuid.h" + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::sys; +using std::string; + +typedef std::vector<std::string> StringSet; + +struct Args : public qpid::TestOptions { + bool init, transfer, check;//actions + uint size; + bool durable; + uint queues; + string base; + uint msgsPerTx; + uint txCount; + uint totalMsgCount; + bool dtx; + bool quiet; + + Args() : init(true), transfer(true), check(true), + size(256), durable(true), queues(2), + base("tx-test"), msgsPerTx(1), txCount(1), totalMsgCount(10), + dtx(false), quiet(false) + { + addOptions() + + ("init", optValue(init, "yes|no"), "Declare queues and populate one with the initial set of messages.") + ("transfer", optValue(transfer, "yes|no"), "'Move' messages from one queue to another using transactions to ensure no message loss.") + ("check", optValue(check, "yes|no"), "Check that the initial messages are all still available.") + ("size", optValue(size, "N"), "message size") + ("durable", optValue(durable, "yes|no"), "use durable messages") + ("queues", optValue(queues, "N"), "number of queues") + ("queue-base-name", optValue(base, "<name>"), "base name for queues") + ("messages-per-tx", optValue(msgsPerTx, "N"), "number of messages transferred per transaction") + ("tx-count", optValue(txCount, "N"), "number of transactions per 'agent'") + ("total-messages", optValue(totalMsgCount, "N"), "total number of messages in 'circulation'") + ("dtx", optValue(dtx, "yes|no"), "use distributed transactions") + ("quiet", optValue(quiet), "reduce output from test"); + } +}; + +const std::string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + +std::string generateData(uint size) +{ + if (size < chars.length()) { + return chars.substr(0, size); + } + std::string data; + for (uint i = 0; i < (size / chars.length()); i++) { + data += chars; + } + data += chars.substr(0, size % chars.length()); + return data; +} + +void generateSet(const std::string& base, uint count, StringSet& collection) +{ + for (uint i = 0; i < count; i++) { + std::ostringstream out; + out << base << "-" << (i+1); + collection.push_back(out.str()); + } +} + +Args opts; + +struct Client +{ + Connection connection; + AsyncSession session; + + Client() + { + opts.open(connection); + session = connection.newSession(); + } + + ~Client() + { + try{ + session.close(); + connection.close(); + } catch(const std::exception& e) { + std::cout << e.what() << std::endl; + } + } +}; + +struct Transfer : public Client, public Runnable +{ + std::string src; + std::string dest; + Thread thread; + uuid_t uuid; + char uuidStr[37]; // Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + trailing \0 + framing::Xid xid; + + Transfer(const std::string& to, const std::string& from) : src(to), dest(from), xid(0x4c414e47, "", from) {} + + void run() + { + try { + + if (opts.dtx) session.dtxSelect(); + else session.txSelect(); + SubscriptionManager subs(session); + + LocalQueue lq; + SubscriptionSettings settings(FlowControl::messageWindow(opts.msgsPerTx)); + settings.autoAck = 0; // Disabled + Subscription sub = subs.subscribe(lq, src, settings); + + for (uint t = 0; t < opts.txCount; t++) { + Message in; + Message out("", dest); + if (opts.dtx) { + setNewXid(xid); + session.dtxStart(arg::xid=xid); + } + for (uint m = 0; m < opts.msgsPerTx; m++) { + in = lq.pop(); + out.setData(in.getData()); + out.getMessageProperties().setCorrelationId(in.getMessageProperties().getCorrelationId()); + out.getDeliveryProperties().setDeliveryMode(in.getDeliveryProperties().getDeliveryMode()); + session.messageTransfer(arg::content=out, arg::acceptMode=1); + } + sub.accept(sub.getUnaccepted()); + if (opts.dtx) { + session.dtxEnd(arg::xid=xid); + session.dtxPrepare(arg::xid=xid); + session.dtxCommit(arg::xid=xid); + } else { + session.txCommit(); + } + } + } catch(const std::exception& e) { + std::cout << "Transfer interrupted: " << e.what() << std::endl; + } + } + + void setNewXid(framing::Xid& xid) { + ::uuid_generate(uuid); + ::uuid_unparse(uuid, uuidStr); + xid.setGlobalId(uuidStr); + } +}; + +struct Controller : public Client +{ + StringSet ids; + StringSet queues; + + Controller() + { + generateSet(opts.base, opts.queues, queues); + generateSet("msg", opts.totalMsgCount, ids); + } + + void init() + { + //declare queues + for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) { + session.queueDeclare(arg::queue=*i, arg::durable=opts.durable); + session.sync(); + } + + Message msg(generateData(opts.size), *queues.begin()); + if (opts.durable) { + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + } + + //publish messages + for (StringSet::iterator i = ids.begin(); i != ids.end(); i++) { + msg.getMessageProperties().setCorrelationId(*i); + session.messageTransfer(arg::content=msg, arg::acceptMode=1); + } + } + + void transfer() + { + boost::ptr_vector<Transfer> agents(opts.queues); + //launch transfer agents + for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) { + StringSet::iterator next = i + 1; + if (next == queues.end()) next = queues.begin(); + + if (!opts.quiet) std::cout << "Transfering from " << *i << " to " << *next << std::endl; + agents.push_back(new Transfer(*i, *next)); + agents.back().thread = Thread(agents.back()); + } + + for (boost::ptr_vector<Transfer>::iterator i = agents.begin(); i != agents.end(); i++) { + i->thread.join(); + } + } + + int check() + { + SubscriptionManager subs(session); + + // Recover DTX transactions (if any) + if (opts.dtx) { + std::vector<std::string> inDoubtXids; + framing::DtxRecoverResult dtxRes = session.dtxRecover().get(); + const framing::Array& xidArr = dtxRes.getInDoubt(); + xidArr.collect(inDoubtXids); + + if (inDoubtXids.size()) { + if (!opts.quiet) std::cout << "Recovering DTX in-doubt transaction(s):" << std::endl; + framing::StructHelper decoder; + framing::Xid xid; + // abort even, commit odd transactions + for (unsigned i = 0; i < inDoubtXids.size(); i++) { + decoder.decode(xid, inDoubtXids[i]); + if (!opts.quiet) std::cout << (i%2 ? " * aborting " : " * committing "); + xid.print(std::cout); + std::cout << std::endl; + if (i%2) { + session.dtxRollback(arg::xid=xid); + } else { + session.dtxCommit(arg::xid=xid); + } + } + } + } + + StringSet drained; + //drain each queue and verify the correct set of messages are available + for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) { + //subscribe, allocate credit and flushn + LocalQueue lq; + SubscriptionSettings settings(FlowControl::unlimited(), ACCEPT_MODE_NONE); + subs.subscribe(lq, *i, settings); + session.messageFlush(arg::destination=*i); + session.sync(); + + uint count(0); + while (!lq.empty()) { + Message m = lq.pop(); + //add correlation ids of received messages to drained + drained.push_back(m.getMessageProperties().getCorrelationId()); + ++count; + } + if (!opts.quiet) std::cout << "Drained " << count << " messages from " << *i << std::endl; + } + + sort(ids.begin(), ids.end()); + sort(drained.begin(), drained.end()); + + //check that drained == ids + StringSet missing; + set_difference(ids.begin(), ids.end(), drained.begin(), drained.end(), back_inserter(missing)); + + StringSet extra; + set_difference(drained.begin(), drained.end(), ids.begin(), ids.end(), back_inserter(extra)); + + if (missing.empty() && extra.empty()) { + std::cout << "All expected messages were retrieved." << std::endl; + return 0; + } else { + if (!missing.empty()) { + std::cout << "The following ids were missing:" << std::endl; + for (StringSet::iterator i = missing.begin(); i != missing.end(); i++) { + std::cout << " '" << *i << "'" << std::endl; + } + } + if (!extra.empty()) { + std::cout << "The following extra ids were encountered:" << std::endl; + for (StringSet::iterator i = extra.begin(); i != extra.end(); i++) { + std::cout << " '" << *i << "'" << std::endl; + } + } + return 1; + } + } +}; + +int main(int argc, char** argv) +{ + try { + opts.parse(argc, argv); + Controller controller; + if (opts.init) controller.init(); + if (opts.transfer) controller.transfer(); + if (opts.check) return controller.check(); + return 0; + } catch(const std::exception& e) { + std::cout << e.what() << std::endl; + } + return 2; +} diff --git a/RC9/qpid/cpp/src/tests/unit_test.cpp b/RC9/qpid/cpp/src/tests/unit_test.cpp new file mode 100644 index 0000000000..00c61242e4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/unit_test.cpp @@ -0,0 +1,23 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Defines test_main function to link with actual unit test code. +#define BOOST_AUTO_TEST_MAIN // Boost 1.33 +#define BOOST_TEST_MAIN +#include "unit_test.h" + diff --git a/RC9/qpid/cpp/src/tests/unit_test.h b/RC9/qpid/cpp/src/tests/unit_test.h new file mode 100644 index 0000000000..df3ebfb1fe --- /dev/null +++ b/RC9/qpid/cpp/src/tests/unit_test.h @@ -0,0 +1,86 @@ +#ifndef QPIPD_TEST_UNIT_TEST_H_ +#define QPIPD_TEST_UNIT_TEST_H_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +// Workaround so we can build against boost 1.33 and boost 1.34. +// Remove when we no longer need to support 1.33. +// +#include <boost/version.hpp> +#include <limits.h> // Must be inclued beofre boost/test headers. + +// #include the correct header file. +// +#if (BOOST_VERSION < 103400) +# include <boost/test/auto_unit_test.hpp> +#else +# include <boost/test/unit_test.hpp> +#endif // BOOST_VERSION + +// Workarounds for BOOST_AUTO_TEST_CASE|SUITE|SUITE_END +// +#if (BOOST_VERSION < 103300) + +# define QPID_AUTO_TEST_SUITE(name) +# define QPID_AUTO_TEST_CASE(name) BOOST_AUTO_UNIT_TEST(name) +# define QPID_AUTO_TEST_SUITE_END() + +#elif (BOOST_VERSION < 103400) +// Note the trailing ';' +# define QPID_AUTO_TEST_SUITE(name) BOOST_AUTO_TEST_SUITE(name); +# define QPID_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END(); + +#endif // Workarounds for BOOST_AUTO_TEST_CASE|SUITE|SUITE_END + +// Workaround for BOOST_AUTO_TEST_SUITE_EXPECTED_FAILURES +// +#if (BOOST_VERSION < 103600) + +// Keep the test function for compilation but do not not register it. +// TODO aconway 2008-04-23: better workaround for expected failures. +# define QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(test_name,n) \ + namespace { struct test_name { void test_method(); }; } \ + void test_name::test_method() + +#endif // Workaround for BOOST_AUTO_TEST_SUITE_EXPECTED_FAILURES + +// +// Default definitions for latest version of boost. +// + +#ifndef QPID_AUTO_TEST_SUITE +# define QPID_AUTO_TEST_SUITE(name) BOOST_AUTO_TEST_SUITE(name) +#endif + +#ifndef QPID_AUTO_TEST_CASE +# define QPID_AUTO_TEST_CASE(name) BOOST_AUTO_TEST_CASE(name) +#endif + +#ifndef QPID_AUTO_TEST_CASE_EXPECTED_FAILURES +# define QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(name,n) BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(name,n) +#endif + +#ifndef QPID_AUTO_TEST_SUITE_END +# define QPID_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() +#endif + +#endif // !QPIPD_TEST_UNIT_TEST_H_ diff --git a/RC9/qpid/cpp/src/tests/vg_check b/RC9/qpid/cpp/src/tests/vg_check new file mode 100644 index 0000000000..c5a1e6d2d0 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/vg_check @@ -0,0 +1,24 @@ +# Check for valgrind errors. Sourced by test scripts. + +vg_failed() { + echo "Valgrind error log in $VG_LOG." 1>&2 + cat $VG_LOG 1>&2 + echo $1 1>&2 + exit 1 +} + +vg_check() +{ + test -z "$1" || VG_LOG=$1 + test -f $VG_LOG || vg_failed Valgrind log file $VG_LOG missing. + # Ensure there is an ERROR SUMMARY line. + grep -E '^==[0-9]+== ERROR SUMMARY:' $VG_LOG > /dev/null || \ + vg_failed "No valgrind ERROR SUMMARY line in $VG_LOG." + # Ensure that the number of errors is 0. + grep -E '^==[0-9]+== ERROR SUMMARY: [^0]' $VG_LOG > /dev/null && \ + vg_failed "Valgrind reported errors in $VG_LOG; see above." + # Check for leaks. + grep -E '^==[0-9]+== +.* lost: [^0]' $VG_LOG && \ + vg_failed "Found memory leaks (see log file, $VG_LOG); see above." + true +} diff --git a/RC9/qpid/cpp/src/windows/QpiddBroker.cpp b/RC9/qpid/cpp/src/windows/QpiddBroker.cpp new file mode 100644 index 0000000000..6714ac2e01 --- /dev/null +++ b/RC9/qpid/cpp/src/windows/QpiddBroker.cpp @@ -0,0 +1,89 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpidd.h" +#include "qpid/Exception.h" +#include "qpid/Options.h" +#include "qpid/Plugin.h" +#include "qpid/sys/IntegerTypes.h" +#include "qpid/sys/windows/check.h" +#include "qpid/broker/Broker.h" + +#include <iostream> + +// These need to be made something sensible, like reading a value from +// the registry. But for now, get things going with a local definition. +namespace { +const char *CONF_FILE = "qpid_broker.conf"; +const char *MODULE_DIR = "."; +} + +using namespace qpid::broker; + +BootstrapOptions::BootstrapOptions(const char* argv0) + : qpid::Options("Options"), + common("", CONF_FILE), + module(MODULE_DIR), + log(argv0) +{ + add(common); + add(module); + add(log); +} + +struct QpiddWindowsOptions : public QpiddOptionsPrivate { + QpiddWindowsOptions(QpiddOptions *parent) : QpiddOptionsPrivate(parent) { + } +}; + +QpiddOptions::QpiddOptions(const char* argv0) + : qpid::Options("Options"), + common("", CONF_FILE), + module(MODULE_DIR), + log(argv0) +{ + add(common); + add(module); + add(broker); + add(log); + + platform.reset(new QpiddWindowsOptions(this)); + qpid::Plugin::addOptions(*this); +} + +void QpiddOptions::usage() const { + std::cout << "Usage: qpidd [OPTIONS]" << std::endl << std::endl + << *this << std::endl; +} + +int QpiddBroker::execute (QpiddOptions *options) { + // Options that affect a running daemon. + QpiddWindowsOptions *myOptions = + reinterpret_cast<QpiddWindowsOptions *>(options->platform.get()); + if (myOptions == 0) + throw qpid::Exception("Internal error obtaining platform options"); + + boost::intrusive_ptr<Broker> brokerPtr(new Broker(options->broker)); + if (options->broker.port == 0) + std::cout << (uint16_t)(brokerPtr->getPort("")) << std::endl; + brokerPtr->run(); + return 0; +} diff --git a/RC9/qpid/cpp/src/xml.mk b/RC9/qpid/cpp/src/xml.mk new file mode 100644 index 0000000000..957a18efde --- /dev/null +++ b/RC9/qpid/cpp/src/xml.mk @@ -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. +# +dmodule_LTLIBRARIES += xml.la + +xml_la_SOURCES = \ + qpid/xml/XmlExchange.cpp \ + qpid/xml/XmlExchange.h \ + qpid/xml/XmlExchangePlugin.cpp + +xml_la_LIBADD = -lxerces-c -lxqilla libqpidbroker.la + +xml_la_LDFLAGS = $(PLUGINLDFLAGS) |