summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Godfrey <rgodfrey@apache.org>2009-10-05 12:26:55 +0000
committerRobert Godfrey <rgodfrey@apache.org>2009-10-05 12:26:55 +0000
commit69e05a6b0979d2e12588635a4ed46a09a87cdec0 (patch)
tree883b432624cb9508d3d275c85aa0eb00388b166a
parent2e20e00f3816aa63f3633a5a16f1891261750c74 (diff)
downloadqpid-python-69e05a6b0979d2e12588635a4ed46a09a87cdec0.tar.gz
Merged from trunk up to r807984
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-broker-0-10@821770 13f79535-47bb-0310-9956-ffa450edef68
-rwxr-xr-xqpid/buildtools/buildCreator/buildCreator.py2
-rw-r--r--qpid/cpp/bindings/qmf/qmfengine.i28
-rw-r--r--qpid/cpp/bindings/qmf/ruby/Makefile.am4
-rw-r--r--qpid/cpp/bindings/qmf/ruby/qmf.rb59
-rw-r--r--qpid/cpp/bindings/qmf/ruby/ruby.i42
-rwxr-xr-xqpid/cpp/bindings/qmf/tests/agent_ruby.rb101
-rwxr-xr-xqpid/cpp/bindings/qmf/tests/python_console.py87
-rw-r--r--qpid/cpp/configure.ac1
-rw-r--r--qpid/cpp/docs/api/doxygen_mainpage.h2
-rw-r--r--qpid/cpp/docs/api/user.doxygen.in4
-rw-r--r--qpid/cpp/examples/CMakeLists.txt1
-rw-r--r--qpid/cpp/examples/Makefile.am2
-rw-r--r--qpid/cpp/examples/messaging/CMakeLists.txt32
-rw-r--r--qpid/cpp/examples/messaging/Makefile.am54
-rw-r--r--qpid/cpp/examples/messaging/client.cpp76
-rw-r--r--qpid/cpp/examples/messaging/map_receiver.cpp54
-rw-r--r--qpid/cpp/examples/messaging/map_sender.cpp66
-rw-r--r--qpid/cpp/examples/messaging/queue_listener.cpp82
-rw-r--r--qpid/cpp/examples/messaging/queue_receiver.cpp65
-rw-r--r--qpid/cpp/examples/messaging/queue_sender.cpp65
-rw-r--r--qpid/cpp/examples/messaging/server.cpp78
-rw-r--r--qpid/cpp/examples/messaging/topic_listener.cpp82
-rw-r--r--qpid/cpp/examples/messaging/topic_receiver.cpp66
-rw-r--r--qpid/cpp/examples/messaging/topic_sender.cpp78
-rw-r--r--qpid/cpp/include/qmf/Agent.h287
-rw-r--r--qpid/cpp/include/qmf/AgentObject.h95
-rw-r--r--qpid/cpp/include/qmf/Connection.h125
-rw-r--r--qpid/cpp/include/qmf/ConnectionSettings.h143
-rw-r--r--qpid/cpp/include/qmf/QmfImportExport.h33
-rw-r--r--qpid/cpp/include/qpid/client/Connection.h14
-rw-r--r--qpid/cpp/include/qpid/client/amqp0_10/Codecs.h61
-rw-r--r--qpid/cpp/include/qpid/framing/FieldTable.h8
-rw-r--r--qpid/cpp/include/qpid/framing/FieldValue.h120
-rw-r--r--qpid/cpp/include/qpid/framing/List.h76
-rw-r--r--qpid/cpp/include/qpid/messaging/Address.h58
-rw-r--r--qpid/cpp/include/qpid/messaging/Codec.h44
-rw-r--r--qpid/cpp/include/qpid/messaging/Connection.h67
-rw-r--r--qpid/cpp/include/qpid/messaging/Filter.h48
-rw-r--r--qpid/cpp/include/qpid/messaging/Message.h90
-rw-r--r--qpid/cpp/include/qpid/messaging/MessageContent.h90
-rw-r--r--qpid/cpp/include/qpid/messaging/MessageListener.h49
-rw-r--r--qpid/cpp/include/qpid/messaging/Receiver.h115
-rw-r--r--qpid/cpp/include/qpid/messaging/Sender.h57
-rw-r--r--qpid/cpp/include/qpid/messaging/Session.h99
-rw-r--r--qpid/cpp/include/qpid/messaging/Variant.h167
-rw-r--r--qpid/cpp/src/CMakeLists.txt63
-rw-r--r--qpid/cpp/src/Makefile.am47
-rw-r--r--qpid/cpp/src/qmf.mk24
-rw-r--r--qpid/cpp/src/qmf/AgentEngine.cpp (renamed from qpid/cpp/src/qmf/Agent.cpp)154
-rw-r--r--qpid/cpp/src/qmf/AgentEngine.h (renamed from qpid/cpp/src/qmf/Agent.h)25
-rw-r--r--qpid/cpp/src/qmf/ConnectionSettingsImpl.cpp323
-rw-r--r--qpid/cpp/src/qmf/ConnectionSettingsImpl.h60
-rw-r--r--qpid/cpp/src/qmf/ConsoleEngine.h (renamed from qpid/cpp/src/qmf/Console.h)13
-rw-r--r--qpid/cpp/src/qmf/Object.h2
-rw-r--r--qpid/cpp/src/qmf/ObjectId.h1
-rw-r--r--qpid/cpp/src/qmf/ObjectIdImpl.cpp5
-rw-r--r--qpid/cpp/src/qmf/ResilientConnection.cpp19
-rw-r--r--qpid/cpp/src/qmf/ResilientConnection.h8
-rw-r--r--qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp2
-rw-r--r--qpid/cpp/src/qpid/broker/Daemon.cpp2
-rw-r--r--qpid/cpp/src/qpid/broker/Exchange.cpp19
-rw-r--r--qpid/cpp/src/qpid/broker/Exchange.h2
-rw-r--r--qpid/cpp/src/qpid/client/Demux.h21
-rw-r--r--qpid/cpp/src/qpid/client/Results.cpp1
-rw-r--r--qpid/cpp/src/qpid/client/SessionImpl.cpp14
-rw-r--r--qpid/cpp/src/qpid/client/SessionImpl.h12
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp464
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h68
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp299
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.cpp48
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h50
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp79
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h43
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp241
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h91
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h50
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h47
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp146
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h76
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp49
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h58
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp287
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h107
-rw-r--r--qpid/cpp/src/qpid/cluster/Cluster.cpp8
-rw-r--r--qpid/cpp/src/qpid/cluster/Event.cpp2
-rw-r--r--qpid/cpp/src/qpid/cluster/Multicaster.cpp32
-rw-r--r--qpid/cpp/src/qpid/cluster/Multicaster.h3
-rw-r--r--qpid/cpp/src/qpid/cluster/UpdateClient.cpp2
-rw-r--r--qpid/cpp/src/qpid/cluster/WatchDogPlugin.cpp22
-rw-r--r--qpid/cpp/src/qpid/cluster/qpidd_watchdog.cpp3
-rw-r--r--qpid/cpp/src/qpid/framing/FieldTable.cpp20
-rw-r--r--qpid/cpp/src/qpid/framing/FieldValue.cpp35
-rw-r--r--qpid/cpp/src/qpid/framing/List.cpp83
-rw-r--r--qpid/cpp/src/qpid/messaging/Address.cpp49
-rw-r--r--qpid/cpp/src/qpid/messaging/Connection.cpp90
-rw-r--r--qpid/cpp/src/qpid/messaging/ConnectionImpl.h45
-rw-r--r--qpid/cpp/src/qpid/messaging/Filter.cpp39
-rw-r--r--qpid/cpp/src/qpid/messaging/Message.cpp325
-rw-r--r--qpid/cpp/src/qpid/messaging/Receiver.cpp51
-rw-r--r--qpid/cpp/src/qpid/messaging/ReceiverImpl.h52
-rw-r--r--qpid/cpp/src/qpid/messaging/Sender.cpp44
-rw-r--r--qpid/cpp/src/qpid/messaging/SenderImpl.h44
-rw-r--r--qpid/cpp/src/qpid/messaging/Session.cpp117
-rw-r--r--qpid/cpp/src/qpid/messaging/SessionImpl.h65
-rw-r--r--qpid/cpp/src/qpid/messaging/Variant.cpp603
-rw-r--r--qpid/cpp/src/qpid/sys/Timer.cpp5
-rw-r--r--qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp17
-rw-r--r--qpid/cpp/src/qpidd.cpp2
-rw-r--r--qpid/cpp/src/tests/CMakeLists.txt2
-rw-r--r--qpid/cpp/src/tests/FieldTable.cpp32
-rw-r--r--qpid/cpp/src/tests/Makefile.am4
-rw-r--r--qpid/cpp/src/tests/MessagingSessionTests.cpp325
-rw-r--r--qpid/cpp/src/tests/Variant.cpp157
-rwxr-xr-xqpid/cpp/src/tests/cli_tests.py22
-rwxr-xr-xqpid/cpp/src/tests/federation.py56
-rw-r--r--qpid/cpp/src/tests/perftest.cpp53
-rw-r--r--[-rwxr-xr-x]qpid/java/broker/bin/qpid-server.bat0
-rw-r--r--qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java319
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java60
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java14
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java803
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java27
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBean.java39
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java46
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java47
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java3
-rw-r--r--qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java396
-rw-r--r--qpid/java/client/example/src/main/java/README.txt7
-rw-r--r--qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java18
-rw-r--r--qpid/java/common/src/main/java/org/apache/qpid/util/FileUtils.java49
-rw-r--r--qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/LoggingManagement.java11
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientListener.java7
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXServerRegistry.java41
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java2
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java4
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/connection/ConnectionOperationsTabControl.java60
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/ExchangeOperationsTabControl.java4
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/HeadersExchangeOperationsTabControl.java144
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/ConfigurationFileTabControl.java54
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/RuntimeTabControl.java92
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/queue/QueueOperationsTabControl.java22
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/MBeanTypeTabControl.java102
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/QueueTypeTabControl.java2
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/vhost/VHostTabControl.java4
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini22
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/BrokerStartupTest.java141
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AlertingTest.java9
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java35
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java25
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/SelectorTest.java10
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/forwardall/Client.java12
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/forwardall/Service.java3
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java59
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java75
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java12
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java29
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitor.java20
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java63
-rw-r--r--qpid/java/test-profiles/010Excludes1
-rw-r--r--qpid/java/test-profiles/cpp.ssl.excludes2
-rw-r--r--qpid/java/test-profiles/default.testprofile3
-rw-r--r--qpid/java/test-profiles/test-provider.properties6
-rw-r--r--qpid/python/Makefile92
-rwxr-xr-xqpid/python/commands/qpid-config8
-rwxr-xr-xqpid/python/preppy67
-rw-r--r--qpid/python/qpid/compat.py7
-rw-r--r--qpid/python/qpid/datatypes.py11
-rw-r--r--qpid/python/qpid/management.py21
-rw-r--r--qpid/python/qpid/managementdata.py3
-rw-r--r--qpid/python/qpid/messaging.py817
-rw-r--r--qpid/python/qpid/session.py14
-rw-r--r--qpid/python/qpid/tests/messaging.py36
-rw-r--r--qpid/python/qpid_config.py8
-rw-r--r--qpid/python/tests/datatypes.py13
-rw-r--r--qpid/python/tests_0-10/management.py4
-rw-r--r--qpid/specs/management-schema.xml12
176 files changed, 11172 insertions, 1251 deletions
diff --git a/qpid/buildtools/buildCreator/buildCreator.py b/qpid/buildtools/buildCreator/buildCreator.py
index 0eae0b5422..6b9fc3c68e 100755
--- a/qpid/buildtools/buildCreator/buildCreator.py
+++ b/qpid/buildtools/buildCreator/buildCreator.py
@@ -1112,7 +1112,7 @@ def downloadSource(source, destination):
command = SVN_BIN+" co "+url+" "+targetdir
if (source.getElementsByTagName(REVISION).length > 0):
revision = getValue(source.getElementsByTagName(REVISION)[0])
- command = SVN_BIN+" co "+url+"@"+revision+" "+targetdir
+ command = SVN_BIN+" co -r"+revision+" "+url+" "+targetdir
else:
if (type == HTTP):
command = WGET_BIN+" --no-directories -P "+targetdir+" "+url
diff --git a/qpid/cpp/bindings/qmf/qmfengine.i b/qpid/cpp/bindings/qmf/qmfengine.i
index 3c67d92031..8ae28730e5 100644
--- a/qpid/cpp/bindings/qmf/qmfengine.i
+++ b/qpid/cpp/bindings/qmf/qmfengine.i
@@ -19,24 +19,22 @@
%{
-#include "Agent.h"
-#include <ResilientConnection.h>
+#include "qmf/AgentEngine.h"
+#include <qmf/ResilientConnection.h>
%}
-
-%include <Query.h>
-%include <Message.h>
-%include <Agent.h>
-%include <ResilientConnection.h>
-%include <Typecode.h>
-%include <Schema.h>
-%include <Value.h>
-%include <ObjectId.h>
-%include <Object.h>
-
-%include <qpid/client/ClientImportExport.h>
-%include <qpid/client/ConnectionSettings.h>
+%include <qmf/QmfImportExport.h>
+%include <qmf/Query.h>
+%include <qmf/Message.h>
+%include <qmf/AgentEngine.h>
+%include <qmf/ConnectionSettings.h>
+%include <qmf/ResilientConnection.h>
+%include <qmf/Typecode.h>
+%include <qmf/Schema.h>
+%include <qmf/Value.h>
+%include <qmf/ObjectId.h>
+%include <qmf/Object.h>
%inline {
diff --git a/qpid/cpp/bindings/qmf/ruby/Makefile.am b/qpid/cpp/bindings/qmf/ruby/Makefile.am
index 532fdb6875..0537dd1cd8 100644
--- a/qpid/cpp/bindings/qmf/ruby/Makefile.am
+++ b/qpid/cpp/bindings/qmf/ruby/Makefile.am
@@ -19,7 +19,7 @@
if HAVE_RUBY_DEVEL
-INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src/qmf -I$(top_srcdir)/src -I$(top_builddir)/src
+INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src -I$(top_builddir)/src
EXTRA_DIST = ruby.i
BUILT_SOURCES = qmfengine.cpp
@@ -36,7 +36,7 @@ rubylibarchdir = $(RUBY_LIB_ARCH)
rubylibarch_LTLIBRARIES = qmfengine.la
qmfengine_la_LDFLAGS = -avoid-version -module -shrext ".$(RUBY_DLEXT)"
-qmfengine_la_LIBADD = $(RUBY_LIBS) -L$(top_builddir)/src/.libs -lqpidclient $(top_builddir)/src/libqmfcommon.la
+qmfengine_la_LIBADD = $(RUBY_LIBS) -L$(top_builddir)/src/.libs -lqpidclient $(top_builddir)/src/libqmfagent.la
qmfengine_la_CXXFLAGS = $(INCLUDES) -I$(RUBY_INC) -I$(RUBY_INC_ARCH)
nodist_qmfengine_la_SOURCES = qmfengine.cpp
diff --git a/qpid/cpp/bindings/qmf/ruby/qmf.rb b/qpid/cpp/bindings/qmf/ruby/qmf.rb
index 7ee447c675..79b18423db 100644
--- a/qpid/cpp/bindings/qmf/ruby/qmf.rb
+++ b/qpid/cpp/bindings/qmf/ruby/qmf.rb
@@ -30,14 +30,40 @@ module Qmf
end
end
- class ConnectionSettings < Qmfengine::ConnectionSettings
+ class ConnectionSettings
+ attr_reader :impl
+
+ def initialize(url = nil)
+ if url
+ @impl = Qmfengine::ConnectionSettings.new(url)
+ else
+ @impl = Qmfengine::ConnectionSettings.new()
+ end
+ end
+
+ def set_attr(key, val)
+ if val.class == String
+ v = Qmfengine::Value.new(TYPE_LSTR)
+ v.setString(val)
+ elsif val.class == TrueClass or val.class == FalseClass
+ v = Qmfengine::Value.new(TYPE_BOOL)
+ v.setBool(val)
+ elsif val.class == Fixnum
+ v = Qmfengine::Value.new(TYPE_UINT32)
+ v.setUint(val)
+ else
+ raise ArgumentError, "Value for attribute '#{key}' has unsupported type: #{val.class}"
+ end
+
+ @impl.setAttr(key, v)
+ end
end
- class ConnectionEvent
+ class ConnectionHandler
def conn_event_connected(); end
def conn_event_disconnected(error); end
- def conn_event_session_closed(context, error); end
- def conn_event_recv(context, message); end
+ def sess_event_session_closed(context, error); end
+ def sess_event_recv(context, message); end
end
class Query
@@ -63,16 +89,11 @@ module Qmf
end
end
- class AgentHandler
- def get_query(context, query, userId); end
- def method_call(context, name, object_id, args, userId); end
- end
-
class Connection
attr_reader :impl
- def initialize(settings, event_handler = nil, delay_min = 1, delay_max = 128, delay_factor = 2)
- @impl = Qmfengine::ResilientConnection.new(settings, delay_min, delay_max, delay_factor)
+ def initialize(settings)
+ @impl = Qmfengine::ResilientConnection.new(settings.impl)
@sockEngine, @sock = Socket::socketpair(Socket::PF_UNIX, Socket::SOCK_STREAM, 0)
@impl.setNotifyFd(@sockEngine.fileno)
@new_conn_handlers = Array.new
@@ -153,12 +174,19 @@ module Qmf
@impl = Qmfengine::ObjectId.new
end
end
+
def object_num_high
return @impl.getObjectNumHi
end
+
def object_num_low
return @impl.getObjectNumLo
end
+
+ def ==(other)
+ return (@impl.getObjectNumHi == other.impl.getObjectNumHi) &&
+ (@impl.getObjectNumLo == other.impl.getObjectNumLo)
+ end
end
class Arguments
@@ -232,7 +260,12 @@ module Qmf
end
end
- class Agent
+ class AgentHandler
+ def get_query(context, query, userId); end
+ def method_call(context, name, object_id, args, userId); end
+ end
+
+ class Agent < ConnectionHandler
def initialize(handler, label="")
if label == ""
@agentLabel = "rb-%s.%d" % [Socket.gethostname, Process::pid]
@@ -241,7 +274,7 @@ module Qmf
end
@conn = nil
@handler = handler
- @impl = Qmfengine::Agent.new(@agentLabel)
+ @impl = Qmfengine::AgentEngine.new(@agentLabel)
@event = Qmfengine::AgentEvent.new
@xmtMessage = Qmfengine::Message.new
end
diff --git a/qpid/cpp/bindings/qmf/ruby/ruby.i b/qpid/cpp/bindings/qmf/ruby/ruby.i
index a8a2a87a97..b7fed403bd 100644
--- a/qpid/cpp/bindings/qmf/ruby/ruby.i
+++ b/qpid/cpp/bindings/qmf/ruby/ruby.i
@@ -38,17 +38,33 @@
%typemap (out) uint16_t
{
- $result = UINT2NUM((unsigned short) $1);
+ $result = UINT2NUM((uint16_t) $1);
}
%typemap (in) uint32_t
{
- $1 = NUM2UINT ($input);
+ if (TYPE($input) == T_BIGNUM)
+ $1 = NUM2UINT($input);
+ else
+ $1 = FIX2UINT($input);
}
%typemap (out) uint32_t
{
- $result = UINT2NUM((unsigned int) $1);
+ $result = UINT2NUM((uint32_t) $1);
+}
+
+%typemap (in) int32_t
+{
+ if (TYPE($input) == T_BIGNUM)
+ $1 = NUM2INT($input);
+ else
+ $1 = FIX2INT($input);
+}
+
+%typemap (out) int32_t
+{
+ $result = INT2NUM((int32_t) $1);
}
%typemap (typecheck, precedence=SWIG_TYPECHECK_INTEGER) uint32_t {
@@ -57,12 +73,28 @@
%typemap (in) uint64_t
{
- $1 = NUM2ULONG ($input);
+ if (TYPE($input) == T_BIGNUM)
+ $1 = NUM2ULL($input);
+ else
+ $1 = (uint64_t) FIX2LONG($input);
}
%typemap (out) uint64_t
{
- $result = ULONG2NUM((unsigned long) $1);
+ $result = ULL2NUM((uint64_t) $1);
+}
+
+%typemap (in) int64_t
+{
+ if (TYPE($input) == T_BIGNUM)
+ $1 = NUM2LL($input);
+ else
+ $1 = (int64_t) FIX2LONG($input);
+}
+
+%typemap (out) int64_t
+{
+ $result = LL2NUM((int64_t) $1);
}
%typemap (typecheck, precedence=SWIG_TYPECHECK_INTEGER) uint64_t {
diff --git a/qpid/cpp/bindings/qmf/tests/agent_ruby.rb b/qpid/cpp/bindings/qmf/tests/agent_ruby.rb
index d395810cb6..0f85c90f37 100755
--- a/qpid/cpp/bindings/qmf/tests/agent_ruby.rb
+++ b/qpid/cpp/bindings/qmf/tests/agent_ruby.rb
@@ -29,9 +29,27 @@ class Model
@parent_class = Qmf::SchemaObjectClass.new("org.apache.qpid.qmf", "parent")
@parent_class.add_property(Qmf::SchemaProperty.new("name", Qmf::TYPE_SSTR, :index => true))
@parent_class.add_property(Qmf::SchemaProperty.new("state", Qmf::TYPE_SSTR))
+
+ @parent_class.add_property(Qmf::SchemaProperty.new("uint64val", Qmf::TYPE_UINT64))
@parent_class.add_property(Qmf::SchemaProperty.new("uint32val", Qmf::TYPE_UINT32))
+ @parent_class.add_property(Qmf::SchemaProperty.new("uint16val", Qmf::TYPE_UINT16))
+ @parent_class.add_property(Qmf::SchemaProperty.new("uint8val", Qmf::TYPE_UINT8))
+
+ @parent_class.add_property(Qmf::SchemaProperty.new("int64val", Qmf::TYPE_INT64))
+ @parent_class.add_property(Qmf::SchemaProperty.new("int32val", Qmf::TYPE_INT32))
+ @parent_class.add_property(Qmf::SchemaProperty.new("int16val", Qmf::TYPE_INT16))
+ @parent_class.add_property(Qmf::SchemaProperty.new("int8val", Qmf::TYPE_INT8))
+
@parent_class.add_statistic(Qmf::SchemaStatistic.new("queryCount", Qmf::TYPE_UINT32, :unit => "query", :desc => "Query count"))
+ method = Qmf::SchemaMethod.new("echo", :desc => "Check responsiveness of the agent object")
+ method.add_argument(Qmf::SchemaArgument.new("sequence", Qmf::TYPE_UINT32, :dir => Qmf::DIR_IN_OUT))
+ @parent_class.add_method(method)
+
+ method = Qmf::SchemaMethod.new("set_numerics", :desc => "Set the numeric values in the object")
+ method.add_argument(Qmf::SchemaArgument.new("test", Qmf::TYPE_SSTR, :dir => Qmf::DIR_IN))
+ @parent_class.add_method(method)
+
method = Qmf::SchemaMethod.new("create_child", :desc => "Create a new child object")
method.add_argument(Qmf::SchemaArgument.new("child_name", Qmf::TYPE_LSTR, :dir => Qmf::DIR_IN))
method.add_argument(Qmf::SchemaArgument.new("child_ref", Qmf::TYPE_REF, :dir => Qmf::DIR_OUT))
@@ -55,24 +73,76 @@ class App < Qmf::AgentHandler
#@parent.inc_attr("queryCount")
if query.class_name == 'parent'
@agent.query_response(context, @parent)
+ elsif query.object_id == @parent_oid
+ @agent.query_response(context, @parent)
end
@agent.query_complete(context)
end
def method_call(context, name, object_id, args, userId)
# puts "Method: user=#{userId} context=#{context} method=#{name} object_num=#{object_id.object_num_low if object_id} args=#{args}"
- oid = @agent.alloc_object_id(2)
- args['child_ref'] = oid
- @child = Qmf::QmfObject.new(@model.child_class)
- @child.set_attr("name", args.by_key("child_name"))
- @child.set_object_id(oid)
- @agent.method_response(context, 0, "OK", args)
+
+ if name == "echo"
+ @agent.method_response(context, 0, "OK", args)
+
+ elsif name == "set_numerics"
+ retCode = 0
+ retText = "OK"
+
+ if args['test'] == "big"
+ @parent.set_attr("uint64val", 0x9494949449494949)
+ @parent.set_attr("uint32val", 0xa5a55a5a)
+ @parent.set_attr("uint16val", 0xb66b)
+ @parent.set_attr("uint8val", 0xc7)
+
+ @parent.set_attr("int64val", 1000000000000000000)
+ @parent.set_attr("int32val", 1000000000)
+ @parent.set_attr("int16val", 10000)
+ @parent.set_attr("int8val", 100)
+
+ elsif args['test'] == "small"
+ @parent.set_attr("uint64val", 4)
+ @parent.set_attr("uint32val", 5)
+ @parent.set_attr("uint16val", 6)
+ @parent.set_attr("uint8val", 7)
+
+ @parent.set_attr("int64val", 8)
+ @parent.set_attr("int32val", 9)
+ @parent.set_attr("int16val", 10)
+ @parent.set_attr("int8val", 11)
+
+ elsif args['test'] == "negative"
+ @parent.set_attr("uint64val", 0)
+ @parent.set_attr("uint32val", 0)
+ @parent.set_attr("uint16val", 0)
+ @parent.set_attr("uint8val", 0)
+
+ @parent.set_attr("int64val", -10000000000)
+ @parent.set_attr("int32val", -100000)
+ @parent.set_attr("int16val", -1000)
+ @parent.set_attr("int8val", -100)
+
+ else
+ retCode = 1
+ retText = "Invalid argument value for test"
+ end
+
+ @agent.method_response(context, retCode, retText, args)
+
+ elsif name == "create_child"
+ oid = @agent.alloc_object_id(2)
+ args['child_ref'] = oid
+ @child = Qmf::QmfObject.new(@model.child_class)
+ @child.set_attr("name", args.by_key("child_name"))
+ @child.set_object_id(oid)
+ @agent.method_response(context, 0, "OK", args)
+ end
end
def main
@settings = Qmf::ConnectionSettings.new
- @settings.host = ARGV[0] if ARGV.size > 0
- @settings.port = ARGV[1].to_i if ARGV.size > 1
+ @settings.set_attr("host", ARGV[0]) if ARGV.size > 0
+ @settings.set_attr("port", ARGV[1].to_i) if ARGV.size > 1
@connection = Qmf::Connection.new(@settings)
@agent = Qmf::Agent.new(self)
@@ -84,10 +154,19 @@ class App < Qmf::AgentHandler
@parent = Qmf::QmfObject.new(@model.parent_class)
@parent.set_attr("name", "Parent One")
@parent.set_attr("state", "OPERATIONAL")
- @parent.set_attr("uint32val", 0xa5a5a5a5)
- oid = @agent.alloc_object_id(1)
- @parent.set_object_id(oid)
+ @parent.set_attr("uint64val", 0)
+ @parent.set_attr("uint32val", 0)
+ @parent.set_attr("uint16val", 0)
+ @parent.set_attr("uint8val", 0)
+
+ @parent.set_attr("int64val", 0)
+ @parent.set_attr("int32val", 0)
+ @parent.set_attr("int16val", 0)
+ @parent.set_attr("int8val", 0)
+
+ @parent_oid = @agent.alloc_object_id(1)
+ @parent.set_object_id(@parent_oid)
sleep
end
diff --git a/qpid/cpp/bindings/qmf/tests/python_console.py b/qpid/cpp/bindings/qmf/tests/python_console.py
index 7a8a2cb9fe..365f2ac33a 100755
--- a/qpid/cpp/bindings/qmf/tests/python_console.py
+++ b/qpid/cpp/bindings/qmf/tests/python_console.py
@@ -36,16 +36,97 @@ class QmfInteropTests(TestBase010):
agents = qmf.getObjects(_class="agent")
sleep(1)
count += 1
- if count > 5:
+ if count > 10:
self.fail("Timed out waiting for remote agent")
- def test_B_basic_types(self):
+ def test_B_basic_method_invocation(self):
self.startQmf();
qmf = self.qmf
parents = qmf.getObjects(_class="parent")
self.assertEqual(len(parents), 1)
- self.assertEqual(parents[0].uint32val, 0xA5A5A5A5)
+ parent = parents[0]
+ for seq in range(10):
+ result = parent.echo(seq)
+ self.assertEqual(result.status, 0)
+ self.assertEqual(result.text, "OK")
+ self.assertEqual(result.sequence, seq)
+
+ result = parent.set_numerics("bogus")
+ self.assertEqual(result.status, 1)
+ self.assertEqual(result.text, "Invalid argument value for test")
+
+ def test_C_basic_types_numeric_big(self):
+ self.startQmf();
+ qmf = self.qmf
+
+ parents = qmf.getObjects(_class="parent")
+ self.assertEqual(len(parents), 1)
+ parent = parents[0]
+
+ result = parent.set_numerics("big")
+ self.assertEqual(result.status, 0)
+ self.assertEqual(result.text, "OK")
+
+ parent.update()
+
+ self.assertEqual(parent.uint64val, 0x9494949449494949)
+ self.assertEqual(parent.uint32val, 0xA5A55A5A)
+ self.assertEqual(parent.uint16val, 0xB66B)
+ self.assertEqual(parent.uint8val, 0xC7)
+
+ self.assertEqual(parent.int64val, 1000000000000000000)
+ self.assertEqual(parent.int32val, 1000000000)
+ self.assertEqual(parent.int16val, 10000)
+ self.assertEqual(parent.int8val, 100)
+
+ def test_C_basic_types_numeric_small(self):
+ self.startQmf();
+ qmf = self.qmf
+
+ parents = qmf.getObjects(_class="parent")
+ self.assertEqual(len(parents), 1)
+ parent = parents[0]
+
+ result = parent.set_numerics("small")
+ self.assertEqual(result.status, 0)
+ self.assertEqual(result.text, "OK")
+
+ parent.update()
+
+ self.assertEqual(parent.uint64val, 4)
+ self.assertEqual(parent.uint32val, 5)
+ self.assertEqual(parent.uint16val, 6)
+ self.assertEqual(parent.uint8val, 7)
+
+ self.assertEqual(parent.int64val, 8)
+ self.assertEqual(parent.int32val, 9)
+ self.assertEqual(parent.int16val, 10)
+ self.assertEqual(parent.int8val, 11)
+
+ def test_C_basic_types_numeric_negative(self):
+ self.startQmf();
+ qmf = self.qmf
+
+ parents = qmf.getObjects(_class="parent")
+ self.assertEqual(len(parents), 1)
+ parent = parents[0]
+
+ result = parent.set_numerics("negative")
+ self.assertEqual(result.status, 0)
+ self.assertEqual(result.text, "OK")
+
+ parent.update()
+
+ self.assertEqual(parent.uint64val, 0)
+ self.assertEqual(parent.uint32val, 0)
+ self.assertEqual(parent.uint16val, 0)
+ self.assertEqual(parent.uint8val, 0)
+
+ self.assertEqual(parent.int64val, -10000000000)
+ self.assertEqual(parent.int32val, -100000)
+ self.assertEqual(parent.int16val, -1000)
+ self.assertEqual(parent.int8val, -100)
def getProperty(self, msg, name):
for h in msg.headers:
diff --git a/qpid/cpp/configure.ac b/qpid/cpp/configure.ac
index adf3da06b8..a4e45ad19b 100644
--- a/qpid/cpp/configure.ac
+++ b/qpid/cpp/configure.ac
@@ -512,6 +512,7 @@ AC_CONFIG_FILES([
examples/xml-exchange/Makefile
examples/qmf-console/Makefile
examples/tradedemo/Makefile
+ examples/messaging/Makefile
bindings/qmf/Makefile
bindings/qmf/ruby/Makefile
bindings/qmf/tests/Makefile
diff --git a/qpid/cpp/docs/api/doxygen_mainpage.h b/qpid/cpp/docs/api/doxygen_mainpage.h
index cb59cfa260..83efaba31d 100644
--- a/qpid/cpp/docs/api/doxygen_mainpage.h
+++ b/qpid/cpp/docs/api/doxygen_mainpage.h
@@ -26,6 +26,7 @@
* <h2>Messaging Client API classes</h2>
* <ul>
* <li><p>\ref clientapi</p></li>
+ * <li><p>\ref qmfapi</p></li>
* </ul>
*
* <h2>Code for common tasks</h2>
@@ -127,5 +128,6 @@
/**
* \defgroup clientapi Qpid C++ Client API
+ * \defgroup qmfapi Qpid Management Framework C++ API
*
*/
diff --git a/qpid/cpp/docs/api/user.doxygen.in b/qpid/cpp/docs/api/user.doxygen.in
index 6ade9ab846..f6f8c161b7 100644
--- a/qpid/cpp/docs/api/user.doxygen.in
+++ b/qpid/cpp/docs/api/user.doxygen.in
@@ -456,7 +456,7 @@ WARN_LOGFILE = doxygen.log
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = @top_srcdir@/include @top_builddir@/include
+INPUT = @top_srcdir@/docs/api @top_srcdir@/include @top_builddir@/include
# If the value of the INPUT tag contains directories, you can use the
@@ -471,7 +471,7 @@ FILE_PATTERNS = *.h
# should be searched for input files as well. Possible values are YES and NO.
# If left blank NO is used.
-RECURSIVE = NO
+RECURSIVE = YES
# The EXCLUDE tag can be used to specify files and/or directories that should
# excluded from the INPUT source files. This way you can easily exclude a
diff --git a/qpid/cpp/examples/CMakeLists.txt b/qpid/cpp/examples/CMakeLists.txt
index 7d0a4d0d21..126adab32e 100644
--- a/qpid/cpp/examples/CMakeLists.txt
+++ b/qpid/cpp/examples/CMakeLists.txt
@@ -62,3 +62,4 @@ add_subdirectory(qmf-console)
add_subdirectory(request-response)
add_subdirectory(tradedemo)
add_subdirectory(xml-exchange)
+add_subdirectory(messaging)
diff --git a/qpid/cpp/examples/Makefile.am b/qpid/cpp/examples/Makefile.am
index 9c355b84e4..b526424e2a 100644
--- a/qpid/cpp/examples/Makefile.am
+++ b/qpid/cpp/examples/Makefile.am
@@ -16,7 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-SUBDIRS = direct fanout pub-sub request-response failover qmf-console tradedemo
+SUBDIRS = direct fanout pub-sub request-response failover qmf-console tradedemo messaging
if HAVE_XML
SUBDIRS += xml-exchange
broker_args = "--no-module-dir --data-dir \"\" --auth no --load-module $(top_builddir)/src/.libs/xml.so"
diff --git a/qpid/cpp/examples/messaging/CMakeLists.txt b/qpid/cpp/examples/messaging/CMakeLists.txt
new file mode 100644
index 0000000000..e7885d0b50
--- /dev/null
+++ b/qpid/cpp/examples/messaging/CMakeLists.txt
@@ -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.
+#
+
+add_example(messaging queue_listener)
+add_example(messaging queue_receiver)
+add_example(messaging queue_sender)
+
+add_example(messaging topic_listener)
+add_example(messaging topic_receiver)
+add_example(messaging topic_sender)
+
+add_example(messaging map_receiver)
+add_example(messaging map_sender)
+
+add_example(messaging client)
+add_example(messaging server)
diff --git a/qpid/cpp/examples/messaging/Makefile.am b/qpid/cpp/examples/messaging/Makefile.am
new file mode 100644
index 0000000000..250e549e82
--- /dev/null
+++ b/qpid/cpp/examples/messaging/Makefile.am
@@ -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.
+#
+examplesdir=$(pkgdatadir)/examples/messaging
+
+MAKELDFLAGS=$(CLIENTFLAGS)
+include $(top_srcdir)/examples/makedist.mk
+
+noinst_PROGRAMS=queue_sender queue_listener queue_receiver topic_sender topic_listener topic_receiver client server map_sender map_receiver
+
+queue_sender_SOURCES=queue_sender.cpp
+queue_sender_LDADD=$(CLIENT_LIB)
+
+queue_listener_SOURCES=queue_listener.cpp
+queue_listener_LDADD=$(CLIENT_LIB)
+
+queue_receiver_SOURCES=queue_receiver.cpp
+queue_receiver_LDADD=$(CLIENT_LIB)
+
+topic_sender_SOURCES=topic_sender.cpp
+topic_sender_LDADD=$(CLIENT_LIB)
+
+topic_listener_SOURCES=topic_listener.cpp
+topic_listener_LDADD=$(CLIENT_LIB)
+
+topic_receiver_SOURCES=topic_receiver.cpp
+topic_receiver_LDADD=$(CLIENT_LIB)
+
+client_SOURCES=client.cpp
+client_LDADD=$(CLIENT_LIB)
+
+server_SOURCES=server.cpp
+server_LDADD=$(CLIENT_LIB)
+
+map_sender_SOURCES=map_sender.cpp
+map_sender_LDADD=$(CLIENT_LIB)
+
+map_receiver_SOURCES=map_receiver.cpp
+map_receiver_LDADD=$(CLIENT_LIB)
diff --git a/qpid/cpp/examples/messaging/client.cpp b/qpid/cpp/examples/messaging/client.cpp
new file mode 100644
index 0000000000..45c065880b
--- /dev/null
+++ b/qpid/cpp/examples/messaging/client.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 <qpid/messaging/Address.h>
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/Receiver.h>
+#include <qpid/messaging/Sender.h>
+#include <qpid/messaging/Session.h>
+
+#include <cstdlib>
+#include <iostream>
+
+#include <sstream>
+
+using namespace qpid::messaging;
+
+using std::stringstream;
+using std::string;
+
+int main(int argc, char** argv) {
+ const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672";
+
+ try {
+ Connection connection = Connection::open(url);
+ Session session = connection.newSession();
+
+ Sender sender = session.createSender("service_queue");
+
+ //create temp queue & receiver...
+ Address responseQueue = session.createTempQueue();
+ Receiver receiver = session.createReceiver(responseQueue);
+
+ // Now send some messages ...
+ string s[] = {
+ "Twas brillig, and the slithy toves",
+ "Did gire and gymble in the wabe.",
+ "All mimsy were the borogroves,",
+ "And the mome raths outgrabe."
+ };
+
+ Message request;
+ request.setReplyTo(responseQueue);
+ for (int i=0; i<4; i++) {
+ request.setContent(s[i]);
+ sender.send(request);
+ Message response = receiver.fetch();
+ std::cout << request.getContent().asString() << " -> " << response.getContent().asString() << std::endl;
+ }
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
+
+
diff --git a/qpid/cpp/examples/messaging/map_receiver.cpp b/qpid/cpp/examples/messaging/map_receiver.cpp
new file mode 100644
index 0000000000..e6557b1560
--- /dev/null
+++ b/qpid/cpp/examples/messaging/map_receiver.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/messaging/Connection.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/Receiver.h>
+#include <qpid/messaging/Session.h>
+
+#include <cstdlib>
+#include <iostream>
+
+#include <sstream>
+
+using namespace qpid::messaging;
+
+using std::stringstream;
+using std::string;
+
+int main(int argc, char** argv) {
+ const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672";
+
+ try {
+ Connection connection = Connection::open(url);
+ Session session = connection.newSession();
+ Receiver receiver = session.createReceiver("message_queue");
+ Message message = receiver.fetch();
+ std::cout << message.getContent().asMap() << std::endl;
+ session.acknowledge();
+ receiver.cancel();
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/examples/messaging/map_sender.cpp b/qpid/cpp/examples/messaging/map_sender.cpp
new file mode 100644
index 0000000000..9301c1fe1f
--- /dev/null
+++ b/qpid/cpp/examples/messaging/map_sender.cpp
@@ -0,0 +1,66 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/Sender.h>
+#include <qpid/messaging/Session.h>
+
+#include <cstdlib>
+#include <iostream>
+
+#include <sstream>
+
+using namespace qpid::messaging;
+
+using std::stringstream;
+using std::string;
+
+int main(int argc, char** argv) {
+ const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672";
+
+ try {
+ Connection connection = Connection::open(url);
+ Session session = connection.newSession();
+ Sender sender = session.createSender("message_queue");
+
+ Message message;
+ message.getContent()["id"] = 987654321;
+ message.getContent()["name"] = "Widget";
+ message.getContent()["price"] = 0.99;//bad use of floating point number, just an example!
+ Variant::List colours;
+ colours.push_back(Variant("red"));
+ colours.push_back(Variant("green"));
+ colours.push_back(Variant("white"));
+ message.getContent()["colours"] = colours;
+
+ sender.send(message);
+ session.sync();
+
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
+
+
diff --git a/qpid/cpp/examples/messaging/queue_listener.cpp b/qpid/cpp/examples/messaging/queue_listener.cpp
new file mode 100644
index 0000000000..099e8e145a
--- /dev/null
+++ b/qpid/cpp/examples/messaging/queue_listener.cpp
@@ -0,0 +1,82 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Session.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/MessageListener.h>
+#include <qpid/messaging/Receiver.h>
+
+#include <cstdlib>
+#include <iostream>
+
+using namespace qpid::messaging;
+
+class Listener : public MessageListener
+{
+ public:
+ Listener(const Receiver& receiver);
+ void received(Message& message);
+ bool isFinished();
+ private:
+ Receiver receiver;
+ bool finished;
+};
+
+Listener::Listener(const Receiver& r) : receiver(r), finished(false) {}
+
+bool Listener::isFinished() { return finished; }
+
+void Listener::received(Message& message)
+{
+ std::cout << "Message: " << message.getContent().asString() << std::endl;
+ if (message.getContent().asString() == "That's all, folks!") {
+ std::cout << "Shutting down listener" << std::endl;
+ receiver.cancel();
+ finished = true;
+ }
+}
+
+int main(int argc, char** argv) {
+ const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672";
+
+ try {
+ Connection connection = Connection::open(url);
+ Session session = connection.newSession();
+
+ Receiver receiver = session.createReceiver("message_queue");
+ Listener listener(receiver);
+ receiver.setListener(&listener);
+ receiver.setCapacity(1);
+ receiver.start();
+ while (session.dispatch()) {
+ session.acknowledge();
+ if (listener.isFinished()) break;
+ }
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
+
+
diff --git a/qpid/cpp/examples/messaging/queue_receiver.cpp b/qpid/cpp/examples/messaging/queue_receiver.cpp
new file mode 100644
index 0000000000..83a44b2ca9
--- /dev/null
+++ b/qpid/cpp/examples/messaging/queue_receiver.cpp
@@ -0,0 +1,65 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/Receiver.h>
+#include <qpid/messaging/Session.h>
+
+#include <cstdlib>
+#include <iostream>
+
+#include <sstream>
+
+using namespace qpid::messaging;
+
+using std::stringstream;
+using std::string;
+
+int main(int argc, char** argv) {
+ const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672";
+
+ try {
+ Variant::Map options;
+ if (argc>2) parseOptionString(argv[2], options);
+ Connection connection = Connection::open(url, options);
+ Session session = connection.newSession();
+ Receiver receiver = session.createReceiver("message_queue");
+ while (true) {
+ Message message = receiver.fetch();
+ std::cout << "Message: " << message.getContent() << std::endl;
+ session.acknowledge();
+ if (message.getContent().asString() == "That's all, folks!") {
+ std::cout << "Cancelling receiver" << std::endl;
+ receiver.cancel();
+ break;
+ }
+ }
+
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
+
+
diff --git a/qpid/cpp/examples/messaging/queue_sender.cpp b/qpid/cpp/examples/messaging/queue_sender.cpp
new file mode 100644
index 0000000000..637e7eb8e4
--- /dev/null
+++ b/qpid/cpp/examples/messaging/queue_sender.cpp
@@ -0,0 +1,65 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/Sender.h>
+#include <qpid/messaging/Session.h>
+
+#include <cstdlib>
+#include <iostream>
+
+#include <sstream>
+
+using namespace qpid::messaging;
+
+using std::stringstream;
+using std::string;
+
+int main(int argc, char** argv) {
+ const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672";
+ int count = argc>2 ? atoi(argv[2]) : 10;
+
+ try {
+ Connection connection = Connection::open(url);
+ Session session = connection.newSession();
+ Sender sender = session.createSender("message_queue");
+
+ // Now send some messages ...
+ for (int i=0; i<count; i++) {
+ Message message;
+ message.getContent() << "Message " << i;
+ sender.send(message);
+ }
+
+ // And send a final message to indicate termination.
+ Message message("That's all, folks!");
+ sender.send(message);
+ session.sync();
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
+
+
diff --git a/qpid/cpp/examples/messaging/server.cpp b/qpid/cpp/examples/messaging/server.cpp
new file mode 100644
index 0000000000..6f0f2af02d
--- /dev/null
+++ b/qpid/cpp/examples/messaging/server.cpp
@@ -0,0 +1,78 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/messaging/Address.h>
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/Receiver.h>
+#include <qpid/messaging/Sender.h>
+#include <qpid/messaging/Session.h>
+#include <qpid/messaging/Variant.h>
+
+#include <algorithm>
+#include <cstdlib>
+#include <iostream>
+#include <memory>
+#include <sstream>
+
+using namespace qpid::messaging;
+
+using std::stringstream;
+using std::string;
+
+int main(int argc, char** argv) {
+ const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672";
+
+ try {
+ Connection connection = Connection::open(url);
+ Session session = connection.newSession();
+ VariantMap options;
+ options["auto_acknowledge"] = 0;
+ Receiver receiver = session.createReceiver("service_queue", options);
+
+ while (true) {
+ Message request = receiver.fetch();
+ const Address& address = request.getReplyTo();
+ if (address) {
+ Sender sender = session.createSender(address);
+ std::string s = request.getContent().asString();
+ std::transform(s.begin(), s.end(), s.begin(), toupper);
+ Message response(s);
+ sender.send(response);
+ std::cout << "Processed request: "
+ << request.getContent().asString()
+ << " -> "
+ << response.getContent().asString() << std::endl;
+ session.acknowledge();
+ } else {
+ std::cerr << "Error: no reply address specified for request: " << request.getContent().asString() << std::endl;
+ session.reject(request);
+ }
+ }
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
+
+
diff --git a/qpid/cpp/examples/messaging/topic_listener.cpp b/qpid/cpp/examples/messaging/topic_listener.cpp
new file mode 100644
index 0000000000..700e03cdf9
--- /dev/null
+++ b/qpid/cpp/examples/messaging/topic_listener.cpp
@@ -0,0 +1,82 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Filter.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/MessageListener.h>
+#include <qpid/messaging/Session.h>
+#include <qpid/messaging/Receiver.h>
+
+#include <cstdlib>
+#include <iostream>
+
+using namespace qpid::messaging;
+
+class Listener : public MessageListener
+{
+ public:
+ Listener(const Receiver& receiver);
+ void received(Message& message);
+ bool isFinished();
+ private:
+ Receiver receiver;
+ bool finished;
+};
+
+Listener::Listener(const Receiver& r) : receiver(r), finished(false) {}
+
+bool Listener::isFinished() { return finished; }
+
+void Listener::received(Message& message)
+{
+ std::cout << "Message: " << message.getContent().asString() << std::endl;
+ if (message.getContent().asString() == "That's all, folks!") {
+ std::cout << "Shutting down listener" << std::endl;
+ receiver.cancel();
+ finished = true;
+ }
+}
+
+int main(int argc, char** argv) {
+ const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672";
+ const char* pattern = argc>2 ? argv[2] : "#.#";
+
+ try {
+ Connection connection = Connection::open(url);
+ Session session = connection.newSession();
+
+ Filter filter(Filter::WILDCARD, pattern, "control");
+ Receiver receiver = session.createReceiver("news_service", filter);
+ Listener listener(receiver);
+ receiver.setListener(&listener);
+ receiver.setCapacity(1);
+ receiver.start();
+ while (session.dispatch() && !listener.isFinished()) ;
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
+
+
diff --git a/qpid/cpp/examples/messaging/topic_receiver.cpp b/qpid/cpp/examples/messaging/topic_receiver.cpp
new file mode 100644
index 0000000000..063f0d9cb0
--- /dev/null
+++ b/qpid/cpp/examples/messaging/topic_receiver.cpp
@@ -0,0 +1,66 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/messaging/Address.h>
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Filter.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/Receiver.h>
+#include <qpid/messaging/Session.h>
+
+#include <cstdlib>
+#include <iostream>
+
+#include <sstream>
+
+using namespace qpid::messaging;
+
+using std::stringstream;
+using std::string;
+
+int main(int argc, char** argv) {
+ const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672";
+ const char* pattern = argc>2 ? argv[2] : "#.#";
+
+ try {
+ Connection connection = Connection::open(url);
+ Session session = connection.newSession();
+ Filter filter(Filter::WILDCARD, pattern, "control");
+ Receiver receiver = session.createReceiver(Address("news_service", "topic"), filter);
+ while (true) {
+ Message message = receiver.fetch();
+ std::cout << "Message: " << message.getContent().asString() << std::endl;
+ if (message.getContent().asString() == "That's all, folks!") {
+ std::cout << "Cancelling receiver" << std::endl;
+ receiver.cancel();
+ break;
+ }
+ }
+
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
+
+
diff --git a/qpid/cpp/examples/messaging/topic_sender.cpp b/qpid/cpp/examples/messaging/topic_sender.cpp
new file mode 100644
index 0000000000..5665fc45f9
--- /dev/null
+++ b/qpid/cpp/examples/messaging/topic_sender.cpp
@@ -0,0 +1,78 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/Sender.h>
+#include <qpid/messaging/Session.h>
+
+#include <cstdlib>
+#include <iostream>
+
+#include <sstream>
+
+using namespace qpid::messaging;
+
+using std::stringstream;
+using std::string;
+
+void sendMessages(Sender& sender, int count, const std::string& subject, const std::string& text)
+{
+ Message message;
+ message.setSubject(subject);
+ for (int i=0; i<count; i++) {
+ stringstream message_data;
+ message_data << text << i;
+
+ message.setContent(message_data.str());
+ sender.send(message);
+ }
+}
+
+int main(int argc, char** argv) {
+ const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672";
+ int count = argc>2 ? atoi(argv[2]) : 10;
+
+ try {
+ Connection connection = Connection::open(url);
+ Session session = connection.newSession();
+ Sender sender = session.createSender("news_service");
+
+ // Now send some messages to each topic...
+ sendMessages(sender, count, "usa.news", "news about the usa");
+ sendMessages(sender, count, "usa.weather", "weather report for the usa");
+ sendMessages(sender, count, "europe.news", "news about europe");
+ sendMessages(sender, count, "europe.weather", "weather report for europe");
+
+ // And send a final message to indicate termination.
+ Message message("That's all, folks!");
+ message.setSubject("control");
+ sender.send(message);
+ session.sync();
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
+
+
diff --git a/qpid/cpp/include/qmf/Agent.h b/qpid/cpp/include/qmf/Agent.h
new file mode 100644
index 0000000000..e61cd737d0
--- /dev/null
+++ b/qpid/cpp/include/qmf/Agent.h
@@ -0,0 +1,287 @@
+#ifndef _QmfAgent_
+#define _QmfAgent_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/QmfImportExport.h"
+
+namespace qmf {
+
+ class AgentImpl;
+ class Connection;
+ class ObjectId;
+ class AgentObject;
+ class Value;
+ class Event;
+ class SchemaObjectClass;
+
+ /**
+ * AgentListener is used by agents that select the internalStore=false option (see Agent
+ * constructor) or by agents that wish to provide access control for queries and methods.
+ *
+ * \ingroup qmfapi
+ */
+ class AgentListener {
+ QMF_EXTERN virtual ~AgentListener();
+
+ /**
+ * allowQuery is called before a query operation is executed. If true is returned
+ * by this function, the query will proceed. If false is returned, the query will
+ * be forbidden.
+ *
+ * @param q The query being requested.
+ * @param userId The authenticated identity of the user requesting the query.
+ */
+ virtual bool allowQuery(const Query& q, const char* userId);
+
+ /**
+ * allowMethod is called before a method call is executed. If true is returned
+ * by this function, the method call will proceed. If false is returned, the method
+ * call will be forbidden.
+ *
+ * @param name The name of the method being called.
+ * @param args A value object (of type "map") that contains both input and output arguments.
+ * @param oid The objectId that identifies the instance of the object being called.
+ * @param cls The Schema describing the object being called.
+ * @param userId The authenticated identity of the requesting user.
+ */
+ virtual bool allowMethod(const char* name, const Value& args, const ObjectId& oid,
+ const SchemaObjectClass& cls, const char* userId);
+
+ /**
+ * query is called when the agent receives a query request. The handler must invoke
+ * Agent::queryResponse zero or more times (using the supplied context) followed by
+ * a single invocation of Agent::queryComplete. These calls do not need to be made
+ * synchronously in the context of this function. They may occur before or after this
+ * function returns.
+ *
+ * This function will only be invoked if internalStore=false in the Agent's constructor.
+ *
+ * @param context A context value to use in resulting calls to queryResponse and quertComplete.
+ * @param q The query requested by the console.
+ * @param userId the authenticated identity of the user requesting the query.
+ */
+ virtual void query(uint32_t context, const Query& q, const char* userId);
+
+ /**
+ * syncStart is called when a console requests a standing query. This function must
+ * behave exactly like AgentListener::query (i.e. send zero or more responses followed
+ * by a queryComplete) except it then remembers the context and the query and makes
+ * subsequent queryResponse calls whenever appropriate according the the query.
+ *
+ * The standing query shall stay in effect until syncStop is called with the same context
+ * value or until a specified period of time elapses without receiving a syncTouch for the
+ * context.
+ *
+ * This function will only be invoked if internalStore=false in the Agent's constructor.
+ *
+ * @param context A context value to use in resulting calls to queryResponse and queryComplete.
+ * @param q The query requested by the console.
+ * @param userId the authenticated identity of the user requesting the query.
+ */
+ virtual void syncStart(uint32_t context, const Query& q, const char* userId);
+
+ /**
+ * syncTouch is called when the console that requested a standing query refreshes its
+ * interest in the query. The console must periodically "touch" a standing query to keep
+ * it alive. This prevents standing queries from accumulating when the console disconnects
+ * before it can stop the query.
+ *
+ * This function will only be invoked if internalStore=false in the Agent's constructor.
+ *
+ * @param context The context supplied in a previous call to syncStart.
+ * @param userId The authenticated identity of the requesting user.
+ */
+ virtual void syncTouch(uint32_t context, const char* userId);
+
+ /**
+ * syncStop is called when the console that requested a standing query no longer wishes to
+ * receive data associated with that query. The application shall stop processing this
+ * query and shall remove its record of the context value.
+ *
+ * This function will only be invoked if internalStore=false in the Agent's constructor.
+ *
+ * @param context The context supplied in a previous call to syncStart.
+ * @param userId The authenticated identity of the requesting user.
+ */
+ virtual void syncStop(uint32_t context, const char* userId);
+
+ /**
+ * methodCall is called when a console invokes a method on a QMF object. The application
+ * must call Agent::methodResponse once in response to this function. The response does
+ * not need to be called synchronously in the context of this function. It may be called
+ * before or after this function returns.
+ *
+ * This function will only be invoked if internalStore=false in the Agent's constructor.
+ *
+ * @param context A context value to use in resulting call to methodResponse.
+ * @param name The name of the method being called.
+ * @param args A value object (of type "map") that contains both input and output arguments.
+ * @param oid The objectId that identifies the instance of the object being called.
+ * @param cls The Schema describing the object being called.
+ * @param userId The authenticated identity of the requesting user.
+ */
+ virtual void methodCall(uint32_t context, const char* name, Value& args,
+ const ObjectId& oid, const SchemaObjectClass& cls, const char* userId);
+ };
+
+ /**
+ * The Agent class is the QMF Agent portal. It should be instantiated once and associated with a
+ * Connection (setConnection) to connect an agent to the QMF infrastructure.
+ *
+ * \ingroup qmfapi
+ */
+ class Agent {
+ public:
+ /**
+ * Create an instance of the Agent class.
+ *
+ * @param label An optional string label that can be used to identify the agent.
+ *
+ * @param internalStore If true, objects shall be tracked internally by the agent.
+ * If false, the user of the agent must track the objects.
+ * If the agent is tracking the objects, queries and syncs are handled by
+ * the agent. The only involvement the user has is to optionally authorize
+ * individual operations. If the user is tracking the objects, the user code
+ * must implement queries and syncs (standing queries).
+ *
+ * @param listener A pointer to a class that implements the AgentListener interface.
+ * This must be supplied if any of the following conditions are true:
+ * - The agent model contains methods
+ * - The user wishes to individually authorize query and sync operations.
+ * - internalStore = false
+ */
+ QMF_EXTERN Agent(char* label="qmfa", bool internalStore=true, AgentListener* listener=0);
+
+ /**
+ * Destroy an instance of the Agent class.
+ */
+ QMF_EXTERN ~Agent();
+
+ /**
+ * Set the persistent store file. This file, if specified, is used to store state information
+ * about the Agent. For example, if object-ids must be persistent across restarts of the Agent
+ * program, this file path must be supplied.
+ *
+ * @param path Full path to a file that is both writable and readable by the Agent program.
+ */
+ QMF_EXTERN void setStoreDir(const char* path);
+
+ /**
+ * Provide a connection (to a Qpid broker) over which the agent can communicate.
+ *
+ * @param conn Pointer to a Connection object.
+ */
+ QMF_EXTERN void setConnection(Connection* conn);
+
+ /**
+ * Register a class schema (object or event) with the agent. The agent must have a registered
+ * schema for an object class or an event class before it can handle objects or events of that
+ * class.
+ *
+ * @param cls Pointer to the schema structure describing the class.
+ */
+ QMF_EXTERN void registerClass(SchemaObjectClass* cls);
+ QMF_EXTERN void registerClass(SchemaEventClass* cls);
+
+ /**
+ * Add an object to the agent (for internal storage mode only).
+ *
+ * @param obj Reference to the object to be managed by the agent.
+ *
+ * @param persistent Iff true, the object ID assigned to the object shall indicate persistence
+ * (i.e. the object ID shall be the same across restarts of the agent program).
+ *
+ * @param oid 64-bit value for the oid (if zero, the agent will assign the value).
+ *
+ * @param oidLo 32-bit value for the lower 32-bits of the oid.
+ *
+ * @param oidHi 32-bit value for the upper 32-bits of the oid.
+ */
+ QMF_EXTERN const ObjectId* addObject(AgentObject& obj, bool persistent=false, uint64_t oid=0);
+ QMF_EXTERN const ObjectId* addObject(AgentObject& obj, bool persistent, uint32_t oidLo, uint32_t oidHi);
+
+ /**
+ * Allocate an object ID for an object (for external storage mode only).
+ *
+ * @param persistent Iff true, the object ID allocated shall indicate persistence
+ * (i.e. the object ID shall be the same across restarts of the agent program).
+ *
+ * @param oid 64-bit value for the oid (if zero, the agent will assign the value).
+ *
+ * @param oidLo 32-bit value for the lower 32-bits of the oid.
+ *
+ * @param oidHi 32-bit value for the upper 32-bits of the oid.
+ */
+ QMF_EXTERN const ObjectId* allocObjectId(bool persistent=false, uint64_t oid=0);
+ QMF_EXTERN const ObjectId* allocObjectId(bool persistent, uint32_t oidLo, uint32_t oidHi);
+
+ /**
+ * Raise a QMF event.
+ *
+ * @param event Reference to an event object to be raised to the QMF infrastructure.
+ */
+ QMF_EXTERN void raiseEvent(Event& event);
+
+ /**
+ * Provide a response to a query (for external storage mode only).
+ *
+ * @param context The context value supplied in the query (via the AgentListener interface).
+ *
+ * @param object A reference to the agent that matched the query criteria.
+ *
+ * @param prop If true, transmit the property attributes of this object.
+ *
+ * @param stat If true, transmit the statistic attributes of this object.
+ */
+ QMF_EXTERN void queryResponse(uint32_t context, AgentObject& object, bool prop = true, bool stat = true);
+
+ /**
+ * Indicate that a query (or the initial dump of a sync) is complete (for external storage mode only).
+ *
+ * @param context The context value supplied in the query/sync (via the AgentListener interface).
+ */
+ QMF_EXTERN void queryComplete(uint32_t context);
+
+ /**
+ * Provide the response to a method call.
+ *
+ * @param context The context value supplied in the method request (via the AgentListener interface).
+ *
+ * @param args The argument list from the method call. Must include the output arguments (may include
+ * the input arguments).
+ *
+ * @param status Numerical return status: zero indicates no error, non-zero indicates error.
+ *
+ * @param exception Pointer to an exception value. If status is non-zero, the exception value is
+ * sent to the caller. It is optional (i.e. leave the pointer as 0), or may be
+ * set to any legal value. A string may be supplied, but an unmanaged object of
+ * any schema may also be passed.
+ */
+ QMF_EXTERN void methodResponse(uint32_t context, const Value& args, uint32_t status=0,
+ const Value* exception=0);
+
+ private:
+ AgentImpl* impl;
+ };
+
+}
+
+#endif
diff --git a/qpid/cpp/include/qmf/AgentObject.h b/qpid/cpp/include/qmf/AgentObject.h
new file mode 100644
index 0000000000..a1878605eb
--- /dev/null
+++ b/qpid/cpp/include/qmf/AgentObject.h
@@ -0,0 +1,95 @@
+#ifndef _QmfAgentObject_
+#define _QmfAgentObject_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/QmfImportExport.h"
+
+namespace qmf {
+
+ class AgentObjectImpl;
+ class SchemaObjectClass;
+ class ObjectId;
+ class Value;
+ class Agent;
+
+ /**
+ * AgentObject is an extension of Object with agent-specific methods added.
+ *
+ * \ingroup qmfapi
+ */
+ class AgentObject : public Object {
+ public:
+ /**
+ * Create a new Object of a specific type.
+ *
+ * @param type Pointer to the schema class to use as a type for this object.
+ */
+ QMF_EXTERN AgentObject(const SchemaObjectClass* type);
+
+ /**
+ * Schedule this object for deletion. Agent objects should never be directly
+ * destroyed, rather this method should be called and all pointers to this
+ * object dropped. The agent will clean up and properly delete the object at
+ * the appropraite time.
+ */
+ QMF_EXTERN void destroy();
+
+ /**
+ * Set the object ID for this object if it is to be managed by the agent.
+ *
+ * @param oid The new object ID for the managed object.
+ */
+ QMF_EXTERN void setObjectId(ObjectId& oid);
+
+ /**
+ * Handler for invoked method calls. This will only be called for objects that
+ * are being managed and stored by an agent (see internalStore argument in Agent::Agent).
+ * If this function is not overridden in a child class, the default implementation will
+ * cause AgentListener::methodCall to be invoked in the application program.
+ *
+ * If this function is overridden in a sub-class, the implementation must perform
+ * the actions associated with the method call (i.e. implement the method). Once the
+ * method execution is complete, it must call Agent::methodResponse with the result
+ * of the method execution. Agent::methodResponse does not need to be called
+ * synchronously in the context of this function call. It may be called at a later
+ * time from a different thread.
+ *
+ * @param context Context supplied by the agent and required to be passed in the
+ * call to Agent::methodResponse
+ *
+ * @param name The name of the method.
+ *
+ * @param args A Value (of type map) that contains the input and output arguments.
+ *
+ * @param userId The authenticated identity of the user who invoked the method.
+ */
+ QMF_EXTERN virtual void methodInvoked(uint32_t context, const char* name, Value& args,
+ const char* userId);
+ private:
+ friend class Agent;
+ virtual ~AgentObject();
+ void setAgent(Agent* agent);
+ AgentObjectImpl* impl;
+ };
+
+}
+
+#endif
diff --git a/qpid/cpp/include/qmf/Connection.h b/qpid/cpp/include/qmf/Connection.h
new file mode 100644
index 0000000000..f648b1427f
--- /dev/null
+++ b/qpid/cpp/include/qmf/Connection.h
@@ -0,0 +1,125 @@
+#ifndef _QmfConnection_
+#define _QmfConnection_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/QmfImportExport.h"
+#include "qmf/ConnectionSettings.h"
+
+namespace qmf {
+
+ /**
+ * Operational states for Connections.
+ *
+ * \ingroup qmfapi
+ */
+ enum ConnectionState {
+ CONNECTION_UP = 1,
+ CONNECTION_DOWN = 2
+ };
+
+ /**
+ * Implement a subclass of ConnectionListener and provide it with the
+ * Connection constructor to receive notification of changes in the
+ * connection state.
+ *
+ * \ingroup qmfapi
+ */
+ class ConnectionListener {
+ QMF_EXTERN virtual ~ConnectionListener();
+
+ /**
+ * Called each time the state of the connection changes.
+ *
+ * @param state the new state
+ */
+ virtual void newState(ConnectionState state);
+
+ /**
+ * Called if the connection requires input from an interactive client.
+ *
+ * @param prompt Text of the prompt - describes what information is required.
+ * @param answer The interactive user input.
+ * @param answerLen on Input - the maximum number of bytes that can be copied to answer.
+ * on Output - the number of bytes copied to answer.
+ */
+ virtual void interactivePrompt(const char* prompt, char* answer, uint32_t answerLen);
+ };
+
+ class ConnectionImpl;
+
+ /**
+ * The Connection class represents a connection to a QPID broker that can
+ * be used by agents and consoles, possibly multiple at the same time.
+ *
+ * \ingroup qmfapi
+ */
+ class Connection {
+ public:
+
+ /**
+ * Creates a connection object and begins the process of attempting to
+ * connect to the QPID broker.
+ *
+ * @param settings The settings that control how the connection is set
+ * up.
+ *
+ * @param listener An optional pointer to a subclass of
+ * ConnectionListener to receive notifications of events related to
+ * this connection.
+ */
+ QMF_EXTERN Connection(const ConnectionSettings& settings,
+ const ConnectionListener* listener = 0);
+
+ /**
+ * Destroys a connection, causing the connection to be closed.
+ */
+ QMF_EXTERN ~Connection();
+
+ /**
+ * Set the administrative state of the connection (enabled or disabled).
+ *
+ * @param enabled True => enable connection, False => disable connection
+ */
+ QMF_EXTERN void setAdminState(bool enabled);
+
+ /**
+ * Return the current operational state of the connection (up or down).
+ *
+ * @return the current connection state.
+ */
+ QMF_EXTERN ConnectionState getOperState() const;
+
+ /**
+ * Get the error message from the last failure to connect.
+ *
+ * @return Null-terminated string containing the error message.
+ */
+ QMF_EXTERN const char* getLastError() const;
+
+ private:
+ friend class AgentImpl;
+ friend class ConsoleImpl;
+ ConnectionImpl* impl;
+ };
+
+}
+
+#endif
diff --git a/qpid/cpp/include/qmf/ConnectionSettings.h b/qpid/cpp/include/qmf/ConnectionSettings.h
new file mode 100644
index 0000000000..9bd6922a56
--- /dev/null
+++ b/qpid/cpp/include/qmf/ConnectionSettings.h
@@ -0,0 +1,143 @@
+#ifndef _QmfConnectionSettings_
+#define _QmfConnectionSettings_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/QmfImportExport.h"
+#include "qpid/sys/IntegerTypes.h"
+
+namespace qmf {
+
+ class ConnectionSettingsImpl;
+ class Value;
+
+ /**
+ * Settings for AMQP connections to the broker.
+ *
+ * \ingroup qmfapi
+ */
+ class ConnectionSettings {
+ public:
+
+ ConnectionSettings(const ConnectionSettings& copy);
+
+ /**
+ * Create a set of default connection settings.
+ *
+ * If no further attributes are set, the settings will cause a connection to be made to
+ * the default broker (on localhost or at a host/port supplied by service discovery) and
+ * authentication will be the best-available (GSSAPI/Kerberos, Anonymous, Plain with prompts
+ * for username and password).
+ */
+ QMF_EXTERN ConnectionSettings();
+
+ /**
+ * Create a set of connection settings by URL.
+ *
+ * @param url Universal resource locator describing the broker address and additional attributes.
+ *
+ * The URL is of the form:
+ * amqp[s]://host[:port][?key=value[&key=value]*]
+ *
+ * For example:
+ * amqp://localhost
+ * amqp://broker?transport=rdma&authmech=GSSAPI&authservice=qpidd
+ * amqps://broker?authmech=PLAIN&authuser=guest&authpass=guest
+ */
+ QMF_EXTERN ConnectionSettings(const char* url);
+
+ /**
+ * Destroy the connection settings object.
+ */
+ QMF_EXTERN ~ConnectionSettings();
+
+ /**
+ * Set an attribute to control connection setup.
+ *
+ * @param key A null-terminated string that is an attribute name.
+ *
+ * @param value Reference to a value to be stored as the attribute. The type of the value
+ * is specific to the key.
+ */
+ QMF_EXTERN void setAttr(const char* key, const Value& value);
+
+ /**
+ * Get the value of an attribute.
+ *
+ * @param key A null-terminated attribute name.
+ *
+ * @return The value associated with the attribute name.
+ */
+ QMF_EXTERN Value getAttr(const char* key) const;
+
+ /**
+ * Get the attribute string (the portion of the URL following the '?') for the settings.
+ *
+ * @return A pointer to the attribute string. If the content of this string needs to be
+ * available beyond the scope of the calling function, it should be copied. The
+ * returned pointer may become invalid if the set of attributes is changed.
+ */
+ QMF_EXTERN const char* getAttrString() const;
+
+ /**
+ * Shortcuts for setting the transport for the connection.
+ *
+ * @param port The port value for the connection address.
+ */
+ QMF_EXTERN void transportTcp(uint16_t port = 5672);
+ QMF_EXTERN void transportSsl(uint16_t port = 5671);
+ QMF_EXTERN void transportRdma(uint16_t port = 5672);
+
+ /**
+ * Shortcuts for setting authentication mechanisms.
+ *
+ * @param username Null-terminated authentication user name.
+ *
+ * @param password Null-terminated authentication password.
+ *
+ * @param serviceName Null-terminated GSSAPI service name (Kerberos service principal)
+ *
+ * @param minSsf Minimum security factor for connections. 0 = encryption not required.
+ *
+ * @param maxSsf Maximum security factor for connections. 0 = encryption not permitted.
+ */
+ QMF_EXTERN void authAnonymous(const char* username = 0);
+ QMF_EXTERN void authPlain(const char* username = 0, const char* password = 0);
+ QMF_EXTERN void authGssapi(const char* serviceName, uint32_t minSsf = 0, uint32_t maxSsf = 256);
+
+ /**
+ * Shortcut for setting connection retry attributes.
+ *
+ * @param delayMin Minimum delay (in seconds) between connection attempts.
+ *
+ * @param delaxMax Maximum delay (in seconds) between connection attempts.
+ *
+ * @param delayFactor Factor to multiply the delay by between failed connection attempts.
+ */
+ QMF_EXTERN void setRetry(int delayMin = 1, int delayMax = 128, int delayFactor = 2);
+
+ private:
+ friend class ResilientConnectionImpl;
+ ConnectionSettingsImpl* impl;
+ };
+
+}
+
+#endif
diff --git a/qpid/cpp/include/qmf/QmfImportExport.h b/qpid/cpp/include/qmf/QmfImportExport.h
new file mode 100644
index 0000000000..8353a3cc16
--- /dev/null
+++ b/qpid/cpp/include/qmf/QmfImportExport.h
@@ -0,0 +1,33 @@
+#ifndef QMF_IMPORT_EXPORT_H
+#define QMF_IMPORT_EXPORT_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#if defined(WIN32) && !defined(QPID_DECLARE_STATIC)
+# if defined(QMF_EXPORT) || defined (qmf_EXPORTS)
+# define QMF_EXTERN __declspec(dllexport)
+# else
+# define QMF_EXTERN __declspec(dllimport)
+# endif
+#else
+# define QMF_EXTERN
+#endif
+
+#endif
diff --git a/qpid/cpp/include/qpid/client/Connection.h b/qpid/cpp/include/qpid/client/Connection.h
index b7b967d232..0f5999cdcc 100644
--- a/qpid/cpp/include/qpid/client/Connection.h
+++ b/qpid/cpp/include/qpid/client/Connection.h
@@ -43,7 +43,21 @@ class ConnectionImpl;
*
* \ingroup clientapi
*
+ * Some methods use an AMQP 0-10 URL to specify connection parameters.
+ * This is defined in the AMQP 0-10 specification (http://jira.amqp.org/confluence/display/AMQP/AMQP+Specification).
+ *
+ * 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 Connection
{
framing::ProtocolVersion version;
diff --git a/qpid/cpp/include/qpid/client/amqp0_10/Codecs.h b/qpid/cpp/include/qpid/client/amqp0_10/Codecs.h
new file mode 100644
index 0000000000..5ef0b9fffe
--- /dev/null
+++ b/qpid/cpp/include/qpid/client/amqp0_10/Codecs.h
@@ -0,0 +1,61 @@
+#ifndef QPID_CLIENT_AMQP0_10_CODECS_H
+#define QPID_CLIENT_AMQP0_10_CODECS_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/Codec.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+
+/**
+ * Codec for encoding/decoding a map of Variants using the AMQP 0-10
+ * map encoding.
+ */
+class MapCodec : public qpid::messaging::Codec
+{
+ public:
+ void encode(const qpid::messaging::Variant&, std::string&);
+ void decode(const std::string&, qpid::messaging::Variant&);
+
+ static const std::string contentType;
+ private:
+};
+
+/**
+ * Codec for encoding/decoding a list of Variants using the AMQP 0-10
+ * list encoding.
+ */
+class ListCodec : public qpid::messaging::Codec
+{
+ public:
+ void encode(const qpid::messaging::Variant&, std::string&);
+ void decode(const std::string&, qpid::messaging::Variant&);
+
+ static const std::string contentType;
+ private:
+};
+
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_CODECS_H*/
diff --git a/qpid/cpp/include/qpid/framing/FieldTable.h b/qpid/cpp/include/qpid/framing/FieldTable.h
index a3a5c8a4ee..b2331cd4e1 100644
--- a/qpid/cpp/include/qpid/framing/FieldTable.h
+++ b/qpid/cpp/include/qpid/framing/FieldTable.h
@@ -51,6 +51,8 @@ class FieldTable
typedef boost::shared_ptr<FieldValue> ValuePtr;
typedef std::map<std::string, ValuePtr> ValueMap;
typedef ValueMap::iterator iterator;
+ typedef ValueMap::const_reference const_reference;
+ typedef ValueMap::value_type value_type;
QPID_COMMON_EXTERN FieldTable() {};
QPID_COMMON_EXTERN FieldTable(const FieldTable& ft);
@@ -97,12 +99,16 @@ class FieldTable
QPID_COMMON_EXTERN 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); }
+ ValueMap::iterator begin() { return values.begin(); }
+ ValueMap::iterator end() { return values.end(); }
+ ValueMap::iterator find(const std::string& s) { return values.find(s); }
+
std::pair <ValueMap::iterator, bool> insert(const ValueMap::value_type&);
+ ValueMap::iterator insert(ValueMap::iterator, const ValueMap::value_type&);
void clear() { values.clear(); }
// ### Hack Alert
diff --git a/qpid/cpp/include/qpid/framing/FieldValue.h b/qpid/cpp/include/qpid/framing/FieldValue.h
index 97fc56d606..ce4d06d2c8 100644
--- a/qpid/cpp/include/qpid/framing/FieldValue.h
+++ b/qpid/cpp/include/qpid/framing/FieldValue.h
@@ -36,7 +36,6 @@
namespace qpid {
namespace framing {
-//class Array;
/**
* Exception that is the base exception for all field table errors.
*
@@ -53,6 +52,8 @@ struct InvalidConversionException : public FieldValueException {
InvalidConversionException() {}
};
+class List;
+
/**
* Value that can appear in an AMQP field table
*
@@ -82,7 +83,7 @@ class FieldValue {
FieldValue(): data(0) {};
// Default assignment operator is fine
void setType(uint8_t type);
- uint8_t getType();
+ QPID_COMMON_EXTERN uint8_t getType();
Data& getData() { return *data; }
uint32_t encodedSize() const { return 1 + data->encodedSize(); };
bool empty() const { return data.get() == 0; }
@@ -96,12 +97,19 @@ class FieldValue {
template <typename T> bool convertsTo() const { return false; }
template <typename T> T get() const { throw InvalidConversionException(); }
+ template <class T, int W> T getIntegerValue() const;
+ template <class T, int W> T getFloatingPointValue() const;
+ template <class T> bool get(T&) const;
+
protected:
FieldValue(uint8_t t, Data* d): typeOctet(t), data(d) {}
+ QPID_COMMON_EXTERN static uint8_t* convertIfRequired(uint8_t* const octets, int width);
+
private:
uint8_t typeOctet;
std::auto_ptr<Data> data;
+
};
template <>
@@ -165,10 +173,52 @@ class FixedWidthValue : public FieldValue::Data {
return v;
}
uint8_t* rawOctets() { return octets; }
+ uint8_t* rawOctets() const { return octets; }
void print(std::ostream& o) const { o << "F" << width << ":"; };
};
+template <class T, int W>
+inline T FieldValue::getIntegerValue() const
+{
+ FixedWidthValue<W>* const fwv = dynamic_cast< FixedWidthValue<W>* const>(data.get());
+ if (fwv) {
+ uint8_t* octets = fwv->rawOctets();
+ T v = 0;
+ for (int i = 0; i < W-1; ++i) {
+ v |= octets[i]; v <<= 8;
+ }
+ v |= octets[W-1];
+ return v;
+ } else {
+ throw InvalidConversionException();
+ }
+}
+
+template <class T, int W>
+inline T FieldValue::getFloatingPointValue() const {
+ FixedWidthValue<W>* const fwv = dynamic_cast< FixedWidthValue<W>* const>(data.get());
+ if (fwv) {
+ T value;
+ uint8_t* const octets = convertIfRequired(fwv->rawOctets(), W);
+ uint8_t* const target = reinterpret_cast<uint8_t*>(&value);
+ for (uint i = 0; i < W; ++i) target[i] = octets[i];
+ return value;
+ } else {
+ throw InvalidConversionException();
+ }
+}
+
+template <>
+inline float FieldValue::get<float>() const {
+ return getFloatingPointValue<float, 4>();
+}
+
+template <>
+inline double FieldValue::get<double>() const {
+ return getFloatingPointValue<double, 8>();
+}
+
template <>
class FixedWidthValue<0> : public FieldValue::Data {
public:
@@ -243,6 +293,27 @@ class EncodedValue : public FieldValue::Data {
void print(std::ostream& o) const { o << "[" << value << "]"; };
};
+/**
+ * Accessor that can be used to get values of type FieldTable, Array
+ * and List.
+ */
+template <class T>
+inline bool FieldValue::get(T& t) const
+{
+ const EncodedValue<T>* v = dynamic_cast< EncodedValue<T>* >(data.get());
+ if (v != 0) {
+ t = v->getValue();
+ return true;
+ } else {
+ try {
+ t = get<T>();
+ return true;
+ } catch (const InvalidConversionException&) {
+ return false;
+ }
+ }
+}
+
class Str8Value : public FieldValue {
public:
QPID_COMMON_EXTERN Str8Value(const std::string& v);
@@ -294,6 +365,7 @@ class Unsigned64Value : public FieldValue {
class FieldTableValue : public FieldValue {
public:
+ typedef FieldTable ValueType;
QPID_COMMON_EXTERN FieldTableValue(const FieldTable&);
};
@@ -302,6 +374,49 @@ class ArrayValue : public FieldValue {
QPID_COMMON_EXTERN ArrayValue(const Array&);
};
+class VoidValue : public FieldValue {
+ public:
+ QPID_COMMON_EXTERN VoidValue();
+};
+
+class BoolValue : public FieldValue {
+ public:
+ QPID_COMMON_EXTERN BoolValue(bool);
+};
+
+class Unsigned8Value : public FieldValue {
+ public:
+ QPID_COMMON_EXTERN Unsigned8Value(uint8_t);
+};
+
+class Unsigned16Value : public FieldValue {
+ public:
+ QPID_COMMON_EXTERN Unsigned16Value(uint16_t);
+};
+
+class Unsigned32Value : public FieldValue {
+ public:
+ QPID_COMMON_EXTERN Unsigned32Value(uint32_t);
+};
+
+class Integer8Value : public FieldValue {
+ public:
+ QPID_COMMON_EXTERN Integer8Value(int8_t);
+};
+
+class Integer16Value : public FieldValue {
+ public:
+ QPID_COMMON_EXTERN Integer16Value(int16_t);
+};
+
+typedef IntegerValue Integer32Value;
+
+class ListValue : public FieldValue {
+ public:
+ typedef List ValueType;
+ QPID_COMMON_EXTERN ListValue(const List&);
+};
+
template <class T>
bool getEncodedValue(FieldTable::ValuePtr vptr, T& value)
{
@@ -315,7 +430,6 @@ bool getEncodedValue(FieldTable::ValuePtr vptr, T& value)
return false;
}
-
}} // qpid::framing
#endif
diff --git a/qpid/cpp/include/qpid/framing/List.h b/qpid/cpp/include/qpid/framing/List.h
new file mode 100644
index 0000000000..cb1129ebf8
--- /dev/null
+++ b/qpid/cpp/include/qpid/framing/List.h
@@ -0,0 +1,76 @@
+#ifndef QPID_FRAMING_LIST_H
+#define QPID_FRAMING_LIST_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/CommonImportExport.h"
+#include "qpid/framing/amqp_types.h"
+#include <iostream>
+#include <list>
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace framing {
+
+class Buffer;
+class FieldValue;
+
+/**
+ * Representation of an AMQP 0-10 list
+ */
+class List
+{
+ public:
+ typedef boost::shared_ptr<FieldValue> ValuePtr;
+ typedef std::list<ValuePtr> Values;
+ typedef Values::const_iterator const_iterator;
+ typedef Values::iterator iterator;
+ typedef Values::const_reference const_reference;
+
+ QPID_COMMON_EXTERN uint32_t encodedSize() const;
+ QPID_COMMON_EXTERN void encode(Buffer& buffer) const;
+ QPID_COMMON_EXTERN void decode(Buffer& buffer);
+
+ QPID_COMMON_EXTERN bool operator==(const List& other) const;
+
+ // std collection interface.
+ QPID_COMMON_EXTERN const_iterator begin() const { return values.begin(); }
+ QPID_COMMON_EXTERN const_iterator end() const { return values.end(); }
+ QPID_COMMON_EXTERN iterator begin() { return values.begin(); }
+ QPID_COMMON_EXTERN iterator end(){ return values.end(); }
+
+ QPID_COMMON_EXTERN ValuePtr front() const { return values.front(); }
+ QPID_COMMON_EXTERN ValuePtr back() const { return values.back(); }
+ QPID_COMMON_EXTERN size_t size() const { return values.size(); }
+
+ QPID_COMMON_EXTERN iterator insert(iterator i, ValuePtr value) { return values.insert(i, value); }
+ QPID_COMMON_EXTERN void erase(iterator i) { values.erase(i); }
+ QPID_COMMON_EXTERN void push_back(ValuePtr value) { values.insert(end(), value); }
+ QPID_COMMON_EXTERN void pop_back() { values.pop_back(); }
+
+ private:
+ Values values;
+
+ friend QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream& out, const List& list);
+};
+}} // namespace qpid::framing
+
+#endif /*!QPID_FRAMING_LIST_H*/
diff --git a/qpid/cpp/include/qpid/messaging/Address.h b/qpid/cpp/include/qpid/messaging/Address.h
new file mode 100644
index 0000000000..e66c52f4c2
--- /dev/null
+++ b/qpid/cpp/include/qpid/messaging/Address.h
@@ -0,0 +1,58 @@
+#ifndef QPID_MESSAGING_ADDRESS_H
+#define QPID_MESSAGING_ADDRESS_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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/ClientImportExport.h"
+#include <ostream>
+
+namespace qpid {
+namespace client {
+}
+
+namespace messaging {
+
+/**
+ * Represents an address to which messages can be sent and from which
+ * messages can be received. Often a simple name is sufficient for
+ * this. However this struct allows the type of address to be
+ * specified allowing more sophisticated treatment if necessary.
+ */
+struct Address
+{
+ std::string value;
+ std::string type;
+
+ QPID_CLIENT_EXTERN Address();
+ QPID_CLIENT_EXTERN Address(const std::string& address);
+ QPID_CLIENT_EXTERN Address(const std::string& address, const std::string& type);
+ QPID_CLIENT_EXTERN operator const std::string&() const;
+ QPID_CLIENT_EXTERN const std::string& toStr() const;
+ QPID_CLIENT_EXTERN operator bool() const;
+ QPID_CLIENT_EXTERN bool operator !() const;
+};
+
+QPID_CLIENT_EXTERN std::ostream& operator<<(std::ostream& out, const Address& address);
+
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_ADDRESS_H*/
diff --git a/qpid/cpp/include/qpid/messaging/Codec.h b/qpid/cpp/include/qpid/messaging/Codec.h
new file mode 100644
index 0000000000..bacec5c786
--- /dev/null
+++ b/qpid/cpp/include/qpid/messaging/Codec.h
@@ -0,0 +1,44 @@
+#ifndef QPID_MESSAGING_CODEC_H
+#define QPID_MESSAGING_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 <string>
+#include "qpid/client/ClientImportExport.h"
+
+namespace qpid {
+namespace messaging {
+
+class Variant;
+/**
+ *
+ */
+class Codec
+{
+ public:
+ QPID_CLIENT_EXTERN virtual ~Codec() {}
+ virtual void encode(const Variant&, std::string&) = 0;
+ virtual void decode(const std::string&, Variant&) = 0;
+ private:
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_CODEC_H*/
diff --git a/qpid/cpp/include/qpid/messaging/Connection.h b/qpid/cpp/include/qpid/messaging/Connection.h
new file mode 100644
index 0000000000..5517e45af9
--- /dev/null
+++ b/qpid/cpp/include/qpid/messaging/Connection.h
@@ -0,0 +1,67 @@
+#ifndef QPID_MESSAGING_CONNECTION_H
+#define QPID_MESSAGING_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 <string>
+#include "qpid/client/ClientImportExport.h"
+#include "qpid/client/Handle.h"
+#include "qpid/messaging/Variant.h"
+
+namespace qpid {
+namespace client {
+
+template <class> class PrivateImplRef;
+
+}
+
+namespace messaging {
+
+class ConnectionImpl;
+class Session;
+
+class Connection : public qpid::client::Handle<ConnectionImpl>
+{
+ public:
+ static Connection open(const std::string& url, const Variant::Map& options = Variant::Map());
+
+ QPID_CLIENT_EXTERN Connection(ConnectionImpl* impl = 0);
+ QPID_CLIENT_EXTERN Connection(const Connection&);
+ QPID_CLIENT_EXTERN ~Connection();
+ QPID_CLIENT_EXTERN Connection& operator=(const Connection&);
+ QPID_CLIENT_EXTERN void close();
+ QPID_CLIENT_EXTERN Session newSession();
+ private:
+ friend class qpid::client::PrivateImplRef<Connection>;
+
+};
+
+struct InvalidOptionString : public qpid::Exception
+{
+ InvalidOptionString(const std::string& msg);
+};
+
+QPID_CLIENT_EXTERN void parseOptionString(const std::string&, Variant::Map&);
+QPID_CLIENT_EXTERN Variant::Map parseOptionString(const std::string&);
+
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_CONNECTION_H*/
diff --git a/qpid/cpp/include/qpid/messaging/Filter.h b/qpid/cpp/include/qpid/messaging/Filter.h
new file mode 100644
index 0000000000..5cd844cf73
--- /dev/null
+++ b/qpid/cpp/include/qpid/messaging/Filter.h
@@ -0,0 +1,48 @@
+#ifndef QPID_MESSAGING_FILTER_H
+#define QPID_MESSAGING_FILTER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <string>
+#include <vector>
+#include "qpid/client/ClientImportExport.h"
+
+namespace qpid {
+namespace client {
+}
+
+namespace messaging {
+
+struct Filter
+{
+ std::string type;
+ std::vector<std::string> patterns;
+
+ QPID_CLIENT_EXTERN Filter(std::string type, std::string pattern);
+ QPID_CLIENT_EXTERN Filter(std::string type, std::string pattern1, std::string pattern2);
+
+ static QPID_CLIENT_EXTERN const std::string WILDCARD;
+ static QPID_CLIENT_EXTERN const std::string EXACT_MATCH;
+};
+
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_FILTER_H*/
diff --git a/qpid/cpp/include/qpid/messaging/Message.h b/qpid/cpp/include/qpid/messaging/Message.h
new file mode 100644
index 0000000000..39edae3637
--- /dev/null
+++ b/qpid/cpp/include/qpid/messaging/Message.h
@@ -0,0 +1,90 @@
+#ifndef QPID_MESSAGING_MESSAGE_H
+#define QPID_MESSAGING_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/messaging/Variant.h"
+#include "qpid/messaging/MessageContent.h"
+#include "qpid/client/ClientImportExport.h"
+
+namespace qpid {
+namespace client {
+}
+
+namespace messaging {
+
+class Address;
+class Codec;
+class MessageImpl;
+
+/**
+ * Representation of a message.
+ */
+class Message
+{
+ public:
+ QPID_CLIENT_EXTERN Message(const std::string& bytes = std::string());
+ QPID_CLIENT_EXTERN Message(const char*, size_t);
+ QPID_CLIENT_EXTERN Message(const Message&);
+ QPID_CLIENT_EXTERN ~Message();
+
+ QPID_CLIENT_EXTERN Message& operator=(const Message&);
+
+ QPID_CLIENT_EXTERN void setReplyTo(const Address&);
+ QPID_CLIENT_EXTERN const Address& getReplyTo() const;
+
+ QPID_CLIENT_EXTERN void setSubject(const std::string&);
+ QPID_CLIENT_EXTERN const std::string& getSubject() const;
+
+ QPID_CLIENT_EXTERN void setContentType(const std::string&);
+ QPID_CLIENT_EXTERN const std::string& getContentType() const;
+
+ QPID_CLIENT_EXTERN const VariantMap& getHeaders() const;
+ QPID_CLIENT_EXTERN VariantMap& getHeaders();
+
+ QPID_CLIENT_EXTERN const std::string& getBytes() const;
+ QPID_CLIENT_EXTERN std::string& getBytes();
+ QPID_CLIENT_EXTERN void setBytes(const std::string&);
+ QPID_CLIENT_EXTERN void setBytes(const char* chars, size_t count);
+ QPID_CLIENT_EXTERN const char* getRawContent() const;
+ QPID_CLIENT_EXTERN size_t getContentSize() const;
+
+
+ QPID_CLIENT_EXTERN MessageContent& getContent();
+ QPID_CLIENT_EXTERN const MessageContent& getContent() const;
+ QPID_CLIENT_EXTERN void setContent(const std::string& s);
+ QPID_CLIENT_EXTERN void setContent(const Variant::Map&);
+ QPID_CLIENT_EXTERN void setContent(const Variant::List&);
+
+ QPID_CLIENT_EXTERN void encode(Codec&);
+ QPID_CLIENT_EXTERN void decode(Codec&);
+
+ //TODO: move this out of the public API
+ QPID_CLIENT_EXTERN void setInternalId(void*);
+ QPID_CLIENT_EXTERN void* getInternalId();
+ private:
+ MessageImpl* impl;
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_MESSAGE_H*/
diff --git a/qpid/cpp/include/qpid/messaging/MessageContent.h b/qpid/cpp/include/qpid/messaging/MessageContent.h
new file mode 100644
index 0000000000..7c3a636c07
--- /dev/null
+++ b/qpid/cpp/include/qpid/messaging/MessageContent.h
@@ -0,0 +1,90 @@
+#ifndef QPID_MESSAGING_MESSAGECONTENT_H
+#define QPID_MESSAGING_MESSAGECONTENT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/messaging/Variant.h"
+#include <string>
+#include "qpid/client/ClientImportExport.h"
+
+namespace qpid {
+namespace messaging {
+
+/**
+ *
+ */
+class MessageContent
+{
+ public:
+ QPID_CLIENT_EXTERN virtual ~MessageContent() {}
+
+ virtual const std::string& asString() const = 0;
+ virtual std::string& asString() = 0;
+
+ virtual const char* asChars() const = 0;
+ virtual size_t size() const = 0;
+
+ virtual const Variant::Map& asMap() const = 0;
+ virtual Variant::Map& asMap() = 0;
+ virtual bool isMap() const = 0;
+
+ virtual const Variant::List& asList() const = 0;
+ virtual Variant::List& asList() = 0;
+ virtual bool isList() const = 0;
+
+ virtual void clear() = 0;
+
+ virtual Variant& operator[](const std::string&) = 0;
+
+
+ virtual std::ostream& print(std::ostream& out) const = 0;
+
+
+ //operator<< for variety of types... (is this a good idea?)
+ virtual MessageContent& operator<<(const std::string&) = 0;
+ virtual MessageContent& operator<<(const char*) = 0;
+ virtual MessageContent& operator<<(bool) = 0;
+ virtual MessageContent& operator<<(int8_t) = 0;
+ virtual MessageContent& operator<<(int16_t) = 0;
+ virtual MessageContent& operator<<(int32_t) = 0;
+ virtual MessageContent& operator<<(int64_t) = 0;
+ virtual MessageContent& operator<<(uint8_t) = 0;
+ virtual MessageContent& operator<<(uint16_t) = 0;
+ virtual MessageContent& operator<<(uint32_t) = 0;
+ virtual MessageContent& operator<<(uint64_t) = 0;
+ virtual MessageContent& operator<<(double) = 0;
+ virtual MessageContent& operator<<(float) = 0;
+
+ //assignment from string, map and list
+ virtual MessageContent& operator=(const std::string&) = 0;
+ virtual MessageContent& operator=(const char*) = 0;
+ virtual MessageContent& operator=(const Variant::Map&) = 0;
+ virtual MessageContent& operator=(const Variant::List&) = 0;
+
+ private:
+};
+
+QPID_CLIENT_EXTERN std::ostream& operator<<(std::ostream& out, const MessageContent& content);
+
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_MESSAGECONTENT_H*/
diff --git a/qpid/cpp/include/qpid/messaging/MessageListener.h b/qpid/cpp/include/qpid/messaging/MessageListener.h
new file mode 100644
index 0000000000..72811e7b9c
--- /dev/null
+++ b/qpid/cpp/include/qpid/messaging/MessageListener.h
@@ -0,0 +1,49 @@
+#ifndef QPID_MESSAGING_MESSAGELISTENER_H
+#define QPID_MESSAGING_MESSAGELISTENER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/ClientImportExport.h"
+
+namespace qpid {
+namespace messaging {
+
+class Message;
+
+/**
+ * To use a push style interface for receiving messages, applications
+ * provide implementations of this interface and pass an implementing
+ * instance to MessageSource::subscribe().
+ *
+ * Messages arriving for that subscription will then be passed to the
+ * implementation via the received() method.
+ */
+class MessageListener
+{
+ public:
+ QPID_CLIENT_EXTERN virtual ~MessageListener() {}
+ virtual void received(Message&) = 0;
+ private:
+};
+
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_MESSAGELISTENER_H*/
diff --git a/qpid/cpp/include/qpid/messaging/Receiver.h b/qpid/cpp/include/qpid/messaging/Receiver.h
new file mode 100644
index 0000000000..e51e1093d1
--- /dev/null
+++ b/qpid/cpp/include/qpid/messaging/Receiver.h
@@ -0,0 +1,115 @@
+#ifndef QPID_MESSAGING_RECEIVER_H
+#define QPID_MESSAGING_RECEIVER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/client/ClientImportExport.h"
+#include "qpid/client/Handle.h"
+#include "qpid/sys/Time.h"
+
+namespace qpid {
+namespace client {
+
+template <class> class PrivateImplRef;
+
+}
+
+namespace messaging {
+
+class Message;
+class MessageListener;
+class ReceiverImpl;
+
+/**
+ * A pull style interface for message retrieval.
+ */
+class Receiver : public qpid::client::Handle<ReceiverImpl>
+{
+ public:
+ struct NoMessageAvailable : qpid::Exception {};
+
+ QPID_CLIENT_EXTERN Receiver(ReceiverImpl* impl = 0);
+ QPID_CLIENT_EXTERN Receiver(const Receiver&);
+ QPID_CLIENT_EXTERN ~Receiver();
+ QPID_CLIENT_EXTERN Receiver& operator=(const Receiver&);
+ /**
+ * Retrieves a message from this receivers local queue, or waits
+ * for upto the specified timeout for a message to become
+ * available. Returns false if there is no message to give after
+ * waiting for the specified timeout.
+ */
+ QPID_CLIENT_EXTERN bool get(Message& message, qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE);
+ /**
+ * Retrieves a message from this receivers local queue, or waits
+ * for upto the specified timeout for a message to become
+ * available. Throws NoMessageAvailable if there is no
+ * message to give after waiting for the specified timeout.
+ */
+ QPID_CLIENT_EXTERN Message get(qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE);
+ /**
+ * Retrieves a message for this receivers subscription or waits
+ * for upto the specified timeout for one to become
+ * available. Unlike get() this method will check with the server
+ * that there is no message for the subscription this receiver is
+ * serving before returning false.
+ */
+ QPID_CLIENT_EXTERN bool fetch(Message& message, qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE);
+ /**
+ * Retrieves a message for this receivers subscription or waits
+ * for upto the specified timeout for one to become
+ * available. Unlike get() this method will check with the server
+ * that there is no message for the subscription this receiver is
+ * serving before throwing an exception.
+ */
+ QPID_CLIENT_EXTERN Message fetch(qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE);
+
+ /**
+ * Enables the message flow for this receiver
+ */
+ QPID_CLIENT_EXTERN void start();
+ /**
+ * Stops the message flow for this receiver (without actually
+ * cancelling the subscription).
+ */
+ QPID_CLIENT_EXTERN void stop();
+ /**
+ * Sets the capacity for the receiver. The capacity determines how
+ * many incoming messages can be held in the receiver before being
+ * requested by a client via fetch() (or pushed to a listener).
+ */
+ QPID_CLIENT_EXTERN void setCapacity(uint32_t);
+
+ /**
+ * Cancels this receiver
+ */
+ QPID_CLIENT_EXTERN void cancel();
+
+ /**
+ * Set a message listener for receiving messages asynchronously.
+ */
+ QPID_CLIENT_EXTERN void setListener(MessageListener* listener);
+ private:
+ friend class qpid::client::PrivateImplRef<Receiver>;
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_RECEIVER_H*/
diff --git a/qpid/cpp/include/qpid/messaging/Sender.h b/qpid/cpp/include/qpid/messaging/Sender.h
new file mode 100644
index 0000000000..657c4b8cfe
--- /dev/null
+++ b/qpid/cpp/include/qpid/messaging/Sender.h
@@ -0,0 +1,57 @@
+#ifndef QPID_MESSAGING_SENDER_H
+#define QPID_MESSAGING_SENDER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/ClientImportExport.h"
+#include "qpid/client/Handle.h"
+
+namespace qpid {
+namespace client {
+
+template <class> class PrivateImplRef;
+
+}
+
+namespace messaging {
+
+class Message;
+class SenderImpl;
+
+/**
+ * Interface through which messages are sent.
+ */
+class Sender : public qpid::client::Handle<SenderImpl>
+{
+ public:
+ QPID_CLIENT_EXTERN Sender(SenderImpl* impl = 0);
+ QPID_CLIENT_EXTERN Sender(const Sender&);
+ QPID_CLIENT_EXTERN ~Sender();
+ QPID_CLIENT_EXTERN Sender& operator=(const Sender&);
+
+ QPID_CLIENT_EXTERN void send(Message& message);
+ QPID_CLIENT_EXTERN void cancel();
+ private:
+ friend class qpid::client::PrivateImplRef<Sender>;
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_SENDER_H*/
diff --git a/qpid/cpp/include/qpid/messaging/Session.h b/qpid/cpp/include/qpid/messaging/Session.h
new file mode 100644
index 0000000000..3a354c009f
--- /dev/null
+++ b/qpid/cpp/include/qpid/messaging/Session.h
@@ -0,0 +1,99 @@
+#ifndef QPID_MESSAGING_SESSION_H
+#define QPID_MESSAGING_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/ClientImportExport.h"
+#include "qpid/client/Handle.h"
+#include "qpid/sys/Time.h"
+#include "Variant.h"
+
+namespace qpid {
+namespace client {
+
+template <class> class PrivateImplRef;
+
+}
+
+namespace messaging {
+
+class Address;
+class Filter;
+class Message;
+class MessageListener;
+class Sender;
+class Receiver;
+class SessionImpl;
+class Subscription;
+
+/**
+ * A session represents a distinct 'conversation' which can involve
+ * sending and receiving messages from different sources and sinks.
+ */
+class Session : public qpid::client::Handle<SessionImpl>
+{
+ public:
+ QPID_CLIENT_EXTERN Session(SessionImpl* impl = 0);
+ QPID_CLIENT_EXTERN Session(const Session&);
+ QPID_CLIENT_EXTERN ~Session();
+ QPID_CLIENT_EXTERN Session& operator=(const Session&);
+
+ QPID_CLIENT_EXTERN void close();
+
+ QPID_CLIENT_EXTERN void commit();
+ QPID_CLIENT_EXTERN void rollback();
+
+ /**
+ * Acknowledges all outstanding messages that have been received
+ * by the application on this session.
+ */
+ QPID_CLIENT_EXTERN void acknowledge();
+ /**
+ * Rejects the specified message. This will prevent the message
+ * being redelivered.
+ */
+ QPID_CLIENT_EXTERN void reject(Message&);
+
+ QPID_CLIENT_EXTERN void sync();
+ QPID_CLIENT_EXTERN void flush();
+
+ QPID_CLIENT_EXTERN bool fetch(Message& message, qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE);
+ QPID_CLIENT_EXTERN Message fetch(qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE);
+ QPID_CLIENT_EXTERN bool dispatch(qpid::sys::Duration timeout=qpid::sys::TIME_INFINITE);
+
+ QPID_CLIENT_EXTERN Sender createSender(const Address& address, const VariantMap& options = VariantMap());
+ QPID_CLIENT_EXTERN Sender createSender(const std::string& address, const VariantMap& options = VariantMap());
+
+ QPID_CLIENT_EXTERN Receiver createReceiver(const Address& address, const VariantMap& options = VariantMap());
+ QPID_CLIENT_EXTERN Receiver createReceiver(const Address& address, const Filter& filter, const VariantMap& options = VariantMap());
+ QPID_CLIENT_EXTERN Receiver createReceiver(const std::string& address, const VariantMap& options = VariantMap());
+ QPID_CLIENT_EXTERN Receiver createReceiver(const std::string& address, const Filter& filter, const VariantMap& options = VariantMap());
+
+ QPID_CLIENT_EXTERN Address createTempQueue(const std::string& baseName = std::string());
+
+ QPID_CLIENT_EXTERN void* getLastConfirmedSent();
+ QPID_CLIENT_EXTERN void* getLastConfirmedAcknowledged();
+ private:
+ friend class qpid::client::PrivateImplRef<Session>;
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_SESSION_H*/
diff --git a/qpid/cpp/include/qpid/messaging/Variant.h b/qpid/cpp/include/qpid/messaging/Variant.h
new file mode 100644
index 0000000000..ac000244c2
--- /dev/null
+++ b/qpid/cpp/include/qpid/messaging/Variant.h
@@ -0,0 +1,167 @@
+#ifndef QPID_MESSAGING_VARIANT_H
+#define QPID_MESSAGING_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.
+ *
+ */
+#include <list>
+#include <map>
+#include <ostream>
+#include <string>
+#include "qpid/Exception.h"
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/client/ClientImportExport.h"
+
+namespace qpid {
+namespace client {
+}
+
+namespace messaging {
+
+/**
+ * Thrown when an illegal conversion of a variant is attempted.
+ */
+struct InvalidConversion : public qpid::Exception
+{
+ InvalidConversion(const std::string& msg);
+};
+
+enum VariantType {
+ VOID = 0,
+ BOOL,
+ UINT8,
+ UINT16,
+ UINT32,
+ UINT64,
+ INT8,
+ INT16,
+ INT32,
+ INT64,
+ FLOAT,
+ DOUBLE,
+ STRING,
+ MAP,
+ LIST
+};
+
+class VariantImpl;
+
+/**
+ * Represents a value of variable type.
+ */
+class Variant
+{
+ public:
+ typedef std::map<std::string, Variant> Map;
+ typedef std::list<Variant> List;
+
+ QPID_CLIENT_EXTERN Variant();
+ QPID_CLIENT_EXTERN Variant(bool);
+ QPID_CLIENT_EXTERN Variant(uint8_t);
+ QPID_CLIENT_EXTERN Variant(uint16_t);
+ QPID_CLIENT_EXTERN Variant(uint32_t);
+ QPID_CLIENT_EXTERN Variant(uint64_t);
+ QPID_CLIENT_EXTERN Variant(int8_t);
+ QPID_CLIENT_EXTERN Variant(int16_t);
+ QPID_CLIENT_EXTERN Variant(int32_t);
+ QPID_CLIENT_EXTERN Variant(int64_t);
+ QPID_CLIENT_EXTERN Variant(float);
+ QPID_CLIENT_EXTERN Variant(double);
+ QPID_CLIENT_EXTERN Variant(const std::string&);
+ QPID_CLIENT_EXTERN Variant(const char*);
+ QPID_CLIENT_EXTERN Variant(const Map&);
+ QPID_CLIENT_EXTERN Variant(const List&);
+ QPID_CLIENT_EXTERN Variant(const Variant&);
+
+ QPID_CLIENT_EXTERN ~Variant();
+
+ QPID_CLIENT_EXTERN VariantType getType() const;
+
+ QPID_CLIENT_EXTERN Variant& operator=(bool);
+ QPID_CLIENT_EXTERN Variant& operator=(uint8_t);
+ QPID_CLIENT_EXTERN Variant& operator=(uint16_t);
+ QPID_CLIENT_EXTERN Variant& operator=(uint32_t);
+ QPID_CLIENT_EXTERN Variant& operator=(uint64_t);
+ QPID_CLIENT_EXTERN Variant& operator=(int8_t);
+ QPID_CLIENT_EXTERN Variant& operator=(int16_t);
+ QPID_CLIENT_EXTERN Variant& operator=(int32_t);
+ QPID_CLIENT_EXTERN Variant& operator=(int64_t);
+ QPID_CLIENT_EXTERN Variant& operator=(float);
+ QPID_CLIENT_EXTERN Variant& operator=(double);
+ QPID_CLIENT_EXTERN Variant& operator=(const std::string&);
+ QPID_CLIENT_EXTERN Variant& operator=(const char*);
+ QPID_CLIENT_EXTERN Variant& operator=(const Map&);
+ QPID_CLIENT_EXTERN Variant& operator=(const List&);
+ QPID_CLIENT_EXTERN Variant& operator=(const Variant&);
+
+ QPID_CLIENT_EXTERN bool asBool() const;
+ QPID_CLIENT_EXTERN uint8_t asUint8() const;
+ QPID_CLIENT_EXTERN uint16_t asUint16() const;
+ QPID_CLIENT_EXTERN uint32_t asUint32() const;
+ QPID_CLIENT_EXTERN uint64_t asUint64() const;
+ QPID_CLIENT_EXTERN int8_t asInt8() const;
+ QPID_CLIENT_EXTERN int16_t asInt16() const;
+ QPID_CLIENT_EXTERN int32_t asInt32() const;
+ QPID_CLIENT_EXTERN int64_t asInt64() const;
+ QPID_CLIENT_EXTERN float asFloat() const;
+ QPID_CLIENT_EXTERN double asDouble() const;
+ QPID_CLIENT_EXTERN std::string asString() const;
+
+ QPID_CLIENT_EXTERN operator bool() const;
+ QPID_CLIENT_EXTERN operator uint8_t() const;
+ QPID_CLIENT_EXTERN operator uint16_t() const;
+ QPID_CLIENT_EXTERN operator uint32_t() const;
+ QPID_CLIENT_EXTERN operator uint64_t() const;
+ QPID_CLIENT_EXTERN operator int8_t() const;
+ QPID_CLIENT_EXTERN operator int16_t() const;
+ QPID_CLIENT_EXTERN operator int32_t() const;
+ QPID_CLIENT_EXTERN operator int64_t() const;
+ QPID_CLIENT_EXTERN operator float() const;
+ QPID_CLIENT_EXTERN operator double() const;
+ QPID_CLIENT_EXTERN operator const char*() const;
+
+ QPID_CLIENT_EXTERN const Map& asMap() const;
+ QPID_CLIENT_EXTERN Map& asMap();
+ QPID_CLIENT_EXTERN const List& asList() const;
+ QPID_CLIENT_EXTERN List& asList();
+ /**
+ * Unlike asString(), getString() will not do any conversions and
+ * will throw InvalidConversion if the type is not STRING.
+ */
+ QPID_CLIENT_EXTERN const std::string& getString() const;
+ QPID_CLIENT_EXTERN std::string& getString();
+
+ QPID_CLIENT_EXTERN void setEncoding(const std::string&);
+ QPID_CLIENT_EXTERN const std::string& getEncoding() const;
+
+ QPID_CLIENT_EXTERN void reset();
+ private:
+ VariantImpl* impl;
+};
+
+QPID_CLIENT_EXTERN std::ostream& operator<<(std::ostream& out, const Variant& value);
+QPID_CLIENT_EXTERN std::ostream& operator<<(std::ostream& out, const Variant::Map& map);
+QPID_CLIENT_EXTERN std::ostream& operator<<(std::ostream& out, const Variant::List& list);
+
+typedef Variant::Map VariantMap;
+
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_VARIANT_H*/
diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt
index a1d01ceca4..7456401618 100644
--- a/qpid/cpp/src/CMakeLists.txt
+++ b/qpid/cpp/src/CMakeLists.txt
@@ -451,6 +451,7 @@ set (libqpidcommon_SOURCES
qpid/framing/FieldValue.cpp
qpid/framing/FrameSet.cpp
qpid/framing/FrameDecoder.cpp
+ qpid/framing/List.cpp
qpid/framing/ProtocolInitiation.cpp
qpid/framing/ProtocolVersion.cpp
qpid/framing/SendContent.cpp
@@ -522,6 +523,35 @@ set (libqpidclient_SOURCES
qpid/client/SubscriptionImpl.cpp
qpid/client/SubscriptionManager.cpp
qpid/client/SubscriptionManagerImpl.cpp
+ qpid/messaging/Address.cpp
+ qpid/messaging/Connection.cpp
+ qpid/messaging/ConnectionImpl.h
+ qpid/messaging/Filter.cpp
+ qpid/messaging/Message.cpp
+ qpid/messaging/Receiver.cpp
+ qpid/messaging/ReceiverImpl.h
+ qpid/messaging/Session.cpp
+ qpid/messaging/SessionImpl.h
+ qpid/messaging/Sender.cpp
+ qpid/messaging/SenderImpl.h
+ qpid/messaging/Variant.cpp
+ qpid/client/amqp0_10/AddressResolution.h
+ qpid/client/amqp0_10/AddressResolution.cpp
+ qpid/client/amqp0_10/Codecs.cpp
+ qpid/client/amqp0_10/CompletionTracker.h
+ qpid/client/amqp0_10/CompletionTracker.cpp
+ qpid/client/amqp0_10/ConnectionImpl.h
+ qpid/client/amqp0_10/ConnectionImpl.cpp
+ qpid/client/amqp0_10/IncomingMessages.h
+ qpid/client/amqp0_10/IncomingMessages.cpp
+ qpid/client/amqp0_10/MessageSink.h
+ qpid/client/amqp0_10/MessageSource.h
+ qpid/client/amqp0_10/ReceiverImpl.h
+ qpid/client/amqp0_10/ReceiverImpl.cpp
+ qpid/client/amqp0_10/SessionImpl.h
+ qpid/client/amqp0_10/SessionImpl.cpp
+ qpid/client/amqp0_10/SenderImpl.h
+ qpid/client/amqp0_10/SenderImpl.cpp
)
add_library (qpidclient SHARED ${libqpidclient_SOURCES})
target_link_libraries (qpidclient qpidcommon)
@@ -615,10 +645,10 @@ target_link_libraries (qpidd qpidbroker qpidcommon ${Boost_PROGRAM_OPTIONS_LIBRA
# qpid/agent/ManagementAgent.h \
# qpid/agent/ManagementAgentImpl.h
set (qmfagent_SOURCES
- ../include/qpid/agent/ManagementAgent.h
- qpid/agent/ManagementAgentImpl.cpp
- qpid/agent/ManagementAgentImpl.h
- qmf/Agent.cpp
+ qmf/AgentEngine.cpp
+ qmf/AgentEngine.h
+ qpid/agent/ManagementAgentImpl.cpp
+ qpid/agent/ManagementAgentImpl.h
)
add_library (qmfagent SHARED ${qmfagent_SOURCES})
target_link_libraries (qmfagent qmfcommon)
@@ -626,14 +656,31 @@ set_target_properties (qmfagent PROPERTIES
VERSION ${qpidc_version})
set (qmfcommon_SOURCES
- qmf/Agent.cpp
- qmf/ResilientConnection.cpp
+ qmf/ConnectionSettingsImpl.cpp
+ qmf/ConnectionSettingsImpl.h
+ qmf/ConsoleEngine.h
+ qmf/Event.h
+ qmf/Message.h
qmf/MessageImpl.cpp
- qmf/SchemaImpl.cpp
- qmf/ValueImpl.cpp
+ qmf/MessageImpl.h
+ qmf/Object.h
+ qmf/ObjectId.h
qmf/ObjectIdImpl.cpp
+ qmf/ObjectIdImpl.h
qmf/ObjectImpl.cpp
+ qmf/ObjectImpl.h
+ qmf/Query.h
qmf/QueryImpl.cpp
+ qmf/QueryImpl.h
+ qmf/ResilientConnection.cpp
+ qmf/ResilientConnection.h
+ qmf/Schema.h
+ qmf/SchemaImpl.cpp
+ qmf/SchemaImpl.h
+ qmf/Typecode.h
+ qmf/Value.h
+ qmf/ValueImpl.cpp
+ qmf/ValueImpl.h
)
add_library (qmfcommon SHARED ${qmfcommon_SOURCES})
target_link_libraries (qmfcommon qpidclient)
diff --git a/qpid/cpp/src/Makefile.am b/qpid/cpp/src/Makefile.am
index 0eff526203..05b5efc5b5 100644
--- a/qpid/cpp/src/Makefile.am
+++ b/qpid/cpp/src/Makefile.am
@@ -378,6 +378,7 @@ libqpidcommon_la_SOURCES += \
qpid/framing/InputHandler.h \
qpid/framing/Invoker.h \
qpid/framing/IsInSequenceSet.h \
+ qpid/framing/List.cpp \
qpid/framing/MethodBodyFactory.h \
qpid/framing/MethodContent.h \
qpid/framing/ModelMethod.h \
@@ -680,7 +681,36 @@ libqpidclient_la_SOURCES = \
qpid/client/SubscriptionImpl.h \
qpid/client/SubscriptionManager.cpp \
qpid/client/SubscriptionManagerImpl.cpp \
- qpid/client/SubscriptionManagerImpl.h
+ qpid/client/SubscriptionManagerImpl.h \
+ qpid/messaging/Address.cpp \
+ qpid/messaging/Connection.cpp \
+ qpid/messaging/Filter.cpp \
+ qpid/messaging/Message.cpp \
+ qpid/messaging/Sender.cpp \
+ qpid/messaging/Receiver.cpp \
+ qpid/messaging/Session.cpp \
+ qpid/messaging/Variant.cpp \
+ qpid/messaging/ConnectionImpl.h \
+ qpid/messaging/SenderImpl.h \
+ qpid/messaging/ReceiverImpl.h \
+ qpid/messaging/SessionImpl.h \
+ qpid/client/amqp0_10/AddressResolution.h \
+ qpid/client/amqp0_10/AddressResolution.cpp \
+ qpid/client/amqp0_10/Codecs.cpp \
+ qpid/client/amqp0_10/ConnectionImpl.h \
+ qpid/client/amqp0_10/ConnectionImpl.cpp \
+ qpid/client/amqp0_10/CompletionTracker.h \
+ qpid/client/amqp0_10/CompletionTracker.cpp \
+ qpid/client/amqp0_10/IncomingMessages.h \
+ qpid/client/amqp0_10/IncomingMessages.cpp \
+ qpid/client/amqp0_10/MessageSink.h \
+ qpid/client/amqp0_10/MessageSource.h \
+ qpid/client/amqp0_10/ReceiverImpl.h \
+ qpid/client/amqp0_10/ReceiverImpl.cpp \
+ qpid/client/amqp0_10/SessionImpl.h \
+ qpid/client/amqp0_10/SessionImpl.cpp \
+ qpid/client/amqp0_10/SenderImpl.h \
+ qpid/client/amqp0_10/SenderImpl.cpp
# NOTE: only public header files (which should be in ../include)
# should go in this list. Private headers should go in the SOURCES
@@ -723,6 +753,7 @@ nobase_include_HEADERS += \
../include/qpid/framing/Buffer.h \
../include/qpid/framing/FieldTable.h \
../include/qpid/framing/FieldValue.h \
+ ../include/qpid/framing/List.h \
../include/qpid/framing/ProtocolVersion.h \
../include/qpid/framing/SequenceNumber.h \
../include/qpid/framing/SequenceSet.h \
@@ -749,7 +780,19 @@ nobase_include_HEADERS += \
../include/qpid/sys/SystemInfo.h \
../include/qpid/sys/Thread.h \
../include/qpid/sys/Time.h \
- ../include/qpid/sys/uuid.h
+ ../include/qpid/sys/uuid.h \
+ ../include/qpid/messaging/Address.h \
+ ../include/qpid/messaging/Connection.h \
+ ../include/qpid/messaging/Codec.h \
+ ../include/qpid/messaging/Filter.h \
+ ../include/qpid/messaging/Message.h \
+ ../include/qpid/messaging/MessageContent.h \
+ ../include/qpid/messaging/MessageListener.h \
+ ../include/qpid/messaging/Sender.h \
+ ../include/qpid/messaging/Receiver.h \
+ ../include/qpid/messaging/Session.h \
+ ../include/qpid/messaging/Variant.h \
+ ../include/qpid/client/amqp0_10/Codecs.h
# Force build of qpidd during dist phase so help2man will work.
dist-hook: $(BUILT_SOURCES)
diff --git a/qpid/cpp/src/qmf.mk b/qpid/cpp/src/qmf.mk
index 62393cdcfb..9d24709b6e 100644
--- a/qpid/cpp/src/qmf.mk
+++ b/qpid/cpp/src/qmf.mk
@@ -18,7 +18,7 @@
#
#
-# qmf agent library makefile fragment, to be included in Makefile.am
+# qmf library makefile fragment, to be included in Makefile.am
#
lib_LTLIBRARIES += \
libqmfcommon.la \
@@ -27,13 +27,17 @@ lib_LTLIBRARIES += \
# Public header files
nobase_include_HEADERS += \
../include/qpid/agent/ManagementAgent.h \
- ../include/qpid/agent/QmfAgentImportExport.h
-
+ ../include/qpid/agent/QmfAgentImportExport.h \
+ ../include/qmf/Agent.h \
+ ../include/qmf/Connection.h \
+ ../include/qmf/QmfImportExport.h \
+ ../include/qmf/ConnectionSettings.h \
+ ../include/qmf/AgentObject.h
libqmfcommon_la_SOURCES = \
- qmf/Agent.cpp \
- qmf/Agent.h \
- qmf/Console.h \
+ qmf/ConnectionSettingsImpl.cpp \
+ qmf/ConnectionSettingsImpl.h \
+ qmf/ConsoleEngine.h \
qmf/Event.h \
qmf/Message.h \
qmf/MessageImpl.cpp \
@@ -58,9 +62,9 @@ libqmfcommon_la_SOURCES = \
qmf/ValueImpl.h
libqmfagent_la_SOURCES = \
- ../include/qpid/agent/ManagementAgent.h \
+ qmf/AgentEngine.cpp \
+ qmf/AgentEngine.h \
qpid/agent/ManagementAgentImpl.cpp \
- qpid/agent/ManagementAgentImpl.h \
- qmf/Agent.cpp
+ qpid/agent/ManagementAgentImpl.h
-libqmfagent_la_LIBADD = libqpidclient.la
+libqmfagent_la_LIBADD = libqpidclient.la libqmfcommon.la
diff --git a/qpid/cpp/src/qmf/Agent.cpp b/qpid/cpp/src/qmf/AgentEngine.cpp
index 6d59ae2750..bef8b3d102 100644
--- a/qpid/cpp/src/qmf/Agent.cpp
+++ b/qpid/cpp/src/qmf/AgentEngine.cpp
@@ -17,7 +17,7 @@
* under the License.
*/
-#include "qmf/Agent.h"
+#include "qmf/AgentEngine.h"
#include "qmf/MessageImpl.h"
#include "qmf/SchemaImpl.h"
#include "qmf/Typecode.h"
@@ -77,13 +77,13 @@ namespace qmf {
AgentQueryContext() : schemaMethod(0) {}
};
- class AgentImpl {
+ class AgentEngineImpl {
public:
- AgentImpl(char* label, bool internalStore);
- ~AgentImpl();
+ AgentEngineImpl(char* label, bool internalStore);
+ ~AgentEngineImpl();
- void setStoreDir(char* path);
- void setTransferDir(char* path);
+ void setStoreDir(const char* path);
+ void setTransferDir(const char* path);
void handleRcvMessage(Message& message);
bool getXmtMessage(Message& item);
void popXmt();
@@ -200,9 +200,9 @@ namespace qmf {
};
}
-const char* AgentImpl::QMF_EXCHANGE = "qpid.management";
-const char* AgentImpl::DIR_EXCHANGE = "amq.direct";
-const char* AgentImpl::BROKER_KEY = "broker";
+const char* AgentEngineImpl::QMF_EXCHANGE = "qpid.management";
+const char* AgentEngineImpl::DIR_EXCHANGE = "amq.direct";
+const char* AgentEngineImpl::BROKER_KEY = "broker";
#define STRING_REF(s) {if (!s.empty()) item.s = const_cast<char*>(s.c_str());}
@@ -228,7 +228,7 @@ AgentEvent AgentEventImpl::copy()
return item;
}
-AgentImpl::AgentImpl(char* _label, bool i) :
+AgentEngineImpl::AgentEngineImpl(char* _label, bool i) :
label(_label), queueName("qmfa-"), internalStore(i), nextTransientId(1),
requestedBrokerBank(0), requestedAgentBank(0),
assignedBrokerBank(0), assignedAgentBank(0),
@@ -237,11 +237,11 @@ AgentImpl::AgentImpl(char* _label, bool i) :
queueName += label;
}
-AgentImpl::~AgentImpl()
+AgentEngineImpl::~AgentEngineImpl()
{
}
-void AgentImpl::setStoreDir(char* path)
+void AgentEngineImpl::setStoreDir(const char* path)
{
Mutex::ScopedLock _lock(lock);
if (path)
@@ -250,7 +250,7 @@ void AgentImpl::setStoreDir(char* path)
storeDir.clear();
}
-void AgentImpl::setTransferDir(char* path)
+void AgentEngineImpl::setTransferDir(const char* path)
{
Mutex::ScopedLock _lock(lock);
if (path)
@@ -259,7 +259,7 @@ void AgentImpl::setTransferDir(char* path)
transferDir.clear();
}
-void AgentImpl::handleRcvMessage(Message& message)
+void AgentEngineImpl::handleRcvMessage(Message& message)
{
Buffer inBuffer(message.body, message.length);
uint8_t opcode;
@@ -277,7 +277,7 @@ void AgentImpl::handleRcvMessage(Message& message)
}
}
-bool AgentImpl::getXmtMessage(Message& item)
+bool AgentEngineImpl::getXmtMessage(Message& item)
{
Mutex::ScopedLock _lock(lock);
if (xmtQueue.empty())
@@ -286,14 +286,14 @@ bool AgentImpl::getXmtMessage(Message& item)
return true;
}
-void AgentImpl::popXmt()
+void AgentEngineImpl::popXmt()
{
Mutex::ScopedLock _lock(lock);
if (!xmtQueue.empty())
xmtQueue.pop_front();
}
-bool AgentImpl::getEvent(AgentEvent& event)
+bool AgentEngineImpl::getEvent(AgentEvent& event)
{
Mutex::ScopedLock _lock(lock);
if (eventQueue.empty())
@@ -302,14 +302,14 @@ bool AgentImpl::getEvent(AgentEvent& event)
return true;
}
-void AgentImpl::popEvent()
+void AgentEngineImpl::popEvent()
{
Mutex::ScopedLock _lock(lock);
if (!eventQueue.empty())
eventQueue.pop_front();
}
-void AgentImpl::newSession()
+void AgentEngineImpl::newSession()
{
Mutex::ScopedLock _lock(lock);
eventQueue.clear();
@@ -319,7 +319,7 @@ void AgentImpl::newSession()
eventQueue.push_back(eventSetupComplete());
}
-void AgentImpl::startProtocol()
+void AgentEngineImpl::startProtocol()
{
Mutex::ScopedLock _lock(lock);
char rawbuffer[512];
@@ -335,7 +335,7 @@ void AgentImpl::startProtocol()
" reqAgent=" << requestedAgentBank);
}
-void AgentImpl::heartbeat()
+void AgentEngineImpl::heartbeat()
{
Mutex::ScopedLock _lock(lock);
Buffer buffer(outputBuffer, MA_BUFFER_SIZE);
@@ -348,7 +348,7 @@ void AgentImpl::heartbeat()
QPID_LOG(trace, "SENT HeartbeatIndication");
}
-void AgentImpl::methodResponse(uint32_t sequence, uint32_t status, char* text,
+void AgentEngineImpl::methodResponse(uint32_t sequence, uint32_t status, char* text,
const Value& argMap)
{
Mutex::ScopedLock _lock(lock);
@@ -381,7 +381,7 @@ void AgentImpl::methodResponse(uint32_t sequence, uint32_t status, char* text,
QPID_LOG(trace, "SENT MethodResponse");
}
-void AgentImpl::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat)
+void AgentEngineImpl::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat)
{
Mutex::ScopedLock _lock(lock);
map<uint32_t, AgentQueryContext::Ptr>::iterator iter = contextMap.find(sequence);
@@ -403,7 +403,7 @@ void AgentImpl::queryResponse(uint32_t sequence, Object& object, bool prop, bool
QPID_LOG(trace, "SENT ContentIndication");
}
-void AgentImpl::queryComplete(uint32_t sequence)
+void AgentEngineImpl::queryComplete(uint32_t sequence)
{
Mutex::ScopedLock _lock(lock);
map<uint32_t, AgentQueryContext::Ptr>::iterator iter = contextMap.find(sequence);
@@ -415,7 +415,7 @@ void AgentImpl::queryComplete(uint32_t sequence)
sendCommandCompleteLH(context->exchange, context->key, context->sequence, 0, "OK");
}
-void AgentImpl::registerClass(SchemaObjectClass* cls)
+void AgentEngineImpl::registerClass(SchemaObjectClass* cls)
{
Mutex::ScopedLock _lock(lock);
SchemaObjectClassImpl* impl = cls->impl;
@@ -433,7 +433,7 @@ void AgentImpl::registerClass(SchemaObjectClass* cls)
// TODO: Indicate this schema if connected.
}
-void AgentImpl::registerClass(SchemaEventClass* cls)
+void AgentEngineImpl::registerClass(SchemaEventClass* cls)
{
Mutex::ScopedLock _lock(lock);
SchemaEventClassImpl* impl = cls->impl;
@@ -451,13 +451,13 @@ void AgentImpl::registerClass(SchemaEventClass* cls)
// TODO: Indicate this schema if connected.
}
-const ObjectId* AgentImpl::addObject(Object&, uint64_t)
+const ObjectId* AgentEngineImpl::addObject(Object&, uint64_t)
{
Mutex::ScopedLock _lock(lock);
return 0;
}
-const ObjectId* AgentImpl::allocObjectId(uint64_t persistId)
+const ObjectId* AgentEngineImpl::allocObjectId(uint64_t persistId)
{
Mutex::ScopedLock _lock(lock);
uint16_t sequence = persistId ? 0 : bootSequence;
@@ -467,17 +467,17 @@ const ObjectId* AgentImpl::allocObjectId(uint64_t persistId)
return oid->envelope;
}
-const ObjectId* AgentImpl::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi)
+const ObjectId* AgentEngineImpl::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi)
{
return allocObjectId(((uint64_t) persistIdHi) << 32 | (uint64_t) persistIdLo);
}
-void AgentImpl::raiseEvent(Event&)
+void AgentEngineImpl::raiseEvent(Event&)
{
Mutex::ScopedLock _lock(lock);
}
-void AgentImpl::encodeHeader(Buffer& buf, uint8_t opcode, uint32_t seq)
+void AgentEngineImpl::encodeHeader(Buffer& buf, uint8_t opcode, uint32_t seq)
{
buf.putOctet('A');
buf.putOctet('M');
@@ -486,7 +486,7 @@ void AgentImpl::encodeHeader(Buffer& buf, uint8_t opcode, uint32_t seq)
buf.putLong (seq);
}
-bool AgentImpl::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq)
+bool AgentEngineImpl::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq)
{
if (buf.getSize() < 8)
return false;
@@ -501,7 +501,7 @@ bool AgentImpl::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq)
return h1 == 'A' && h2 == 'M' && h3 == '3';
}
-AgentEventImpl::Ptr AgentImpl::eventDeclareQueue(const string& name)
+AgentEventImpl::Ptr AgentEngineImpl::eventDeclareQueue(const string& name)
{
AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::DECLARE_QUEUE));
event->name = name;
@@ -509,8 +509,8 @@ AgentEventImpl::Ptr AgentImpl::eventDeclareQueue(const string& name)
return event;
}
-AgentEventImpl::Ptr AgentImpl::eventBind(const string& exchange, const string& queue,
- const string& key)
+AgentEventImpl::Ptr AgentEngineImpl::eventBind(const string& exchange, const string& queue,
+ const string& key)
{
AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::BIND));
event->name = queue;
@@ -520,14 +520,14 @@ AgentEventImpl::Ptr AgentImpl::eventBind(const string& exchange, const string& q
return event;
}
-AgentEventImpl::Ptr AgentImpl::eventSetupComplete()
+AgentEventImpl::Ptr AgentEngineImpl::eventSetupComplete()
{
AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::SETUP_COMPLETE));
return event;
}
-AgentEventImpl::Ptr AgentImpl::eventQuery(uint32_t num, const string& userId, const string& package,
- const string& cls, boost::shared_ptr<ObjectId> oid)
+AgentEventImpl::Ptr AgentEngineImpl::eventQuery(uint32_t num, const string& userId, const string& package,
+ const string& cls, boost::shared_ptr<ObjectId> oid)
{
AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::GET_QUERY));
event->sequence = num;
@@ -538,9 +538,9 @@ AgentEventImpl::Ptr AgentImpl::eventQuery(uint32_t num, const string& userId, co
return event;
}
-AgentEventImpl::Ptr AgentImpl::eventMethod(uint32_t num, const string& userId, const string& method,
- boost::shared_ptr<ObjectId> oid, boost::shared_ptr<Value> argMap,
- SchemaObjectClass* objectClass)
+AgentEventImpl::Ptr AgentEngineImpl::eventMethod(uint32_t num, const string& userId, const string& method,
+ boost::shared_ptr<ObjectId> oid, boost::shared_ptr<Value> argMap,
+ SchemaObjectClass* objectClass)
{
AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::METHOD_CALL));
event->sequence = num;
@@ -552,7 +552,7 @@ AgentEventImpl::Ptr AgentImpl::eventMethod(uint32_t num, const string& userId, c
return event;
}
-void AgentImpl::sendBufferLH(Buffer& buf, const string& destination, const string& routingKey)
+void AgentEngineImpl::sendBufferLH(Buffer& buf, const string& destination, const string& routingKey)
{
uint32_t length = buf.getPosition();
MessageImpl::Ptr message(new MessageImpl);
@@ -567,7 +567,7 @@ void AgentImpl::sendBufferLH(Buffer& buf, const string& destination, const strin
xmtQueue.push_back(message);
}
-void AgentImpl::sendPackageIndicationLH(const string& packageName)
+void AgentEngineImpl::sendPackageIndicationLH(const string& packageName)
{
Buffer buffer(outputBuffer, MA_BUFFER_SIZE);
encodeHeader(buffer, 'p');
@@ -576,7 +576,7 @@ void AgentImpl::sendPackageIndicationLH(const string& packageName)
QPID_LOG(trace, "SENT PackageIndication: package_name=" << packageName);
}
-void AgentImpl::sendClassIndicationLH(ClassKind kind, const string& packageName, const SchemaClassKey& key)
+void AgentEngineImpl::sendClassIndicationLH(ClassKind kind, const string& packageName, const SchemaClassKey& key)
{
Buffer buffer(outputBuffer, MA_BUFFER_SIZE);
encodeHeader(buffer, 'q');
@@ -588,8 +588,8 @@ void AgentImpl::sendClassIndicationLH(ClassKind kind, const string& packageName,
QPID_LOG(trace, "SENT ClassIndication: package_name=" << packageName << " class_name=" << key.name);
}
-void AgentImpl::sendCommandCompleteLH(const string& exchange, const string& replyToKey,
- uint32_t sequence, uint32_t code, const string& text)
+void AgentEngineImpl::sendCommandCompleteLH(const string& exchange, const string& replyToKey,
+ uint32_t sequence, uint32_t code, const string& text)
{
Buffer buffer(outputBuffer, MA_BUFFER_SIZE);
encodeHeader(buffer, 'z', sequence);
@@ -599,7 +599,7 @@ void AgentImpl::sendCommandCompleteLH(const string& exchange, const string& repl
QPID_LOG(trace, "SENT CommandComplete: seq=" << sequence << " code=" << code << " text=" << text);
}
-void AgentImpl::sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t code, const string& text)
+void AgentEngineImpl::sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t code, const string& text)
{
Buffer buffer(outputBuffer, MA_BUFFER_SIZE);
encodeHeader(buffer, 'm', sequence);
@@ -625,7 +625,7 @@ void AgentImpl::sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t
QPID_LOG(trace, "SENT MethodResponse: errorCode=" << code << " text=" << fulltext);
}
-void AgentImpl::handleAttachResponse(Buffer& inBuffer)
+void AgentEngineImpl::handleAttachResponse(Buffer& inBuffer)
{
Mutex::ScopedLock _lock(lock);
@@ -672,18 +672,18 @@ void AgentImpl::handleAttachResponse(Buffer& inBuffer)
}
}
-void AgentImpl::handlePackageRequest(Buffer&)
+void AgentEngineImpl::handlePackageRequest(Buffer&)
{
Mutex::ScopedLock _lock(lock);
}
-void AgentImpl::handleClassQuery(Buffer&)
+void AgentEngineImpl::handleClassQuery(Buffer&)
{
Mutex::ScopedLock _lock(lock);
}
-void AgentImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence,
- const string& replyExchange, const string& replyKey)
+void AgentEngineImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence,
+ const string& replyExchange, const string& replyKey)
{
Mutex::ScopedLock _lock(lock);
string rExchange(replyExchange);
@@ -731,7 +731,7 @@ void AgentImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence,
sendCommandCompleteLH(rExchange, rKey, sequence, 1, "class not found");
}
-void AgentImpl::handleGetQuery(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId)
+void AgentEngineImpl::handleGetQuery(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId)
{
Mutex::ScopedLock _lock(lock);
FieldTable ft;
@@ -783,7 +783,7 @@ void AgentImpl::handleGetQuery(Buffer& inBuffer, uint32_t sequence, const string
eventQueue.push_back(eventQuery(contextNum, userId, pname, cname, oid));
}
-void AgentImpl::handleMethodRequest(Buffer& buffer, uint32_t sequence, const string& replyTo, const string& userId)
+void AgentEngineImpl::handleMethodRequest(Buffer& buffer, uint32_t sequence, const string& replyTo, const string& userId)
{
Mutex::ScopedLock _lock(lock);
string pname;
@@ -842,7 +842,7 @@ void AgentImpl::handleMethodRequest(Buffer& buffer, uint32_t sequence, const str
eventQueue.push_back(eventMethod(contextNum, userId, method, oid, argMap, schema->envelope));
}
-void AgentImpl::handleConsoleAddedIndication()
+void AgentEngineImpl::handleConsoleAddedIndication()
{
Mutex::ScopedLock _lock(lock);
}
@@ -851,107 +851,107 @@ void AgentImpl::handleConsoleAddedIndication()
// Wrappers
//==================================================================
-Agent::Agent(char* label, bool internalStore)
+AgentEngine::AgentEngine(char* label, bool internalStore)
{
- impl = new AgentImpl(label, internalStore);
+ impl = new AgentEngineImpl(label, internalStore);
}
-Agent::~Agent()
+AgentEngine::~AgentEngine()
{
delete impl;
}
-void Agent::setStoreDir(char* path)
+void AgentEngine::setStoreDir(const char* path)
{
impl->setStoreDir(path);
}
-void Agent::setTransferDir(char* path)
+void AgentEngine::setTransferDir(const char* path)
{
impl->setTransferDir(path);
}
-void Agent::handleRcvMessage(Message& message)
+void AgentEngine::handleRcvMessage(Message& message)
{
impl->handleRcvMessage(message);
}
-bool Agent::getXmtMessage(Message& item)
+bool AgentEngine::getXmtMessage(Message& item)
{
return impl->getXmtMessage(item);
}
-void Agent::popXmt()
+void AgentEngine::popXmt()
{
impl->popXmt();
}
-bool Agent::getEvent(AgentEvent& event)
+bool AgentEngine::getEvent(AgentEvent& event)
{
return impl->getEvent(event);
}
-void Agent::popEvent()
+void AgentEngine::popEvent()
{
impl->popEvent();
}
-void Agent::newSession()
+void AgentEngine::newSession()
{
impl->newSession();
}
-void Agent::startProtocol()
+void AgentEngine::startProtocol()
{
impl->startProtocol();
}
-void Agent::heartbeat()
+void AgentEngine::heartbeat()
{
impl->heartbeat();
}
-void Agent::methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments)
+void AgentEngine::methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments)
{
impl->methodResponse(sequence, status, text, arguments);
}
-void Agent::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat)
+void AgentEngine::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat)
{
impl->queryResponse(sequence, object, prop, stat);
}
-void Agent::queryComplete(uint32_t sequence)
+void AgentEngine::queryComplete(uint32_t sequence)
{
impl->queryComplete(sequence);
}
-void Agent::registerClass(SchemaObjectClass* cls)
+void AgentEngine::registerClass(SchemaObjectClass* cls)
{
impl->registerClass(cls);
}
-void Agent::registerClass(SchemaEventClass* cls)
+void AgentEngine::registerClass(SchemaEventClass* cls)
{
impl->registerClass(cls);
}
-const ObjectId* Agent::addObject(Object& obj, uint64_t persistId)
+const ObjectId* AgentEngine::addObject(Object& obj, uint64_t persistId)
{
return impl->addObject(obj, persistId);
}
-const ObjectId* Agent::allocObjectId(uint64_t persistId)
+const ObjectId* AgentEngine::allocObjectId(uint64_t persistId)
{
return impl->allocObjectId(persistId);
}
-const ObjectId* Agent::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi)
+const ObjectId* AgentEngine::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi)
{
return impl->allocObjectId(persistIdLo, persistIdHi);
}
-void Agent::raiseEvent(Event& event)
+void AgentEngine::raiseEvent(Event& event)
{
impl->raiseEvent(event);
}
diff --git a/qpid/cpp/src/qmf/Agent.h b/qpid/cpp/src/qmf/AgentEngine.h
index d8f784e9d8..d18a104e96 100644
--- a/qpid/cpp/src/qmf/Agent.h
+++ b/qpid/cpp/src/qmf/AgentEngine.h
@@ -1,5 +1,5 @@
-#ifndef _QmfAgent_
-#define _QmfAgent_
+#ifndef _QmfAgentEngine_
+#define _QmfAgentEngine_
/*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -64,15 +64,15 @@ namespace qmf {
SchemaObjectClass* objectClass; // (METHOD_CALL)
};
- class AgentImpl;
+ class AgentEngineImpl;
/**
- * Agent - Protocol engine for the QMF agent
+ * AgentEngine - Protocol engine for the QMF agent
*/
- class Agent {
+ class AgentEngine {
public:
- Agent(char* label, bool internalStore=true);
- ~Agent();
+ AgentEngine(char* label, bool internalStore=true);
+ ~AgentEngine();
/**
* Configure the directory path for storing persistent data.
@@ -80,7 +80,7 @@ namespace qmf {
* created, written, and read. If NULL, no persistent storage will be
* attempted.
*/
- void setStoreDir(char* path);
+ void setStoreDir(const char* path);
/**
* Configure the directory path for files transferred over QMF.
@@ -88,7 +88,7 @@ namespace qmf {
* created, deleted, written, and read. If NULL, file transfers shall not
* be permitted.
*/
- void setTransferDir(char* path);
+ void setTransferDir(const char* path);
/**
* Pass messages received from the AMQP session to the Agent engine.
@@ -182,11 +182,12 @@ namespace qmf {
*@return The objectId of the managed object.
*/
const ObjectId* addObject(Object& obj, uint64_t persistId);
+ const ObjectId* addObject(Object& obj, uint32_t persistIdLo, uint32_t persistIdHi);
/**
- * Allocate an objecc-id for an object that will be managed by the application.
+ * Allocate an object-id for an object that will be managed by the application.
*@param persistId A unique non-zero value if the object-id is to be persistent.
- @return The objectId structure for the allocated ID.
+ *@return The objectId structure for the allocated ID.
*/
const ObjectId* allocObjectId(uint64_t persistId);
const ObjectId* allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi);
@@ -198,7 +199,7 @@ namespace qmf {
void raiseEvent(Event& event);
private:
- AgentImpl* impl;
+ AgentEngineImpl* impl;
};
}
diff --git a/qpid/cpp/src/qmf/ConnectionSettingsImpl.cpp b/qpid/cpp/src/qmf/ConnectionSettingsImpl.cpp
new file mode 100644
index 0000000000..034ab18395
--- /dev/null
+++ b/qpid/cpp/src/qmf/ConnectionSettingsImpl.cpp
@@ -0,0 +1,323 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/ConnectionSettingsImpl.h"
+#include "qmf/Typecode.h"
+
+using namespace std;
+using namespace qmf;
+using namespace qpid;
+
+const string attrProtocol("protocol");
+const string attrHost("host");
+const string attrPort("port");
+const string attrVirtualhost("virtualhost");
+const string attrUsername("username");
+const string attrPassword("password");
+const string attrMechanism("mechanism");
+const string attrLocale("locale");
+const string attrHeartbeat("heartbeat");
+const string attrMaxChannels("maxChannels");
+const string attrMaxFrameSize("maxFrameSize");
+const string attrBounds("bounds");
+const string attrTcpNoDelay("tcpNoDelay");
+const string attrService("service");
+const string attrMinSsf("minSsf");
+const string attrMaxSsf("maxSsf");
+const string attrRetryDelayMin("retryDelayMin");
+const string attrRetryDelayMax("retryDelayMax");
+const string attrRetryDelayFactor("retryDelayFactor");
+
+ConnectionSettingsImpl::ConnectionSettingsImpl(ConnectionSettings* e) :
+ envelope(e), retryDelayMin(1), retryDelayMax(64), retryDelayFactor(2)
+{
+}
+
+ConnectionSettingsImpl::ConnectionSettingsImpl(ConnectionSettings* e, const string& /*url*/) :
+ envelope(e), retryDelayMin(1), retryDelayMax(64), retryDelayFactor(2)
+{
+ // TODO: Parse the URL
+}
+
+void ConnectionSettingsImpl::setAttr(const string& key, const Value& value)
+{
+ if (key == attrProtocol) clientSettings.protocol = value.asString();
+ else if (key == attrHost) clientSettings.host = value.asString();
+ else if (key == attrPort) clientSettings.port = value.asUint();
+ else if (key == attrVirtualhost) clientSettings.virtualhost = value.asString();
+ else if (key == attrUsername) clientSettings.username = value.asString();
+ else if (key == attrPassword) clientSettings.password = value.asString();
+ else if (key == attrMechanism) clientSettings.mechanism = value.asString();
+ else if (key == attrLocale) clientSettings.locale = value.asString();
+ else if (key == attrHeartbeat) clientSettings.heartbeat = value.asUint();
+ else if (key == attrMaxChannels) clientSettings.maxChannels = value.asUint();
+ else if (key == attrMaxFrameSize) clientSettings.maxFrameSize = value.asUint();
+ else if (key == attrBounds) clientSettings.bounds = value.asUint();
+ else if (key == attrTcpNoDelay) clientSettings.tcpNoDelay = value.asBool();
+ else if (key == attrService) clientSettings.service = value.asString();
+ else if (key == attrMinSsf) clientSettings.minSsf = value.asUint();
+ else if (key == attrMaxSsf) clientSettings.maxSsf = value.asUint();
+
+ else if (key == attrRetryDelayMin) retryDelayMin = value.asUint();
+ else if (key == attrRetryDelayMax) retryDelayMax = value.asUint();
+ else if (key == attrRetryDelayFactor) retryDelayFactor = value.asUint();
+}
+
+Value ConnectionSettingsImpl::getAttr(const string& key) const
+{
+ Value strval(TYPE_LSTR);
+ Value intval(TYPE_UINT32);
+ Value boolval(TYPE_BOOL);
+
+ if (key == attrProtocol) {
+ strval.setString(clientSettings.protocol.c_str());
+ return strval;
+ }
+
+ if (key == attrHost) {
+ strval.setString(clientSettings.host.c_str());
+ return strval;
+ }
+
+ if (key == attrPort) {
+ intval.setUint(clientSettings.port);
+ return intval;
+ }
+
+ if (key == attrVirtualhost) {
+ strval.setString(clientSettings.virtualhost.c_str());
+ return strval;
+ }
+
+ if (key == attrUsername) {
+ strval.setString(clientSettings.username.c_str());
+ return strval;
+ }
+
+ if (key == attrPassword) {
+ strval.setString(clientSettings.password.c_str());
+ return strval;
+ }
+
+ if (key == attrMechanism) {
+ strval.setString(clientSettings.mechanism.c_str());
+ return strval;
+ }
+
+ if (key == attrLocale) {
+ strval.setString(clientSettings.locale.c_str());
+ return strval;
+ }
+
+ if (key == attrHeartbeat) {
+ intval.setUint(clientSettings.heartbeat);
+ return intval;
+ }
+
+ if (key == attrMaxChannels) {
+ intval.setUint(clientSettings.maxChannels);
+ return intval;
+ }
+
+ if (key == attrMaxFrameSize) {
+ intval.setUint(clientSettings.maxFrameSize);
+ return intval;
+ }
+
+ if (key == attrBounds) {
+ intval.setUint(clientSettings.bounds);
+ return intval;
+ }
+
+ if (key == attrTcpNoDelay) {
+ boolval.setBool(clientSettings.tcpNoDelay);
+ return boolval;
+ }
+
+ if (key == attrService) {
+ strval.setString(clientSettings.service.c_str());
+ return strval;
+ }
+
+ if (key == attrMinSsf) {
+ intval.setUint(clientSettings.minSsf);
+ return intval;
+ }
+
+ if (key == attrMaxSsf) {
+ intval.setUint(clientSettings.maxSsf);
+ return intval;
+ }
+
+ if (key == attrRetryDelayMin) {
+ intval.setUint(retryDelayMin);
+ return intval;
+ }
+
+ if (key == attrRetryDelayMax) {
+ intval.setUint(retryDelayMax);
+ return intval;
+ }
+
+ if (key == attrRetryDelayFactor) {
+ intval.setUint(retryDelayFactor);
+ return intval;
+ }
+
+ return strval;
+}
+
+const string& ConnectionSettingsImpl::getAttrString() const
+{
+ // TODO: build and return attribute string
+ return attrString;
+}
+
+void ConnectionSettingsImpl::transportTcp(uint16_t port)
+{
+ clientSettings.protocol = "tcp";
+ clientSettings.port = port;
+}
+
+void ConnectionSettingsImpl::transportSsl(uint16_t port)
+{
+ clientSettings.protocol = "ssl";
+ clientSettings.port = port;
+}
+
+void ConnectionSettingsImpl::transportRdma(uint16_t port)
+{
+ clientSettings.protocol = "rdma";
+ clientSettings.port = port;
+}
+
+void ConnectionSettingsImpl::authAnonymous(const string& username)
+{
+ clientSettings.mechanism = "ANONYMOUS";
+ clientSettings.username = username;
+}
+
+void ConnectionSettingsImpl::authPlain(const string& username, const string& password)
+{
+ clientSettings.mechanism = "PLAIN";
+ clientSettings.username = username;
+ clientSettings.password = password;
+}
+
+void ConnectionSettingsImpl::authGssapi(const string& serviceName, uint32_t minSsf, uint32_t maxSsf)
+{
+ clientSettings.mechanism = "GSSAPI";
+ clientSettings.service = serviceName;
+ clientSettings.minSsf = minSsf;
+ clientSettings.maxSsf = maxSsf;
+}
+
+void ConnectionSettingsImpl::setRetry(int delayMin, int delayMax, int delayFactor)
+{
+ retryDelayMin = delayMin;
+ retryDelayMax = delayMax;
+ retryDelayFactor = delayFactor;
+}
+
+const client::ConnectionSettings& ConnectionSettingsImpl::getClientSettings() const
+{
+ return clientSettings;
+}
+
+void ConnectionSettingsImpl::getRetrySettings(int* min, int* max, int* factor) const
+{
+ *min = retryDelayMin;
+ *max = retryDelayMax;
+ *factor = retryDelayFactor;
+}
+
+//==================================================================
+// Wrappers
+//==================================================================
+
+ConnectionSettings::ConnectionSettings(const ConnectionSettings& from)
+{
+ impl = new ConnectionSettingsImpl(*from.impl);
+}
+
+ConnectionSettings::ConnectionSettings()
+{
+ impl = new ConnectionSettingsImpl(this);
+}
+
+ConnectionSettings::ConnectionSettings(const char* url)
+{
+ impl = new ConnectionSettingsImpl(this, url);
+}
+
+ConnectionSettings::~ConnectionSettings()
+{
+ delete impl;
+}
+
+void ConnectionSettings::setAttr(const char* key, const Value& value)
+{
+ impl->setAttr(key, value);
+}
+
+Value ConnectionSettings::getAttr(const char* key) const
+{
+ return impl->getAttr(key);
+}
+
+const char* ConnectionSettings::getAttrString() const
+{
+ return impl->getAttrString().c_str();
+}
+
+void ConnectionSettings::transportTcp(uint16_t port)
+{
+ impl->transportTcp(port);
+}
+
+void ConnectionSettings::transportSsl(uint16_t port)
+{
+ impl->transportSsl(port);
+}
+
+void ConnectionSettings::transportRdma(uint16_t port)
+{
+ impl->transportRdma(port);
+}
+
+void ConnectionSettings::authAnonymous(const char* username)
+{
+ impl->authAnonymous(username);
+}
+
+void ConnectionSettings::authPlain(const char* username, const char* password)
+{
+ impl->authPlain(username, password);
+}
+
+void ConnectionSettings::authGssapi(const char* serviceName, uint32_t minSsf, uint32_t maxSsf)
+{
+ impl->authGssapi(serviceName, minSsf, maxSsf);
+}
+
+void ConnectionSettings::setRetry(int delayMin, int delayMax, int delayFactor)
+{
+ impl->setRetry(delayMin, delayMax, delayFactor);
+}
+
diff --git a/qpid/cpp/src/qmf/ConnectionSettingsImpl.h b/qpid/cpp/src/qmf/ConnectionSettingsImpl.h
new file mode 100644
index 0000000000..a177233cf3
--- /dev/null
+++ b/qpid/cpp/src/qmf/ConnectionSettingsImpl.h
@@ -0,0 +1,60 @@
+#ifndef _QmfConnectionSettingsImpl_
+#define _QmfConnectionSettingsImpl_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/ConnectionSettings.h"
+#include "qmf/Value.h"
+#include "qpid/client/ConnectionSettings.h"
+#include <string>
+#include <map>
+
+namespace qmf {
+
+ class ConnectionSettingsImpl {
+ ConnectionSettings* envelope;
+ qpid::client::ConnectionSettings clientSettings;
+ mutable std::string attrString;
+ int retryDelayMin;
+ int retryDelayMax;
+ int retryDelayFactor;
+
+ public:
+ ConnectionSettingsImpl(ConnectionSettings* e);
+ ConnectionSettingsImpl(ConnectionSettings* e, const std::string& url);
+ ~ConnectionSettingsImpl() {}
+ void setAttr(const std::string& key, const Value& value);
+ Value getAttr(const std::string& key) const;
+ const std::string& getAttrString() const;
+ void transportTcp(uint16_t port);
+ void transportSsl(uint16_t port);
+ void transportRdma(uint16_t port);
+ void authAnonymous(const std::string& username);
+ void authPlain(const std::string& username, const std::string& password);
+ void authGssapi(const std::string& serviceName, uint32_t minSsf, uint32_t maxSsf);
+ void setRetry(int delayMin, int delayMax, int delayFactor);
+
+ const qpid::client::ConnectionSettings& getClientSettings() const;
+ void getRetrySettings(int* delayMin, int* delayMax, int* delayFactor) const;
+ };
+
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/Console.h b/qpid/cpp/src/qmf/ConsoleEngine.h
index de7949e1de..823e281b14 100644
--- a/qpid/cpp/src/qmf/Console.h
+++ b/qpid/cpp/src/qmf/ConsoleEngine.h
@@ -1,5 +1,5 @@
-#ifndef _QmfConsole_
-#define _QmfConsole_
+#ifndef _QmfConsoleEngine_
+#define _QmfConsoleEngine_
/*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -21,7 +21,6 @@
*/
#include <qmf/ManagedConnection.h>
-#include <qmf/Agent.h>
#include <qmf/Broker.h>
#include <qmf/Package.h>
#include <qmf/SchemaClassTable.h>
@@ -50,10 +49,10 @@ namespace qmf {
getTimeout(20) {}
};
- class Console {
+ class ConsoleEngine {
public:
- Console(ConsoleHandler* handler = 0, ConsoleSettings settings = ConsoleSettings());
- ~Console();
+ ConsoleEngine(ConsoleHandler* handler = 0, ConsoleSettings settings = ConsoleSettings());
+ ~ConsoleEngine();
Broker* addConnection(ManagedConnection& connection);
void delConnection(Broker* broker);
@@ -66,6 +65,7 @@ namespace qmf {
void bindClass(const SchemaClass& otype);
void bindClass(const std::string& packageName, const std::string& className);
+ /*
void getAgents(std::set<Agent>& agents, Broker* = 0);
void getObjects(std::vector<Object>& objects, const std::string& typeName,
const std::string& packageName = "",
@@ -75,6 +75,7 @@ namespace qmf {
const std::map<std::string, std::string>& query,
Broker* broker = 0,
Agent* agent = 0);
+ */
};
}
diff --git a/qpid/cpp/src/qmf/Object.h b/qpid/cpp/src/qmf/Object.h
index 8caab8d6dc..eb92cbbe45 100644
--- a/qpid/cpp/src/qmf/Object.h
+++ b/qpid/cpp/src/qmf/Object.h
@@ -31,7 +31,7 @@ namespace qmf {
public:
Object(const SchemaObjectClass* type);
Object(ObjectImpl* impl);
- ~Object();
+ virtual ~Object();
void destroy();
const ObjectId* getObjectId() const;
diff --git a/qpid/cpp/src/qmf/ObjectId.h b/qpid/cpp/src/qmf/ObjectId.h
index 1ceae20bd8..ffd1b6978b 100644
--- a/qpid/cpp/src/qmf/ObjectId.h
+++ b/qpid/cpp/src/qmf/ObjectId.h
@@ -39,7 +39,6 @@ namespace qmf {
bool isDurable() const;
bool operator==(const ObjectId& other) const;
- bool operator!=(const ObjectId& other) const;
bool operator<(const ObjectId& other) const;
bool operator>(const ObjectId& other) const;
bool operator<=(const ObjectId& other) const;
diff --git a/qpid/cpp/src/qmf/ObjectIdImpl.cpp b/qpid/cpp/src/qmf/ObjectIdImpl.cpp
index efa8e7119b..75661fdb47 100644
--- a/qpid/cpp/src/qmf/ObjectIdImpl.cpp
+++ b/qpid/cpp/src/qmf/ObjectIdImpl.cpp
@@ -166,11 +166,6 @@ bool ObjectId::operator==(const ObjectId& other) const
return *impl == *other.impl;
}
-bool ObjectId::operator!=(const ObjectId& other) const
-{
- return !(*impl == *other.impl);
-}
-
bool ObjectId::operator<(const ObjectId& other) const
{
return *impl < *other.impl;
diff --git a/qpid/cpp/src/qmf/ResilientConnection.cpp b/qpid/cpp/src/qmf/ResilientConnection.cpp
index 610306f896..3531e104b0 100644
--- a/qpid/cpp/src/qmf/ResilientConnection.cpp
+++ b/qpid/cpp/src/qmf/ResilientConnection.cpp
@@ -19,6 +19,8 @@
#include "qmf/ResilientConnection.h"
#include "qmf/MessageImpl.h"
+#include "qmf/ConnectionSettingsImpl.h"
+#include <qpid/client/Connection.h>
#include <qpid/client/Session.h>
#include <qpid/client/MessageListener.h>
#include <qpid/client/SubscriptionManager.h>
@@ -77,8 +79,7 @@ namespace qmf {
class ResilientConnectionImpl : public qpid::sys::Runnable {
public:
- ResilientConnectionImpl(ConnectionSettings& settings,
- int dmin, int dmax, int dfactor);
+ ResilientConnectionImpl(ConnectionSettings& settings);
~ResilientConnectionImpl();
bool isConnected() const;
@@ -174,12 +175,11 @@ void RCSession::received(qpid::client::Message& msg)
connImpl.EnqueueEvent(ResilientConnectionEvent::RECV, userContext, qmsg);
}
-ResilientConnectionImpl::ResilientConnectionImpl(ConnectionSettings& _settings,
- int dmin, int dmax, int dfactor) :
- notifyFd(-1), connected(false), shutdown(false), settings(_settings),
- delayMin(dmin), delayMax(dmax), delayFactor(dfactor), connThread(*this)
+ResilientConnectionImpl::ResilientConnectionImpl(ConnectionSettings& _settings) :
+ notifyFd(-1), connected(false), shutdown(false), settings(_settings), connThread(*this)
{
connection.registerFailureCallback(boost::bind(&ResilientConnectionImpl::failure, this));
+ settings.impl->getRetrySettings(&delayMin, &delayMax, &delayFactor);
}
ResilientConnectionImpl::~ResilientConnectionImpl()
@@ -318,7 +318,7 @@ void ResilientConnectionImpl::run()
while (true) {
try {
- connection.open(settings);
+ connection.open(settings.impl->getClientSettings());
{
Mutex::ScopedLock _lock(lock);
connected = true;
@@ -396,10 +396,9 @@ void ResilientConnectionImpl::EnqueueEvent(ResilientConnectionEvent::EventKind k
// Wrappers
//==================================================================
-ResilientConnection::ResilientConnection(ConnectionSettings& settings,
- int delayMin, int delayMax, int delayFactor)
+ResilientConnection::ResilientConnection(ConnectionSettings& settings)
{
- impl = new ResilientConnectionImpl(settings, delayMin, delayMax, delayFactor);
+ impl = new ResilientConnectionImpl(settings);
}
ResilientConnection::~ResilientConnection()
diff --git a/qpid/cpp/src/qmf/ResilientConnection.h b/qpid/cpp/src/qmf/ResilientConnection.h
index bb565e27ae..6e05541253 100644
--- a/qpid/cpp/src/qmf/ResilientConnection.h
+++ b/qpid/cpp/src/qmf/ResilientConnection.h
@@ -21,8 +21,7 @@
*/
#include <qmf/Message.h>
-#include <qpid/client/Connection.h>
-#include <qpid/client/ConnectionSettings.h>
+#include <qmf/ConnectionSettings.h>
#include <string>
namespace qmf {
@@ -68,10 +67,7 @@ namespace qmf {
*@param delayMax Maximum delay (in seconds) between retries.
*@param delayFactor Factor to multiply retry delay by after each failure.
*/
- ResilientConnection(qpid::client::ConnectionSettings& settings,
- int delayMin = 1,
- int delayMax = 128,
- int delayFactor = 2);
+ ResilientConnection(ConnectionSettings& settings);
~ResilientConnection();
/**
diff --git a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp
index 7f69c73ef7..4a6590fb5f 100644
--- a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp
+++ b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp
@@ -78,7 +78,7 @@ ManagementAgent* ManagementAgent::Singleton::getInstance()
const string ManagementAgentImpl::storeMagicNumber("MA02");
ManagementAgentImpl::ManagementAgentImpl() :
- extThread(false),
+ interval(10), extThread(false),
initialized(false), connected(false), lastFailure("never connected"),
clientWasAdded(true), requestedBrokerBank(0), requestedAgentBank(0),
assignedBrokerBank(0), assignedAgentBank(0), bootSequence(0),
diff --git a/qpid/cpp/src/qpid/broker/Daemon.cpp b/qpid/cpp/src/qpid/broker/Daemon.cpp
index aa8dd8855b..21848e23de 100644
--- a/qpid/cpp/src/qpid/broker/Daemon.cpp
+++ b/qpid/cpp/src/qpid/broker/Daemon.cpp
@@ -85,7 +85,7 @@ void Daemon::fork()
child();
}
catch (const exception& e) {
- QPID_LOG(critical, "Daemon startup failed: " << e.what());
+ QPID_LOG(critical, "Unexpected error: " << e.what());
uint16_t port = 0;
int unused_ret; //Supress warning about ignoring return value.
unused_ret = write(pipeFds[1], &port, sizeof(uint16_t));
diff --git a/qpid/cpp/src/qpid/broker/Exchange.cpp b/qpid/cpp/src/qpid/broker/Exchange.cpp
index 17fc0e23ca..b1fc1295f3 100644
--- a/qpid/cpp/src/qpid/broker/Exchange.cpp
+++ b/qpid/cpp/src/qpid/broker/Exchange.cpp
@@ -91,7 +91,9 @@ Exchange::Exchange (const string& _name, Manageable* parent, Broker* b) :
ManagementAgent* agent = broker->getManagementAgent();
if (agent != 0)
{
- mgmtExchange = new _qmf::Exchange (agent, this, parent, _name, durable);
+ mgmtExchange = new _qmf::Exchange (agent, this, parent, _name);
+ mgmtExchange->set_durable(durable);
+ mgmtExchange->set_autoDelete(false);
agent->addObject (mgmtExchange);
}
}
@@ -109,7 +111,9 @@ Exchange::Exchange(const string& _name, bool _durable, const qpid::framing::Fiel
ManagementAgent* agent = broker->getManagementAgent();
if (agent != 0)
{
- mgmtExchange = new _qmf::Exchange (agent, this, parent, _name, durable);
+ mgmtExchange = new _qmf::Exchange (agent, this, parent, _name);
+ mgmtExchange->set_durable(durable);
+ mgmtExchange->set_autoDelete(false);
mgmtExchange->set_arguments(args);
if (!durable) {
if (name.empty()) {
@@ -139,6 +143,17 @@ Exchange::~Exchange ()
mgmtExchange->resourceDestroy ();
}
+void Exchange::setAlternate(Exchange::shared_ptr _alternate)
+{
+ alternate = _alternate;
+ if (mgmtExchange != 0) {
+ if (alternate.get() != 0)
+ mgmtExchange->set_altExchange(alternate->GetManagementObject()->getObjectId());
+ else
+ mgmtExchange->clr_altExchange();
+ }
+}
+
void Exchange::setPersistenceId(uint64_t id) const
{
if (mgmtExchange != 0 && persistenceId == 0)
diff --git a/qpid/cpp/src/qpid/broker/Exchange.h b/qpid/cpp/src/qpid/broker/Exchange.h
index ca8525d55e..c1e878200f 100644
--- a/qpid/cpp/src/qpid/broker/Exchange.h
+++ b/qpid/cpp/src/qpid/broker/Exchange.h
@@ -132,7 +132,7 @@ public:
qpid::framing::FieldTable& getArgs() { return args; }
Exchange::shared_ptr getAlternate() { return alternate; }
- void setAlternate(Exchange::shared_ptr _alternate) { alternate = _alternate; }
+ void setAlternate(Exchange::shared_ptr _alternate);
void incAlternateUsers() { alternateUsers++; }
void decAlternateUsers() { alternateUsers--; }
bool inUseAsAlternate() { return alternateUsers > 0; }
diff --git a/qpid/cpp/src/qpid/client/Demux.h b/qpid/cpp/src/qpid/client/Demux.h
index 7304e799bb..31dc3f9c06 100644
--- a/qpid/cpp/src/qpid/client/Demux.h
+++ b/qpid/cpp/src/qpid/client/Demux.h
@@ -25,6 +25,7 @@
#include "qpid/framing/FrameSet.h"
#include "qpid/sys/Mutex.h"
#include "qpid/sys/BlockingQueue.h"
+#include "qpid/client/ClientImportExport.h"
#ifndef _Demux_
#define _Demux_
@@ -49,17 +50,17 @@ public:
typedef sys::BlockingQueue<framing::FrameSet::shared_ptr> Queue;
typedef boost::shared_ptr<Queue> QueuePtr;
- Demux();
- ~Demux();
+ QPID_CLIENT_EXTERN Demux();
+ QPID_CLIENT_EXTERN ~Demux();
- void handle(framing::FrameSet::shared_ptr);
- void close(const sys::ExceptionHolder& ex);
- void open();
-
- QueuePtr add(const std::string& name, Condition);
- void remove(const std::string& name);
- QueuePtr get(const std::string& name);
- QueuePtr getDefault();
+ QPID_CLIENT_EXTERN void handle(framing::FrameSet::shared_ptr);
+ QPID_CLIENT_EXTERN void close(const sys::ExceptionHolder& ex);
+ QPID_CLIENT_EXTERN void open();
+
+ QPID_CLIENT_EXTERN QueuePtr add(const std::string& name, Condition);
+ QPID_CLIENT_EXTERN void remove(const std::string& name);
+ QPID_CLIENT_EXTERN QueuePtr get(const std::string& name);
+ QPID_CLIENT_EXTERN QueuePtr getDefault();
private:
struct Record
diff --git a/qpid/cpp/src/qpid/client/Results.cpp b/qpid/cpp/src/qpid/client/Results.cpp
index acb2684727..0de3e8bd04 100644
--- a/qpid/cpp/src/qpid/client/Results.cpp
+++ b/qpid/cpp/src/qpid/client/Results.cpp
@@ -24,7 +24,6 @@
#include "qpid/framing/SequenceSet.h"
using namespace qpid::framing;
-using namespace boost;
namespace qpid {
namespace client {
diff --git a/qpid/cpp/src/qpid/client/SessionImpl.cpp b/qpid/cpp/src/qpid/client/SessionImpl.cpp
index 43b62ec6dc..8ead44a172 100644
--- a/qpid/cpp/src/qpid/client/SessionImpl.cpp
+++ b/qpid/cpp/src/qpid/client/SessionImpl.cpp
@@ -202,6 +202,16 @@ bool SessionImpl::isCompleteUpTo(const SequenceNumber& id)
return f.result;
}
+framing::SequenceNumber SessionImpl::getCompleteUpTo()
+{
+ SequenceNumber firstIncomplete;
+ {
+ Lock l(state);
+ firstIncomplete = incompleteIn.front();
+ }
+ return --firstIncomplete;
+}
+
struct MarkCompleted
{
const SequenceNumber& id;
@@ -319,7 +329,7 @@ struct MethodContentAdaptor : MethodContent
}
-Future SessionImpl::send(const AMQBody& command, const FrameSet& content) {
+Future SessionImpl::send(const AMQBody& command, const FrameSet& content, bool reframe) {
Acquire a(sendLock);
SequenceNumber id = nextOut++;
{
@@ -337,7 +347,7 @@ Future SessionImpl::send(const AMQBody& command, const FrameSet& content) {
frame.setEof(false);
handleOut(frame);
- if (content.isComplete()) {
+ if (reframe) {
MethodContentAdaptor c(content);
sendContent(c);
} else {
diff --git a/qpid/cpp/src/qpid/client/SessionImpl.h b/qpid/cpp/src/qpid/client/SessionImpl.h
index cae1148e9f..49d268c44d 100644
--- a/qpid/cpp/src/qpid/client/SessionImpl.h
+++ b/qpid/cpp/src/qpid/client/SessionImpl.h
@@ -25,6 +25,7 @@
#include "qpid/client/Demux.h"
#include "qpid/client/Execution.h"
#include "qpid/client/Results.h"
+#include "qpid/client/ClientImportExport.h"
#include "qpid/SessionId.h"
#include "qpid/SessionState.h"
@@ -86,7 +87,15 @@ public:
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);
+ /**
+ * This method takes the content as a FrameSet; if reframe=false,
+ * the caller is resposnible for ensuring that the header and
+ * content frames in that set are correct for this connection
+ * (right flags, right fragmentation etc). If reframe=true, then
+ * the header and content from the frameset will be copied and
+ * reframed correctly for the connection.
+ */
+ QPID_CLIENT_EXTERN Future send(const framing::AMQBody& command, const framing::FrameSet& content, bool reframe=false);
void sendRawFrame(framing::AMQFrame& frame);
Demux& getDemux();
@@ -94,6 +103,7 @@ public:
void markCompleted(const framing::SequenceSet& ids, bool notifyPeer);
bool isComplete(const framing::SequenceNumber& id);
bool isCompleteUpTo(const framing::SequenceNumber& id);
+ framing::SequenceNumber getCompleteUpTo();
void waitForCompletion(const framing::SequenceNumber& id);
void sendCompletion();
void sendFlush();
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
new file mode 100644
index 0000000000..6ff9c2397a
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
@@ -0,0 +1,464 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/amqp0_10/AddressResolution.h"
+#include "qpid/client/amqp0_10/Codecs.h"
+#include "qpid/client/amqp0_10/MessageSource.h"
+#include "qpid/client/amqp0_10/MessageSink.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Filter.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/Exception.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/enum.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/ReplyTo.h"
+#include "qpid/framing/reply_exceptions.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+using qpid::Exception;
+using qpid::messaging::Address;
+using qpid::messaging::Filter;
+using qpid::messaging::Variant;
+using qpid::framing::FieldTable;
+using qpid::framing::ReplyTo;
+using namespace qpid::framing::message;
+
+
+namespace{
+const Variant EMPTY_VARIANT;
+const FieldTable EMPTY_FIELD_TABLE;
+const std::string EMPTY_STRING;
+
+//option names
+const std::string BROWSE("browse");
+const std::string EXCLUSIVE("exclusive");
+const std::string MODE("mode");
+const std::string NAME("name");
+const std::string UNACKNOWLEDGED("unacknowledged");
+
+const std::string QUEUE_ADDRESS("queue");
+const std::string TOPIC_ADDRESS("topic");
+const std::string TOPIC_ADDRESS_AND_SUBJECT("topic+");
+const std::string DIVIDER("/");
+
+const std::string SIMPLE_SUBSCRIPTION("simple");
+const std::string RELIABLE_SUBSCRIPTION("reliable");
+const std::string DURABLE_SUBSCRIPTION("durable");
+}
+
+class QueueSource : public MessageSource
+{
+ public:
+ QueueSource(const std::string& name, AcceptMode=ACCEPT_MODE_EXPLICIT, AcquireMode=ACQUIRE_MODE_PRE_ACQUIRED,
+ bool exclusive = false, const FieldTable& options = EMPTY_FIELD_TABLE);
+ void subscribe(qpid::client::AsyncSession& session, const std::string& destination);
+ void cancel(qpid::client::AsyncSession& session, const std::string& destination);
+ private:
+ const std::string name;
+ const AcceptMode acceptMode;
+ const AcquireMode acquireMode;
+ const bool exclusive;
+ const FieldTable options;
+};
+
+class Subscription : public MessageSource
+{
+ public:
+ enum SubscriptionMode {SIMPLE, RELIABLE, DURABLE};
+
+ Subscription(const std::string& name, SubscriptionMode mode = SIMPLE,
+ const FieldTable& queueOptions = EMPTY_FIELD_TABLE, const FieldTable& subscriptionOptions = EMPTY_FIELD_TABLE);
+ void add(const std::string& exchange, const std::string& key, const FieldTable& options = EMPTY_FIELD_TABLE);
+ void subscribe(qpid::client::AsyncSession& session, const std::string& destination);
+ void cancel(qpid::client::AsyncSession& session, const std::string& destination);
+
+ static SubscriptionMode getMode(const std::string& mode);
+ private:
+ struct Binding
+ {
+ Binding(const std::string& exchange, const std::string& key, const FieldTable& options = EMPTY_FIELD_TABLE);
+
+ std::string exchange;
+ std::string key;
+ FieldTable options;
+ };
+
+ typedef std::vector<Binding> Bindings;
+
+ const std::string name;
+ const bool autoDelete;
+ const bool durable;
+ const FieldTable queueOptions;
+ const FieldTable subscriptionOptions;
+ Bindings bindings;
+ std::string queue;
+};
+
+class Exchange : public MessageSink
+{
+ public:
+ Exchange(const std::string& name, const std::string& defaultSubject = EMPTY_STRING,
+ bool passive = true, const std::string& type = EMPTY_STRING, bool durable = false,
+ const FieldTable& options = EMPTY_FIELD_TABLE);
+ void declare(qpid::client::AsyncSession& session, const std::string& name);
+ void send(qpid::client::AsyncSession& session, const std::string& name, qpid::messaging::Message& message);
+ void cancel(qpid::client::AsyncSession& session, const std::string& name);
+ private:
+ const std::string name;
+ const std::string defaultSubject;
+ const bool passive;
+ const std::string type;
+ const bool durable;
+ const FieldTable options;
+};
+
+class QueueSink : public MessageSink
+{
+ public:
+ QueueSink(const std::string& name, bool passive=true, bool exclusive=false,
+ bool autoDelete=false, bool durable=false, const FieldTable& options = EMPTY_FIELD_TABLE);
+ void declare(qpid::client::AsyncSession& session, const std::string& name);
+ void send(qpid::client::AsyncSession& session, const std::string& name, qpid::messaging::Message& message);
+ void cancel(qpid::client::AsyncSession& session, const std::string& name);
+ private:
+ const std::string name;
+ const bool passive;
+ const bool exclusive;
+ const bool autoDelete;
+ const bool durable;
+ const FieldTable options;
+};
+
+
+bool isQueue(qpid::client::Session session, const qpid::messaging::Address& address);
+bool isTopic(qpid::client::Session session, const qpid::messaging::Address& address, std::string& subject);
+
+const Variant& getOption(const std::string& key, const Variant::Map& options)
+{
+ Variant::Map::const_iterator i = options.find(key);
+ if (i == options.end()) return EMPTY_VARIANT;
+ else return i->second;
+}
+
+std::auto_ptr<MessageSource> AddressResolution::resolveSource(qpid::client::Session session,
+ const Address& address,
+ const Filter* filter,
+ const Variant::Map& options)
+{
+ //TODO: handle case where there exists a queue and an exchange of
+ //the same name (hence an unqualified address is ambiguous)
+
+ //TODO: make sure specified address type gives sane error message
+ //if it does npt match the configuration on server
+
+ if (isQueue(session, address)) {
+ //TODO: support auto-created queue as source, if requested by specific option
+
+ AcceptMode accept = getOption(UNACKNOWLEDGED, options).asBool() ? ACCEPT_MODE_NONE : ACCEPT_MODE_EXPLICIT;
+ AcquireMode acquire = getOption(BROWSE, options).asBool() ? ACQUIRE_MODE_NOT_ACQUIRED : ACQUIRE_MODE_PRE_ACQUIRED;
+ bool exclusive = getOption(EXCLUSIVE, options).asBool();
+ FieldTable arguments;
+ //TODO: extract subscribe arguments from options (e.g. either
+ //filter out already processed keys and send the rest, or have
+ //a nested map)
+
+ std::auto_ptr<MessageSource> source =
+ std::auto_ptr<MessageSource>(new QueueSource(address.value, accept, acquire, exclusive, arguments));
+ return source;
+ } else {
+ //TODO: extract queue options (e.g. no-local) and subscription options (e.g. less important)
+ std::auto_ptr<Subscription> bindings =
+ std::auto_ptr<Subscription>(new Subscription(getOption(NAME, options).asString(),
+ Subscription::getMode(getOption(MODE, options).asString())));
+
+ qpid::framing::ExchangeQueryResult result = session.exchangeQuery(address.value);
+ if (result.getNotFound()) {
+ throw qpid::framing::NotFoundException(QPID_MSG("Address not known: " << address));
+ } else if (result.getType() == "topic") {
+ if (filter) {
+ if (filter->type != Filter::WILDCARD) {
+ throw qpid::framing::NotImplementedException(
+ QPID_MSG("Filters of type " << filter->type << " not supported by address " << address));
+
+ }
+ for (std::vector<std::string>::const_iterator i = filter->patterns.begin(); i != filter->patterns.end(); i++) {
+ bindings->add(address.value, *i, qpid::framing::FieldTable());
+ }
+ } else {
+ //default is to receive all messages
+ bindings->add(address.value, "*", qpid::framing::FieldTable());
+ }
+ } else if (result.getType() == "fanout") {
+ if (filter) {
+ throw qpid::framing::NotImplementedException(QPID_MSG("Filters are not supported by address " << address));
+ }
+ bindings->add(address.value, address.value, qpid::framing::FieldTable());
+ } else if (result.getType() == "direct") {
+ //TODO: ????
+ } else {
+ //TODO: xml and headers exchanges
+ throw qpid::framing::NotImplementedException(QPID_MSG("Address type not recognised for " << address));
+ }
+ std::auto_ptr<MessageSource> source = std::auto_ptr<MessageSource>(bindings.release());
+ return source;
+ }
+}
+
+
+std::auto_ptr<MessageSink> AddressResolution::resolveSink(qpid::client::Session session,
+ const qpid::messaging::Address& address,
+ const qpid::messaging::Variant::Map& /*options*/)
+{
+ std::auto_ptr<MessageSink> sink;
+ if (isQueue(session, address)) {
+ //TODO: support for auto-created queues as sink
+ sink = std::auto_ptr<MessageSink>(new QueueSink(address.value));
+ } else {
+ std::string subject;
+ if (isTopic(session, address, subject)) {
+ //TODO: support for auto-created exchanges as sink
+ sink = std::auto_ptr<MessageSink>(new Exchange(address.value, subject));
+ } else {
+ if (address.type.empty()) {
+ throw qpid::framing::NotFoundException(QPID_MSG("Address not known: " << address));
+ } else {
+ throw qpid::framing::NotImplementedException(QPID_MSG("Address type not recognised: " << address.type));
+ }
+ }
+ }
+ return sink;
+}
+
+QueueSource::QueueSource(const std::string& _name, AcceptMode _acceptMode, AcquireMode _acquireMode, bool _exclusive, const FieldTable& _options) :
+ name(_name), acceptMode(_acceptMode), acquireMode(_acquireMode), exclusive(_exclusive), options(_options) {}
+
+void QueueSource::subscribe(qpid::client::AsyncSession& session, const std::string& destination)
+{
+ session.messageSubscribe(arg::queue=name,
+ arg::destination=destination,
+ arg::acceptMode=acceptMode,
+ arg::acquireMode=acquireMode,
+ arg::exclusive=exclusive,
+ arg::arguments=options);
+}
+
+void QueueSource::cancel(qpid::client::AsyncSession& session, const std::string& destination)
+{
+ session.messageCancel(destination);
+}
+
+Subscription::Subscription(const std::string& _name, SubscriptionMode mode, const FieldTable& qOptions, const FieldTable& sOptions)
+ : name(_name), autoDelete(mode == SIMPLE), durable(mode == DURABLE),
+ queueOptions(qOptions), subscriptionOptions(sOptions) {}
+
+void Subscription::add(const std::string& exchange, const std::string& key, const FieldTable& options)
+{
+ bindings.push_back(Binding(exchange, key, options));
+}
+
+void Subscription::subscribe(qpid::client::AsyncSession& session, const std::string& destination)
+{
+ if (name.empty()) {
+ //TODO: use same scheme as JMS client for subscription queue name generation?
+ queue = session.getId().getName() + destination;
+ } else {
+ queue = name;
+ }
+ session.queueDeclare(arg::queue=queue, arg::exclusive=true,
+ arg::autoDelete=autoDelete, arg::durable=durable, arg::arguments=queueOptions);
+ for (Bindings::const_iterator i = bindings.begin(); i != bindings.end(); ++i) {
+ session.exchangeBind(arg::queue=queue, arg::exchange=i->exchange, arg::bindingKey=i->key, arg::arguments=i->options);
+ }
+ AcceptMode accept = autoDelete ? ACCEPT_MODE_NONE : ACCEPT_MODE_EXPLICIT;
+ session.messageSubscribe(arg::queue=queue, arg::destination=destination,
+ arg::exclusive=true, arg::acceptMode=accept, arg::arguments=subscriptionOptions);
+}
+
+void Subscription::cancel(qpid::client::AsyncSession& session, const std::string& destination)
+{
+ session.messageCancel(destination);
+ session.queueDelete(arg::queue=queue);
+}
+
+Subscription::Binding::Binding(const std::string& e, const std::string& k, const FieldTable& o):
+ exchange(e), key(k), options(o) {}
+
+Subscription::SubscriptionMode Subscription::getMode(const std::string& s)
+{
+ if (s.empty() || s == SIMPLE_SUBSCRIPTION) return SIMPLE;
+ else if (s == RELIABLE_SUBSCRIPTION) return RELIABLE;
+ else if (s == DURABLE_SUBSCRIPTION) return DURABLE;
+ else throw Exception(QPID_MSG("Unrecognised subscription mode: " << s));
+}
+
+void convert(qpid::messaging::Message& from, qpid::client::Message& to);
+
+Exchange::Exchange(const std::string& _name, const std::string& _defaultSubject,
+ bool _passive, const std::string& _type, bool _durable, const FieldTable& _options) :
+ name(_name), defaultSubject(_defaultSubject), passive(_passive), type(_type), durable(_durable), options(_options) {}
+
+void Exchange::declare(qpid::client::AsyncSession& session, const std::string&)
+{
+ //TODO: should this really by synchronous? want to get error if not valid...
+ if (passive) {
+ sync(session).exchangeDeclare(arg::exchange=name, arg::passive=true);
+ } else {
+ sync(session).exchangeDeclare(arg::exchange=name, arg::type=type, arg::durable=durable, arg::arguments=options);
+ }
+}
+
+void Exchange::send(qpid::client::AsyncSession& session, const std::string&, qpid::messaging::Message& m)
+{
+ qpid::client::Message message;
+ convert(m, message);
+ if (message.getDeliveryProperties().getRoutingKey().empty() && !defaultSubject.empty()) {
+ message.getDeliveryProperties().setRoutingKey(defaultSubject);
+ }
+ session.messageTransfer(arg::destination=name, arg::content=message);
+}
+
+void Exchange::cancel(qpid::client::AsyncSession&, const std::string&) {}
+
+QueueSink::QueueSink(const std::string& _name, bool _passive, bool _exclusive,
+ bool _autoDelete, bool _durable, const FieldTable& _options) :
+ name(_name), passive(_passive), exclusive(_exclusive),
+ autoDelete(_autoDelete), durable(_durable), options(_options) {}
+
+void QueueSink::declare(qpid::client::AsyncSession& session, const std::string&)
+{
+ //TODO: should this really by synchronous?
+ if (passive) {
+ sync(session).queueDeclare(arg::queue=name, arg::passive=true);
+ } else {
+ sync(session).queueDeclare(arg::queue=name, arg::exclusive=exclusive, arg::durable=durable,
+ arg::autoDelete=autoDelete, arg::arguments=options);
+ }
+}
+void QueueSink::send(qpid::client::AsyncSession& session, const std::string&, qpid::messaging::Message& m)
+{
+ qpid::client::Message message;
+ convert(m, message);
+ message.getDeliveryProperties().setRoutingKey(name);
+ session.messageTransfer(arg::content=message);
+}
+
+void QueueSink::cancel(qpid::client::AsyncSession&, const std::string&) {}
+
+template <class T> void encode(qpid::messaging::Message& from)
+{
+ T codec;
+ from.encode(codec);
+ from.setContentType(T::contentType);
+}
+
+void translate(const Variant::Map& from, FieldTable& to);//implementation in Codecs.cpp
+
+void convert(qpid::messaging::Message& from, qpid::client::Message& to)
+{
+ //TODO: need to avoid copying as much as possible
+ if (from.getContent().isList()) encode<ListCodec>(from);
+ if (from.getContent().isMap()) encode<MapCodec>(from);
+ to.setData(from.getBytes());
+ to.getDeliveryProperties().setRoutingKey(from.getSubject());
+ //TODO: set other delivery properties
+ to.getMessageProperties().setContentType(from.getContentType());
+ const Address& address = from.getReplyTo();
+ if (!address.value.empty()) {
+ to.getMessageProperties().setReplyTo(AddressResolution::convert(address));
+ }
+ translate(from.getHeaders(), to.getMessageProperties().getApplicationHeaders());
+ //TODO: set other message properties
+}
+
+Address AddressResolution::convert(const qpid::framing::ReplyTo& rt)
+{
+ if (rt.getExchange().empty()) {
+ if (rt.getRoutingKey().empty()) {
+ return Address();//empty address
+ } else {
+ return Address(rt.getRoutingKey(), QUEUE_ADDRESS);
+ }
+ } else {
+ if (rt.getRoutingKey().empty()) {
+ return Address(rt.getExchange(), TOPIC_ADDRESS);
+ } else {
+ return Address(rt.getExchange() + DIVIDER + rt.getRoutingKey(), TOPIC_ADDRESS_AND_SUBJECT);
+ }
+ }
+}
+
+qpid::framing::ReplyTo AddressResolution::convert(const Address& address)
+{
+ if (address.type == QUEUE_ADDRESS || address.type.empty()) {
+ return ReplyTo(EMPTY_STRING, address.value);
+ } else if (address.type == TOPIC_ADDRESS) {
+ return ReplyTo(address.value, EMPTY_STRING);
+ } else if (address.type == TOPIC_ADDRESS_AND_SUBJECT) {
+ //need to split the value
+ string::size_type i = address.value.find(DIVIDER);
+ if (i != string::npos) {
+ std::string exchange = address.value.substr(0, i);
+ std::string routingKey;
+ if (i+1 < address.value.size()) {
+ routingKey = address.value.substr(i+1);
+ }
+ return ReplyTo(exchange, routingKey);
+ } else {
+ return ReplyTo(address.value, EMPTY_STRING);
+ }
+ } else {
+ QPID_LOG(notice, "Unrecognised type for reply-to: " << address.type);
+ //treat as queue
+ return ReplyTo(EMPTY_STRING, address.value);
+ }
+}
+
+bool isQueue(qpid::client::Session session, const qpid::messaging::Address& address)
+{
+ return address.type == QUEUE_ADDRESS ||
+ (address.type.empty() && session.queueQuery(address.value).getQueue() == address.value);
+}
+
+bool isTopic(qpid::client::Session session, const qpid::messaging::Address& address, std::string& subject)
+{
+ if (address.type.empty()) {
+ return !session.exchangeQuery(address.value).getNotFound();
+ } else if (address.type == TOPIC_ADDRESS) {
+ return true;
+ } else if (address.type == TOPIC_ADDRESS_AND_SUBJECT) {
+ string::size_type i = address.value.find(DIVIDER);
+ if (i != string::npos) {
+ std::string exchange = address.value.substr(0, i);
+ if (i+1 < address.value.size()) {
+ subject = address.value.substr(i+1);
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h
new file mode 100644
index 0000000000..87758abe6d
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h
@@ -0,0 +1,68 @@
+#ifndef QPID_CLIENT_AMQP0_10_ADDRESSRESOLUTION_H
+#define QPID_CLIENT_AMQP0_10_ADDRESSRESOLUTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/Variant.h"
+#include "qpid/client/Session.h"
+
+namespace qpid {
+
+namespace framing{
+class ReplyTo;
+}
+
+namespace messaging {
+class Address;
+class Filter;
+}
+
+namespace client {
+namespace amqp0_10 {
+
+class MessageSource;
+class MessageSink;
+
+/**
+ * Maps from a generic Address and optional Filter to an AMQP 0-10
+ * MessageSource which will then be used by a ReceiverImpl instance
+ * created for the address.
+ */
+class AddressResolution
+{
+ public:
+ std::auto_ptr<MessageSource> resolveSource(qpid::client::Session session,
+ const qpid::messaging::Address& address,
+ const qpid::messaging::Filter* filter,
+ const qpid::messaging::Variant::Map& options);
+
+ std::auto_ptr<MessageSink> resolveSink(qpid::client::Session session,
+ const qpid::messaging::Address& address,
+ const qpid::messaging::Variant::Map& options);
+
+ static qpid::messaging::Address convert(const qpid::framing::ReplyTo&);
+ static qpid::framing::ReplyTo convert(const qpid::messaging::Address&);
+
+ private:
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_ADDRESSRESOLUTION_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp b/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp
new file mode 100644
index 0000000000..9aee3118fe
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp
@@ -0,0 +1,299 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/amqp0_10/Codecs.h"
+#include "qpid/messaging/Variant.h"
+#include "qpid/framing/Array.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/framing/List.h"
+#include <algorithm>
+#include <functional>
+
+using namespace qpid::framing;
+using namespace qpid::messaging;
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+namespace {
+const std::string iso885915("iso-8859-15");
+const std::string utf8("utf8");
+const std::string utf16("utf16");
+const std::string amqp0_10_binary("amqp0-10:binary");
+const std::string amqp0_10_bit("amqp0-10:bit");
+const std::string amqp0_10_datetime("amqp0-10:datetime");
+const std::string amqp0_10_struct("amqp0-10:struct");
+}
+
+template <class T, class U, class F> void convert(const T& from, U& to, F f)
+{
+ std::transform(from.begin(), from.end(), std::inserter(to, to.begin()), f);
+}
+
+Variant::Map::value_type toVariantMapEntry(const FieldTable::value_type& in);
+FieldTable::value_type toFieldTableEntry(const Variant::Map::value_type& in);
+Variant toVariant(boost::shared_ptr<FieldValue> in);
+boost::shared_ptr<FieldValue> toFieldValue(const Variant& in);
+
+template <class T, class U, class F> void translate(boost::shared_ptr<FieldValue> in, U& u, F f)
+{
+ T t;
+ getEncodedValue<T>(in, t);
+ convert(t, u, f);
+}
+
+template <class T, class U, class F> T* toFieldValueCollection(const U& u, F f)
+{
+ typename T::ValueType t;
+ convert(u, t, f);
+ return new T(t);
+}
+
+FieldTableValue* toFieldTableValue(const Variant::Map& map)
+{
+ FieldTable ft;
+ convert(map, ft, &toFieldTableEntry);
+ return new FieldTableValue(ft);
+}
+
+ListValue* toListValue(const Variant::List& list)
+{
+ List l;
+ convert(list, l, &toFieldValue);
+ return new ListValue(l);
+}
+
+void setEncodingFor(Variant& out, uint8_t code)
+{
+ switch(code){
+ case 0x80:
+ case 0x90:
+ case 0xa0:
+ out.setEncoding(amqp0_10_binary);
+ break;
+ case 0x84:
+ case 0x94:
+ out.setEncoding(iso885915);
+ break;
+ case 0x85:
+ case 0x95:
+ out.setEncoding(utf8);
+ break;
+ case 0x86:
+ case 0x96:
+ out.setEncoding(utf16);
+ break;
+ case 0xab:
+ out.setEncoding(amqp0_10_struct);
+ break;
+ default:
+ //do nothing
+ break;
+ }
+}
+
+Variant toVariant(boost::shared_ptr<FieldValue> in)
+{
+ Variant out;
+ //based on AMQP 0-10 typecode, pick most appropriate variant type
+ switch (in->getType()) {
+ //Fixed Width types:
+ case 0x01: out.setEncoding(amqp0_10_binary);
+ case 0x02: out = in->getIntegerValue<int8_t, 1>(); break;
+ case 0x03: out = in->getIntegerValue<uint8_t, 1>(); break;
+ case 0x04: break; //TODO: iso-8859-15 char
+ case 0x08: out = in->getIntegerValue<bool, 1>(); break;
+ case 0x010: out.setEncoding(amqp0_10_binary);
+ case 0x011: out = in->getIntegerValue<int16_t, 2>(); break;
+ case 0x012: out = in->getIntegerValue<uint16_t, 2>(); break;
+ case 0x020: out.setEncoding(amqp0_10_binary);
+ case 0x021: out = in->getIntegerValue<int32_t, 4>(); break;
+ case 0x022: out = in->getIntegerValue<uint32_t, 4>(); break;
+ case 0x023: out = in->get<float>(); break;
+ case 0x027: break; //TODO: utf-32 char
+ case 0x030: out.setEncoding(amqp0_10_binary);
+ case 0x031: out = in->getIntegerValue<int64_t, 8>(); break;
+ case 0x038: out.setEncoding(amqp0_10_datetime); //treat datetime as uint64_t, but set encoding
+ case 0x032: out = in->getIntegerValue<uint64_t, 8>(); break;
+ case 0x033:out = in->get<double>(); break;
+
+ //TODO: figure out whether and how to map values with codes 0x40-0xd8
+
+ case 0xf0: break;//void, which is the default value for Variant
+ case 0xf1: out.setEncoding(amqp0_10_bit); break;//treat 'bit' as void, which is the default value for Variant
+
+ //Variable Width types:
+ //strings:
+ case 0x80:
+ case 0x84:
+ case 0x85:
+ case 0x86:
+ case 0x90:
+ case 0x94:
+ case 0x95:
+ case 0x96:
+ case 0xa0:
+ case 0xab:
+ setEncodingFor(out, in->getType());
+ out = in->get<std::string>();
+ break;
+
+ case 0xa8:
+ out = Variant::Map();
+ translate<FieldTable>(in, out.asMap(), &toVariantMapEntry);
+ break;
+
+ case 0xa9:
+ out = Variant::List();
+ translate<List>(in, out.asList(), &toVariant);
+ break;
+ case 0xaa: //convert amqp0-10 array into variant list
+ out = Variant::List();
+ translate<Array>(in, out.asList(), &toVariant);
+ break;
+
+ default:
+ //error?
+ break;
+ }
+ return out;
+}
+
+boost::shared_ptr<FieldValue> toFieldValue(const Variant& in)
+{
+ boost::shared_ptr<FieldValue> out;
+ switch (in.getType()) {
+ case VOID: out = boost::shared_ptr<FieldValue>(new VoidValue()); break;
+ case BOOL: out = boost::shared_ptr<FieldValue>(new BoolValue(in.asBool())); break;
+ case UINT8: out = boost::shared_ptr<FieldValue>(new Unsigned8Value(in.asUint8())); break;
+ case UINT16: out = boost::shared_ptr<FieldValue>(new Unsigned16Value(in.asUint16())); break;
+ case UINT32: out = boost::shared_ptr<FieldValue>(new Unsigned32Value(in.asUint32())); break;
+ case UINT64: out = boost::shared_ptr<FieldValue>(new Unsigned64Value(in.asUint64())); break;
+ case INT8: out = boost::shared_ptr<FieldValue>(new Integer8Value(in.asInt8())); break;
+ case INT16: out = boost::shared_ptr<FieldValue>(new Integer16Value(in.asInt16())); break;
+ case INT32: out = boost::shared_ptr<FieldValue>(new Integer32Value(in.asInt32())); break;
+ case INT64: out = boost::shared_ptr<FieldValue>(new Integer64Value(in.asInt64())); break;
+ case FLOAT: out = boost::shared_ptr<FieldValue>(new FloatValue(in.asFloat())); break;
+ case DOUBLE: out = boost::shared_ptr<FieldValue>(new DoubleValue(in.asDouble())); break;
+ //TODO: check encoding (and length?) when deciding what AMQP type to treat string as
+ case STRING: out = boost::shared_ptr<FieldValue>(new Str16Value(in.asString())); break;
+ case MAP:
+ //out = boost::shared_ptr<FieldValue>(toFieldValueCollection<FieldTableValue>(in.asMap(), &toFieldTableEntry));
+ out = boost::shared_ptr<FieldValue>(toFieldTableValue(in.asMap()));
+ break;
+ case LIST:
+ //out = boost::shared_ptr<FieldValue>(toFieldValueCollection<ListValue>(in.asList(), &toFieldValue));
+ out = boost::shared_ptr<FieldValue>(toListValue(in.asList()));
+ break;
+ }
+ return out;
+}
+
+Variant::Map::value_type toVariantMapEntry(const FieldTable::value_type& in)
+{
+ return Variant::Map::value_type(in.first, toVariant(in.second));
+}
+
+FieldTable::value_type toFieldTableEntry(const Variant::Map::value_type& in)
+{
+ return FieldTable::value_type(in.first, toFieldValue(in.second));
+}
+
+struct EncodeBuffer
+{
+ char* data;
+ Buffer buffer;
+
+ EncodeBuffer(size_t size) : data(new char[size]), buffer(data, size) {}
+ ~EncodeBuffer() { delete[] data; }
+
+ template <class T> void encode(T& t) { t.encode(buffer); }
+
+ void getData(std::string& s) {
+ s.assign(data, buffer.getSize());
+ }
+};
+
+struct DecodeBuffer
+{
+ Buffer buffer;
+
+ DecodeBuffer(const std::string& s) : buffer(const_cast<char*>(s.data()), s.size()) {}
+
+ template <class T> void decode(T& t) { t.decode(buffer); }
+
+};
+
+template <class T, class U, class F> void _encode(const U& value, std::string& data, F f)
+{
+ T t;
+ convert(value, t, f);
+ EncodeBuffer buffer(t.encodedSize());
+ buffer.encode(t);
+ buffer.getData(data);
+}
+
+template <class T, class U, class F> void _decode(const std::string& data, U& value, F f)
+{
+ T t;
+ DecodeBuffer buffer(data);
+ buffer.decode(t);
+ convert(t, value, f);
+}
+
+void MapCodec::encode(const Variant& value, std::string& data)
+{
+ _encode<FieldTable>(value.asMap(), data, &toFieldTableEntry);
+}
+
+void MapCodec::decode(const std::string& data, Variant& value)
+{
+ value = Variant::Map();
+ _decode<FieldTable>(data, value.asMap(), &toVariantMapEntry);
+}
+
+void ListCodec::encode(const Variant& value, std::string& data)
+{
+ _encode<List>(value.asList(), data, &toFieldValue);
+}
+
+void ListCodec::decode(const std::string& data, Variant& value)
+{
+ value = Variant::List();
+ _decode<List>(data, value.asList(), &toVariant);
+}
+
+void translate(const Variant::Map& from, FieldTable& to)
+{
+ convert(from, to, &toFieldTableEntry);
+}
+
+void translate(const FieldTable& from, Variant::Map& to)
+{
+ convert(from, to, &toVariantMapEntry);
+}
+
+const std::string ListCodec::contentType("amqp0_10/list");
+const std::string MapCodec::contentType("amqp0_10/map");
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.cpp b/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.cpp
new file mode 100644
index 0000000000..52b623b65c
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.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 "CompletionTracker.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+using qpid::framing::SequenceNumber;
+
+void CompletionTracker::track(SequenceNumber command, void* token)
+{
+ tokens[command] = token;
+}
+
+void CompletionTracker::completedTo(SequenceNumber command)
+{
+ Tokens::iterator i = tokens.lower_bound(command);
+ if (i != tokens.end()) {
+ lastCompleted = i->second;
+ tokens.erase(tokens.begin(), ++i);
+ }
+}
+
+void* CompletionTracker::getLastCompletedToken()
+{
+ return lastCompleted;
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h b/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h
new file mode 100644
index 0000000000..6147c5682e
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h
@@ -0,0 +1,50 @@
+#ifndef QPID_CLIENT_AMQP0_10_COMPLETIONTRACKER_H
+#define QPID_CLIENT_AMQP0_10_COMPLETIONTRACKER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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>
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+/**
+ * Provides a mapping from command ids to application supplied
+ * 'tokens', and is used to determine when the sending or
+ * acknowledging of a specific message is complete.
+ */
+class CompletionTracker
+{
+ public:
+ void track(qpid::framing::SequenceNumber command, void* token);
+ void completedTo(qpid::framing::SequenceNumber command);
+ void* getLastCompletedToken();
+ private:
+ typedef std::map<qpid::framing::SequenceNumber, void*> Tokens;
+ Tokens tokens;
+ void* lastCompleted;
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_COMPLETIONTRACKER_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
new file mode 100644
index 0000000000..9f738731e2
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.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 "ConnectionImpl.h"
+#include "SessionImpl.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+using qpid::messaging::Variant;
+
+template <class T> void setIfFound(const Variant::Map& map, const std::string& key, T& value)
+{
+ Variant::Map::const_iterator i = map.find(key);
+ if (i != map.end()) {
+ value = (T) i->second;
+ }
+}
+
+void convert(const Variant::Map& from, ConnectionSettings& to)
+{
+ setIfFound(from, "username", to.username);
+ setIfFound(from, "password", to.password);
+ setIfFound(from, "sasl-mechanism", to.mechanism);
+ setIfFound(from, "sasl-service", to.service);
+ setIfFound(from, "sasl-min-ssf", to.minSsf);
+ setIfFound(from, "sasl-max-ssf", to.maxSsf);
+
+ setIfFound(from, "heartbeat", to.heartbeat);
+ setIfFound(from, "tcp-nodelay", to.tcpNoDelay);
+
+ setIfFound(from, "locale", to.locale);
+ setIfFound(from, "max-channels", to.maxChannels);
+ setIfFound(from, "max-frame-size", to.maxFrameSize);
+ setIfFound(from, "bounds", to.bounds);
+}
+
+ConnectionImpl::ConnectionImpl(const std::string& url, const Variant::Map& options)
+{
+ QPID_LOG(debug, "Opening connection to " << url << " with " << options);
+ Url u(url);
+ ConnectionSettings settings;
+ convert(options, settings);
+ connection.open(u, settings);
+}
+
+void ConnectionImpl::close()
+{
+ connection.close();
+}
+
+qpid::messaging::Session ConnectionImpl::newSession()
+{
+ qpid::messaging::Session impl(new SessionImpl(connection.newSession()));
+ return impl;
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h
new file mode 100644
index 0000000000..120a8ab9d8
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h
@@ -0,0 +1,43 @@
+#ifndef QPID_CLIENT_AMQP0_10_CONNECTIONIMPL_H
+#define QPID_CLIENT_AMQP0_10_CONNECTIONIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/ConnectionImpl.h"
+#include "qpid/messaging/Variant.h"
+#include "qpid/client/Connection.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+class ConnectionImpl : public qpid::messaging::ConnectionImpl
+{
+ public:
+ ConnectionImpl(const std::string& url, const qpid::messaging::Variant::Map& options);
+ void close();
+ qpid::messaging::Session newSession();
+ private:
+ qpid::client::Connection connection;
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_CONNECTIONIMPL_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp
new file mode 100644
index 0000000000..b69c1917e6
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp
@@ -0,0 +1,241 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/amqp0_10/IncomingMessages.h"
+#include "qpid/client/amqp0_10/AddressResolution.h"
+#include "qpid/client/amqp0_10/Codecs.h"
+#include "qpid/client/SessionImpl.h"
+#include "qpid/client/SessionBase_0_10Access.h"
+#include "qpid/log/Statement.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Variant.h"
+#include "qpid/framing/DeliveryProperties.h"
+#include "qpid/framing/FrameSet.h"
+#include "qpid/framing/MessageProperties.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/enum.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+using namespace qpid::framing;
+using namespace qpid::framing::message;
+using qpid::sys::AbsTime;
+using qpid::sys::Duration;
+using qpid::messaging::Variant;
+
+namespace {
+const std::string EMPTY_STRING;
+
+
+struct GetNone : IncomingMessages::Handler
+{
+ bool accept(IncomingMessages::MessageTransfer&) { return false; }
+};
+
+struct GetAny : IncomingMessages::Handler
+{
+ bool accept(IncomingMessages::MessageTransfer& transfer)
+ {
+ transfer.retrieve(0);
+ return true;
+ }
+};
+
+struct MatchAndTrack
+{
+ const std::string destination;
+ SequenceSet ids;
+
+ MatchAndTrack(const std::string& d) : destination(d) {}
+
+ bool operator()(boost::shared_ptr<qpid::framing::FrameSet> command)
+ {
+ if (command->as<MessageTransferBody>()->getDestination() == destination) {
+ ids.add(command->getId());
+ return true;
+ } else {
+ return false;
+ }
+ }
+};
+}
+
+IncomingMessages::IncomingMessages(qpid::client::AsyncSession s) :
+ session(s),
+ incoming(SessionBase_0_10Access(session).get()->getDemux().getDefault()) {}
+
+bool IncomingMessages::get(Handler& handler, Duration timeout)
+{
+ //search through received list for any transfer of interest:
+ for (FrameSetQueue::iterator i = received.begin(); i != received.end(); i++)
+ {
+ MessageTransfer transfer(*i, *this);
+ if (handler.accept(transfer)) {
+ received.erase(i);
+ return true;
+ }
+ }
+ //none found, check incoming:
+ return process(&handler, timeout);
+}
+
+void IncomingMessages::accept()
+{
+ session.messageAccept(unaccepted);
+ unaccepted.clear();
+}
+
+void IncomingMessages::releaseAll()
+{
+ //first process any received messages...
+ while (!received.empty()) {
+ retrieve(received.front(), 0);
+ received.pop_front();
+ }
+ //then pump out any available messages from incoming queue...
+ GetAny handler;
+ while (process(&handler, 0)) ;
+ //now release all messages
+ session.messageRelease(unaccepted);
+ unaccepted.clear();
+}
+
+void IncomingMessages::releasePending(const std::string& destination)
+{
+ //first pump all available messages from incoming to received...
+ while (process(0, 0)) ;
+
+ //now remove all messages for this destination from received list, recording their ids...
+ MatchAndTrack match(destination);
+ for (FrameSetQueue::iterator i = received.begin(); i != received.end(); i = match(*i) ? received.erase(i) : ++i) ;
+ //now release those messages
+ session.messageRelease(match.ids);
+}
+
+/**
+ * Get a frameset from session queue, waiting for up to the specified
+ * duration and returning true if this could be achieved, false
+ * otherwise. If a destination is supplied, only return a message for
+ * that destination. In this case messages from other destinations
+ * will be held on a received queue.
+ */
+bool IncomingMessages::process(Handler* handler, qpid::sys::Duration duration)
+{
+ AbsTime deadline(AbsTime::now(), duration);
+ FrameSet::shared_ptr content;
+ for (Duration timeout = duration; incoming->pop(content, timeout); timeout = Duration(AbsTime::now(), deadline)) {
+ if (content->isA<MessageTransferBody>()) {
+ MessageTransfer transfer(content, *this);
+ if (handler && handler->accept(transfer)) {
+ QPID_LOG(debug, "Delivered " << *content->getMethod());
+ return true;
+ } else {
+ //received message for another destination, keep for later
+ QPID_LOG(debug, "Pushed " << *content->getMethod() << " to received queue");
+ received.push_back(content);
+ }
+ } else {
+ //TODO: handle other types of commands (e.g. message-accept, message-flow etc)
+ }
+ }
+ return false;
+}
+
+void populate(qpid::messaging::Message& message, FrameSet& command);
+
+/**
+ * Called when message is retrieved; records retrieval for subsequent
+ * acceptance, marks the command as completed and converts command to
+ * message if message is required
+ */
+void IncomingMessages::retrieve(FrameSetPtr command, qpid::messaging::Message* message)
+{
+ if (message) {
+ populate(*message, *command);
+ }
+ const MessageTransferBody* transfer = command->as<MessageTransferBody>();
+ if (transfer->getAcquireMode() == ACQUIRE_MODE_PRE_ACQUIRED && transfer->getAcceptMode() == ACCEPT_MODE_EXPLICIT) {
+ unaccepted.add(command->getId());
+ }
+ session.markCompleted(command->getId(), false, false);
+}
+
+IncomingMessages::MessageTransfer::MessageTransfer(FrameSetPtr c, IncomingMessages& p) : content(c), parent(p) {}
+
+const std::string& IncomingMessages::MessageTransfer::getDestination()
+{
+ return content->as<MessageTransferBody>()->getDestination();
+}
+void IncomingMessages::MessageTransfer::retrieve(qpid::messaging::Message* message)
+{
+ parent.retrieve(content, message);
+}
+
+void translate(const FieldTable& from, Variant::Map& to);//implemented in Codecs.cpp
+
+void populateHeaders(qpid::messaging::Message& message,
+ const DeliveryProperties* deliveryProperties,
+ const MessageProperties* messageProperties)
+{
+ if (deliveryProperties) {
+ message.setSubject(deliveryProperties->getRoutingKey());
+ //TODO: convert other delivery properties
+ }
+ if (messageProperties) {
+ message.setContentType(messageProperties->getContentType());
+ if (messageProperties->hasReplyTo()) {
+ message.setReplyTo(AddressResolution::convert(messageProperties->getReplyTo()));
+ }
+ translate(messageProperties->getApplicationHeaders(), message.getHeaders());
+ //TODO: convert other message properties
+ }
+}
+
+void populateHeaders(qpid::messaging::Message& message, const AMQHeaderBody* headers)
+{
+ populateHeaders(message, headers->get<DeliveryProperties>(), headers->get<MessageProperties>());
+}
+
+void populate(qpid::messaging::Message& message, FrameSet& command)
+{
+ //need to be able to link the message back to the transfer it was delivered by
+ //e.g. for rejecting. TODO: hide this from API
+ uint32_t commandId = command.getId();
+ message.setInternalId(reinterpret_cast<void*>(commandId));
+
+ command.getContent(message.getBytes());
+
+ populateHeaders(message, command.getHeaders());
+
+ //decode content if necessary
+ if (message.getContentType() == ListCodec::contentType) {
+ ListCodec codec;
+ message.decode(codec);
+ } else if (message.getContentType() == MapCodec::contentType) {
+ MapCodec codec;
+ message.decode(codec);
+ }
+}
+
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h
new file mode 100644
index 0000000000..c4346fd7d7
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h
@@ -0,0 +1,91 @@
+#ifndef QPID_CLIENT_AMQP0_10_INCOMINGMESSAGES_H
+#define QPID_CLIENT_AMQP0_10_INCOMINGMESSAGES_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include "qpid/client/AsyncSession.h"
+#include "qpid/framing/SequenceSet.h"
+#include "qpid/sys/BlockingQueue.h"
+#include "qpid/sys/Time.h"
+
+namespace qpid {
+
+namespace framing{
+class FrameSet;
+}
+
+namespace messaging {
+class Message;
+}
+
+namespace client {
+namespace amqp0_10 {
+
+/**
+ *
+ */
+class IncomingMessages
+{
+ public:
+ typedef boost::shared_ptr<qpid::framing::FrameSet> FrameSetPtr;
+ class MessageTransfer
+ {
+ public:
+ const std::string& getDestination();
+ void retrieve(qpid::messaging::Message* message);
+ private:
+ FrameSetPtr content;
+ IncomingMessages& parent;
+
+ MessageTransfer(FrameSetPtr, IncomingMessages&);
+ friend class IncomingMessages;
+ };
+
+ struct Handler
+ {
+ virtual ~Handler() {}
+ virtual bool accept(MessageTransfer& transfer) = 0;
+ };
+
+ IncomingMessages(qpid::client::AsyncSession session);
+ bool get(Handler& handler, qpid::sys::Duration timeout);
+ //bool get(qpid::messaging::Message& message, qpid::sys::Duration timeout);
+ //bool get(const std::string& destination, qpid::messaging::Message& message, qpid::sys::Duration timeout);
+ void accept();
+ void releaseAll();
+ void releasePending(const std::string& destination);
+ private:
+ typedef std::deque<FrameSetPtr> FrameSetQueue;
+
+ qpid::client::AsyncSession session;
+ qpid::framing::SequenceSet unaccepted;
+ boost::shared_ptr< sys::BlockingQueue<FrameSetPtr> > incoming;
+ FrameSetQueue received;
+
+ bool process(Handler*, qpid::sys::Duration);
+ void retrieve(FrameSetPtr, qpid::messaging::Message*);
+
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_INCOMINGMESSAGES_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h b/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h
new file mode 100644
index 0000000000..19d5e4ef82
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h
@@ -0,0 +1,50 @@
+#ifndef QPID_CLIENT_AMQP0_10_MESSAGESINK_H
+#define QPID_CLIENT_AMQP0_10_MESSAGESINK_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <string>
+#include "qpid/client/AsyncSession.h"
+
+namespace qpid {
+
+namespace messaging {
+class Message;
+}
+
+namespace client {
+namespace amqp0_10 {
+
+/**
+ *
+ */
+class MessageSink
+{
+ public:
+ virtual ~MessageSink() {}
+ virtual void declare(qpid::client::AsyncSession& session, const std::string& name) = 0;
+ virtual void send(qpid::client::AsyncSession& session, const std::string& name, qpid::messaging::Message& message) = 0;
+ virtual void cancel(qpid::client::AsyncSession& session, const std::string& name) = 0;
+ private:
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_MESSAGESINK_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h b/qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h
new file mode 100644
index 0000000000..74f2732f59
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h
@@ -0,0 +1,47 @@
+#ifndef QPID_CLIENT_AMQP0_10_MESSAGESOURCE_H
+#define QPID_CLIENT_AMQP0_10_MESSAGESOURCE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <string>
+#include "qpid/client/AsyncSession.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+/**
+ * Abstraction behind which the AMQP 0-10 commands required to
+ * establish (and tear down) an incoming stream of messages from a
+ * given address are hidden.
+ */
+class MessageSource
+{
+ public:
+ virtual ~MessageSource() {}
+ virtual void subscribe(qpid::client::AsyncSession& session, const std::string& destination) = 0;
+ virtual void cancel(qpid::client::AsyncSession& session, const std::string& destination) = 0;
+
+ private:
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_MESSAGESOURCE_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp
new file mode 100644
index 0000000000..e6ed4bfc4e
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.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 "ReceiverImpl.h"
+#include "MessageSource.h"
+#include "SessionImpl.h"
+#include "qpid/messaging/MessageListener.h"
+#include "qpid/messaging/Receiver.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+using qpid::messaging::Receiver;
+
+void ReceiverImpl::received(qpid::messaging::Message&)
+{
+ //TODO: should this be configurable
+ if (capacity && --window <= capacity/2) {
+ session.sendCompletion();
+ window = capacity;
+ }
+}
+
+bool ReceiverImpl::get(qpid::messaging::Message& message, qpid::sys::Duration timeout)
+{
+ return parent.get(*this, message, timeout);
+}
+
+qpid::messaging::Message ReceiverImpl::get(qpid::sys::Duration timeout)
+{
+ qpid::messaging::Message result;
+ if (!get(result, timeout)) throw Receiver::NoMessageAvailable();
+ return result;
+}
+
+bool ReceiverImpl::fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout)
+{
+ if (capacity == 0 && !cancelled) {
+ session.messageFlow(destination, CREDIT_UNIT_MESSAGE, 1);
+ if (!started) session.messageFlow(destination, CREDIT_UNIT_BYTE, byteCredit);
+ }
+
+ if (get(message, timeout)) {
+ return true;
+ } else {
+ if (!cancelled) {
+ sync(session).messageFlush(destination);
+ start();//reallocate credit
+ }
+ return get(message, 0);
+ }
+}
+
+qpid::messaging::Message ReceiverImpl::fetch(qpid::sys::Duration timeout)
+{
+ qpid::messaging::Message result;
+ if (!fetch(result, timeout)) throw Receiver::NoMessageAvailable();
+ return result;
+}
+
+void ReceiverImpl::cancel()
+{
+ if (!cancelled) {
+ //TODO: should syncronicity be an optional argument to this call?
+ source->cancel(session, destination);
+ //need to be sure cancel is complete and all incoming
+ //framesets are processed before removing the receiver
+ parent.receiverCancelled(destination);
+ cancelled = true;
+ }
+}
+
+void ReceiverImpl::start()
+{
+ if (!cancelled) {
+ started = true;
+ session.messageSetFlowMode(destination, capacity > 0);
+ session.messageFlow(destination, CREDIT_UNIT_MESSAGE, capacity);
+ session.messageFlow(destination, CREDIT_UNIT_BYTE, byteCredit);
+ window = capacity;
+ }
+}
+
+void ReceiverImpl::stop()
+{
+ session.messageStop(destination);
+ started = false;
+}
+
+void ReceiverImpl::subscribe()
+{
+ source->subscribe(session, destination);
+}
+
+void ReceiverImpl::setSession(qpid::client::AsyncSession s)
+{
+ session = s;
+ if (!cancelled) {
+ subscribe();
+ //if we were in started state before the session was changed,
+ //start again on this new session
+ //TODO: locking if receiver is to be threadsafe...
+ if (started) start();
+ }
+}
+
+void ReceiverImpl::setCapacity(uint32_t c)
+{
+ if (c != capacity) {
+ capacity = c;
+ if (!cancelled && started) {
+ stop();
+ start();
+ }
+ }
+}
+
+void ReceiverImpl::setListener(qpid::messaging::MessageListener* l) { listener = l; }
+qpid::messaging::MessageListener* ReceiverImpl::getListener() { return listener; }
+
+const std::string& ReceiverImpl::getName() const { return destination; }
+
+ReceiverImpl::ReceiverImpl(SessionImpl& p, const std::string& name, std::auto_ptr<MessageSource> s) :
+ parent(p), source(s), destination(name), byteCredit(0xFFFFFFFF),
+ capacity(0), started(false), cancelled(false), listener(0), window(0) {}
+
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h
new file mode 100644
index 0000000000..b549242d35
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h
@@ -0,0 +1,76 @@
+#ifndef QPID_CLIENT_AMQP0_10_RECEIVERIMPL_H
+#define QPID_CLIENT_AMQP0_10_RECEIVERIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/ReceiverImpl.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/sys/Time.h"
+#include <memory>
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+class MessageSource;
+class SessionImpl;
+
+/**
+ * A receiver implementation based on an AMQP 0-10 subscription.
+ */
+class ReceiverImpl : public qpid::messaging::ReceiverImpl
+{
+ public:
+
+ ReceiverImpl(SessionImpl& parent, const std::string& name, std::auto_ptr<MessageSource> source);
+
+ bool get(qpid::messaging::Message& message, qpid::sys::Duration timeout);
+ qpid::messaging::Message get(qpid::sys::Duration timeout);
+ bool fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout);
+ qpid::messaging::Message fetch(qpid::sys::Duration timeout);
+ void cancel();
+ void start();
+ void stop();
+ void subscribe();
+ void setSession(qpid::client::AsyncSession s);
+ const std::string& getName() const;
+ void setCapacity(uint32_t);
+ void setListener(qpid::messaging::MessageListener* listener);
+ qpid::messaging::MessageListener* getListener();
+ void received(qpid::messaging::Message& message);
+ private:
+ SessionImpl& parent;
+ const std::auto_ptr<MessageSource> source;
+ const std::string destination;
+ const uint32_t byteCredit;
+
+ uint32_t capacity;
+ qpid::client::AsyncSession session;
+ bool started;
+ bool cancelled;
+ qpid::messaging::MessageListener* listener;
+ uint32_t window;
+};
+
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_RECEIVERIMPL_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp
new file mode 100644
index 0000000000..ac36eb1537
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.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 "SenderImpl.h"
+#include "MessageSink.h"
+#include "SessionImpl.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+SenderImpl::SenderImpl(SessionImpl& _parent, const std::string& _name, std::auto_ptr<MessageSink> _sink) :
+ parent(_parent), name(_name), sink(_sink) {}
+
+void SenderImpl::send(qpid::messaging::Message& m)
+{
+ sink->send(session, name, m);
+}
+
+void SenderImpl::cancel()
+{
+ sink->cancel(session, name);
+ parent.senderCancelled(name);
+}
+
+void SenderImpl::setSession(qpid::client::AsyncSession s)
+{
+ session = s;
+ sink->declare(session, name);
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h
new file mode 100644
index 0000000000..e737450ba1
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h
@@ -0,0 +1,58 @@
+#ifndef QPID_CLIENT_AMQP0_10_SENDERIMPL_H
+#define QPID_CLIENT_AMQP0_10_SENDERIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/SenderImpl.h"
+#include "qpid/client/AsyncSession.h"
+#include <memory>
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+class MessageSink;
+class SessionImpl;
+
+/**
+ *
+ */
+class SenderImpl : public qpid::messaging::SenderImpl
+{
+ public:
+ SenderImpl(SessionImpl& parent, const std::string& name, std::auto_ptr<MessageSink> sink);
+ void send(qpid::messaging::Message&);
+ void cancel();
+ void setSession(qpid::client::AsyncSession);
+
+ private:
+ SessionImpl& parent;
+ const std::string name;
+ std::auto_ptr<MessageSink> sink;
+
+ qpid::client::AsyncSession session;
+ std::string destination;
+ std::string routingKey;
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_SENDERIMPL_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp
new file mode 100644
index 0000000000..9ea2a9f598
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp
@@ -0,0 +1,287 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/amqp0_10/SessionImpl.h"
+#include "qpid/client/amqp0_10/ReceiverImpl.h"
+#include "qpid/client/amqp0_10/SenderImpl.h"
+#include "qpid/client/amqp0_10/MessageSource.h"
+#include "qpid/client/amqp0_10/MessageSink.h"
+#include "qpid/client/PrivateImplRef.h"
+#include "qpid/Exception.h"
+#include "qpid/log/Statement.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Filter.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/MessageListener.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/framing/reply_exceptions.h"
+#include <boost/format.hpp>
+#include <boost/function.hpp>
+#include <boost/intrusive_ptr.hpp>
+
+using qpid::messaging::Filter;
+using qpid::messaging::Sender;
+using qpid::messaging::Receiver;
+using qpid::messaging::VariantMap;
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+SessionImpl::SessionImpl(qpid::client::Session s) : session(s), incoming(session) {}
+
+
+void SessionImpl::commit()
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ incoming.accept();
+ session.txCommit();
+}
+
+void SessionImpl::rollback()
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ for (Receivers::iterator i = receivers.begin(); i != receivers.end(); ++i) i->second.stop();
+ //ensure that stop has been processed and all previously sent
+ //messages are available for release:
+ session.sync();
+ incoming.releaseAll();
+ session.txRollback();
+ for (Receivers::iterator i = receivers.begin(); i != receivers.end(); ++i) i->second.start();
+}
+
+void SessionImpl::acknowledge()
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ incoming.accept();
+}
+
+void SessionImpl::reject(qpid::messaging::Message& m)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ //TODO: how do I get the id of the original transfer command? think this through some more...
+
+ // [tross] The following hack was added to get this code to compile on a 64-bit machine.
+ // It should be functionally equivalent to the original on a 32-bit architecture
+ // but is almost certainly not what was intended by the author.
+ uint64_t rawId(reinterpret_cast<uint64_t>(m.getInternalId()));
+ SequenceNumber id((uint32_t) ((rawId & 0xFFFFFFFF) ^ ((rawId >> 32) & 0xFFFFFFFF)));
+
+ SequenceSet set;
+ set.add(id);
+ session.messageReject(set);
+}
+
+void SessionImpl::close()
+{
+ session.close();
+}
+
+void translate(const VariantMap& options, SubscriptionSettings& settings)
+{
+ //TODO: fill this out
+ VariantMap::const_iterator i = options.find("auto_acknowledge");
+ if (i != options.end()) {
+ settings.autoAck = i->second.asInt32();
+ }
+}
+
+template <class T, class S> boost::intrusive_ptr<S> getImplPtr(T& t)
+{
+ return boost::dynamic_pointer_cast<S>(qpid::client::PrivateImplRef<T>::get(t));
+}
+
+template <class T> void getFreeKey(std::string& key, T& map)
+{
+ std::string name = key;
+ int count = 1;
+ for (typename T::const_iterator i = map.find(name); i != map.end(); i = map.find(name)) {
+ name = (boost::format("%1%_%2%") % key % ++count).str();
+ }
+ key = name;
+}
+
+Sender SessionImpl::createSender(const qpid::messaging::Address& address, const VariantMap& options)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ std::auto_ptr<MessageSink> sink = resolver.resolveSink(session, address, options);
+ std::string name = address;
+ getFreeKey(name, senders);
+ Sender sender(new SenderImpl(*this, name, sink));
+ getImplPtr<Sender, SenderImpl>(sender)->setSession(session);
+ senders[name] = sender;
+ return sender;
+}
+Receiver SessionImpl::createReceiver(const qpid::messaging::Address& address, const VariantMap& options)
+{
+ return addReceiver(address, 0, options);
+}
+Receiver SessionImpl::createReceiver(const qpid::messaging::Address& address, const Filter& filter, const VariantMap& options)
+{
+ return addReceiver(address, &filter, options);
+}
+
+Receiver SessionImpl::addReceiver(const qpid::messaging::Address& address, const Filter* filter, const VariantMap& options)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ std::auto_ptr<MessageSource> source = resolver.resolveSource(session, address, filter, options);
+ std::string name = address;
+ getFreeKey(name, receivers);
+ Receiver receiver(new ReceiverImpl(*this, name, source));
+ getImplPtr<Receiver, ReceiverImpl>(receiver)->setSession(session);
+ receivers[name] = receiver;
+ return receiver;
+}
+
+qpid::messaging::Address SessionImpl::createTempQueue(const std::string& baseName)
+{
+ std::string name = baseName + std::string("_") + session.getId().getName();
+ session.queueDeclare(arg::queue=name, arg::exclusive=true, arg::autoDelete=true);
+ return qpid::messaging::Address(name);
+}
+
+SessionImpl& SessionImpl::convert(qpid::messaging::Session& s)
+{
+ boost::intrusive_ptr<SessionImpl> impl = getImplPtr<qpid::messaging::Session, SessionImpl>(s);
+ if (!impl) {
+ throw qpid::Exception(QPID_MSG("Configuration error; require qpid::client::amqp0_10::SessionImpl"));
+ }
+ return *impl;
+}
+
+namespace {
+
+struct IncomingMessageHandler : IncomingMessages::Handler
+{
+ typedef boost::function1<bool, IncomingMessages::MessageTransfer&> Callback;
+ Callback callback;
+
+ IncomingMessageHandler(Callback c) : callback(c) {}
+
+ bool accept(IncomingMessages::MessageTransfer& transfer)
+ {
+ return callback(transfer);
+ }
+};
+
+}
+
+bool SessionImpl::accept(ReceiverImpl* receiver,
+ qpid::messaging::Message* message,
+ bool isDispatch,
+ IncomingMessages::MessageTransfer& transfer)
+{
+ if (receiver->getName() == transfer.getDestination()) {
+ transfer.retrieve(message);
+ if (isDispatch) {
+ qpid::sys::Mutex::ScopedUnlock u(lock);
+ qpid::messaging::MessageListener* listener = receiver->getListener();
+ if (listener) listener->received(*message);
+ }
+ receiver->received(*message);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool SessionImpl::acceptAny(qpid::messaging::Message* message, bool isDispatch, IncomingMessages::MessageTransfer& transfer)
+{
+ Receivers::iterator i = receivers.find(transfer.getDestination());
+ if (i == receivers.end()) {
+ QPID_LOG(error, "Received message for unknown destination " << transfer.getDestination());
+ return false;
+ } else {
+ boost::intrusive_ptr<ReceiverImpl> receiver = getImplPtr<Receiver, ReceiverImpl>(i->second);
+ return receiver && (!isDispatch || receiver->getListener()) && accept(receiver.get(), message, isDispatch, transfer);
+ }
+}
+
+bool SessionImpl::getIncoming(IncomingMessages::Handler& handler, qpid::sys::Duration timeout)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return incoming.get(handler, timeout);
+}
+
+bool SessionImpl::dispatch(qpid::sys::Duration timeout)
+{
+ qpid::messaging::Message message;
+ IncomingMessageHandler handler(boost::bind(&SessionImpl::acceptAny, this, &message, true, _1));
+ return getIncoming(handler, timeout);
+}
+
+bool SessionImpl::get(ReceiverImpl& receiver, qpid::messaging::Message& message, qpid::sys::Duration timeout)
+{
+ IncomingMessageHandler handler(boost::bind(&SessionImpl::accept, this, &receiver, &message, false, _1));
+ return getIncoming(handler, timeout);
+}
+
+bool SessionImpl::fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout)
+{
+ IncomingMessageHandler handler(boost::bind(&SessionImpl::acceptAny, this, &message, false, _1));
+ return getIncoming(handler, timeout);
+}
+
+qpid::messaging::Message SessionImpl::fetch(qpid::sys::Duration timeout)
+{
+ qpid::messaging::Message result;
+ if (!fetch(result, timeout)) throw Receiver::NoMessageAvailable();
+ return result;
+}
+
+void SessionImpl::receiverCancelled(const std::string& name)
+{
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ receivers.erase(name);
+ }
+ session.sync();
+ incoming.releasePending(name);
+}
+
+void SessionImpl::senderCancelled(const std::string& name)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ senders.erase(name);
+}
+
+void SessionImpl::sync()
+{
+ session.sync();
+}
+
+void SessionImpl::flush()
+{
+ session.flush();
+}
+
+void* SessionImpl::getLastConfirmedSent()
+{
+ return 0;
+}
+
+void* SessionImpl::getLastConfirmedAcknowledged()
+{
+ return 0;
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h
new file mode 100644
index 0000000000..6926fb0235
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h
@@ -0,0 +1,107 @@
+#ifndef QPID_CLIENT_AMQP0_10_SESSIONIMPL_H
+#define QPID_CLIENT_AMQP0_10_SESSIONIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/SessionImpl.h"
+#include "qpid/messaging/Variant.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/amqp0_10/AddressResolution.h"
+#include "qpid/client/amqp0_10/IncomingMessages.h"
+#include "qpid/sys/Mutex.h"
+
+namespace qpid {
+
+namespace messaging {
+class Address;
+class Filter;
+class Message;
+class Receiver;
+class Sender;
+class Session;
+}
+
+namespace client {
+namespace amqp0_10 {
+
+class ReceiverImpl;
+class SenderImpl;
+
+/**
+ * Implementation of the protocol independent Session interface using
+ * AMQP 0-10.
+ */
+class SessionImpl : public qpid::messaging::SessionImpl
+{
+ public:
+ SessionImpl(qpid::client::Session);
+ void commit();
+ void rollback();
+ void acknowledge();
+ void reject(qpid::messaging::Message&);
+ void close();
+ void sync();
+ void flush();
+ qpid::messaging::Address createTempQueue(const std::string& baseName);
+ qpid::messaging::Sender createSender(const qpid::messaging::Address& address,
+ const qpid::messaging::VariantMap& options);
+ qpid::messaging::Receiver createReceiver(const qpid::messaging::Address& address,
+ const qpid::messaging::VariantMap& options);
+ qpid::messaging::Receiver createReceiver(const qpid::messaging::Address& address,
+ const qpid::messaging::Filter& filter,
+ const qpid::messaging::VariantMap& options);
+
+ void* getLastConfirmedSent();
+ void* getLastConfirmedAcknowledged();
+
+ bool fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout);
+ qpid::messaging::Message fetch(qpid::sys::Duration timeout);
+ bool dispatch(qpid::sys::Duration timeout);
+
+ bool get(ReceiverImpl& receiver, qpid::messaging::Message& message, qpid::sys::Duration timeout);
+
+ void receiverCancelled(const std::string& name);
+ void senderCancelled(const std::string& name);
+
+ static SessionImpl& convert(qpid::messaging::Session&);
+
+ qpid::client::Session session;
+ private:
+ typedef std::map<std::string, qpid::messaging::Receiver> Receivers;
+ typedef std::map<std::string, qpid::messaging::Sender> Senders;
+
+ qpid::sys::Mutex lock;
+ AddressResolution resolver;
+ IncomingMessages incoming;
+ Receivers receivers;
+ Senders senders;
+
+ qpid::messaging::Receiver addReceiver(const qpid::messaging::Address& address,
+ const qpid::messaging::Filter* filter,
+ const qpid::messaging::VariantMap& options);
+ bool acceptAny(qpid::messaging::Message*, bool, IncomingMessages::MessageTransfer&);
+ bool accept(ReceiverImpl*, qpid::messaging::Message*, bool, IncomingMessages::MessageTransfer&);
+ bool getIncoming(IncomingMessages::Handler& handler, qpid::sys::Duration timeout);
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_SESSIONIMPL_H*/
diff --git a/qpid/cpp/src/qpid/cluster/Cluster.cpp b/qpid/cpp/src/qpid/cluster/Cluster.cpp
index 7bdc066767..e35d3e4175 100644
--- a/qpid/cpp/src/qpid/cluster/Cluster.cpp
+++ b/qpid/cpp/src/qpid/cluster/Cluster.cpp
@@ -323,11 +323,9 @@ void Cluster::deliver(
{
MemberId from(nodeid, pid);
framing::Buffer buf(static_cast<char*>(msg), msg_len);
- while (buf.available()) {
- Event e(Event::decodeCopy(from, buf));
- LATENCY_TRACK(if (e.getConnectionId().getMember() == self) mcast.cpgLatency.finish());
- deliverEvent(e);
- }
+ Event e(Event::decodeCopy(from, buf));
+ LATENCY_TRACK(if (e.getConnectionId().getMember() == self) mcast.cpgLatency.finish());
+ deliverEvent(e);
}
LATENCY_TRACK(sys::LatencyTracker<const char*> eventQueueLatencyTracker("EventQueue");)
diff --git a/qpid/cpp/src/qpid/cluster/Event.cpp b/qpid/cpp/src/qpid/cluster/Event.cpp
index 9fcf9f5ce1..30866d3154 100644
--- a/qpid/cpp/src/qpid/cluster/Event.cpp
+++ b/qpid/cpp/src/qpid/cluster/Event.cpp
@@ -72,7 +72,7 @@ Event Event::decodeCopy(const MemberId& m, framing::Buffer& buf) {
if (buf.available() < e.size)
throw Exception("Not enough data for multicast event");
e.store = RefCountedBuffer::create(e.size + HEADER_SIZE);
- buf.getRawData((uint8_t*)(e.getData()), e.size);
+ memcpy(e.getData(), buf.getPointer() + buf.getPosition(), e.size);
return e;
}
diff --git a/qpid/cpp/src/qpid/cluster/Multicaster.cpp b/qpid/cpp/src/qpid/cluster/Multicaster.cpp
index 4123e11c92..7e97963318 100644
--- a/qpid/cpp/src/qpid/cluster/Multicaster.cpp
+++ b/qpid/cpp/src/qpid/cluster/Multicaster.cpp
@@ -24,14 +24,10 @@
#include "qpid/log/Statement.h"
#include "qpid/framing/AMQBody.h"
#include "qpid/framing/AMQFrame.h"
-#include <boost/bind.hpp>
-#include <algorithm>
namespace qpid {
namespace cluster {
-static const int MCAST_IOV_MAX=63; // Limit imposed by CPG
-
Multicaster::Multicaster(Cpg& cpg_,
const boost::shared_ptr<sys::Poller>& poller,
boost::function<void()> onError_) :
@@ -40,8 +36,7 @@ Multicaster::Multicaster(Cpg& cpg_,
#endif
onError(onError_), cpg(cpg_),
queue(boost::bind(&Multicaster::sendMcast, this, _1), poller),
- holding(true),
- ioVector(MCAST_IOV_MAX)
+ holding(true)
{
queue.start();
}
@@ -75,29 +70,26 @@ void Multicaster::mcast(const Event& e) {
queue.push(e);
}
-Multicaster::PollableEventQueue::Batch::const_iterator Multicaster::sendMcast(
- const PollableEventQueue::Batch& events)
-{
- PollableEventQueue::Batch::const_iterator i = events.begin();
+
+Multicaster::PollableEventQueue::Batch::const_iterator Multicaster::sendMcast(const PollableEventQueue::Batch& values) {
try {
- while (i < events.end()) {
- size_t count = std::min(MCAST_IOV_MAX, int(events.end() - i));
- std::transform(i, i+count, ioVector.begin(),
- boost::bind(&Event::toIovec, _1));
- if (!cpg.mcast(&ioVector.front(), count)) {
- QPID_LOG(trace, "CPG flow control, will resend "
- << events.end() - i << " events");
- break;
+ PollableEventQueue::Batch::const_iterator i = values.begin();
+ while( i != values.end()) {
+ iovec iov = i->toIovec();
+ if (!cpg.mcast(&iov, 1)) {
+ // cpg didn't send because of CPG flow control.
+ break;
}
- i += count;
+ ++i;
}
+ return i;
}
catch (const std::exception& e) {
QPID_LOG(critical, "Multicast error: " << e.what());
queue.stop();
onError();
+ return values.end();
}
- return i;
}
void Multicaster::release() {
diff --git a/qpid/cpp/src/qpid/cluster/Multicaster.h b/qpid/cpp/src/qpid/cluster/Multicaster.h
index 652900aa0f..f2ee5099bb 100644
--- a/qpid/cpp/src/qpid/cluster/Multicaster.h
+++ b/qpid/cpp/src/qpid/cluster/Multicaster.h
@@ -29,7 +29,6 @@
#include "qpid/sys/LatencyTracker.h"
#include <boost/shared_ptr.hpp>
#include <deque>
-#include <vector>
namespace qpid {
@@ -73,7 +72,7 @@ class Multicaster
PollableEventQueue queue;
bool holding;
PlainEventQueue holdingQueue;
- std::vector< ::iovec> ioVector;
+ std::vector<struct ::iovec> ioVector;
};
}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/UpdateClient.cpp b/qpid/cpp/src/qpid/cluster/UpdateClient.cpp
index ac418ffbb6..2e557f2ab6 100644
--- a/qpid/cpp/src/qpid/cluster/UpdateClient.cpp
+++ b/qpid/cpp/src/qpid/cluster/UpdateClient.cpp
@@ -213,7 +213,7 @@ class MessageUpdater {
framing::MessageTransferBody transfer(
framing::ProtocolVersion(), UpdateClient::UPDATE, message::ACCEPT_MODE_NONE, message::ACQUIRE_MODE_PRE_ACQUIRED);
- sb.get()->send(transfer, message.payload->getFrames());
+ sb.get()->send(transfer, message.payload->getFrames(), !message.payload->isContentReleased());
if (message.payload->isContentReleased()){
uint16_t maxFrameSize = sb.get()->getConnection()->getNegotiatedSettings().maxFrameSize;
uint16_t maxContentSize = maxFrameSize - AMQFrame::frameOverhead();
diff --git a/qpid/cpp/src/qpid/cluster/WatchDogPlugin.cpp b/qpid/cpp/src/qpid/cluster/WatchDogPlugin.cpp
index 1b813411f6..43dba5e09b 100644
--- a/qpid/cpp/src/qpid/cluster/WatchDogPlugin.cpp
+++ b/qpid/cpp/src/qpid/cluster/WatchDogPlugin.cpp
@@ -16,6 +16,28 @@
*
*/
+/**@file
+
+ The watchdog plug-in will kill the qpidd broker process if it
+ becomes stuck for longer than a configured interval.
+
+ If the watchdog plugin is loaded and the --watchdog-interval=N
+ option is set then the broker starts a watchdog process and signals
+ it every N/2 seconds.
+
+ The watchdog process runs a very simple program that starts a timer
+ for N seconds, and resets the timer to N seconds whenever it is
+ signalled by the broker. If the timer ever reaches 0 the watchdog
+ kills the broker process (with kill -9) and exits.
+
+ This is useful in a cluster setting because in some insttances
+ (e.g. while resolving an error) it's possible for a stuck process
+ to hang other cluster members that are waiting for it to send a
+ message. Using the watchdog, the stuck process is terminated and
+ removed fromt the cluster allowing other members to continue and
+ clients of the stuck process to fail over to other members.
+
+*/
#include "qpid/Plugin.h"
#include "qpid/Options.h"
#include "qpid/log/Statement.h"
diff --git a/qpid/cpp/src/qpid/cluster/qpidd_watchdog.cpp b/qpid/cpp/src/qpid/cluster/qpidd_watchdog.cpp
index 0e7f4f18fd..51c5ed4b3f 100644
--- a/qpid/cpp/src/qpid/cluster/qpidd_watchdog.cpp
+++ b/qpid/cpp/src/qpid/cluster/qpidd_watchdog.cpp
@@ -18,6 +18,9 @@
* under the License.
*
*/
+
+/** @file helper executable for WatchDogPlugin.cpp */
+
#include <sys/types.h>
#include <sys/time.h>
#include <signal.h>
diff --git a/qpid/cpp/src/qpid/framing/FieldTable.cpp b/qpid/cpp/src/qpid/framing/FieldTable.cpp
index bc832e9db4..255a3b74a4 100644
--- a/qpid/cpp/src/qpid/framing/FieldTable.cpp
+++ b/qpid/cpp/src/qpid/framing/FieldTable.cpp
@@ -185,13 +185,8 @@ 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;
- }
+ value = vptr->get<T>();
+ return true;
}
return false;
}
@@ -370,5 +365,16 @@ void FieldTable::erase(const std::string& name)
values.erase(name);
}
+std::pair<FieldTable::ValueMap::iterator, bool> FieldTable::insert(const ValueMap::value_type& value)
+{
+ return values.insert(value);
+}
+
+FieldTable::ValueMap::iterator FieldTable::insert(ValueMap::iterator position, const ValueMap::value_type& value)
+{
+ return values.insert(position, value);
+}
+
+
}
}
diff --git a/qpid/cpp/src/qpid/framing/FieldValue.cpp b/qpid/cpp/src/qpid/framing/FieldValue.cpp
index 5f7248a751..5bac931b83 100644
--- a/qpid/cpp/src/qpid/framing/FieldValue.cpp
+++ b/qpid/cpp/src/qpid/framing/FieldValue.cpp
@@ -22,6 +22,7 @@
#include "qpid/framing/Array.h"
#include "qpid/framing/Buffer.h"
#include "qpid/framing/Endian.h"
+#include "qpid/framing/List.h"
#include "qpid/framing/reply_exceptions.h"
namespace qpid {
@@ -37,6 +38,8 @@ void FieldValue::setType(uint8_t type)
typeOctet = type;
if (typeOctet == 0xA8) {
data.reset(new EncodedValue<FieldTable>());
+ } else if (typeOctet == 0xA9) {
+ data.reset(new EncodedValue<List>());
} else if (typeOctet == 0xAA) {
data.reset(new EncodedValue<Array>());
} else {
@@ -164,10 +167,37 @@ FieldTableValue::FieldTableValue(const FieldTable& f) : FieldValue(0xa8, new Enc
{
}
+ListValue::ListValue(const List& l) : FieldValue(0xa9, new EncodedValue<List>(l))
+{
+}
+
ArrayValue::ArrayValue(const Array& a) : FieldValue(0xaa, new EncodedValue<Array>(a))
{
}
+VoidValue::VoidValue() : FieldValue(0xf0, new FixedWidthValue<0>()) {}
+
+BoolValue::BoolValue(bool b) :
+ FieldValue(0x08, new FixedWidthValue<1>(b))
+{}
+
+Unsigned8Value::Unsigned8Value(uint8_t v) :
+ FieldValue(0x02, new FixedWidthValue<1>(v))
+{}
+Unsigned16Value::Unsigned16Value(uint16_t v) :
+ FieldValue(0x12, new FixedWidthValue<2>(v))
+{}
+Unsigned32Value::Unsigned32Value(uint32_t v) :
+ FieldValue(0x22, new FixedWidthValue<4>(v))
+{}
+
+Integer8Value::Integer8Value(int8_t v) :
+ FieldValue(0x01, new FixedWidthValue<1>(v))
+{}
+Integer16Value::Integer16Value(int16_t v) :
+ FieldValue(0x11, new FixedWidthValue<2>(v))
+{}
+
void FieldValue::print(std::ostream& out) const {
data->print(out);
out << TypeCode(typeOctet) << '(';
@@ -177,4 +207,9 @@ void FieldValue::print(std::ostream& out) const {
out << ')';
}
+uint8_t* FieldValue::convertIfRequired(uint8_t* const octets, int width)
+{
+ return Endian::convertIfRequired(octets, width);
+}
+
}}
diff --git a/qpid/cpp/src/qpid/framing/List.cpp b/qpid/cpp/src/qpid/framing/List.cpp
new file mode 100644
index 0000000000..bde7dabbac
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/List.cpp
@@ -0,0 +1,83 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/framing/List.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/reply_exceptions.h"
+
+namespace qpid {
+namespace framing {
+
+uint32_t List::encodedSize() const
+{
+ uint32_t len(4/*size*/ + 4/*count*/);
+ for(Values::const_iterator i = values.begin(); i != values.end(); ++i) {
+ len += (*i)->encodedSize();
+ }
+ return len;
+}
+
+void List::encode(Buffer& buffer) const
+{
+ buffer.putLong(encodedSize() - 4);
+ buffer.putLong(size());
+ for (Values::const_iterator i = values.begin(); i!=values.end(); ++i) {
+ (*i)->encode(buffer);
+ }
+}
+
+void List::decode(Buffer& buffer)
+{
+ values.clear();
+ uint32_t size = buffer.getLong();
+ uint32_t available = buffer.available();
+ if (available < size) {
+ throw IllegalArgumentException(QPID_MSG("Not enough data for list, expected "
+ << size << " bytes but only " << available << " available"));
+ }
+ if (size) {
+ uint32_t count = buffer.getLong();
+ for (uint32_t i = 0; i < count; i++) {
+ ValuePtr value(new FieldValue);
+ value->decode(buffer);
+ values.push_back(value);
+ }
+ }
+}
+
+
+bool List::operator==(const List& other) const {
+ return values.size() == other.values.size() &&
+ std::equal(values.begin(), values.end(), other.values.begin());
+}
+
+std::ostream& operator<<(std::ostream& out, const List& l)
+{
+ out << "{";
+ for(List::Values::const_iterator i = l.values.begin(); i != l.values.end(); ++i) {
+ if (i != l.values.begin()) out << ", ";
+ (*i)->print(out);
+ }
+ return out << "}";
+}
+
+}} // namespace qpid::framing
diff --git a/qpid/cpp/src/qpid/messaging/Address.cpp b/qpid/cpp/src/qpid/messaging/Address.cpp
new file mode 100644
index 0000000000..ed35054a00
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/Address.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/messaging/Address.h"
+
+namespace qpid {
+namespace client {
+}
+
+namespace messaging {
+
+Address::Address() {}
+Address::Address(const std::string& address) : value(address) {}
+Address::Address(const std::string& address, const std::string& t) : value(address), type(t) {}
+Address::operator const std::string&() const { return value; }
+const std::string& Address::toStr() const { return value; }
+Address::operator bool() const { return !value.empty(); }
+bool Address::operator !() const { return value.empty(); }
+
+const std::string TYPE_SEPARATOR(":");
+
+std::ostream& operator<<(std::ostream& out, const Address& address)
+{
+ if (!address.type.empty()) {
+ out << address.type;
+ out << TYPE_SEPARATOR;
+ }
+ out << address.value;
+ return out;
+}
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/Connection.cpp b/qpid/cpp/src/qpid/messaging/Connection.cpp
new file mode 100644
index 0000000000..feb6566008
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/Connection.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/messaging/Connection.h"
+#include "qpid/messaging/ConnectionImpl.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/messaging/SessionImpl.h"
+#include "qpid/client/PrivateImplRef.h"
+#include "qpid/client/amqp0_10/ConnectionImpl.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace client {
+
+typedef PrivateImplRef<qpid::messaging::Connection> PI;
+
+}
+
+namespace messaging {
+
+using qpid::client::PI;
+
+Connection Connection::open(const std::string& url, const Variant::Map& options)
+{
+ //only support amqp 0-10 at present
+ Connection connection(new qpid::client::amqp0_10::ConnectionImpl(url, options));
+ return connection;
+}
+
+Connection::Connection(ConnectionImpl* impl) { PI::ctor(*this, impl); }
+Connection::Connection(const Connection& c) : qpid::client::Handle<ConnectionImpl>() { PI::copy(*this, c); }
+Connection& Connection::operator=(const Connection& c) { return PI::assign(*this, c); }
+Connection::~Connection() { PI::dtor(*this); }
+
+void Connection::close() { impl->close(); }
+Session Connection::newSession() { return impl->newSession(); }
+
+InvalidOptionString::InvalidOptionString(const std::string& msg) : Exception(msg) {}
+
+void parseKeyValuePair(const std::string& in, Variant::Map& out)
+{
+ std::string::size_type i = in.find('=');
+ if (i == std::string::npos || i == in.size() || in.find('=', i+1) != std::string::npos) {
+ throw InvalidOptionString(QPID_MSG("Cannot parse name-value pair from " << in));
+ } else {
+ out[in.substr(0, i)] = in.substr(i+1);
+ }
+}
+
+void parseOptionString(const std::string& in, Variant::Map& out)
+{
+ std::string::size_type start = 0;
+ std::string::size_type i = in.find('&');
+ while (i != std::string::npos) {
+ parseKeyValuePair(in.substr(start, i-start), out);
+ if (i < in.size()) {
+ start = i+1;
+ i = in.find('&', start);
+ } else {
+ i = std::string::npos;
+ }
+ }
+ parseKeyValuePair(in.substr(start), out);
+}
+
+Variant::Map parseOptionString(const std::string& in)
+{
+ Variant::Map map;
+ parseOptionString(in, map);
+ return map;
+}
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/ConnectionImpl.h b/qpid/cpp/src/qpid/messaging/ConnectionImpl.h
new file mode 100644
index 0000000000..aa9e5b5fbe
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/ConnectionImpl.h
@@ -0,0 +1,45 @@
+#ifndef QPID_MESSAGING_CONNECTIONIMPL_H
+#define QPID_MESSAGING_CONNECTIONIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <string>
+#include "qpid/RefCounted.h"
+
+namespace qpid {
+namespace client {
+}
+
+namespace messaging {
+
+class Session;
+
+class ConnectionImpl : public virtual qpid::RefCounted
+{
+ public:
+ virtual ~ConnectionImpl() {}
+ virtual void close() = 0;
+ virtual Session newSession() = 0;
+ private:
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_CONNECTIONIMPL_H*/
diff --git a/qpid/cpp/src/qpid/messaging/Filter.cpp b/qpid/cpp/src/qpid/messaging/Filter.cpp
new file mode 100644
index 0000000000..b06cbdb373
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/Filter.cpp
@@ -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.
+ *
+ */
+#include "qpid/messaging/Filter.h"
+
+namespace qpid {
+namespace client {
+}
+
+namespace messaging {
+
+Filter::Filter(std::string t, std::string pattern) : type(t) { patterns.push_back(pattern); }
+Filter::Filter(std::string t, std::string pattern1, std::string pattern2) : type(t)
+{
+ patterns.push_back(pattern1);
+ patterns.push_back(pattern2);
+}
+
+const std::string Filter::WILDCARD("WILDCARD");
+const std::string Filter::EXACT_MATCH("EXACT_MATCH");
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/Message.cpp b/qpid/cpp/src/qpid/messaging/Message.cpp
new file mode 100644
index 0000000000..e95a05db17
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/Message.cpp
@@ -0,0 +1,325 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Codec.h"
+#include "qpid/messaging/MessageContent.h"
+#include "qpid/messaging/Variant.h"
+
+namespace qpid {
+namespace client {
+}
+
+namespace messaging {
+
+namespace {
+const std::string EMPTY_STRING = "";
+}
+
+struct MessageImpl : MessageContent
+{
+ Address replyTo;
+ std::string subject;
+ std::string contentType;
+ VariantMap headers;
+
+ std::string bytes;
+ Variant content;//used only for LIST and MAP
+ VariantType type;//if LIST, MAP content holds the value; if VOID bytes holds the value
+
+ void* internalId;
+
+ MessageImpl(const std::string& c);
+ MessageImpl(const char* chars, size_t count);
+
+ void setReplyTo(const Address& d);
+ const Address& getReplyTo() const;
+
+ void setSubject(const std::string& s);
+ const std::string& getSubject() const;
+
+ void setContentType(const std::string& s);
+ const std::string& getContentType() const;
+
+ const VariantMap& getHeaders() const;
+ VariantMap& getHeaders();
+
+ void setBytes(const std::string& bytes);
+ void setBytes(const char* chars, size_t count);
+ const std::string& getBytes() const;
+ std::string& getBytes();
+
+ void setInternalId(void*);
+ void* getInternalId();
+
+ bool isVoid() const;
+
+ const std::string& asString() const;
+ std::string& asString();
+
+ const char* asChars() const;
+ size_t size() const;
+
+ const Variant::Map& asMap() const;
+ Variant::Map& asMap();
+ bool isMap() const;
+
+ const Variant::List& asList() const;
+ Variant::List& asList();
+ bool isList() const;
+
+ void clear();
+
+ void encode(Codec& codec);
+ void decode(Codec& codec);
+
+ Variant& operator[](const std::string&);
+
+ std::ostream& print(std::ostream& out) const;
+
+ //operator<< for variety of types...
+ MessageContent& operator<<(const std::string&);
+ MessageContent& operator<<(const char*);
+ MessageContent& operator<<(bool);
+ MessageContent& operator<<(int8_t);
+ MessageContent& operator<<(int16_t);
+ MessageContent& operator<<(int32_t);
+ MessageContent& operator<<(int64_t);
+ MessageContent& operator<<(uint8_t);
+ MessageContent& operator<<(uint16_t);
+ MessageContent& operator<<(uint32_t);
+ MessageContent& operator<<(uint64_t);
+ MessageContent& operator<<(double);
+ MessageContent& operator<<(float);
+
+ //assignment from string, map and list
+ MessageContent& operator=(const std::string&);
+ MessageContent& operator=(const char*);
+ MessageContent& operator=(const Variant::Map&);
+ MessageContent& operator=(const Variant::List&);
+
+ template <class T> MessageContent& append(T& t);
+};
+
+MessageImpl::MessageImpl(const std::string& c) : bytes(c), type(VOID), internalId(0) {}
+MessageImpl::MessageImpl(const char* chars, size_t count) : bytes(chars, count), type(VOID), internalId(0) {}
+
+void MessageImpl::setReplyTo(const Address& d) { replyTo = d; }
+const Address& MessageImpl::getReplyTo() const { return replyTo; }
+
+void MessageImpl::setSubject(const std::string& s) { subject = s; }
+const std::string& MessageImpl::getSubject() const { return subject; }
+
+void MessageImpl::setContentType(const std::string& s) { contentType = s; }
+const std::string& MessageImpl::getContentType() const { return contentType; }
+
+const VariantMap& MessageImpl::getHeaders() const { return headers; }
+VariantMap& MessageImpl::getHeaders() { return headers; }
+
+//should these methods be on MessageContent?
+void MessageImpl::setBytes(const std::string& c) { clear(); bytes = c; }
+void MessageImpl::setBytes(const char* chars, size_t count) { clear(); bytes.assign(chars, count); }
+const std::string& MessageImpl::getBytes() const { return bytes; }
+std::string& MessageImpl::getBytes() { return bytes; }
+
+
+Variant& MessageImpl::operator[](const std::string& key) { return asMap()[key]; }
+
+std::ostream& MessageImpl::print(std::ostream& out) const
+{
+ if (type == MAP) {
+ return out << content.asMap();
+ } else if (type == LIST) {
+ return out << content.asList();
+ } else {
+ return out << bytes;
+ }
+}
+
+template <class T> MessageContent& MessageImpl::append(T& t)
+{
+ if (type == VOID) {
+ //TODO: this is inefficient, probably want to hold on to the stream object
+ std::stringstream s;
+ s << bytes;
+ s << t;
+ bytes = s.str();
+ } else if (type == LIST) {
+ content.asList().push_back(Variant(t));
+ } else {
+ throw InvalidConversion("<< operator only valid on strings and lists");
+ }
+ return *this;
+}
+
+MessageContent& MessageImpl::operator<<(const std::string& v) { return append(v); }
+MessageContent& MessageImpl::operator<<(const char* v) { return append(v); }
+MessageContent& MessageImpl::operator<<(bool v) { return append(v); }
+MessageContent& MessageImpl::operator<<(int8_t v) { return append(v); }
+MessageContent& MessageImpl::operator<<(int16_t v) { return append(v); }
+MessageContent& MessageImpl::operator<<(int32_t v) { return append(v); }
+MessageContent& MessageImpl::operator<<(int64_t v) { return append(v); }
+MessageContent& MessageImpl::operator<<(uint8_t v) { return append(v); }
+MessageContent& MessageImpl::operator<<(uint16_t v) { return append(v); }
+MessageContent& MessageImpl::operator<<(uint32_t v) { return append(v); }
+MessageContent& MessageImpl::operator<<(uint64_t v) { return append(v); }
+MessageContent& MessageImpl::operator<<(double v) { return append(v); }
+MessageContent& MessageImpl::operator<<(float v) { return append(v); }
+MessageContent& MessageImpl::operator=(const std::string& s)
+{
+ type = VOID;
+ bytes = s;
+ return *this;
+}
+MessageContent& MessageImpl::operator=(const char* c)
+{
+ type = VOID;
+ bytes = c;
+ return *this;
+}
+MessageContent& MessageImpl::operator=(const Variant::Map& m)
+{
+ type = MAP;
+ content = m;
+ return *this;
+}
+
+MessageContent& MessageImpl::operator=(const Variant::List& l)
+{
+ type = LIST;
+ content = l;
+ return *this;
+}
+
+void MessageImpl::encode(Codec& codec)
+{
+ if (content.getType() != VOID) {
+ bytes = EMPTY_STRING;
+ codec.encode(content, bytes);
+ }
+}
+
+void MessageImpl::decode(Codec& codec)
+{
+ codec.decode(bytes, content);
+ if (content.getType() == MAP) type = MAP;
+ else if (content.getType() == LIST) type = LIST;
+ else type = VOID;//TODO: what if codec set some type other than map or list??
+}
+
+void MessageImpl::setInternalId(void* i) { internalId = i; }
+void* MessageImpl::getInternalId() { return internalId; }
+
+bool MessageImpl::isVoid() const { return type == VOID; }
+
+const std::string& MessageImpl::asString() const
+{
+ if (isVoid()) return getBytes();
+ else return content.getString();//will throw an error
+}
+std::string& MessageImpl::asString()
+{
+ if (isVoid()) return getBytes();
+ else return content.getString();//will throw an error
+}
+
+const char* MessageImpl::asChars() const
+{
+ if (!isVoid()) throw InvalidConversion("Content is of structured type.");
+ return bytes.data();
+}
+size_t MessageImpl::size() const
+{
+ return bytes.size();
+}
+
+const Variant::Map& MessageImpl::asMap() const { return content.asMap(); }
+Variant::Map& MessageImpl::asMap()
+{
+ if (isVoid()) {
+ content = Variant::Map();
+ type = MAP;
+ }
+ return content.asMap();
+}
+bool MessageImpl::isMap() const { return type == MAP; }
+
+const Variant::List& MessageImpl::asList() const { return content.asList(); }
+Variant::List& MessageImpl::asList()
+{
+ if (isVoid()) {
+ content = Variant::List();
+ type = LIST;
+ }
+ return content.asList();
+}
+bool MessageImpl::isList() const { return type == LIST; }
+
+void MessageImpl::clear() { bytes = EMPTY_STRING; content.reset(); type = VOID; }
+
+
+Message::Message(const std::string& bytes) : impl(new MessageImpl(bytes)) {}
+Message::Message(const char* bytes, size_t count) : impl(new MessageImpl(bytes, count)) {}
+
+Message::Message(const Message& m) : impl(new MessageImpl(m.getBytes())) {}
+Message::~Message() { delete impl; }
+
+Message& Message::operator=(const Message& m) { *impl = *m.impl; return *this; }
+
+void Message::setReplyTo(const Address& d) { impl->setReplyTo(d); }
+const Address& Message::getReplyTo() const { return impl->getReplyTo(); }
+
+void Message::setSubject(const std::string& s) { impl->setSubject(s); }
+const std::string& Message::getSubject() const { return impl->getSubject(); }
+
+void Message::setContentType(const std::string& s) { impl->setContentType(s); }
+const std::string& Message::getContentType() const { return impl->getContentType(); }
+
+const VariantMap& Message::getHeaders() const { return impl->getHeaders(); }
+VariantMap& Message::getHeaders() { return impl->getHeaders(); }
+
+void Message::setBytes(const std::string& c) { impl->setBytes(c); }
+void Message::setBytes(const char* chars, size_t count) { impl->setBytes(chars, count); }
+const std::string& Message::getBytes() const { return impl->getBytes(); }
+std::string& Message::getBytes() { return impl->getBytes(); }
+
+const char* Message::getRawContent() const { return impl->getBytes().data(); }
+size_t Message::getContentSize() const { return impl->getBytes().size(); }
+
+MessageContent& Message::getContent() { return *impl; }
+const MessageContent& Message::getContent() const { return *impl; }
+void Message::setContent(const std::string& s) { *impl = s; }
+void Message::setContent(const Variant::Map& m) { *impl = m; }
+void Message::setContent(const Variant::List& l) { *impl = l; }
+
+void Message::encode(Codec& codec) { impl->encode(codec); }
+
+void Message::decode(Codec& codec) { impl->decode(codec); }
+
+void Message::setInternalId(void* i) { impl->setInternalId(i); }
+void* Message::getInternalId() { return impl->getInternalId(); }
+
+std::ostream& operator<<(std::ostream& out, const MessageContent& content)
+{
+ return content.print(out);
+}
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/Receiver.cpp b/qpid/cpp/src/qpid/messaging/Receiver.cpp
new file mode 100644
index 0000000000..2e8b89d27f
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/Receiver.cpp
@@ -0,0 +1,51 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/ReceiverImpl.h"
+#include "qpid/client/PrivateImplRef.h"
+
+namespace qpid {
+namespace client {
+
+typedef PrivateImplRef<qpid::messaging::Receiver> PI;
+
+}
+
+namespace messaging {
+
+using qpid::client::PI;
+
+Receiver::Receiver(ReceiverImpl* impl) { PI::ctor(*this, impl); }
+Receiver::Receiver(const Receiver& s) : qpid::client::Handle<ReceiverImpl>() { PI::copy(*this, s); }
+Receiver::~Receiver() { PI::dtor(*this); }
+Receiver& Receiver::operator=(const Receiver& s) { return PI::assign(*this, s); }
+bool Receiver::get(Message& message, qpid::sys::Duration timeout) { return impl->get(message, timeout); }
+Message Receiver::get(qpid::sys::Duration timeout) { return impl->get(timeout); }
+bool Receiver::fetch(Message& message, qpid::sys::Duration timeout) { return impl->fetch(message, timeout); }
+Message Receiver::fetch(qpid::sys::Duration timeout) { return impl->fetch(timeout); }
+void Receiver::start() { impl->start(); }
+void Receiver::stop() { impl->stop(); }
+void Receiver::setCapacity(uint32_t c) { impl->setCapacity(c); }
+void Receiver::cancel() { impl->cancel(); }
+void Receiver::setListener(MessageListener* listener) { impl->setListener(listener); }
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/ReceiverImpl.h b/qpid/cpp/src/qpid/messaging/ReceiverImpl.h
new file mode 100644
index 0000000000..77697b730c
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/ReceiverImpl.h
@@ -0,0 +1,52 @@
+#ifndef QPID_MESSAGING_RECEIVERIMPL_H
+#define QPID_MESSAGING_RECEIVERIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/RefCounted.h"
+#include "qpid/sys/Time.h"
+
+namespace qpid {
+namespace client {
+}
+
+namespace messaging {
+
+class Message;
+class MessageListener;
+
+class ReceiverImpl : public virtual qpid::RefCounted
+{
+ public:
+ virtual ~ReceiverImpl() {}
+ virtual bool get(Message& message, qpid::sys::Duration timeout) = 0;
+ virtual Message get(qpid::sys::Duration timeout) = 0;
+ virtual bool fetch(Message& message, qpid::sys::Duration timeout) = 0;
+ virtual Message fetch(qpid::sys::Duration timeout) = 0;
+ virtual void start() = 0;
+ virtual void stop() = 0;
+ virtual void setCapacity(uint32_t) = 0;
+ virtual void cancel() = 0;
+ virtual void setListener(MessageListener*) = 0;
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_RECEIVERIMPL_H*/
diff --git a/qpid/cpp/src/qpid/messaging/Sender.cpp b/qpid/cpp/src/qpid/messaging/Sender.cpp
new file mode 100644
index 0000000000..12a3a8eb0f
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/Sender.cpp
@@ -0,0 +1,44 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/SenderImpl.h"
+#include "qpid/client/PrivateImplRef.h"
+
+namespace qpid {
+namespace client {
+
+typedef PrivateImplRef<qpid::messaging::Sender> PI;
+
+}
+
+namespace messaging {
+
+using qpid::client::PI;
+
+Sender::Sender(SenderImpl* impl) { PI::ctor(*this, impl); }
+Sender::Sender(const Sender& s) : qpid::client::Handle<SenderImpl>() { PI::copy(*this, s); }
+Sender::~Sender() { PI::dtor(*this); }
+Sender& Sender::operator=(const Sender& s) { return PI::assign(*this, s); }
+void Sender::send(Message& message) { impl->send(message); }
+void Sender::cancel() { impl->cancel(); }
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/SenderImpl.h b/qpid/cpp/src/qpid/messaging/SenderImpl.h
new file mode 100644
index 0000000000..3b61a37423
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/SenderImpl.h
@@ -0,0 +1,44 @@
+#ifndef QPID_MESSAGING_SENDERIMPL_H
+#define QPID_MESSAGING_SENDERIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/RefCounted.h"
+
+namespace qpid {
+namespace client {
+}
+
+namespace messaging {
+
+class Message;
+
+class SenderImpl : public virtual qpid::RefCounted
+{
+ public:
+ virtual ~SenderImpl() {}
+ virtual void send(Message& message) = 0;
+ virtual void cancel() = 0;
+ private:
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_SENDERIMPL_H*/
diff --git a/qpid/cpp/src/qpid/messaging/Session.cpp b/qpid/cpp/src/qpid/messaging/Session.cpp
new file mode 100644
index 0000000000..284b20dacc
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/Session.cpp
@@ -0,0 +1,117 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/Session.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Filter.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/SessionImpl.h"
+#include "qpid/client/PrivateImplRef.h"
+
+namespace qpid {
+namespace client {
+
+typedef PrivateImplRef<qpid::messaging::Session> PI;
+
+}
+
+namespace messaging {
+
+using qpid::client::PI;
+
+Session::Session(SessionImpl* impl) { PI::ctor(*this, impl); }
+Session::Session(const Session& s) : qpid::client::Handle<SessionImpl>() { PI::copy(*this, s); }
+Session::~Session() { PI::dtor(*this); }
+Session& Session::operator=(const Session& s) { return PI::assign(*this, s); }
+void Session::commit() { impl->commit(); }
+void Session::rollback() { impl->rollback(); }
+void Session::acknowledge() { impl->acknowledge(); }
+void Session::reject(Message& m) { impl->reject(m); }
+void Session::close() { impl->close(); }
+
+Sender Session::createSender(const Address& address, const VariantMap& options)
+{
+ return impl->createSender(address, options);
+}
+Receiver Session::createReceiver(const Address& address, const VariantMap& options)
+{
+ return impl->createReceiver(address, options);
+}
+Receiver Session::createReceiver(const Address& address, const Filter& filter, const VariantMap& options)
+{
+ return impl->createReceiver(address, filter, options);
+}
+
+Sender Session::createSender(const std::string& address, const VariantMap& options)
+{
+ return impl->createSender(Address(address), options);
+}
+Receiver Session::createReceiver(const std::string& address, const VariantMap& options)
+{
+ return impl->createReceiver(Address(address), options);
+}
+Receiver Session::createReceiver(const std::string& address, const Filter& filter, const VariantMap& options)
+{
+ return impl->createReceiver(Address(address), filter, options);
+}
+
+Address Session::createTempQueue(const std::string& baseName)
+{
+ return impl->createTempQueue(baseName);
+}
+
+void Session::sync()
+{
+ impl->sync();
+}
+
+void Session::flush()
+{
+ impl->flush();
+}
+
+bool Session::fetch(Message& message, qpid::sys::Duration timeout)
+{
+ return impl->fetch(message, timeout);
+}
+
+Message Session::fetch(qpid::sys::Duration timeout)
+{
+ return impl->fetch(timeout);
+}
+
+bool Session::dispatch(qpid::sys::Duration timeout)
+{
+ return impl->dispatch(timeout);
+}
+
+void* Session::getLastConfirmedSent()
+{
+ return impl->getLastConfirmedSent();
+}
+
+void* Session::getLastConfirmedAcknowledged()
+{
+ return impl->getLastConfirmedAcknowledged();
+}
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/SessionImpl.h b/qpid/cpp/src/qpid/messaging/SessionImpl.h
new file mode 100644
index 0000000000..7a7ce731f8
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/SessionImpl.h
@@ -0,0 +1,65 @@
+#ifndef QPID_MESSAGING_SESSIONIMPL_H
+#define QPID_MESSAGING_SESSIONIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/RefCounted.h"
+#include <string>
+#include "qpid/messaging/Variant.h"
+#include "qpid/sys/Time.h"
+
+namespace qpid {
+namespace client {
+}
+
+namespace messaging {
+
+class Address;
+class Filter;
+class Message;
+class Sender;
+class Receiver;
+
+class SessionImpl : public virtual qpid::RefCounted
+{
+ public:
+ virtual ~SessionImpl() {}
+ virtual void commit() = 0;
+ virtual void rollback() = 0;
+ virtual void acknowledge() = 0;
+ virtual void reject(Message&) = 0;
+ virtual void close() = 0;
+ virtual void sync() = 0;
+ virtual void flush() = 0;
+ virtual bool fetch(Message& message, qpid::sys::Duration timeout) = 0;
+ virtual Message fetch(qpid::sys::Duration timeout) = 0;
+ virtual bool dispatch(qpid::sys::Duration timeout) = 0;
+ virtual Address createTempQueue(const std::string& baseName) = 0;
+ virtual Sender createSender(const Address& address, const VariantMap& options) = 0;
+ virtual Receiver createReceiver(const Address& address, const VariantMap& options) = 0;
+ virtual Receiver createReceiver(const Address& address, const Filter& filter, const VariantMap& options) = 0;
+ virtual void* getLastConfirmedSent() = 0;
+ virtual void* getLastConfirmedAcknowledged() = 0;
+ private:
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_SESSIONIMPL_H*/
diff --git a/qpid/cpp/src/qpid/messaging/Variant.cpp b/qpid/cpp/src/qpid/messaging/Variant.cpp
new file mode 100644
index 0000000000..59770939e1
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/Variant.cpp
@@ -0,0 +1,603 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/Variant.h"
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace qpid {
+namespace client {
+}
+
+namespace messaging {
+
+InvalidConversion::InvalidConversion(const std::string& msg) : Exception(msg) {}
+
+
+namespace {
+std::string EMPTY;
+}
+
+class VariantImpl
+{
+ public:
+ VariantImpl();
+ VariantImpl(bool);
+ VariantImpl(uint8_t);
+ VariantImpl(uint16_t);
+ VariantImpl(uint32_t);
+ VariantImpl(uint64_t);
+ VariantImpl(int8_t);
+ VariantImpl(int16_t);
+ VariantImpl(int32_t);
+ VariantImpl(int64_t);
+ VariantImpl(float);
+ VariantImpl(double);
+ VariantImpl(const std::string&);
+ VariantImpl(const Variant::Map&);
+ VariantImpl(const Variant::List&);
+ ~VariantImpl();
+
+ VariantType getType() const;
+
+ bool asBool() const;
+ uint8_t asUint8() const;
+ uint16_t asUint16() const;
+ uint32_t asUint32() const;
+ uint64_t asUint64() const;
+ int8_t asInt8() const;
+ int16_t asInt16() const;
+ int32_t asInt32() const;
+ int64_t asInt64() const;
+ float asFloat() const;
+ double asDouble() const;
+ std::string asString() const;
+
+ const Variant::Map& asMap() const;
+ Variant::Map& asMap();
+ const Variant::List& asList() const;
+ Variant::List& asList();
+
+ const std::string& getString() const;
+ std::string& getString();
+
+ void setEncoding(const std::string&);
+ const std::string& getEncoding() const;
+
+ static VariantImpl* create(const Variant&);
+ private:
+ const VariantType type;
+ union {
+ bool b;
+ uint8_t ui8;
+ uint16_t ui16;
+ uint32_t ui32;
+ uint64_t ui64;
+ int8_t i8;
+ int16_t i16;
+ int32_t i32;
+ int64_t i64;
+ float f;
+ double d;
+ void* v;//variable width data
+ } value;
+ std::string encoding;//optional encoding for variable length data
+
+ std::string getTypeName(VariantType type) const;
+ template<class T> T convertFromString() const
+ {
+ std::string* s = reinterpret_cast<std::string*>(value.v);
+ try {
+ return boost::lexical_cast<T>(*s);
+ } catch(const boost::bad_lexical_cast&) {
+ throw InvalidConversion(QPID_MSG("Cannot convert " << *s));
+ }
+ }
+};
+
+
+VariantImpl::VariantImpl() : type(VOID) { value.i64 = 0; }
+VariantImpl::VariantImpl(bool b) : type(BOOL) { value.b = b; }
+VariantImpl::VariantImpl(uint8_t i) : type(UINT8) { value.ui8 = i; }
+VariantImpl::VariantImpl(uint16_t i) : type(UINT16) { value.ui16 = i; }
+VariantImpl::VariantImpl(uint32_t i) : type(UINT32) { value.ui32 = i; }
+VariantImpl::VariantImpl(uint64_t i) : type(UINT64) { value.ui64 = i; }
+VariantImpl::VariantImpl(int8_t i) : type(INT8) { value.i8 = i; }
+VariantImpl::VariantImpl(int16_t i) : type(INT16) { value.i16 = i; }
+VariantImpl::VariantImpl(int32_t i) : type(INT32) { value.i32 = i; }
+VariantImpl::VariantImpl(int64_t i) : type(INT64) { value.i64 = i; }
+VariantImpl::VariantImpl(float f) : type(FLOAT) { value.f = f; }
+VariantImpl::VariantImpl(double d) : type(DOUBLE) { value.d = d; }
+VariantImpl::VariantImpl(const std::string& s) : type(STRING) { value.v = new std::string(s); }
+VariantImpl::VariantImpl(const Variant::Map& m) : type(MAP) { value.v = new Variant::Map(m); }
+VariantImpl::VariantImpl(const Variant::List& l) : type(LIST) { value.v = new Variant::List(l); }
+
+VariantImpl::~VariantImpl() {
+ switch (type) {
+ case STRING:
+ delete reinterpret_cast<std::string*>(value.v);
+ break;
+ case MAP:
+ delete reinterpret_cast<Variant::Map*>(value.v);
+ break;
+ case LIST:
+ delete reinterpret_cast<Variant::List*>(value.v);
+ break;
+ default:
+ break;
+ }
+}
+
+VariantType VariantImpl::getType() const { return type; }
+
+namespace {
+
+bool same_char(char a, char b)
+{
+ return toupper(a) == toupper(b);
+}
+
+bool caseInsensitiveMatch(const std::string& a, const std::string& b)
+{
+ return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin(), &same_char);
+}
+
+const std::string TRUE("True");
+const std::string FALSE("False");
+
+bool toBool(const std::string& s)
+{
+ if (caseInsensitiveMatch(s, TRUE)) return true;
+ if (caseInsensitiveMatch(s, FALSE)) return false;
+ try { return boost::lexical_cast<int>(s); } catch(const boost::bad_lexical_cast&) {}
+ throw InvalidConversion(QPID_MSG("Cannot convert " << s << " to bool"));
+}
+
+}
+
+bool VariantImpl::asBool() const
+{
+ switch(type) {
+ case VOID: return false;
+ case BOOL: return value.b;
+ case UINT8: return value.ui8;
+ case UINT16: return value.ui16;
+ case UINT32: return value.ui32;
+ case UINT64: return value.ui64;
+ case INT8: return value.i8;
+ case INT16: return value.i16;
+ case INT32: return value.i32;
+ case INT64: return value.i64;
+ case STRING: return toBool(*reinterpret_cast<std::string*>(value.v));
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(BOOL)));
+ }
+}
+uint8_t VariantImpl::asUint8() const
+{
+ switch(type) {
+ case UINT8: return value.ui8;
+ case STRING: return convertFromString<uint8_t>();
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(UINT8)));
+ }
+}
+uint16_t VariantImpl::asUint16() const
+{
+ switch(type) {
+ case UINT8: return value.ui8;
+ case UINT16: return value.ui16;
+ case STRING: return convertFromString<uint16_t>();
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(UINT16)));
+ }
+}
+uint32_t VariantImpl::asUint32() const
+{
+ switch(type) {
+ case UINT8: return value.ui8;
+ case UINT16: return value.ui16;
+ case UINT32: return value.ui32;
+ case STRING: return convertFromString<uint32_t>();
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(UINT32)));
+ }
+}
+uint64_t VariantImpl::asUint64() const
+{
+ switch(type) {
+ case UINT8: return value.ui8;
+ case UINT16: return value.ui16;
+ case UINT32: return value.ui32;
+ case UINT64: return value.ui64;
+ case STRING: return convertFromString<uint64_t>();
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(UINT64)));
+ }
+}
+int8_t VariantImpl::asInt8() const
+{
+ switch(type) {
+ case INT8: return value.i8;
+ case STRING: return convertFromString<int8_t>();
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(INT8)));
+ }
+}
+int16_t VariantImpl::asInt16() const
+{
+ switch(type) {
+ case INT8: return value.i8;
+ case INT16: return value.i16;
+ case STRING: return convertFromString<int16_t>();
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(INT16)));
+ }
+}
+int32_t VariantImpl::asInt32() const
+{
+ switch(type) {
+ case INT8: return value.i8;
+ case INT16: return value.i16;
+ case INT32: return value.i32;
+ case STRING: return convertFromString<int32_t>();
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(INT32)));
+ }
+}
+int64_t VariantImpl::asInt64() const
+{
+ switch(type) {
+ case INT8: return value.i8;
+ case INT16: return value.i16;
+ case INT32: return value.i32;
+ case INT64: return value.i64;
+ case STRING: return convertFromString<int64_t>();
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(INT64)));
+ }
+}
+float VariantImpl::asFloat() const
+{
+ switch(type) {
+ case FLOAT: return value.f;
+ case STRING: return convertFromString<float>();
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(FLOAT)));
+ }
+}
+double VariantImpl::asDouble() const
+{
+ switch(type) {
+ case FLOAT: return value.f;
+ case DOUBLE: return value.d;
+ case STRING: return convertFromString<double>();
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(DOUBLE)));
+ }
+}
+std::string VariantImpl::asString() const
+{
+ switch(type) {
+ case VOID: return EMPTY;
+ case BOOL: return value.b ? TRUE : FALSE;
+ case UINT8: return boost::lexical_cast<std::string>((int) value.ui8);
+ case UINT16: return boost::lexical_cast<std::string>(value.ui16);
+ case UINT32: return boost::lexical_cast<std::string>(value.ui32);
+ case UINT64: return boost::lexical_cast<std::string>(value.ui64);
+ case INT8: return boost::lexical_cast<std::string>((int) value.i8);
+ case INT16: return boost::lexical_cast<std::string>(value.i16);
+ case INT32: return boost::lexical_cast<std::string>(value.i32);
+ case INT64: return boost::lexical_cast<std::string>(value.i64);
+ case DOUBLE: return boost::lexical_cast<std::string>(value.d);
+ case FLOAT: return boost::lexical_cast<std::string>(value.f);
+ case STRING: return *reinterpret_cast<std::string*>(value.v);
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(STRING)));
+ }
+}
+
+const Variant::Map& VariantImpl::asMap() const
+{
+ switch(type) {
+ case MAP: return *reinterpret_cast<Variant::Map*>(value.v);
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(MAP)));
+ }
+}
+
+Variant::Map& VariantImpl::asMap()
+{
+ switch(type) {
+ case MAP: return *reinterpret_cast<Variant::Map*>(value.v);
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(MAP)));
+ }
+}
+
+const Variant::List& VariantImpl::asList() const
+{
+ switch(type) {
+ case LIST: return *reinterpret_cast<Variant::List*>(value.v);
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(LIST)));
+ }
+}
+
+Variant::List& VariantImpl::asList()
+{
+ switch(type) {
+ case LIST: return *reinterpret_cast<Variant::List*>(value.v);
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(LIST)));
+ }
+}
+
+std::string& VariantImpl::getString()
+{
+ switch(type) {
+ case STRING: return *reinterpret_cast<std::string*>(value.v);
+ default: throw InvalidConversion(QPID_MSG("Variant is not a string; use asString() if conversion is required."));
+ }
+}
+
+const std::string& VariantImpl::getString() const
+{
+ switch(type) {
+ case STRING: return *reinterpret_cast<std::string*>(value.v);
+ default: throw InvalidConversion(QPID_MSG("Variant is not a string; use asString() if conversion is required."));
+ }
+}
+
+void VariantImpl::setEncoding(const std::string& s) { encoding = s; }
+const std::string& VariantImpl::getEncoding() const { return encoding; }
+
+std::string VariantImpl::getTypeName(VariantType type) const
+{
+ switch (type) {
+ case VOID: return "void";
+ case BOOL: return "bool";
+ case UINT8: return "uint8";
+ case UINT16: return "uint16";
+ case UINT32: return "uint32";
+ case UINT64: return "uint64";
+ case INT8: return "int8";
+ case INT16: return "int16";
+ case INT32: return "int32";
+ case INT64: return "int64";
+ case FLOAT: return "float";
+ case DOUBLE: return "double";
+ case STRING: return "string";
+ case MAP: return "map";
+ case LIST: return "list";
+ }
+ return "<unknown>";//should never happen
+}
+
+VariantImpl* VariantImpl::create(const Variant& v)
+{
+ switch (v.getType()) {
+ case BOOL: return new VariantImpl(v.asBool());
+ case UINT8: return new VariantImpl(v.asUint8());
+ case UINT16: return new VariantImpl(v.asUint16());
+ case UINT32: return new VariantImpl(v.asUint32());
+ case UINT64: return new VariantImpl(v.asUint64());
+ case INT8: return new VariantImpl(v.asInt8());
+ case INT16: return new VariantImpl(v.asInt16());
+ case INT32: return new VariantImpl(v.asInt32());
+ case INT64: return new VariantImpl(v.asInt64());
+ case FLOAT: return new VariantImpl(v.asFloat());
+ case DOUBLE: return new VariantImpl(v.asDouble());
+ case STRING: return new VariantImpl(v.asString());
+ case MAP: return new VariantImpl(v.asMap());
+ case LIST: return new VariantImpl(v.asList());
+ default: return new VariantImpl();
+ }
+}
+
+Variant::Variant() : impl(new VariantImpl()) {}
+Variant::Variant(bool b) : impl(new VariantImpl(b)) {}
+Variant::Variant(uint8_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(uint16_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(uint32_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(uint64_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(int8_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(int16_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(int32_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(int64_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(float f) : impl(new VariantImpl(f)) {}
+Variant::Variant(double d) : impl(new VariantImpl(d)) {}
+Variant::Variant(const std::string& s) : impl(new VariantImpl(s)) {}
+Variant::Variant(const char* s) : impl(new VariantImpl(std::string(s))) {}
+Variant::Variant(const Map& m) : impl(new VariantImpl(m)) {}
+Variant::Variant(const List& l) : impl(new VariantImpl(l)) {}
+Variant::Variant(const Variant& v) : impl(VariantImpl::create(v)) {}
+
+Variant::~Variant() { if (impl) delete impl; }
+
+void Variant::reset()
+{
+ if (impl) delete impl;
+ impl = new VariantImpl();
+}
+
+
+Variant& Variant::operator=(bool b)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(b);
+ return *this;
+}
+
+Variant& Variant::operator=(uint8_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+Variant& Variant::operator=(uint16_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+Variant& Variant::operator=(uint32_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+Variant& Variant::operator=(uint64_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+
+Variant& Variant::operator=(int8_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+Variant& Variant::operator=(int16_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+Variant& Variant::operator=(int32_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+Variant& Variant::operator=(int64_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+
+Variant& Variant::operator=(float f)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(f);
+ return *this;
+}
+Variant& Variant::operator=(double d)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(d);
+ return *this;
+}
+
+Variant& Variant::operator=(const std::string& s)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(s);
+ return *this;
+}
+
+Variant& Variant::operator=(const char* s)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(std::string(s));
+ return *this;
+}
+
+Variant& Variant::operator=(const Map& m)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(m);
+ return *this;
+}
+
+Variant& Variant::operator=(const List& l)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(l);
+ return *this;
+}
+
+Variant& Variant::operator=(const Variant& v)
+{
+ if (impl) delete impl;
+ impl = VariantImpl::create(v);
+ return *this;
+}
+
+VariantType Variant::getType() const { return impl->getType(); }
+bool Variant::asBool() const { return impl->asBool(); }
+uint8_t Variant::asUint8() const { return impl->asUint8(); }
+uint16_t Variant::asUint16() const { return impl->asUint16(); }
+uint32_t Variant::asUint32() const { return impl->asUint32(); }
+uint64_t Variant::asUint64() const { return impl->asUint64(); }
+int8_t Variant::asInt8() const { return impl->asInt8(); }
+int16_t Variant::asInt16() const { return impl->asInt16(); }
+int32_t Variant::asInt32() const { return impl->asInt32(); }
+int64_t Variant::asInt64() const { return impl->asInt64(); }
+float Variant::asFloat() const { return impl->asFloat(); }
+double Variant::asDouble() const { return impl->asDouble(); }
+std::string Variant::asString() const { return impl->asString(); }
+const Variant::Map& Variant::asMap() const { return impl->asMap(); }
+Variant::Map& Variant::asMap() { return impl->asMap(); }
+const Variant::List& Variant::asList() const { return impl->asList(); }
+Variant::List& Variant::asList() { return impl->asList(); }
+const std::string& Variant::getString() const { return impl->getString(); }
+std::string& Variant::getString() { return impl->getString(); }
+void Variant::setEncoding(const std::string& s) { impl->setEncoding(s); }
+const std::string& Variant::getEncoding() const { return impl->getEncoding(); }
+
+Variant::operator bool() const { return asBool(); }
+Variant::operator uint8_t() const { return asUint8(); }
+Variant::operator uint16_t() const { return asUint16(); }
+Variant::operator uint32_t() const { return asUint32(); }
+Variant::operator uint64_t() const { return asUint64(); }
+Variant::operator int8_t() const { return asInt8(); }
+Variant::operator int16_t() const { return asInt16(); }
+Variant::operator int32_t() const { return asInt32(); }
+Variant::operator int64_t() const { return asInt64(); }
+Variant::operator float() const { return asFloat(); }
+Variant::operator double() const { return asDouble(); }
+Variant::operator const char*() const { return asString().c_str(); }
+
+std::ostream& operator<<(std::ostream& out, const Variant::Map& map)
+{
+ for (Variant::Map::const_iterator i = map.begin(); i != map.end(); ++i) {
+ if (i != map.begin()) out << ", ";
+ out << i->first << ":" << i->second;
+ }
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const Variant::List& list)
+{
+ for (Variant::List::const_iterator i = list.begin(); i != list.end(); ++i) {
+ if (i != list.begin()) out << ", ";
+ out << *i;
+ }
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const Variant& value)
+{
+ switch (value.getType()) {
+ case MAP:
+ out << "{" << value.asMap() << "}";
+ break;
+ case LIST:
+ out << "[" << value.asList() << "]";
+ break;
+ case VOID:
+ out << "<void>";
+ break;
+ default:
+ out << value.asString();
+ break;
+ }
+ return out;
+}
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/sys/Timer.cpp b/qpid/cpp/src/qpid/sys/Timer.cpp
index d569fb3bb7..3d2a900455 100644
--- a/qpid/cpp/src/qpid/sys/Timer.cpp
+++ b/qpid/cpp/src/qpid/sys/Timer.cpp
@@ -84,6 +84,7 @@ Timer::~Timer()
stop();
}
+// TODO AStitcher 21/08/09 The threshholds for emitting warnings are a little arbitrary
void Timer::run()
{
Monitor::ScopedLock l(monitor);
@@ -111,8 +112,8 @@ void Timer::run()
// Warn on callback overrun
AbsTime end(AbsTime::now());
Duration overrun(tasks.top()->nextFireTime, end);
- bool late = delay > 1 * TIME_MSEC;
- bool overran = overrun > 1 * TIME_MSEC;
+ bool late = delay > 10 * TIME_MSEC;
+ bool overran = overrun > 2 * TIME_MSEC;
if (late)
if (overran) {
QPID_LOG(warning,
diff --git a/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp b/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp
index dcd2f71a65..8905b87838 100644
--- a/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp
+++ b/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp
@@ -45,19 +45,6 @@ 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.
*/
@@ -642,12 +629,10 @@ void AsynchIO::close(void) {
}
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)
@@ -674,10 +659,8 @@ void AsynchIO::writeComplete(AsynchWriteResult *result) {
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
diff --git a/qpid/cpp/src/qpidd.cpp b/qpid/cpp/src/qpidd.cpp
index 9c3c6b0c4b..1839a62205 100644
--- a/qpid/cpp/src/qpidd.cpp
+++ b/qpid/cpp/src/qpidd.cpp
@@ -77,7 +77,7 @@ int main(int argc, char* argv[])
return broker.execute(options.get());
}
catch(const exception& e) {
- QPID_LOG(critical, "Broker start-up failed: " << e.what());
+ QPID_LOG(critical, "Unexpected error: " << e.what());
}
return 1;
}
diff --git a/qpid/cpp/src/tests/CMakeLists.txt b/qpid/cpp/src/tests/CMakeLists.txt
index 34f5d35a9a..56a4aaf2b0 100644
--- a/qpid/cpp/src/tests/CMakeLists.txt
+++ b/qpid/cpp/src/tests/CMakeLists.txt
@@ -95,6 +95,7 @@ set(unit_tests_to_build
InlineAllocator
InlineVector
ClientSessionTest
+ MessagingSessionTest
SequenceSet
StringUtils
IncompleteMessageList
@@ -128,6 +129,7 @@ set(unit_tests_to_build
ReplicationTest
ClientMessageTest
PollableCondition
+ Variant
${xml_tests}
CACHE STRING "Which unit tests to build"
)
diff --git a/qpid/cpp/src/tests/FieldTable.cpp b/qpid/cpp/src/tests/FieldTable.cpp
index a02bbd5194..5b43871f6d 100644
--- a/qpid/cpp/src/tests/FieldTable.cpp
+++ b/qpid/cpp/src/tests/FieldTable.cpp
@@ -22,6 +22,7 @@
#include "qpid/framing/Array.h"
#include "qpid/framing/FieldTable.h"
#include "qpid/framing/FieldValue.h"
+#include "qpid/framing/List.h"
#include "qpid/sys/alloca.h"
#include "unit_test.h"
@@ -86,7 +87,9 @@ QPID_AUTO_TEST_CASE(testAssignment)
QPID_AUTO_TEST_CASE(testNestedValues)
{
- char buff[100];
+ double d = 1.2345;
+ uint32_t u = 101;
+ char buff[1000];
{
FieldTable a;
FieldTable b;
@@ -94,11 +97,17 @@ QPID_AUTO_TEST_CASE(testNestedValues)
items.push_back("one");
items.push_back("two");
Array c(items);
+ List list;
+ list.push_back(List::ValuePtr(new Str16Value("red")));
+ list.push_back(List::ValuePtr(new Unsigned32Value(u)));
+ list.push_back(List::ValuePtr(new Str8Value("yellow")));
+ list.push_back(List::ValuePtr(new DoubleValue(d)));
a.setString("id", "A");
b.setString("id", "B");
a.setTable("B", b);
a.setArray("C", c);
+ a.set("my-list", FieldTable::ValuePtr(new ListValue(list)));
Buffer wbuffer(buff, 100);
@@ -119,6 +128,27 @@ QPID_AUTO_TEST_CASE(testNestedValues)
BOOST_CHECK((uint) 2 == items.size());
BOOST_CHECK(string("one") == items[0]);
BOOST_CHECK(string("two") == items[1]);
+
+ List list;
+ BOOST_CHECK(a.get("my-list")->get<List>(list));
+ List::const_iterator i = list.begin();
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(std::string("red"), (*i)->get<std::string>());
+
+ i++;
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(u, (uint32_t) (*i)->get<int>());
+
+ i++;
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(std::string("yellow"), (*i)->get<std::string>());
+
+ i++;
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(d, (*i)->get<double>());
+
+ i++;
+ BOOST_CHECK(i == list.end());
}
}
diff --git a/qpid/cpp/src/tests/Makefile.am b/qpid/cpp/src/tests/Makefile.am
index db4c8ba914..2e04c85b93 100644
--- a/qpid/cpp/src/tests/Makefile.am
+++ b/qpid/cpp/src/tests/Makefile.am
@@ -65,6 +65,7 @@ unit_test_LDADD=-lboost_unit_test_framework -lboost_regex \
$(lib_client) $(lib_broker) $(lib_console)
unit_test_SOURCES= unit_test.cpp unit_test.h \
+ MessagingSessionTests.cpp \
ClientSessionTest.cpp \
BrokerFixture.h SocketProxy.h \
exception_test.cpp \
@@ -111,7 +112,8 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \
FrameDecoder.cpp \
ReplicationTest.cpp \
ClientMessageTest.cpp \
- PollableCondition.cpp
+ PollableCondition.cpp \
+ Variant.cpp
if HAVE_XML
unit_test_SOURCES+= XmlClientSessionTest.cpp
diff --git a/qpid/cpp/src/tests/MessagingSessionTests.cpp b/qpid/cpp/src/tests/MessagingSessionTests.cpp
new file mode 100644
index 0000000000..6eebf717bf
--- /dev/null
+++ b/qpid/cpp/src/tests/MessagingSessionTests.cpp
@@ -0,0 +1,325 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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/messaging/Connection.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/MessageListener.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/Session.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/sys/Time.h"
+#include <boost/assign.hpp>
+#include <boost/format.hpp>
+#include <string>
+#include <vector>
+
+QPID_AUTO_TEST_SUITE(MessagingSessionTests)
+
+using namespace qpid::messaging;
+using namespace qpid;
+using qpid::broker::Broker;
+
+struct BrokerAdmin
+{
+ qpid::client::Connection connection;
+ qpid::client::Session session;
+
+ BrokerAdmin(uint16_t port)
+ {
+ connection.open("localhost", port);
+ session = connection.newSession();
+ }
+
+ void createQueue(const std::string& name)
+ {
+ session.queueDeclare(qpid::client::arg::queue=name);
+ }
+
+ void deleteQueue(const std::string& name)
+ {
+ session.queueDelete(qpid::client::arg::queue=name);
+ }
+
+ void createExchange(const std::string& name, const std::string& type)
+ {
+ session.exchangeDeclare(qpid::client::arg::exchange=name, qpid::client::arg::type=type);
+ }
+
+ void deleteExchange(const std::string& name)
+ {
+ session.exchangeDelete(qpid::client::arg::exchange=name);
+ }
+
+ ~BrokerAdmin()
+ {
+ session.close();
+ connection.close();
+ }
+};
+
+struct MessagingFixture : public BrokerFixture
+{
+ Connection connection;
+ Session session;
+ BrokerAdmin admin;
+
+ MessagingFixture(Broker::Options opts = Broker::Options()) :
+ BrokerFixture(opts),
+ connection(Connection::open((boost::format("amqp:tcp:localhost:%1%") % (broker->getPort(Broker::TCP_TRANSPORT))).str())),
+ session(connection.newSession()),
+ admin(broker->getPort(Broker::TCP_TRANSPORT)) {}
+
+ ~MessagingFixture()
+ {
+ session.close();
+ connection.close();
+ }
+};
+
+struct QueueFixture : MessagingFixture
+{
+ std::string queue;
+
+ QueueFixture(const std::string& name = "test-queue") : queue(name)
+ {
+ admin.createQueue(queue);
+ }
+
+ ~QueueFixture()
+ {
+ admin.deleteQueue(queue);
+ }
+
+};
+
+struct TopicFixture : MessagingFixture
+{
+ std::string topic;
+
+ TopicFixture(const std::string& name = "test-topic", const std::string& type="fanout") : topic(name)
+ {
+ admin.createExchange(topic, type);
+ }
+
+ ~TopicFixture()
+ {
+ admin.deleteExchange(topic);
+ }
+
+};
+
+struct MultiQueueFixture : MessagingFixture
+{
+ typedef std::vector<std::string>::const_iterator const_iterator;
+ std::vector<std::string> queues;
+
+ MultiQueueFixture(const std::vector<std::string>& names = boost::assign::list_of<std::string>("q1")("q2")("q3")) : queues(names)
+ {
+ for (const_iterator i = queues.begin(); i != queues.end(); ++i) {
+ admin.createQueue(*i);
+ }
+ }
+
+ ~MultiQueueFixture()
+ {
+ for (const_iterator i = queues.begin(); i != queues.end(); ++i) {
+ admin.deleteQueue(*i);
+ }
+ }
+
+};
+
+struct MessageDataCollector : MessageListener
+{
+ std::vector<std::string> messageData;
+
+ void received(Message& message) {
+ messageData.push_back(message.getBytes());
+ }
+};
+
+std::vector<std::string> fetch(Receiver& receiver, int count, qpid::sys::Duration timeout=qpid::sys::TIME_SEC*5)
+{
+ std::vector<std::string> data;
+ Message message;
+ for (int i = 0; i < count && receiver.fetch(message, timeout); i++) {
+ data.push_back(message.getBytes());
+ }
+ return data;
+}
+
+QPID_AUTO_TEST_CASE(testSimpleSendReceive)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("test-message");
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * qpid::sys::TIME_SEC);
+ fix.session.acknowledge();
+ BOOST_CHECK_EQUAL(in.getBytes(), out.getBytes());
+}
+
+QPID_AUTO_TEST_CASE(testSenderError)
+{
+ MessagingFixture fix;
+ //TODO: this is the wrong type for the exception; define explicit set in messaging namespace
+ BOOST_CHECK_THROW(fix.session.createSender("NonExistentAddress"), qpid::framing::NotFoundException);
+}
+
+QPID_AUTO_TEST_CASE(testReceiverError)
+{
+ MessagingFixture fix;
+ //TODO: this is the wrong type for the exception; define explicit set in messaging namespace
+ BOOST_CHECK_THROW(fix.session.createReceiver("NonExistentAddress"), qpid::framing::NotFoundException);
+}
+
+QPID_AUTO_TEST_CASE(testSimpleTopic)
+{
+ TopicFixture fix;
+
+ Sender sender = fix.session.createSender(fix.topic);
+ Message msg("one");
+ sender.send(msg);
+ Receiver sub1 = fix.session.createReceiver(fix.topic);
+ sub1.setCapacity(10u);
+ sub1.start();
+ msg.setBytes("two");
+ sender.send(msg);
+ Receiver sub2 = fix.session.createReceiver(fix.topic);
+ sub2.setCapacity(10u);
+ sub2.start();
+ msg.setBytes("three");
+ sender.send(msg);
+ Receiver sub3 = fix.session.createReceiver(fix.topic);
+ sub3.setCapacity(10u);
+ sub3.start();
+ msg.setBytes("four");
+ sender.send(msg);
+ BOOST_CHECK_EQUAL(fetch(sub2, 2), boost::assign::list_of<std::string>("three")("four"));
+ sub2.cancel();
+
+ msg.setBytes("five");
+ sender.send(msg);
+ BOOST_CHECK_EQUAL(fetch(sub1, 4), boost::assign::list_of<std::string>("two")("three")("four")("five"));
+ BOOST_CHECK_EQUAL(fetch(sub3, 2), boost::assign::list_of<std::string>("four")("five"));
+ Message in;
+ BOOST_CHECK(!sub2.fetch(in, 0));//TODO: or should this raise an error?
+
+
+ //TODO: check pending messages...
+}
+
+QPID_AUTO_TEST_CASE(testSessionFetch)
+{
+ MultiQueueFixture fix;
+
+ for (uint i = 0; i < fix.queues.size(); i++) {
+ Receiver r = fix.session.createReceiver(fix.queues[i]);
+ r.setCapacity(10u);
+ r.start();//TODO: add Session::start
+ }
+
+ for (uint i = 0; i < fix.queues.size(); i++) {
+ Sender s = fix.session.createSender(fix.queues[i]);
+ Message msg((boost::format("Message_%1%") % (i+1)).str());
+ s.send(msg);
+ }
+
+ for (uint i = 0; i < fix.queues.size(); i++) {
+ Message msg;
+ BOOST_CHECK(fix.session.fetch(msg, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL(msg.getBytes(), (boost::format("Message_%1%") % (i+1)).str());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testSessionDispatch)
+{
+ MultiQueueFixture fix;
+
+ MessageDataCollector collector;
+ for (uint i = 0; i < fix.queues.size(); i++) {
+ Receiver r = fix.session.createReceiver(fix.queues[i]);
+ r.setListener(&collector);
+ r.setCapacity(10u);
+ r.start();//TODO: add Session::start
+ }
+
+ for (uint i = 0; i < fix.queues.size(); i++) {
+ Sender s = fix.session.createSender(fix.queues[i]);
+ Message msg((boost::format("Message_%1%") % (i+1)).str());
+ s.send(msg);
+ }
+
+ while (fix.session.dispatch(qpid::sys::TIME_SEC)) ;
+
+ BOOST_CHECK_EQUAL(collector.messageData, boost::assign::list_of<std::string>("Message_1")("Message_2")("Message_3"));
+}
+
+
+QPID_AUTO_TEST_CASE(testMapMessage)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out;
+ out.getContent().asMap()["abc"] = "def";
+ out.getContent().asMap()["pi"] = 3.14f;
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * qpid::sys::TIME_SEC);
+ BOOST_CHECK_EQUAL(in.getBytes(), out.getBytes());
+ BOOST_CHECK_EQUAL(in.getContent().asMap()["abc"].asString(), "def");
+ BOOST_CHECK_EQUAL(in.getContent().asMap()["pi"].asFloat(), 3.14f);
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testListMessage)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out;
+ out.getContent() = Variant::List();
+ out.getContent() << "abc";
+ out.getContent() << 1234;
+ out.getContent() << "def";
+ out.getContent() << 56.789;
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * qpid::sys::TIME_SEC);
+ BOOST_CHECK_EQUAL(in.getBytes(), out.getBytes());
+ Variant::List& list = in.getContent().asList();
+ BOOST_CHECK_EQUAL(list.size(), out.getContent().asList().size());
+ BOOST_CHECK_EQUAL(list.front().asString(), "abc");
+ list.pop_front();
+ BOOST_CHECK_EQUAL(list.front().asInt64(), 1234);
+ list.pop_front();
+ BOOST_CHECK_EQUAL(list.front().asString(), "def");
+ list.pop_front();
+ BOOST_CHECK_EQUAL(list.front().asDouble(), 56.789);
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/Variant.cpp b/qpid/cpp/src/tests/Variant.cpp
new file mode 100644
index 0000000000..1bf2ed98ce
--- /dev/null
+++ b/qpid/cpp/src/tests/Variant.cpp
@@ -0,0 +1,157 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <iostream>
+#include "qpid/messaging/Variant.h"
+
+#include "unit_test.h"
+
+using namespace qpid::messaging;
+
+QPID_AUTO_TEST_SUITE(VariantSuite)
+
+QPID_AUTO_TEST_CASE(testConversions)
+{
+ Variant value;
+
+ //string to float/double
+ value = "1.5";
+ BOOST_CHECK_EQUAL((float) 1.5, value.asFloat());
+ BOOST_CHECK_EQUAL((double) 1.5, value.asDouble());
+
+ //float to string or double
+ value = 1.5f;
+ BOOST_CHECK_EQUAL((float) 1.5, value.asFloat());
+ BOOST_CHECK_EQUAL((double) 1.5, value.asDouble());
+ BOOST_CHECK_EQUAL(std::string("1.5"), value.asString());
+
+ //double to string (conversion to float not valid)
+ value = 1.5;
+ BOOST_CHECK_THROW(value.asFloat(), InvalidConversion);
+ BOOST_CHECK_EQUAL((double) 1.5, value.asDouble());
+ BOOST_CHECK_EQUAL(std::string("1.5"), value.asString());
+
+ //uint8 to larger unsigned ints and string
+ value = (uint8_t) 7;
+ BOOST_CHECK_EQUAL((uint8_t) 7, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 7, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 7, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 7, value.asUint64());
+ BOOST_CHECK_EQUAL(std::string("7"), value.asString());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+
+ value = (uint16_t) 8;
+ BOOST_CHECK_EQUAL(std::string("8"), value.asString());
+ value = (uint32_t) 9;
+ BOOST_CHECK_EQUAL(std::string("9"), value.asString());
+
+ //uint32 to larger unsigned ints and string
+ value = (uint32_t) 9999999;
+ BOOST_CHECK_EQUAL((uint32_t) 9999999, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 9999999, value.asUint64());
+ BOOST_CHECK_EQUAL(std::string("9999999"), value.asString());
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+
+ value = "true";
+ BOOST_CHECK(value.asBool());
+ value = "false";
+ BOOST_CHECK(!value.asBool());
+ value = "1";
+ BOOST_CHECK(value.asBool());
+ value = "0";
+ BOOST_CHECK(!value.asBool());
+ value = "other";
+ BOOST_CHECK_THROW(value.asBool(), InvalidConversion);
+}
+
+QPID_AUTO_TEST_CASE(testAssignment)
+{
+ Variant value("abc");
+ Variant other = value;
+ BOOST_CHECK_EQUAL(STRING, value.getType());
+ BOOST_CHECK_EQUAL(other.getType(), value.getType());
+ BOOST_CHECK_EQUAL(other.asString(), value.asString());
+
+ const uint32_t i(1000);
+ value = i;
+ BOOST_CHECK_EQUAL(UINT32, value.getType());
+ BOOST_CHECK_EQUAL(STRING, other.getType());
+}
+
+QPID_AUTO_TEST_CASE(testList)
+{
+ const std::string s("abc");
+ const float f(9.876);
+ const int16_t x(1000);
+
+ Variant value = Variant::List();
+ value.asList().push_back(Variant(s));
+ value.asList().push_back(Variant(f));
+ value.asList().push_back(Variant(x));
+ BOOST_CHECK_EQUAL(3u, value.asList().size());
+ Variant::List::const_iterator i = value.asList().begin();
+
+ BOOST_CHECK(i != value.asList().end());
+ BOOST_CHECK_EQUAL(STRING, i->getType());
+ BOOST_CHECK_EQUAL(s, i->asString());
+ i++;
+
+ BOOST_CHECK(i != value.asList().end());
+ BOOST_CHECK_EQUAL(FLOAT, i->getType());
+ BOOST_CHECK_EQUAL(f, i->asFloat());
+ i++;
+
+ BOOST_CHECK(i != value.asList().end());
+ BOOST_CHECK_EQUAL(INT16, i->getType());
+ BOOST_CHECK_EQUAL(x, i->asInt16());
+ i++;
+
+ BOOST_CHECK(i == value.asList().end());
+}
+
+QPID_AUTO_TEST_CASE(testMap)
+{
+ const std::string red("red");
+ const float pi(3.14);
+ const int16_t x(1000);
+
+ Variant value = Variant::Map();
+ value.asMap()["colour"] = red;
+ value.asMap()["pi"] = pi;
+ value.asMap()["my-key"] = x;
+ BOOST_CHECK_EQUAL(3u, value.asMap().size());
+
+ BOOST_CHECK_EQUAL(STRING, value.asMap()["colour"].getType());
+ BOOST_CHECK_EQUAL(red, value.asMap()["colour"].asString());
+
+ BOOST_CHECK_EQUAL(FLOAT, value.asMap()["pi"].getType());
+ BOOST_CHECK_EQUAL(pi, value.asMap()["pi"].asFloat());
+
+ BOOST_CHECK_EQUAL(INT16, value.asMap()["my-key"].getType());
+ BOOST_CHECK_EQUAL(x, value.asMap()["my-key"].asInt16());
+
+ value.asMap()["my-key"] = "now it's a string";
+ BOOST_CHECK_EQUAL(STRING, value.asMap()["my-key"].getType());
+ BOOST_CHECK_EQUAL(std::string("now it's a string"), value.asMap()["my-key"].asString());
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/cli_tests.py b/qpid/cpp/src/tests/cli_tests.py
index 2af1a20a87..4309b66271 100755
--- a/qpid/cpp/src/tests/cli_tests.py
+++ b/qpid/cpp/src/tests/cli_tests.py
@@ -123,6 +123,28 @@ class CliTests(TestBase010):
found = True
self.assertEqual(found, False)
+ def test_qpid_config_altex(self):
+ self.startQmf();
+ qmf = self.qmf
+ exName = "testalt"
+ altName = "amq.direct"
+
+ ret = os.system(self.command(" add exchange topic %s --alternate-exchange=%s" % (exName, altName)))
+ self.assertEqual(ret, 0)
+
+ exchanges = qmf.getObjects(_class="exchange")
+ found = False
+ for exchange in exchanges:
+ if exchange.name == altName:
+ self.assertEqual(exchange.altExchange, None)
+
+ if exchange.name == exName:
+ found = True
+ if not exchange.altExchange:
+ self.fail("Alternate exchange not set")
+ self.assertEqual(exchange._altExchange_.name, altName)
+ self.assertEqual(found, True)
+
def test_qpid_route(self):
self.startQmf();
qmf = self.qmf
diff --git a/qpid/cpp/src/tests/federation.py b/qpid/cpp/src/tests/federation.py
index daf831f3ed..174932adf8 100755
--- a/qpid/cpp/src/tests/federation.py
+++ b/qpid/cpp/src/tests/federation.py
@@ -32,6 +32,17 @@ class FederationTests(TestBase010):
def remote_port(self):
return int(self.defines["remote-port"])
+ def verify_cleanup(self):
+ attempts = 0
+ total = len(self.qmf.getObjects(_class="bridge")) + len(self.qmf.getObjects(_class="link"))
+ while total > 0:
+ attempts += 1
+ if attempts >= 10:
+ self.fail("Bridges and links didn't clean up")
+ return
+ sleep(1)
+ total = len(self.qmf.getObjects(_class="bridge")) + len(self.qmf.getObjects(_class="link"))
+
def test_bridge_create_and_close(self):
self.startQmf();
qmf = self.qmf
@@ -51,9 +62,7 @@ class FederationTests(TestBase010):
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)
+ self.verify_cleanup()
def test_pull_from_exchange(self):
session = self.session
@@ -98,9 +107,7 @@ class FederationTests(TestBase010):
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)
+ self.verify_cleanup()
def test_push_to_exchange(self):
session = self.session
@@ -144,9 +151,7 @@ class FederationTests(TestBase010):
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)
+ self.verify_cleanup()
def test_pull_from_queue(self):
session = self.session
@@ -199,9 +204,7 @@ class FederationTests(TestBase010):
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)
+ self.verify_cleanup()
def test_tracing_automatic(self):
remoteUrl = "%s:%d" % (self.remote_host(), self.remote_port())
@@ -312,9 +315,7 @@ class FederationTests(TestBase010):
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)
+ self.verify_cleanup()
def test_dynamic_fanout(self):
session = self.session
@@ -358,9 +359,7 @@ class FederationTests(TestBase010):
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)
+ self.verify_cleanup()
def test_dynamic_direct(self):
@@ -405,10 +404,7 @@ class FederationTests(TestBase010):
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)
-
+ self.verify_cleanup()
def test_dynamic_topic(self):
session = self.session
@@ -452,9 +448,7 @@ class FederationTests(TestBase010):
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)
+ self.verify_cleanup()
def test_dynamic_topic_reorigin(self):
session = self.session
@@ -512,11 +506,7 @@ class FederationTests(TestBase010):
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)
-
-
+ self.verify_cleanup()
def test_dynamic_direct_reorigin(self):
session = self.session
@@ -574,11 +564,7 @@ class FederationTests(TestBase010):
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)
-
-
+ self.verify_cleanup()
def getProperty(self, msg, name):
for h in msg.headers:
diff --git a/qpid/cpp/src/tests/perftest.cpp b/qpid/cpp/src/tests/perftest.cpp
index f55528fcd5..d383e0eb80 100644
--- a/qpid/cpp/src/tests/perftest.cpp
+++ b/qpid/cpp/src/tests/perftest.cpp
@@ -75,6 +75,7 @@ struct Opts : public TestOptions {
// Queue policy
uint32_t queueMaxCount;
uint64_t queueMaxSize;
+ std::string baseName;
bool queueDurable;
// Publisher
@@ -106,8 +107,8 @@ struct Opts : public TestOptions {
static const std::string helpText;
Opts() :
- TestOptions(helpText),
- setup(false), control(false), publish(false), subscribe(false),
+ TestOptions(helpText),
+ setup(false), control(false), publish(false), subscribe(false), baseName("perftest"),
pubs(1), count(500000), size(1024), confirm(true), durable(false), uniqueData(false), syncPub(false),
subs(1), ack(0),
qt(1),singleConnect(false), iterations(1), mode(SHARED), summary(false),
@@ -144,6 +145,7 @@ struct Opts : public TestOptions {
("queue-max-count", optValue(queueMaxCount, "N"), "queue policy: count to trigger 'flow to disk'")
("queue-max-size", optValue(queueMaxSize, "N"), "queue policy: accumulated size to trigger 'flow to disk'")
+ ("base-name", optValue(baseName, "NAME"), "base name used for queues or topics")
("queue-durable", optValue(queueDurable, "N"), "Make queue durable (implied if durable set)")
("interval_sub", optValue(intervalSub, "ms"), ">=0 delay between msg consume")
@@ -219,6 +221,13 @@ const std::string Opts::helpText=
Opts opts;
Connection globalConnection;
+std::string fqn(const std::string& name)
+{
+ ostringstream fqn;
+ fqn << opts.baseName << "_" << name;
+ return fqn.str();
+}
+
struct Client : public Runnable {
Connection* connection;
Connection localConnection;
@@ -257,18 +266,18 @@ struct Setup : public Client {
}
void run() {
- queueInit("pub_start");
- queueInit("pub_done");
- queueInit("sub_ready");
- queueInit("sub_done");
- if (opts.iterations > 1) queueInit("sub_iteration");
+ queueInit(fqn("pub_start"));
+ queueInit(fqn("pub_done"));
+ queueInit(fqn("sub_ready"));
+ queueInit(fqn("sub_done"));
+ if (opts.iterations > 1) queueInit(fqn("sub_iteration"));
if (opts.mode==SHARED) {
framing::FieldTable settings;//queue policy settings
settings.setInt("qpid.max_count", opts.queueMaxCount);
settings.setInt("qpid.max_size", opts.queueMaxSize);
for (size_t i = 0; i < opts.qt; ++i) {
ostringstream qname;
- qname << "perftest" << i;
+ qname << opts.baseName << i;
queueInit(qname.str(), opts.durable || opts.queueDurable, settings);
}
}
@@ -384,13 +393,13 @@ struct Controller : public Client {
void run() { // Controller
try {
// Wait for subscribers to be ready.
- process(opts.totalSubs, "sub_ready", bind(expect, _1, "ready"));
+ process(opts.totalSubs, fqn("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");
+ subs.subscribe(pubDone, fqn("pub_done"));
+ subs.subscribe(subDone, fqn("sub_done"));
double txrateTotal(0);
double mbytesTotal(0);
@@ -399,16 +408,16 @@ struct Controller : public Client {
for (size_t j = 0; j < opts.iterations; ++j) {
AbsTime start=now();
- send(opts.totalPubs, "pub_start", "start"); // Start publishers
+ send(opts.totalPubs, fqn("pub_start"), "start"); // Start publishers
if (j) {
- send(opts.totalPubs, "sub_iteration", "next"); // Start subscribers on next iteration
+ send(opts.totalPubs, fqn("sub_iteration"), "next"); // Start subscribers on next iteration
}
Stats pubRates;
Stats subRates;
- process(opts.totalPubs, pubDone, "pub_done", boost::ref(pubRates));
- process(opts.totalSubs, subDone, "sub_done", boost::ref(subRates));
+ process(opts.totalPubs, pubDone, fqn("pub_done"), boost::ref(pubRates));
+ process(opts.totalSubs, subDone, fqn("sub_done"), boost::ref(subRates));
AbsTime end=now();
@@ -497,7 +506,7 @@ struct PublishThread : public Client {
SubscriptionManager subs(session);
LocalQueue lq;
subs.setFlowControl(1, SubscriptionManager::UNLIMITED, true);
- subs.subscribe(lq, "pub_start");
+ subs.subscribe(lq, fqn("pub_start"));
for (size_t j = 0; j < opts.iterations; ++j) {
expect(lq.pop().getData(), "start");
@@ -533,7 +542,7 @@ struct PublishThread : public Client {
double time=secs(start,end);
// Send result to controller.
- Message report(lexical_cast<string>(opts.count/time), "pub_done");
+ Message report(lexical_cast<string>(opts.count/time), fqn("pub_done"));
session.messageTransfer(arg::content=report, arg::acceptMode=1);
if (opts.txPub){
sync(session).txCommit();
@@ -587,7 +596,7 @@ struct SubscribeThread : public Client {
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);
+ session.messageTransfer(arg::content=Message("ready", fqn("sub_ready")), arg::acceptMode=1);
if (opts.txSub) {
if (opts.commitAsync) session.txCommit();
else sync(session).txCommit();
@@ -595,13 +604,13 @@ struct SubscribeThread : public Client {
LocalQueue iterationControl;
if (opts.iterations > 1) {
- subs.subscribe(iterationControl, "sub_iteration", SubscriptionSettings(FlowControl::messageCredit(0)));
+ subs.subscribe(iterationControl, fqn("sub_iteration"), SubscriptionSettings(FlowControl::messageCredit(0)));
}
for (size_t j = 0; j < opts.iterations; ++j) {
if (j > 0) {
//need to wait here until all subs are done
- session.messageFlow("sub_iteration", 0, 1);
+ session.messageFlow(fqn("sub_iteration"), 0, 1);
iterationControl.pop();
//need to allocate some more credit for subscription
@@ -643,7 +652,7 @@ struct SubscribeThread : public Client {
// Report to publisher.
Message result(lexical_cast<string>(opts.subQuota/secs(start,end)),
- "sub_done");
+ fqn("sub_done"));
session.messageTransfer(arg::content=result, arg::acceptMode=1);
if (opts.txSub) sync(session).txCommit();
}
@@ -680,7 +689,7 @@ int main(int argc, char** argv) {
// 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.
+ key << opts.baseName << i; // Queue or topic name.
if (opts.publish) {
size_t n = singleProcess ? opts.pubs : 1;
for (size_t j = 0; j < n; ++j) {
diff --git a/qpid/java/broker/bin/qpid-server.bat b/qpid/java/broker/bin/qpid-server.bat
index 2687baa111..2687baa111 100755..100644
--- a/qpid/java/broker/bin/qpid-server.bat
+++ b/qpid/java/broker/bin/qpid-server.bat
diff --git a/qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java b/qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java
new file mode 100644
index 0000000000..4afc209ba7
--- /dev/null
+++ b/qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java
@@ -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.
+ *
+ */
+package org.apache.log4j.xml;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.logging.management.LoggingManagementMBean;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Substitute for the Log4J XMLWatchdog (as used by DOMConfigurator.configureAndWatch)
+ *
+ * Extends the default behaviour with a strict parser check on the XML file before allowing the reconfiguration to proceed,
+ * ensuring that any parser error or warning prevents initiation of a configuration update by Log4J, which aborts mid-update
+ * upon fatal errors from the parser and proceeds in the event of 'regular' parser errors and warnings, in all cases allowing
+ * startup to proceed with whatever half-baked configuration then exists.
+ */
+public class QpidLog4JConfigurator
+{
+ //lock to protect access to the configuration file
+ //shared with LoggingManagementMBean
+ public static final ReentrantLock LOCK = new ReentrantLock();
+ private static Logger _logger;
+ private static DOMConfigurator domConfig = new DOMConfigurator();
+
+ private QpidLog4JConfigurator()
+ {
+ //no instances
+ }
+
+ public static void configure(String filename) throws IOException, ParserConfigurationException,
+ SAXException, IllegalLoggerLevelException
+ {
+ try
+ {
+ LOCK.lock();
+
+ parseXMLConfigFile(filename);
+ checkLoggerLevels(filename);
+
+ DOMConfigurator.configure(filename);
+
+ if(_logger == null)
+ {
+ _logger = Logger.getLogger(QpidLog4JConfigurator.class);
+ }
+ }
+ finally
+ {
+ LOCK.unlock();
+ }
+ }
+
+ public static void configureAndWatch(String filename, long delay) throws IOException, ParserConfigurationException,
+ SAXException, IllegalLoggerLevelException
+ {
+ parseXMLConfigFile(filename);
+ checkLoggerLevels(filename);
+
+ QpidLog4JXMLWatchdog watchdog = new QpidLog4JXMLWatchdog(filename);
+ watchdog.setDelay(delay);
+ watchdog.start();
+ }
+
+ private static void parseXMLConfigFile(String fileName) throws IOException, SAXException,
+ ParserConfigurationException
+ {
+ try
+ {
+ LOCK.lock();
+
+ //check file was specified, exists, and is readable
+ if(fileName == null)
+ {
+ throw new IOException("Provided log4j XML configuration filename was null");
+ }
+
+ File configFile = new File(fileName);
+
+ if (!configFile.exists())
+ {
+ throw new IOException("The log4j XML configuration file does not exist: " + fileName);
+ }
+ else if (!configFile.canRead())
+ {
+ throw new IOException("The log4j XML configuration file is not readable: " + fileName);
+ }
+
+ //parse it
+ DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder docBuilder;
+
+ ErrorHandler errHandler = new QpidLog4JSaxErrorHandler();
+
+ docFactory.setValidating(true);
+ docBuilder = docFactory.newDocumentBuilder();
+ docBuilder.setErrorHandler(errHandler);
+ docBuilder.setEntityResolver(new Log4jEntityResolver());
+ docBuilder.parse(fileName);
+ }
+ finally
+ {
+ LOCK.unlock();
+ }
+ }
+
+ public static class QpidLog4JSaxErrorHandler implements ErrorHandler
+ {
+ public void error(SAXParseException e) throws SAXException
+ {
+ if(_logger != null)
+ {
+ _logger.warn(constructMessage("Error parsing XML file", e));
+ }
+ else
+ {
+ System.err.println(constructMessage("Error parsing XML file", e));
+ }
+ }
+
+ public void fatalError(SAXParseException e) throws SAXException
+ {
+ throw new SAXException(constructMessage("Fatal error parsing XML file", e));
+ }
+
+ public void warning(SAXParseException e) throws SAXException
+ {
+ if(_logger != null)
+ {
+ _logger.warn(constructMessage("Warning parsing XML file", e));
+ }
+ else
+ {
+ System.err.println(constructMessage("Warning parsing XML file", e));
+ }
+ }
+
+ private static String constructMessage(final String msg, final SAXParseException ex)
+ {
+ return new String(msg + ": Line " + ex.getLineNumber()+" column " +ex.getColumnNumber() + ": " + ex.getMessage());
+ }
+ }
+
+ private static class QpidLog4JXMLWatchdog extends XMLWatchdog
+ {
+ public QpidLog4JXMLWatchdog(String filename)
+ {
+ super(filename);
+ }
+
+ public void doOnChange()
+ {
+ try
+ {
+ LOCK.lock();
+
+ try
+ {
+ parseXMLConfigFile(filename);
+ }
+ catch (Exception e)
+ {
+ //logger will be instantiated following first configuration success, which has been pre-validated
+ //and so the null check should never actually be required.
+ if(_logger != null)
+ {
+ _logger.warn("Parsing the log4j XML configuration file generated errors/warnings. " +
+ "The new configuration was not applied. Correct the issues to prompt " +
+ "another update attempt: " + e.getMessage());
+ }
+ return;
+ }
+
+ try
+ {
+ checkLoggerLevels(filename);
+ }
+ catch (Exception e)
+ {
+ //logger will be instantiated following first configuration success, which has been pre-validated
+ //and so the null check should never actually be required.
+ if(_logger != null)
+ {
+ _logger.warn("Errors were found when validating the logger level values in the " +
+ "log4j XML configuration file. The new configuration was not applied. " +
+ "Correct the issues to prompt another update attempt: " + e.getMessage());
+ }
+ return;
+ }
+
+ //everything checked was ok, let the normal update process proceed
+ super.doOnChange();
+
+ //a configuration has now been applied, enable logging for future attempts
+ if(_logger == null)
+ {
+ _logger = Logger.getLogger(QpidLog4JConfigurator.class);
+ }
+
+ _logger.info("Applied log4j configuration from: " + filename);
+ }
+ finally
+ {
+ LOCK.unlock();
+ }
+
+ }
+ }
+
+ protected static void checkLoggerLevels(String filename) throws IllegalLoggerLevelException, IOException
+ {
+ //check that the logger levels specified in the XML are actually valid
+
+ try
+ {
+ LOCK.lock();
+
+ //get the Logger levels to check
+ Map<String, String> loggersLevels;
+ loggersLevels = LoggingManagementMBean.retrieveConfigFileLoggersLevels(filename);
+ //add the RootLogger to the list too
+ String rootLoggerlevelString = LoggingManagementMBean.retrieveConfigFileRootLoggerLevel(filename);
+ loggersLevels.put("Root", rootLoggerlevelString);
+
+
+ for (String loggerName : loggersLevels.keySet())
+ {
+ String levelString = loggersLevels.get(loggerName);
+
+ //let log4j replace any properties in the string
+ String log4jConfiguredString = domConfig.subst(levelString);
+
+ if(log4jConfiguredString.equals("") & ! log4jConfiguredString.equals(levelString))
+ {
+ //log4j has returned an empty string but this isnt what we gave it.
+ //There may have been an undefined property. Unlike an incorrect
+ //literal value, we will allow this case to proceed, but warn users.
+
+ if(_logger != null)
+ {
+ _logger.warn("Unable to detect Level value from '" + levelString
+ +"' for logger '" + loggerName + "', Log4J will default this to DEBUG");
+ }
+ else
+ {
+ System.err.println("Unable to detect Level value from '" + levelString
+ +"' for logger " + loggerName + ", Log4J will default this to DEBUG");
+ }
+
+ continue;
+ }
+
+ checkLevel(loggerName,log4jConfiguredString);
+ }
+ }
+ finally
+ {
+ LOCK.unlock();
+ }
+ }
+
+ private static void checkLevel(String loggerName, String levelString) throws IllegalLoggerLevelException
+ {
+ if("null".equalsIgnoreCase(levelString) || "inherited".equalsIgnoreCase(levelString))
+ {
+ //the string "null" signals to inherit from a parent logger
+ return;
+ }
+
+ Level level = Level.toLevel(levelString);
+
+ //above Level.toLevel call returns a DEBUG Level if the request fails. Check the result.
+ if (level.equals(Level.DEBUG) && !(levelString.equalsIgnoreCase("debug")))
+ {
+ //received DEBUG but we did not ask for it, the Level request failed.
+ throw new IllegalLoggerLevelException("Level '" + levelString + "' specified for Logger '" + loggerName + "' is invalid");
+ }
+ }
+
+ public static class IllegalLoggerLevelException extends Exception
+ {
+ private static final long serialVersionUID = 1L;
+
+ public IllegalLoggerLevelException(String msg)
+ {
+ super(msg);
+ }
+ }
+}
+
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java
index d94e9e7498..8a1437f3d6 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java
@@ -27,9 +27,9 @@ import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
-import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
-import org.apache.log4j.xml.DOMConfigurator;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.xml.QpidLog4JConfigurator;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.FixedSizeByteBufferAllocator;
import org.apache.mina.common.IoAcceptor;
@@ -62,9 +62,11 @@ import org.apache.qpid.server.transport.QpidAcceptor;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.net.BindException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.util.Properties;
/**
* Main entry point for AMQPD.
@@ -73,8 +75,8 @@ import java.net.InetSocketAddress;
@SuppressWarnings({"AccessStaticViaInstance"})
public class Main
{
- private static final Logger _logger = Logger.getLogger(Main.class);
- public static final Logger _brokerLogger = Logger.getLogger("Qpid.Broker");
+ private static Logger _logger;
+ private static Logger _brokerLogger;
private static final String DEFAULT_CONFIG_FILE = "etc/config.xml";
@@ -206,7 +208,7 @@ public class Main
}
catch (InitException e)
{
- System.out.println(e.getMessage());
+ System.out.println("Initialisation Error : " + e.getMessage());
_brokerLogger.error("Initialisation Error : " + e.getMessage());
shutdown(1);
}
@@ -499,7 +501,18 @@ public class Main
public static void main(String[] args)
{
-
+ //if the -Dlog4j.configuration property has not been set, enable the init override
+ //to stop Log4J wondering off and picking up the first log4j.xml/properties file it
+ //finds from the classpath when we get the first Loggers
+ if(System.getProperty("log4j.configuration") == null)
+ {
+ System.setProperty("log4j.defaultInitOverride", "true");
+ }
+
+ //now that the override status is know, we can instantiate the Loggers
+ _logger = Logger.getLogger(Main.class);
+ _brokerLogger = Logger.getLogger("Qpid.Broker");
+
new Main(args);
}
@@ -531,7 +544,7 @@ public class Main
return ip;
}
- private void configureLogging(File logConfigFile, int logWatchTime)
+ private void configureLogging(File logConfigFile, int logWatchTime) throws InitException, IOException
{
if (logConfigFile.exists() && logConfigFile.canRead())
{
@@ -542,18 +555,43 @@ public class Main
System.out.println("log file " + logConfigFile.getAbsolutePath() + " will be checked for changes every "
+ logWatchTime + " seconds");
// log4j expects the watch interval in milliseconds
- DOMConfigurator.configureAndWatch(logConfigFile.getAbsolutePath(), logWatchTime * 1000);
+ try
+ {
+ QpidLog4JConfigurator.configureAndWatch(logConfigFile.getPath(), logWatchTime * 1000);
+ }
+ catch (Exception e)
+ {
+ throw new InitException(e.getMessage(),e);
+ }
}
else
{
- DOMConfigurator.configure(logConfigFile.getAbsolutePath());
+ try
+ {
+ QpidLog4JConfigurator.configure(logConfigFile.getPath());
+ }
+ catch (Exception e)
+ {
+ throw new InitException(e.getMessage(),e);
+ }
}
}
else
{
System.err.println("Logging configuration error: unable to read file " + logConfigFile.getAbsolutePath());
- System.err.println("Using basic log4j configuration");
- BasicConfigurator.configure();
+ System.err.println("Using the fallback internal log4j.properties configuration");
+
+ InputStream propsFile = this.getClass().getResourceAsStream("/log4j.properties");
+ if(propsFile == null)
+ {
+ throw new IOException("Unable to load the fallback internal log4j.properties configuration file");
+ }
+ else
+ {
+ Properties fallbackProps = new Properties();
+ fallbackProps.load(propsFile);
+ PropertyConfigurator.configure(fallbackProps);
+ }
}
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java
index 0a2d678073..12867b224d 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java
@@ -206,11 +206,21 @@ public class HeadersExchange extends AbstractExchange
for (int i = 0; i < bindings.length; i++)
{
String[] keyAndValue = bindings[i].split("=");
- if (keyAndValue == null || keyAndValue.length < 2)
+ if (keyAndValue == null || keyAndValue.length == 0 || keyAndValue.length > 2)
{
throw new JMException("Format for headers binding should be \"<attribute1>=<value1>,<attribute2>=<value2>\" ");
}
- bindingMap.setString(keyAndValue[0], keyAndValue[1]);
+
+ if(keyAndValue.length ==1)
+ {
+ //no value was given, only a key. Use an empty value
+ //to signal match on key presence alone
+ bindingMap.setString(keyAndValue[0], "");
+ }
+ else
+ {
+ bindingMap.setString(keyAndValue[0], keyAndValue[1]);
+ }
}
_bindings.add(new Registration(new HeadersBinding(bindingMap), queue, new AMQShortString(binding)));
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java
index 3cebb1353b..3c47cdd094 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java
@@ -20,20 +20,28 @@
*/
package org.apache.qpid.server.logging.management;
+import static org.apache.log4j.xml.QpidLog4JConfigurator.LOCK;
+
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import org.apache.qpid.management.common.mbeans.LoggingManagement;
import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription;
import org.apache.qpid.server.management.AMQManagedObject;
+import org.apache.qpid.util.FileUtils;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.xml.Log4jEntityResolver;
+import org.apache.log4j.xml.QpidLog4JConfigurator;
+import org.apache.log4j.xml.QpidLog4JConfigurator.QpidLog4JSaxErrorHandler;
+import org.apache.log4j.xml.QpidLog4JConfigurator.IllegalLoggerLevelException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
@@ -70,10 +78,12 @@ public class LoggingManagementMBean extends AMQManagedObject implements LoggingM
private static final Logger _logger = Logger.getLogger(LoggingManagementMBean.class);
private String _log4jConfigFileName;
private int _log4jLogWatchInterval;
+ private static final String INHERITED = "INHERITED";
private static final String[] LEVELS = new String[]{Level.ALL.toString(), Level.TRACE.toString(),
Level.DEBUG.toString(), Level.INFO.toString(),
Level.WARN.toString(), Level.ERROR.toString(),
- Level.FATAL.toString(),Level.OFF.toString()};
+ Level.FATAL.toString(),Level.OFF.toString(),
+ INHERITED};
static TabularType _loggerLevelTabularType;
static CompositeType _loggerLevelCompositeType;
@@ -210,27 +220,41 @@ public class LoggingManagementMBean extends AMQManagedObject implements LoggingM
public synchronized boolean setRuntimeRootLoggerLevel(String level)
{
- Level newLevel;
- try
- {
- newLevel = getLevel(level);
- }
- catch (Exception e)
- {
- return false;
- }
-
- _logger.info("Setting RootLogger level to " + level);
-
- Logger log = Logger.getRootLogger();
- log.setLevel(newLevel);
-
- return true;
+ Level newLevel;
+ try
+ {
+ newLevel = getLevel(level);
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+
+ if(newLevel == null)
+ {
+ //A null Level reference implies inheritance. Setting the runtime RootLogger
+ //to null is catastrophic (and prevented by Log4J at startup and runtime anyway).
+ return false;
+ }
+
+ _logger.info("Setting RootLogger level to " + level);
+
+ Logger log = Logger.getRootLogger();
+ log.setLevel(newLevel);
+
+ return true;
}
//method to convert from a string to a log4j Level, throws exception if the given value is invalid
private Level getLevel(String level) throws Exception
{
+ if("null".equalsIgnoreCase(level) || INHERITED.equalsIgnoreCase(level))
+ {
+ //the string "null" or "inherited" signals to inherit from a parent logger,
+ //using a null Level reference for the logger.
+ return null;
+ }
+
Level newLevel = Level.toLevel(level);
//above Level.toLevel call returns a DEBUG Level if the request fails. Check the result.
@@ -243,137 +267,161 @@ public class LoggingManagementMBean extends AMQManagedObject implements LoggingM
return newLevel;
}
- //handler to catch errors signalled by the JAXP parser and throw an appropriate exception
- private class SaxErrorHandler implements ErrorHandler
+ //method to parse the XML configuration file, validating it in the process, and returning a DOM Document of the content.
+ private static synchronized Document parseConfigFile(String fileName) throws IOException
{
-
- public void error(SAXParseException e) throws SAXException
- {
- throw new SAXException("Error parsing XML file: " + e.getMessage());
- }
-
- public void fatalError(SAXParseException e) throws SAXException
+ try
{
- throw new SAXException("Fatal error parsing XML file: " + e.getMessage());
- }
+ LOCK.lock();
- public void warning(SAXParseException e) throws SAXException
- {
- throw new SAXException("Warning parsing XML file: " + e.getMessage());
- }
- }
+ //check file was specified, exists, and is readable
+ if(fileName == null)
+ {
+ _logger.warn("Provided log4j XML configuration filename is null");
+ throw new IOException("Provided log4j XML configuration filename is null");
+ }
- //method to parse the XML configuration file, validating it in the process, and returning a DOM Document of the content.
- private synchronized Document parseConfigFile(String fileName) throws IOException
- {
- //check file was specified, exists, and is readable
- if(fileName == null)
- {
- _logger.warn("No log4j XML configuration file has been set");
- throw new IOException("No log4j XML configuration file has been set");
- }
+ File configFile = new File(fileName);
- File configFile = new File(fileName);
+ if (!configFile.exists())
+ {
+ _logger.warn("The log4j XML configuration file could not be found: " + fileName);
+ throw new IOException("The log4j XML configuration file could not be found");
+ }
+ else if (!configFile.canRead())
+ {
+ _logger.warn("The log4j XML configuration file is not readable: " + fileName);
+ throw new IOException("The log4j XML configuration file is not readable");
+ }
- if (!configFile.exists())
- {
- _logger.warn("Specified log4j XML configuration file does not exist: " + fileName);
- throw new IOException("Specified log4j XML configuration file does not exist");
- }
- else if (!configFile.canRead())
- {
- _logger.warn("Specified log4j XML configuration file is not readable: " + fileName);
- throw new IOException("Specified log4j XML configuration file is not readable");
- }
+ //parse it
+ DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder docBuilder;
+ Document doc;
- //parse it
- DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
- DocumentBuilder docBuilder;
- Document doc;
+ ErrorHandler errHandler = new QpidLog4JSaxErrorHandler();
+ try
+ {
+ docFactory.setValidating(true);
+ docBuilder = docFactory.newDocumentBuilder();
+ docBuilder.setErrorHandler(errHandler);
+ docBuilder.setEntityResolver(new Log4jEntityResolver());
+ doc = docBuilder.parse(fileName);
+ }
+ catch (ParserConfigurationException e)
+ {
+ _logger.warn("Unable to parse the log4j XML file due to possible configuration error: " + e);
+ //recommended that MBeans should use java.* and javax.* exceptions only
+ throw new IOException("Unable to parse the log4j XML file due to possible configuration error: " + e.getMessage());
+ }
+ catch (SAXException e)
+ {
+ _logger.warn("The specified log4j XML file is invalid: " + e);
+ //recommended that MBeans should use standard java.* and javax.* exceptions only
+ throw new IOException("The specified log4j XML file is invalid: " + e.getMessage());
+ }
+ catch (IOException e)
+ {
+ _logger.warn("Unable to parse the specified log4j XML file" + e);
+ throw new IOException("Unable to parse the specified log4j XML file: " + e.getMessage());
+ }
- ErrorHandler errHandler = new SaxErrorHandler();
- try
- {
- docFactory.setValidating(true);
- docBuilder = docFactory.newDocumentBuilder();
- docBuilder.setErrorHandler(errHandler);
- docBuilder.setEntityResolver(new Log4jEntityResolver());
- doc = docBuilder.parse(fileName);
+ return doc;
}
- catch (ParserConfigurationException e)
+ finally
{
- _logger.warn("Unable to parse the log4j XML file due to possible configuration error: " + e);
- //recommended that MBeans should use java.* and javax.* exceptions only
- throw new IOException("Unable to parse the log4j XML file due to possible configuration error: " + e.getMessage());
+ LOCK.unlock();
}
- catch (SAXException e)
- {
- _logger.warn("The specified log4j XML file is invalid: " + e);
- //recommended that MBeans should use standard java.* and javax.* exceptions only
- throw new IOException("The specified log4j XML file is invalid: " + e.getMessage());
- }
- catch (IOException e)
- {
- _logger.warn("Unable to parse the specified log4j XML file" + e);
- throw new IOException("Unable to parse the specified log4j XML file", e);
- }
-
- return doc;
}
- private synchronized boolean writeUpdatedConfigFile(String log4jConfigFileName, Document doc) throws IOException
+ private static synchronized boolean writeUpdatedConfigFile(String log4jConfigFileName, Document doc) throws IOException
{
- File log4jConfigFile = new File(log4jConfigFileName);
-
- if (!log4jConfigFile.canWrite())
- {
- _logger.warn("Specified log4j XML configuration file is not writable: " + log4jConfigFile);
- throw new IOException("Specified log4j XML configuration file is not writable");
- }
-
- Transformer transformer = null;
try
{
- transformer = TransformerFactory.newInstance().newTransformer();
- }
- catch (Exception e)
- {
- _logger.warn("Could not create an XML transformer: " +e);
- return false;
- }
+ LOCK.lock();
- transformer.setOutputProperty(OutputKeys.INDENT, "yes");
- transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "log4j.dtd");
- DOMSource source = new DOMSource(doc);
-
- File tmp;
- try
- {
- tmp = File.createTempFile("LogManMBeanTemp", ".tmp");
- tmp.deleteOnExit();
- StreamResult result = new StreamResult(tmp);
- transformer.transform(source, result);
- }
- catch (TransformerException e)
- {
- _logger.warn("Could not transform the XML into new file: " +e);
- return false;
- }
- catch (IOException e)
- {
- _logger.warn("Could not create the new file: " +e);
- return false;
- }
+ File log4jConfigFile = new File(log4jConfigFileName);
+
+ if (!log4jConfigFile.canWrite())
+ {
+ _logger.warn("Specified log4j XML configuration file is not writable: " + log4jConfigFile);
+ throw new IOException("Specified log4j XML configuration file is not writable");
+ }
- // Swap temp file in to replace existing configuration file.
- File old = new File(log4jConfigFile.getAbsoluteFile() + ".old");
- if (old.exists())
+ Transformer transformer = null;
+ try
+ {
+ transformer = TransformerFactory.newInstance().newTransformer();
+ }
+ catch (Exception e)
+ {
+ _logger.warn("Could not create an XML transformer: " +e);
+ return false;
+ }
+
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "log4j.dtd");
+ DOMSource source = new DOMSource(doc);
+
+ File tmp;
+ try
+ {
+ tmp = File.createTempFile("LogManMBeanTemp", ".tmp");
+ tmp.deleteOnExit();
+ StreamResult result = new StreamResult(tmp);
+ transformer.transform(source, result);
+ }
+ catch (TransformerException e)
+ {
+ _logger.warn("Could not transform the XML into new file: " +e);
+ throw new IOException("Could not transform the XML into new file: " +e);
+ }
+ catch (IOException e)
+ {
+ _logger.warn("Could not create the new log4j XML file: " +e);
+ throw new IOException("Could not create the new log4j XML file: " +e);
+ }
+
+ // Swap temp file in to replace existing configuration file.
+ File old = new File(log4jConfigFile.getAbsoluteFile() + ".old");
+ if (old.exists())
+ {
+ old.delete();
+ }
+
+ try
+ {
+ if(!log4jConfigFile.renameTo(old))
+ {
+ FileUtils.copyCheckedEx(log4jConfigFile, old);
+ }
+ }
+ catch (IOException e)
+ {
+ _logger.warn("Could not backup the existing log4j XML file: " +e);
+ throw new IOException("Could not backup the existing log4j XML file: " +e);
+ }
+
+ try
+ {
+ if(!tmp.renameTo(log4jConfigFile))
+ {
+ FileUtils.copyCheckedEx(tmp, log4jConfigFile);
+ }
+ }
+ catch (IOException e)
+ {
+ _logger.warn("Could not copy the new configuration into place: " +e);
+ throw new IOException("Could not copy the new configuration into place: " +e);
+ }
+
+ return true;
+ }
+ finally
{
- old.delete();
+ LOCK.unlock();
}
- log4jConfigFile.renameTo(old);
- return tmp.renameTo(log4jConfigFile);
}
@@ -389,177 +437,209 @@ public class LoggingManagementMBean extends AMQManagedObject implements LoggingM
* and not the only possible child element.
*/
-
- public synchronized TabularData viewConfigFileLoggerLevels() throws IOException
+ public static synchronized Map<String,String> retrieveConfigFileLoggersLevels(String fileName) throws IOException
{
- if (_loggerLevelTabularType == null)
+ try
{
- _logger.warn("TabluarData type not set up correctly");
- return null;
- }
-
- _logger.info("Getting logger levels from log4j configuration file");
-
- Document doc = parseConfigFile(_log4jConfigFileName);
+ LOCK.lock();
- TabularData loggerLevelList = new TabularDataSupport(_loggerLevelTabularType);
+ Document doc = parseConfigFile(fileName);
- //retrieve the 'category' element nodes
- NodeList categoryElements = doc.getElementsByTagName("category");
-
- String categoryName;
- String priority = null;
-
- for (int i = 0; i < categoryElements.getLength(); i++)
- {
- Element categoryElement = (Element) categoryElements.item(i);
- categoryName = categoryElement.getAttribute("name");
+ HashMap<String,String> loggerLevelList = new HashMap<String,String>();
- //retrieve the category's mandatory 'priority' or 'level' element's value.
- //It may not be the only child node, so request by tag name.
- NodeList priorityElements = categoryElement.getElementsByTagName("priority");
- NodeList levelElements = categoryElement.getElementsByTagName("level");
+ //retrieve the 'category' element nodes
+ NodeList categoryElements = doc.getElementsByTagName("category");
- if (priorityElements.getLength() != 0)
+ String categoryName;
+ String priority = null;
+
+ for (int i = 0; i < categoryElements.getLength(); i++)
{
- Element priorityElement = (Element) priorityElements.item(0);
- priority = priorityElement.getAttribute("value").toUpperCase();
+ Element categoryElement = (Element) categoryElements.item(i);
+ categoryName = categoryElement.getAttribute("name");
+
+ //retrieve the category's mandatory 'priority' or 'level' element's value.
+ //It may not be the only child node, so request by tag name.
+ NodeList priorityElements = categoryElement.getElementsByTagName("priority");
+ NodeList levelElements = categoryElement.getElementsByTagName("level");
+
+ if (priorityElements.getLength() != 0)
+ {
+ Element priorityElement = (Element) priorityElements.item(0);
+ priority = priorityElement.getAttribute("value");
+ }
+ else if (levelElements.getLength() != 0)
+ {
+ Element levelElement = (Element) levelElements.item(0);
+ priority = levelElement.getAttribute("value");
+ }
+ else
+ {
+ //there is no exiting priority or level to view, move onto next category/logger
+ continue;
+ }
+
+ loggerLevelList.put(categoryName, priority);
}
- else if (levelElements.getLength() != 0)
+
+ //retrieve the 'logger' element nodes
+ NodeList loggerElements = doc.getElementsByTagName("logger");
+
+ String loggerName;
+ String level;
+
+ for (int i = 0; i < loggerElements.getLength(); i++)
{
+ Element loggerElement = (Element) loggerElements.item(i);
+ loggerName = loggerElement.getAttribute("name");
+
+ //retrieve the logger's mandatory 'level' element's value
+ //It may not be the only child node, so request by tag name.
+ NodeList levelElements = loggerElement.getElementsByTagName("level");
+
Element levelElement = (Element) levelElements.item(0);
- priority = levelElement.getAttribute("value").toUpperCase();
- }
- else
- {
- //there is no exiting priority or level to view, move onto next category/logger
- continue;
- }
+ level = levelElement.getAttribute("value");
- try
- {
- Object[] itemData = {categoryName, priority};
- CompositeData loggerData = new CompositeDataSupport(_loggerLevelCompositeType, COMPOSITE_ITEM_NAMES, itemData);
- loggerLevelList.put(loggerData);
+ loggerLevelList.put(loggerName, level);
}
- catch (OpenDataException e)
+
+ return loggerLevelList;
+ }
+ finally
+ {
+ LOCK.unlock();
+ }
+ }
+
+ public synchronized TabularData viewConfigFileLoggerLevels() throws IOException
+ {
+ try
+ {
+ LOCK.lock();
+
+ if (_loggerLevelTabularType == null)
{
- _logger.warn("Unable to create logger level list due to :" + e);
+ _logger.warn("TabluarData type not set up correctly");
return null;
}
- }
- //retrieve the 'logger' element nodes
- NodeList loggerElements = doc.getElementsByTagName("logger");
-
- String loggerName;
- String level;
+ _logger.info("Getting logger levels from log4j configuration file");
- for (int i = 0; i < loggerElements.getLength(); i++)
- {
- Element loggerElement = (Element) loggerElements.item(i);
- loggerName = loggerElement.getAttribute("name");
+ TabularData loggerLevelList = new TabularDataSupport(_loggerLevelTabularType);
- //retrieve the logger's mandatory 'level' element's value
- //It may not be the only child node, so request by tag name.
- NodeList levelElements = loggerElement.getElementsByTagName("level");
+ Map<String,String> levels = retrieveConfigFileLoggersLevels(_log4jConfigFileName);
- Element levelElement = (Element) levelElements.item(0);
- level = levelElement.getAttribute("value").toUpperCase();
-
- try
+ for (String loggerName : levels.keySet())
{
- Object[] itemData = {loggerName, level};
- CompositeData loggerData = new CompositeDataSupport(_loggerLevelCompositeType, COMPOSITE_ITEM_NAMES, itemData);
- loggerLevelList.put(loggerData);
- }
- catch (OpenDataException e)
- {
- _logger.warn("Unable to create logger level list due to :" + e);
- return null;
+ String level = levels.get(loggerName);
+
+ try
+ {
+ Object[] itemData = {loggerName, level.toUpperCase()};
+ CompositeData loggerData = new CompositeDataSupport(_loggerLevelCompositeType, COMPOSITE_ITEM_NAMES, itemData);
+ loggerLevelList.put(loggerData);
+ }
+ catch (OpenDataException e)
+ {
+ _logger.warn("Unable to create logger level list due to :" + e);
+ return null;
+ }
}
+
+ return loggerLevelList;
+ }
+ finally
+ {
+ LOCK.unlock();
}
-
- return loggerLevelList;
}
public synchronized boolean setConfigFileLoggerLevel(String logger, String level) throws IOException
{
- //check that the specified level is a valid log4j Level
try
{
- getLevel(level);
- }
- catch (Exception e)
- {
- //it isnt a valid level
- return false;
- }
-
- _logger.info("Setting level to " + level + " for logger '" + logger
- + "' in log4j xml configuration file: " + _log4jConfigFileName);
-
- Document doc = parseConfigFile(_log4jConfigFileName);
+ LOCK.lock();
- //retrieve the 'category' and 'logger' element nodes
- NodeList categoryElements = doc.getElementsByTagName("category");
- NodeList loggerElements = doc.getElementsByTagName("logger");
-
- //collect them into a single elements list
- List<Element> logElements = new ArrayList<Element>();
-
- for (int i = 0; i < categoryElements.getLength(); i++)
- {
- logElements.add((Element) categoryElements.item(i));
- }
- for (int i = 0; i < loggerElements.getLength(); i++)
- {
- logElements.add((Element) loggerElements.item(i));
- }
+ //check that the specified level is a valid log4j Level
+ try
+ {
+ getLevel(level);
+ }
+ catch (Exception e)
+ {
+ //it isnt a valid level
+ return false;
+ }
- //try to locate the specified logger/category in the elements retrieved
- Element logElement = null;
- for (Element e : logElements)
- {
- if (e.getAttribute("name").equals(logger))
+ _logger.info("Setting level to " + level + " for logger '" + logger
+ + "' in log4j xml configuration file: " + _log4jConfigFileName);
+
+ Document doc = parseConfigFile(_log4jConfigFileName);
+
+ //retrieve the 'category' and 'logger' element nodes
+ NodeList categoryElements = doc.getElementsByTagName("category");
+ NodeList loggerElements = doc.getElementsByTagName("logger");
+
+ //collect them into a single elements list
+ List<Element> logElements = new ArrayList<Element>();
+
+ for (int i = 0; i < categoryElements.getLength(); i++)
{
- logElement = e;
- break;
+ logElements.add((Element) categoryElements.item(i));
+ }
+ for (int i = 0; i < loggerElements.getLength(); i++)
+ {
+ logElements.add((Element) loggerElements.item(i));
}
- }
- if (logElement == null)
- {
- //no loggers/categories with given name found, does not exist to update
- _logger.warn("Specified logger does not exist in the configuration file: " +logger);
- return false;
- }
+ //try to locate the specified logger/category in the elements retrieved
+ Element logElement = null;
+ for (Element e : logElements)
+ {
+ if (e.getAttribute("name").equals(logger))
+ {
+ logElement = e;
+ break;
+ }
+ }
- //retrieve the optional 'priority' or 'level' sub-element value.
- //It may not be the only child node, so request by tag name.
- NodeList priorityElements = logElement.getElementsByTagName("priority");
- NodeList levelElements = logElement.getElementsByTagName("level");
+ if (logElement == null)
+ {
+ //no loggers/categories with given name found, does not exist to update
+ _logger.warn("Specified logger does not exist in the configuration file: " +logger);
+ return false;
+ }
- Element levelElement = null;
- if (priorityElements.getLength() != 0)
- {
- levelElement = (Element) priorityElements.item(0);
- }
- else if (levelElements.getLength() != 0)
- {
- levelElement = (Element) levelElements.item(0);
+ //retrieve the optional 'priority' or 'level' sub-element value.
+ //It may not be the only child node, so request by tag name.
+ NodeList priorityElements = logElement.getElementsByTagName("priority");
+ NodeList levelElements = logElement.getElementsByTagName("level");
+
+ Element levelElement = null;
+ if (priorityElements.getLength() != 0)
+ {
+ levelElement = (Element) priorityElements.item(0);
+ }
+ else if (levelElements.getLength() != 0)
+ {
+ levelElement = (Element) levelElements.item(0);
+ }
+ else
+ {
+ //there is no exiting priority or level element to update
+ return false;
+ }
+
+ //update the element with the new level/priority
+ levelElement.setAttribute("value", level.toLowerCase());
+
+ //output the new file
+ return writeUpdatedConfigFile(_log4jConfigFileName, doc);
}
- else
+ finally
{
- //there is no exiting priority or level element to update
- return false;
+ LOCK.unlock();
}
-
- //update the element with the new level/priority
- levelElement.setAttribute("value", level);
-
- //output the new file
- return writeUpdatedConfigFile(_log4jConfigFileName, doc);
}
@@ -574,102 +654,167 @@ public class LoggingManagementMBean extends AMQManagedObject implements LoggingM
* and not the only possible child element.
*/
- public synchronized String getConfigFileRootLoggerLevel() throws IOException
+ public static synchronized String retrieveConfigFileRootLoggerLevel(String fileName) throws IOException
{
- _logger.info("Getting root logger level from log4j configuration file");
+ try
+ {
+ LOCK.lock();
- Document doc = parseConfigFile(_log4jConfigFileName);
-
- //retrieve the optional 'root' element node
- NodeList rootElements = doc.getElementsByTagName("root");
+ Document doc = parseConfigFile(fileName);
- if (rootElements.getLength() == 0)
- {
- //there is not root logger definition
- return null;
- }
+ //retrieve the optional 'root' element node
+ NodeList rootElements = doc.getElementsByTagName("root");
- Element rootElement = (Element) rootElements.item(0);
+ if (rootElements.getLength() == 0)
+ {
+ //there is no root logger definition
+ return "N/A";
+ }
- //retrieve the optional 'priority' or 'level' element value.
- //It may not be the only child node, so request by tag name.
- NodeList priorityElements = rootElement.getElementsByTagName("priority");
- NodeList levelElements = rootElement.getElementsByTagName("level");
- String priority = null;
-
- if (priorityElements.getLength() != 0)
- {
- Element priorityElement = (Element) priorityElements.item(0);
- priority = priorityElement.getAttribute("value");
- }
- else if(levelElements.getLength() != 0)
- {
- Element levelElement = (Element) levelElements.item(0);
- priority = levelElement.getAttribute("value");
- }
+ Element rootElement = (Element) rootElements.item(0);
- if(priority != null)
- {
- return priority.toUpperCase();
+ //retrieve the optional 'priority' or 'level' element value.
+ //It may not be the only child node, so request by tag name.
+ NodeList priorityElements = rootElement.getElementsByTagName("priority");
+ NodeList levelElements = rootElement.getElementsByTagName("level");
+ String priority = null;
+
+ if (priorityElements.getLength() != 0)
+ {
+ Element priorityElement = (Element) priorityElements.item(0);
+ priority = priorityElement.getAttribute("value");
+ }
+ else if(levelElements.getLength() != 0)
+ {
+ Element levelElement = (Element) levelElements.item(0);
+ priority = levelElement.getAttribute("value");
+ }
+
+ if(priority != null)
+ {
+ return priority;
+ }
+ else
+ {
+ return "N/A";
+ }
}
- else
+ finally
{
- return null;
+ LOCK.unlock();
}
}
+ public synchronized String getConfigFileRootLoggerLevel() throws IOException
+ {
+ return retrieveConfigFileRootLoggerLevel(_log4jConfigFileName).toUpperCase();
+ }
+
public synchronized boolean setConfigFileRootLoggerLevel(String level) throws IOException
{
- //check that the specified level is a valid log4j Level
try
{
- getLevel(level);
+ LOCK.lock();
+
+ //check that the specified level is a valid log4j Level
+ try
+ {
+ Level newLevel = getLevel(level);
+ if(newLevel == null)
+ {
+ //A null Level reference implies inheritance. Setting the config file RootLogger
+ //to "null" or "inherited" just ensures it defaults to DEBUG at startup as Log4J
+ //prevents this catastrophic situation at startup and runtime anyway.
+ return false;
+ }
+ }
+ catch (Exception e)
+ {
+ //it isnt a valid level
+ return false;
+ }
+
+ _logger.info("Setting level to " + level + " for the Root logger in " +
+ "log4j xml configuration file: " + _log4jConfigFileName);
+
+ Document doc = parseConfigFile(_log4jConfigFileName);
+
+ //retrieve the optional 'root' element node
+ NodeList rootElements = doc.getElementsByTagName("root");
+
+ if (rootElements.getLength() == 0)
+ {
+ return false;
+ }
+
+ Element rootElement = (Element) rootElements.item(0);
+
+ //retrieve the optional 'priority' or 'level' sub-element value.
+ //It may not be the only child node, so request by tag name.
+ NodeList priorityElements = rootElement.getElementsByTagName("priority");
+ NodeList levelElements = rootElement.getElementsByTagName("level");
+
+ Element levelElement = null;
+ if (priorityElements.getLength() != 0)
+ {
+ levelElement = (Element) priorityElements.item(0);
+ }
+ else if (levelElements.getLength() != 0)
+ {
+ levelElement = (Element) levelElements.item(0);
+ }
+ else
+ {
+ //there is no exiting priority/level to update
+ return false;
+ }
+
+ //update the element with the new level/priority
+ levelElement.setAttribute("value", level);
+
+ //output the new file
+ return writeUpdatedConfigFile(_log4jConfigFileName, doc);
}
- catch (Exception e)
+ finally
{
- //it isnt a valid level
- return false;
+ LOCK.unlock();
}
-
- _logger.info("Setting level to " + level + " for the Root logger in " +
- "log4j xml configuration file: " + _log4jConfigFileName);
+ }
- Document doc = parseConfigFile(_log4jConfigFileName);
-
- //retrieve the optional 'root' element node
- NodeList rootElements = doc.getElementsByTagName("root");
+ public synchronized void reloadConfigFile() throws IOException
+ {
+ try
+ {
+ LOCK.lock();
- if (rootElements.getLength() == 0)
+ QpidLog4JConfigurator.configure(_log4jConfigFileName);
+ _logger.info("Applied log4j configuration from: " + _log4jConfigFileName);
+ }
+ catch (IllegalLoggerLevelException e)
{
- return false;
+ _logger.warn("The log4j configuration reload request was aborted: " + e);
+ //recommended that MBeans should use standard java.* and javax.* exceptions only
+ throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage());
}
-
- Element rootElement = (Element) rootElements.item(0);
-
- //retrieve the optional 'priority' or 'level' sub-element value.
- //It may not be the only child node, so request by tag name.
- NodeList priorityElements = rootElement.getElementsByTagName("priority");
- NodeList levelElements = rootElement.getElementsByTagName("level");
-
- Element levelElement = null;
- if (priorityElements.getLength() != 0)
+ catch (ParserConfigurationException e)
+ {
+ _logger.warn("The log4j configuration reload request was aborted: " + e);
+ throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage());
+ }
+ catch (SAXException e)
{
- levelElement = (Element) priorityElements.item(0);
+ _logger.warn("The log4j configuration reload request was aborted: " + e);
+ //recommended that MBeans should use standard java.* and javax.* exceptions only
+ throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage());
}
- else if (levelElements.getLength() != 0)
+ catch (IOException e)
{
- levelElement = (Element) levelElements.item(0);
+ _logger.warn("The log4j configuration reload request was aborted: " + e);
+ throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage());
}
- else
+ finally
{
- //there is no exiting priority/level to update
- return false;
+ LOCK.unlock();
}
-
- //update the element with the new level/priority
- levelElement.setAttribute("value", level);
-
- //output the new file
- return writeUpdatedConfigFile(_log4jConfigFileName, doc);
}
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java
index 5ffcee77f2..42b3b05ac5 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java
@@ -35,11 +35,7 @@ import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import javax.management.NotificationListener;
-import javax.management.MalformedObjectNameException;
-import javax.management.NotificationFilter;
import javax.management.NotificationFilterSupport;
-import javax.management.InstanceNotFoundException;
-import javax.management.relation.MBeanServerNotificationFilter;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.MBeanServerForwarder;
@@ -82,6 +78,7 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
public static final int PORT_EXPORT_OFFSET = 100;
private final MBeanServer _mbeanServer;
+ private JMXConnectorServer _cs;
private Registry _rmiRegistry;
@@ -120,7 +117,6 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
Map<String, PrincipalDatabase> map = appRegistry.getDatabaseManager().getDatabases();
PrincipalDatabase db = map.get(jmxDatabaseName);
- final JMXConnectorServer cs;
HashMap<String,Object> env = new HashMap<String,Object>();
//Socket factories for the RMIConnectorServer, either default or SLL depending on configuration
@@ -246,7 +242,7 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
"service:jmx:rmi://"+hostname+":"+(port+PORT_EXPORT_OFFSET)+"/jndi/rmi://"+hostname+":"+port+"/jmxrmi");
final JMXServiceURL internalUrl = new JMXServiceURL("rmi", hostname, port+PORT_EXPORT_OFFSET);
- cs = new RMIConnectorServer(internalUrl, env, rmiConnectorServerStub, _mbeanServer)
+ _cs = new RMIConnectorServer(internalUrl, env, rmiConnectorServerStub, _mbeanServer)
{
@Override
public synchronized void start() throws IOException
@@ -282,7 +278,7 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
//Add the custom invoker as an MBeanServerForwarder, and start the RMIConnectorServer.
MBeanServerForwarder mbsf = MBeanInvocationHandlerImpl.newProxyInstance();
- cs.setMBeanServerForwarder(mbsf);
+ _cs.setMBeanServerForwarder(mbsf);
NotificationFilterSupport filter = new NotificationFilterSupport();
filter.enableType(JMXConnectionNotification.OPENED);
@@ -290,9 +286,9 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
filter.enableType(JMXConnectionNotification.FAILED);
// Get the handler that is used by the above MBInvocationHandler Proxy.
// which is the MBeanInvocationHandlerImpl and so also a NotificationListener
- cs.addNotificationListener((NotificationListener) Proxy.getInvocationHandler(mbsf), filter, null);
+ _cs.addNotificationListener((NotificationListener) Proxy.getInvocationHandler(mbsf), filter, null);
- cs.start();
+ _cs.start();
CurrentActor.get().message(ManagementConsoleMessages.MNG_1004());
@@ -377,6 +373,19 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
UnicastRemoteObject.unexportObject(_rmiRegistry, true);
}
+ if (_cs != null)
+ {
+ // Stopping the JMX ConnectorServer
+ try
+ {
+ _cs.stop();
+ }
+ catch (IOException e)
+ {
+ _log.warn("Error while closing the JMX ConnectorServer: " + e.getMessage());
+ }
+ }
+
//ObjectName query to gather all Qpid related MBeans
ObjectName mbeanNameQuery = null;
try
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBean.java
index 25c3754462..b6d2c3ab67 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBean.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBean.java
@@ -27,6 +27,7 @@ import org.apache.qpid.server.management.AMQManagedObject;
import org.apache.qpid.server.management.MBeanInvocationHandlerImpl;
import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
+import org.apache.qpid.util.FileUtils;
import org.apache.log4j.Logger;
import org.apache.commons.configuration.ConfigurationException;
@@ -439,16 +440,44 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana
// Create temporary file
File tmp = File.createTempFile(_accessFile.getName(), ".tmp");
+ tmp.deleteOnExit();
FileOutputStream output = new FileOutputStream(tmp);
_accessRights.store(output, "Generated by AMQUserManagementMBean Console : Last edited by user:" + getCurrentJMXUser());
output.close();
- // Rename new file to main file
- tmp.renameTo(_accessFile);
-
- // delete tmp
- tmp.delete();
+ // Swap temp file to main rights file.
+ File old = new File(_accessFile.getAbsoluteFile() + ".old");
+ if (old.exists())
+ {
+ old.delete();
+ }
+
+ try
+ {
+ if(!_accessFile.renameTo(old))
+ {
+ FileUtils.copyCheckedEx(_accessFile, old);
+ }
+ }
+ catch (IOException e)
+ {
+ _logger.warn("Could not backup the existing management rights file: " +e);
+ throw new IOException("Could not backup the existing management rights file: " +e);
+ }
+
+ try
+ {
+ if(!tmp.renameTo(_accessFile))
+ {
+ FileUtils.copyCheckedEx(tmp, _accessFile);
+ }
+ }
+ catch (IOException e)
+ {
+ _logger.warn("Could not copy the new management rights file into place: " +e);
+ throw new IOException("Could not copy the new management rights file into place" +e);
+ }
}
finally
{
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java
index 3c211746e3..cd4eb0bec7 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java
@@ -25,6 +25,7 @@ import org.apache.qpid.server.security.access.management.AMQUserManagementMBean;
import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser;
import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HashedInitialiser;
+import org.apache.qpid.util.FileUtils;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.AccountNotFoundException;
@@ -428,6 +429,7 @@ public class Base64MD5PasswordFilePrincipalDatabase implements PrincipalDatabase
BufferedReader reader = null;
PrintStream writer = null;
File tmp = File.createTempFile(_passwordFile.getName(), ".tmp");
+ tmp.deleteOnExit();
try
{
@@ -501,6 +503,11 @@ public class Base64MD5PasswordFilePrincipalDatabase implements PrincipalDatabase
}
}
}
+ catch(IOException e)
+ {
+ _logger.error("Unable to create the new password file: " + e);
+ throw new IOException("Unable to create the new password file" + e);
+ }
finally
{
if (reader != null)
@@ -512,16 +519,39 @@ public class Base64MD5PasswordFilePrincipalDatabase implements PrincipalDatabase
{
writer.close();
}
-
- // Swap temp file to main password file.
- File old = new File(_passwordFile.getAbsoluteFile() + ".old");
- if (old.exists())
+ }
+
+ // Swap temp file to main password file.
+ File old = new File(_passwordFile.getAbsoluteFile() + ".old");
+ if (old.exists())
+ {
+ old.delete();
+ }
+
+ try
+ {
+ if(!_passwordFile.renameTo(old))
{
- old.delete();
+ FileUtils.copyCheckedEx(_passwordFile, old);
}
- _passwordFile.renameTo(old);
- tmp.renameTo(_passwordFile);
- tmp.delete();
+ }
+ catch (IOException e)
+ {
+ _logger.error("Could not backup the existing password file: " +e);
+ throw new IOException("Could not backup the existing password file: " + e);
+ }
+
+ try
+ {
+ if(!tmp.renameTo(_passwordFile))
+ {
+ FileUtils.copyCheckedEx(tmp, _passwordFile);
+ }
+ }
+ catch (IOException e)
+ {
+ _logger.error("Could not copy the new password file into place: " +e);
+ throw new IOException("Could not copy the new password file into place: " + e);
}
}
finally
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java
index 5e4678a63b..6ec7cea4c0 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java
@@ -26,6 +26,7 @@ import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
import org.apache.qpid.server.security.auth.sasl.amqplain.AmqPlainInitialiser;
import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5Initialiser;
import org.apache.qpid.server.security.auth.sasl.plain.PlainInitialiser;
+import org.apache.qpid.util.FileUtils;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.AccountNotFoundException;
@@ -395,6 +396,7 @@ public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase
BufferedReader reader = null;
PrintStream writer = null;
File tmp = File.createTempFile(_passwordFile.getName(), ".tmp");
+ tmp.deleteOnExit();
try
{
@@ -452,6 +454,11 @@ public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase
}
}
}
+ catch(IOException e)
+ {
+ _logger.error("Unable to create the new password file: " + e);
+ throw new IOException("Unable to create the new password file" + e);
+ }
finally
{
if (reader != null)
@@ -463,17 +470,41 @@ public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase
{
writer.close();
}
-
- // Swap temp file to main password file.
- File old = new File(_passwordFile.getAbsoluteFile() + ".old");
- if (old.exists())
+ }
+
+ // Swap temp file to main password file.
+ File old = new File(_passwordFile.getAbsoluteFile() + ".old");
+ if (old.exists())
+ {
+ old.delete();
+ }
+
+ try
+ {
+ if(!_passwordFile.renameTo(old))
{
- old.delete();
+ FileUtils.copyCheckedEx(_passwordFile, old);
}
- _passwordFile.renameTo(old);
- tmp.renameTo(_passwordFile);
- tmp.delete();
}
+ catch (IOException e)
+ {
+ _logger.error("Could not backup the existing password file: " +e);
+ throw new IOException("Could not backup the existing password file: " + e);
+ }
+
+ try
+ {
+ if(!tmp.renameTo(_passwordFile))
+ {
+ FileUtils.copyCheckedEx(tmp, _passwordFile);
+ }
+ }
+ catch (IOException e)
+ {
+ _logger.error("Could not copy the new password file into place: " +e);
+ throw new IOException("Could not copy the new password file into place: " + e);
+ }
+
}
finally
{
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java
index 1b07cbdf65..64f5d0dfaa 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java
@@ -615,7 +615,6 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
if(_state.compareAndSet(State.SUSPENDED, State.ACTIVE))
{
_stateListener.stateChange(this, State.SUSPENDED, State.ACTIVE);
- CurrentActor.get().message(_logSubject,SubscriptionMessages.SUB_1003(_state.get().toString()));
}
else
{
@@ -628,9 +627,9 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED))
{
_stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED);
- CurrentActor.get().message(_logSubject,SubscriptionMessages.SUB_1003(_state.get().toString()));
}
}
+ CurrentActor.get().message(_logSubject,SubscriptionMessages.SUB_1003(_state.get().toString()));
}
public State getState()
diff --git a/qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java b/qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java
new file mode 100644
index 0000000000..9f350033d6
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java
@@ -0,0 +1,396 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.log4j.xml;
+
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import org.apache.log4j.xml.QpidLog4JConfigurator.IllegalLoggerLevelException;
+
+import junit.framework.TestCase;
+
+public class QpidLog4JConfiguratorTest extends TestCase
+{
+ private static final String NEWLINE = System.getProperty("line.separator");
+
+ private File _testConfigFile;
+
+ private File createTempTestLog4JConfig(String loggerLevel,String rootLoggerLevel, boolean missingTagClose, boolean incorrectAttribute)
+ {
+ File tmpFile = null;
+ try
+ {
+ tmpFile = File.createTempFile("LogManMBeanTestLog4jConfig", ".tmp");
+ tmpFile.deleteOnExit();
+
+ FileWriter fstream = new FileWriter(tmpFile);
+ BufferedWriter writer = new BufferedWriter(fstream);
+
+ writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+NEWLINE);
+ writer.write("<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">"+NEWLINE);
+
+ writer.write("<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\" debug=\"null\" " +
+ "threshold=\"null\">"+NEWLINE);
+
+ writer.write(" <appender class=\"org.apache.log4j.ConsoleAppender\" name=\"STDOUT\">"+NEWLINE);
+ writer.write(" <layout class=\"org.apache.log4j.PatternLayout\">"+NEWLINE);
+ writer.write(" <param name=\"ConversionPattern\" value=\"%d %-5p [%t] %C{2} (%F:%L) - %m%n\"/>"+NEWLINE);
+ writer.write(" </layout>"+NEWLINE);
+ writer.write(" </appender>"+NEWLINE);
+
+ String closeTag="/";
+ if(missingTagClose)
+ {
+ closeTag="";
+ }
+
+ //Example of a 'category' with a 'priority'
+ writer.write(" <category additivity=\"true\" name=\"logger1\">"+NEWLINE);
+ writer.write(" <priority value=\"" + loggerLevel+ "\"" + closeTag + ">"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </category>"+NEWLINE);
+
+ String attributeName="value";
+ if(incorrectAttribute)
+ {
+ attributeName="values";
+ }
+
+ //Example of a 'category' with a 'level'
+ writer.write(" <category additivity=\"true\" name=\"logger2\">"+NEWLINE);
+ writer.write(" <level " + attributeName + "=\"" + loggerLevel+ "\"/>"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </category>"+NEWLINE);
+
+ //Example of a 'logger' with a 'level'
+ writer.write(" <logger additivity=\"true\" name=\"logger3\">"+NEWLINE);
+ writer.write(" <level value=\"" + loggerLevel+ "\"/>"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </logger>"+NEWLINE);
+
+ //'root' logger
+ writer.write(" <root>"+NEWLINE);
+ writer.write(" <priority value=\"" + rootLoggerLevel+ "\"/>"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </root>"+NEWLINE);
+
+ writer.write("</log4j:configuration>"+NEWLINE);
+
+ writer.flush();
+ writer.close();
+ }
+ catch (IOException e)
+ {
+ fail("Unable to create temporary test log4j configuration");
+ }
+
+ return tmpFile;
+ }
+
+
+
+ //******* Test Methods ******* //
+
+ public void testCheckLevelsAndStrictParser()
+ {
+ //try the valid logger levels
+ _testConfigFile = createTempTestLog4JConfig("all", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("trace", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("debug", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("warn", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("error", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("fatal", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("off", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("null", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("inherited", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ //now try an invalid logger level
+ _testConfigFile = createTempTestLog4JConfig("madeup", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ fail("IllegalLoggerLevelException expected, invalid levels used");
+ }
+ catch (IllegalLoggerLevelException e)
+ {
+ //expected, ignore
+ }
+ catch (IOException e)
+ {
+ fail("Incorrect Exception, expected an IllegalLoggerLevelException");
+ }
+
+
+
+ //now try the valid rootLogger levels
+ _testConfigFile = createTempTestLog4JConfig("info", "all", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "trace", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "debug", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "warn", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "error", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "fatal", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "off", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "null", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "inherited", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "debug", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ //now try an invalid logger level
+ _testConfigFile = createTempTestLog4JConfig("info", "madeup", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ fail("IllegalLoggerLevelException expected, invalid levels used");
+ }
+ catch (IllegalLoggerLevelException e)
+ {
+ //expected, ignore
+ }
+ catch (IOException e)
+ {
+ fail("Incorrect Exception, expected an IllegalLoggerLevelException");
+ }
+
+
+
+ //now try invalid xml
+ _testConfigFile = createTempTestLog4JConfig("info", "info", true, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ fail("IOException expected, malformed XML used");
+ }
+ catch (IllegalLoggerLevelException e)
+ {
+ fail("Incorrect Exception, expected an IOException");
+ }
+ catch (IOException e)
+ {
+ //expected, ignore
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "info", false, true);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ fail("IOException expected, malformed XML used");
+ }
+ catch (IllegalLoggerLevelException e)
+ {
+ //expected, ignore
+ }
+ catch (IOException e)
+ {
+ fail("Incorrect Exception, expected an IllegalLoggerLevelException");
+ }
+ }
+}
diff --git a/qpid/java/client/example/src/main/java/README.txt b/qpid/java/client/example/src/main/java/README.txt
index c9367b706d..c99c3fddec 100644
--- a/qpid/java/client/example/src/main/java/README.txt
+++ b/qpid/java/client/example/src/main/java/README.txt
@@ -3,14 +3,15 @@ variables, QPID_HOME and QPID_SAMPLE. If not the default values will be used.
QPID_HOME
---------
-This is the distribution directory. You would have set the QPID_HOME when you
-started the Qpid Java broker.
+This is the directory that contains the QPID distribution. If you are running the Qpid
+Java broker on the same machine as the examples, you have already set QPID_HOME to this
+directory.
default: /usr/share/java/
QPID_SAMPLE
-----------
-This is the parent directory of the directory in which you find the runSample.sh
+This is the parent directory of the 'java' directory in which you find 'runSample.sh'
(Ex:- $QPID_SRC_HOME/java/client/example/src/main)
default: $PWD
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java b/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java
index bddbc329ab..ee7fc533a3 100644
--- a/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java
+++ b/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java
@@ -22,10 +22,11 @@ package org.apache.qpid.client.util;
import java.util.Iterator;
import java.util.Queue;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
/**
* A blocking queue that emits events above a user specified threshold allowing the caller to take action (e.g. flow
* control) to try to prevent the queue growing (much) further. The underlying queue itself is not bounded therefore the
@@ -36,6 +37,8 @@ import java.util.concurrent.ConcurrentLinkedQueue;
*/
public class FlowControllingBlockingQueue
{
+ private static final Logger _logger = LoggerFactory.getLogger(FlowControllingBlockingQueue.class);
+
/** This queue is bounded and is used to store messages before being dispatched to the consumer */
private final Queue _queue = new ConcurrentLinkedQueue();
@@ -46,6 +49,8 @@ public class FlowControllingBlockingQueue
/** We require a separate count so we can track whether we have reached the threshold */
private int _count;
+
+ private boolean disableFlowControl;
public boolean isEmpty()
{
@@ -69,6 +74,10 @@ public class FlowControllingBlockingQueue
_flowControlHighThreshold = highThreshold;
_flowControlLowThreshold = lowThreshold;
_listener = listener;
+ if (highThreshold == 0)
+ {
+ disableFlowControl = true;
+ }
}
public Object take() throws InterruptedException
@@ -84,7 +93,7 @@ public class FlowControllingBlockingQueue
}
}
}
- if (_listener != null)
+ if (!disableFlowControl && _listener != null)
{
synchronized (_listener)
{
@@ -93,6 +102,7 @@ public class FlowControllingBlockingQueue
_listener.underThreshold(_count);
}
}
+
}
return o;
@@ -106,7 +116,7 @@ public class FlowControllingBlockingQueue
notifyAll();
}
- if (_listener != null)
+ if (!disableFlowControl && _listener != null)
{
synchronized (_listener)
{
diff --git a/qpid/java/common/src/main/java/org/apache/qpid/util/FileUtils.java b/qpid/java/common/src/main/java/org/apache/qpid/util/FileUtils.java
index 515c849290..7ba38f4743 100644
--- a/qpid/java/common/src/main/java/org/apache/qpid/util/FileUtils.java
+++ b/qpid/java/common/src/main/java/org/apache/qpid/util/FileUtils.java
@@ -196,24 +196,7 @@ public class FileUtils
{
try
{
- InputStream in = new FileInputStream(src);
- if (!dst.exists())
- {
- dst.createNewFile();
- }
-
- OutputStream out = new FileOutputStream(dst);
-
- // Transfer bytes from in to out
- byte[] buf = new byte[1024];
- int len;
- while ((len = in.read(buf)) > 0)
- {
- out.write(buf, 0, len);
- }
-
- in.close();
- out.close();
+ copyCheckedEx(src, dst);
}
catch (IOException e)
{
@@ -221,6 +204,36 @@ public class FileUtils
}
}
+ /**
+ * Copies the specified source file to the specified destination file. If the destination file does not exist,
+ * it is created.
+ *
+ * @param src The source file name.
+ * @param dst The destination file name.
+ * @throws IOException
+ */
+ public static void copyCheckedEx(File src, File dst) throws IOException
+ {
+ InputStream in = new FileInputStream(src);
+ if (!dst.exists())
+ {
+ dst.createNewFile();
+ }
+
+ OutputStream out = new FileOutputStream(dst);
+
+ // Transfer bytes from in to out
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0)
+ {
+ out.write(buf, 0, len);
+ }
+
+ in.close();
+ out.close();
+ }
+
/*
* Deletes a given file
*/
diff --git a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/LoggingManagement.java b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/LoggingManagement.java
index d9c5f59bd0..98dba9fed1 100644
--- a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/LoggingManagement.java
+++ b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/LoggingManagement.java
@@ -36,7 +36,7 @@ import javax.management.openmbean.TabularData;
public interface LoggingManagement
{
String TYPE = "LoggingManagement";
- int VERSION = 1;
+ int VERSION = 2;
//TabularType and contained CompositeType key/description information
//For compatibility reasons, DONT MODIFY the existing key values if expanding the set.
@@ -107,6 +107,15 @@ public interface LoggingManagement
//****** log4j XML configuration file operations ****** //
/**
+ * Reloads the log4j configuration file, applying any changes made.
+ *
+ * @throws IOException
+ * @since Qpid JMX API 1.3
+ */
+ @MBeanOperation(name = "reloadConfigFile", description = "Reload the log4j xml configuration file", impact = MBeanOperationInfo.ACTION)
+ void reloadConfigFile() throws IOException;
+
+ /**
* Updates the level of an existing Log4J logger within the xml configuration file
* @param logger The name of the logger
* @param level The level to set the logger to
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientListener.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientListener.java
index c3348b32f0..d88e0f38bb 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientListener.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientListener.java
@@ -59,7 +59,12 @@ public class ClientListener implements NotificationListener
else if (JMXConnectionNotification.FAILED.equals(type))
{
ApplicationRegistry.serverConnectionClosed(server);
- MBeanUtility.printOutput("Recieved notification from " + server.getName() + ": " + type );
+ MBeanUtility.printOutput("JMX Connection to " + server.getName() + " failed.");
+ }
+ else if (JMXConnectionNotification.CLOSED.equals(type))
+ {
+ ApplicationRegistry.serverConnectionClosed(server);
+ MBeanUtility.printOutput("JMX Connection to " + server.getName() + " was closed.");
}
}
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXServerRegistry.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXServerRegistry.java
index 2b459c858f..ca07e6acf4 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXServerRegistry.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXServerRegistry.java
@@ -22,6 +22,7 @@ package org.apache.qpid.management.ui.jmx;
import static org.apache.qpid.management.ui.Constants.ALL;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
@@ -29,7 +30,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
-import javax.management.ListenerNotFoundException;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.Notification;
@@ -116,25 +116,54 @@ public class JMXServerRegistry extends ServerRegistry
* removes all listeners from the mbean server. This is required when user
* disconnects the Qpid server connection
*/
- public void closeServerConnection() throws Exception
+ public void closeServerConnection() throws IOException
{
try
{
+ //remove the listener from the JMXConnector
if (_jmxc != null && _clientListener != null)
+ {
_jmxc.removeConnectionNotificationListener(_clientListener);
+ }
+ }
+ catch (Exception e)
+ {
+ //ignore
+ }
+ try
+ {
+ //remove the listener from the MBeanServerDelegate MBean
if (_mbsc != null && _clientListener != null)
+ {
_mbsc.removeNotificationListener(_serverObjectName, _clientListener);
+ }
+ }
+ catch (Exception e)
+ {
+ //ignore
+ }
- // remove mbean notification listeners
+ if (_mbsc != null && _clientListener != null)
+ {
+ //remove any listeners from the Qpid MBeans
for (String mbeanName : _subscribedNotificationMap.keySet())
{
- _mbsc.removeNotificationListener(new ObjectName(mbeanName), _notificationListener);
+ try
+ {
+ _mbsc.removeNotificationListener(new ObjectName(mbeanName), _notificationListener);
+ }
+ catch (Exception e)
+ {
+ //ignore
+ }
}
}
- catch (ListenerNotFoundException ex)
+
+ //close the JMXConnector
+ if (_jmxc != null)
{
- MBeanUtility.printOutput(ex.toString());
+ _jmxc.close();
}
}
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java
index 2408faae3a..f21647b2d2 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java
@@ -882,7 +882,7 @@ public class AttributesTabControl extends TabControl
{
attribute = (AttributeData) element;
if (attribute.isWritable())
- return Display.getCurrent().getSystemColor(SWT.COLOR_DARK_BLUE);
+ return Display.getCurrent().getSystemColor(SWT.COLOR_BLUE);
else
return Display.getCurrent().getSystemColor(SWT.COLOR_BLACK);
}
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java
index 0d290ab1c4..056d365f8e 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java
@@ -790,9 +790,11 @@ public class NavigationView extends ViewPart
return;
}
- serverRegistry.closeServerConnection();
// Add server to the closed server list and the worker thread will remove the server from required places.
ApplicationRegistry.serverConnectionClosed(managedServer);
+
+ //close the connection
+ serverRegistry.closeServerConnection();
}
/**
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/connection/ConnectionOperationsTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/connection/ConnectionOperationsTabControl.java
index e981ec1c3c..f82d37dcd1 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/connection/ConnectionOperationsTabControl.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/connection/ConnectionOperationsTabControl.java
@@ -28,10 +28,13 @@ import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.TabularDataSupport;
+import org.apache.qpid.management.ui.ApplicationRegistry;
import org.apache.qpid.management.ui.ManagedBean;
+import org.apache.qpid.management.ui.ServerRegistry;
import org.apache.qpid.management.common.mbeans.ManagedConnection;
import org.apache.qpid.management.ui.jmx.JMXManagedObject;
import org.apache.qpid.management.ui.jmx.MBeanUtility;
+import org.apache.qpid.management.ui.views.MBeanView;
import org.apache.qpid.management.ui.views.TabControl;
import org.apache.qpid.management.ui.views.ViewUtility;
import org.eclipse.jface.viewers.ISelectionChangedListener;
@@ -43,6 +46,8 @@ import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
@@ -55,6 +60,8 @@ import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.widgets.Form;
import org.eclipse.ui.forms.widgets.FormToolkit;
@@ -199,6 +206,8 @@ public class ConnectionOperationsTabControl extends TabControl
_tableViewer.setContentProvider(new ContentProviderImpl());
_tableViewer.setLabelProvider(new LabelProviderImpl());
_tableViewer.setSorter(tableSorter);
+ _table.setSortColumn(_table.getColumn(0));
+ _table.setSortDirection(SWT.UP);
Composite buttonsComposite = _toolkit.createComposite(viewChannelsGroup);
gridData = new GridData(SWT.RIGHT, SWT.BOTTOM, false, false);
@@ -278,6 +287,19 @@ public class ConnectionOperationsTabControl extends TabControl
}
});
+ //listener for double clicking to open the selection mbean
+ _table.addMouseListener(new MouseListener()
+ {
+ // MouseListener implementation
+ public void mouseDoubleClick(MouseEvent event)
+ {
+ openMBean(_table);
+ }
+
+ public void mouseDown(MouseEvent e){}
+ public void mouseUp(MouseEvent e){}
+ });
+
_tableViewer.addSelectionChangedListener(new ISelectionChangedListener(){
public void selectionChanged(SelectionChangedEvent evt)
{
@@ -465,5 +487,43 @@ public class ConnectionOperationsTabControl extends TabControl
return comparison;
}
}
+
+ private void openMBean(Table table)
+ {
+ int selectionIndex = table.getSelectionIndex();
+
+ if (selectionIndex == -1)
+ {
+ return;
+ }
+
+ CompositeData channelResult = (CompositeData) table.getItem(selectionIndex).getData();
+ String queueName = (String) channelResult.get(DEFAULT_QUEUE);
+
+ if(queueName == null)
+ {
+ return;
+ }
+
+ ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(_mbean);
+ ManagedBean selectedMBean = serverRegistry.getQueue(queueName, _mbean.getVirtualHostName());
+
+ if(selectedMBean == null)
+ {
+ ViewUtility.popupErrorMessage("Error", "Unable to retrieve the selected MBean to open it");
+ return;
+ }
+
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ MBeanView view = (MBeanView) window.getActivePage().findView(MBeanView.ID);
+ try
+ {
+ view.openMBean(selectedMBean);
+ }
+ catch (Exception ex)
+ {
+ MBeanUtility.handleException(selectedMBean, ex);
+ }
+ }
}
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/ExchangeOperationsTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/ExchangeOperationsTabControl.java
index 35b3e8150c..e3dea6e96b 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/ExchangeOperationsTabControl.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/ExchangeOperationsTabControl.java
@@ -237,6 +237,8 @@ public class ExchangeOperationsTabControl extends TabControl
_keysTableViewer.setContentProvider(new ContentProviderImpl(BINDING_KEY));
_keysTableViewer.setLabelProvider(new LabelProviderImpl(BINDING_KEY));
_keysTableViewer.setSorter(tableSorter);
+ _keysTable.setSortColumn(_keysTable.getColumn(0));
+ _keysTable.setSortDirection(SWT.UP);
_queuesTable = new Table (tablesComposite, SWT.SINGLE | SWT.SCROLL_LINE | SWT.BORDER | SWT.FULL_SELECTION);
@@ -287,6 +289,8 @@ public class ExchangeOperationsTabControl extends TabControl
_queuesTableViewer.setContentProvider(new ContentProviderImpl(QUEUES));
_queuesTableViewer.setLabelProvider(new LabelProviderImpl(QUEUES));
_queuesTableViewer.setSorter(queuesTableSorter);
+ _queuesTable.setSortColumn(_queuesTable.getColumn(0));
+ _queuesTable.setSortDirection(SWT.UP);
_queuesTableViewer.setInput(new String[]{"Select a binding key to view queues"});
//listener for double clicking to open the selection mbean
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/HeadersExchangeOperationsTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/HeadersExchangeOperationsTabControl.java
index 5146bab74c..fcce0e67b6 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/HeadersExchangeOperationsTabControl.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/exchange/HeadersExchangeOperationsTabControl.java
@@ -22,6 +22,7 @@ package org.apache.qpid.management.ui.views.exchange;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import javax.management.MBeanServerConnection;
@@ -48,6 +49,7 @@ import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionAdapter;
@@ -60,6 +62,7 @@ import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.Table;
@@ -182,7 +185,7 @@ public class HeadersExchangeOperationsTabControl extends TabControl
final TableSorter tableSorter = new TableSorter(BINDING_NUM);
String[] titles = {"Binding Number", "Queue Name"};
- int[] bounds = {125, 175};
+ int[] bounds = {135, 175};
for (int i = 0; i < titles.length; i++)
{
final int index = i;
@@ -220,6 +223,8 @@ public class HeadersExchangeOperationsTabControl extends TabControl
_bindingNumberTableViewer.setContentProvider(new ContentProviderImpl(BINDING_NUM));
_bindingNumberTableViewer.setLabelProvider(new LabelProviderImpl(BINDING_NUM));
_bindingNumberTableViewer.setSorter(tableSorter);
+ _bindingNumberTable.setSortColumn(_bindingNumberTable.getColumn(0));
+ _bindingNumberTable.setSortDirection(SWT.UP);
//table of header bindings
_headersTable = new Table (tablesComposite, SWT.SINGLE | SWT.SCROLL_LINE | SWT.BORDER | SWT.FULL_SELECTION);
@@ -272,6 +277,8 @@ public class HeadersExchangeOperationsTabControl extends TabControl
_headersTableViewer.setContentProvider(new ContentProviderImpl(HEADER_BINDINGS));
_headersTableViewer.setLabelProvider(new LabelProviderImpl(HEADER_BINDINGS));
_headersTableViewer.setSorter(queuesTableSorter);
+ _headersTable.setSortColumn(_headersTable.getColumn(0));
+ _headersTable.setSortDirection(SWT.UP);
_headersTableViewer.setInput(new String[]{"Select a binding to view key-value pairs"});
_bindingNumberTableViewer.addSelectionChangedListener(new ISelectionChangedListener(){
@@ -490,25 +497,38 @@ public class HeadersExchangeOperationsTabControl extends TabControl
private void createNewBinding(Shell parent)
{
final Shell shell = ViewUtility.createModalDialogShell(parent, "Create New Binding");
-
- Composite destinationComposite = _toolkit.createComposite(shell, SWT.NONE);
- destinationComposite.setBackground(shell.getBackground());
- destinationComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
- destinationComposite.setLayout(new GridLayout(2,false));
+
+ Composite queueNameComposite = _toolkit.createComposite(shell, SWT.NONE);
+ queueNameComposite.setBackground(shell.getBackground());
+ GridData layoutData = new GridData(SWT.CENTER, SWT.TOP, true, false);
+ layoutData.minimumWidth = 300;
+ queueNameComposite.setLayoutData(layoutData);
+ queueNameComposite.setLayout(new GridLayout(2,false));
- _toolkit.createLabel(destinationComposite,"Queue:").setBackground(shell.getBackground());
- final Combo destinationCombo = new Combo(destinationComposite,SWT.NONE | SWT.READ_ONLY);
+ _toolkit.createLabel(queueNameComposite,"Queue:").setBackground(shell.getBackground());
+ final Combo destinationCombo = new Combo(queueNameComposite,SWT.NONE | SWT.READ_ONLY);
destinationCombo.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
- Composite bindingComposite = _toolkit.createComposite(shell, SWT.NONE);
- bindingComposite.setBackground(shell.getBackground());
- bindingComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
- bindingComposite.setLayout(new GridLayout(2,false));
+ final ScrolledComposite scrolledComposite = new ScrolledComposite(shell, SWT.V_SCROLL);
+ scrolledComposite.setExpandHorizontal(true);
+ scrolledComposite.setLayout(new GridLayout());
+ scrolledComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ scrolledComposite.setBackground(shell.getBackground());
+
+ final Composite bindingComposite = _toolkit.createComposite(scrolledComposite, SWT.NONE);
+ bindingComposite.setBackground(scrolledComposite.getBackground());
+ bindingComposite.setLayout(new GridLayout(2,true));
+ layoutData = new GridData(SWT.FILL, SWT.TOP, true, false);
+ bindingComposite.setLayoutData(layoutData);
+ scrolledComposite.setContent(bindingComposite);
+
+ Composite addMoreButtonComp = _toolkit.createComposite(shell);
+ addMoreButtonComp.setBackground(shell.getBackground());
+ addMoreButtonComp.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, true, true));
+ addMoreButtonComp.setLayout(new GridLayout());
+
+ final Button addMoreButton = _toolkit.createButton(addMoreButtonComp, "Add additional field", SWT.PUSH);
- _toolkit.createLabel(bindingComposite,"Binding:").setBackground(shell.getBackground());
- final Text bindingText = new Text(bindingComposite, SWT.BORDER);
- bindingText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
-
Composite okCancelButtonsComp = _toolkit.createComposite(shell);
okCancelButtonsComp.setBackground(shell.getBackground());
okCancelButtonsComp.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, true, true));
@@ -532,26 +552,106 @@ public class HeadersExchangeOperationsTabControl extends TabControl
destinationCombo.setItems(queueList.toArray(new String[0]));
}
destinationCombo.select(0);
+
+ final HashMap<Text, Text> headerBindingHashMap = new HashMap<Text, Text>();
+
+ //add headings
+ Label keyLabel = _toolkit.createLabel(bindingComposite,"Key:");
+ keyLabel.setBackground(bindingComposite.getBackground());
+ keyLabel.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
+
+ Label valueLabel = _toolkit.createLabel(bindingComposite,"Value:");
+ valueLabel.setBackground(bindingComposite.getBackground());
+ valueLabel.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
+
+ //add the x-match key by default and offer a comobo to select its value
+ final Text xmatchKeyText = new Text(bindingComposite, SWT.BORDER);
+ xmatchKeyText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ xmatchKeyText.setText("x-match");
+ xmatchKeyText.setEditable(false);
+
+ final Combo xmatchValueCombo = new Combo(bindingComposite,SWT.NONE | SWT.READ_ONLY);
+ xmatchValueCombo.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ xmatchValueCombo.setItems(new String[]{"any", "all"});
+ xmatchValueCombo.select(0);
+
+ //make some empty key-value fields
+ for(int i=0; i < 4; i++)
+ {
+ Text keyText = new Text(bindingComposite, SWT.BORDER);
+ Text valueText = new Text(bindingComposite, SWT.BORDER);
+ keyText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ valueText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+
+ headerBindingHashMap.put(keyText, valueText);
+ }
+ bindingComposite.setSize(bindingComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+
+ //allow adding more fields for additional key-value pairs
+ addMoreButton.addSelectionListener(new SelectionAdapter()
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ Text keyText = new Text(bindingComposite, SWT.BORDER);
+ Text valueText = new Text(bindingComposite, SWT.BORDER);
+ keyText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ valueText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+
+ headerBindingHashMap.put(keyText, valueText);
+
+ bindingComposite.setSize(bindingComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ bindingComposite.layout(true);
+ scrolledComposite.layout(true);
+ }
+ });
okButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent e)
{
- String binding = bindingText.getText();
+ String xMatchString = xmatchValueCombo.getText();
- if (binding == null || binding.length() == 0)
+ String destQueue = destinationCombo.getItem(destinationCombo.getSelectionIndex()).toString();
+
+ StringBuffer bindingValue = new StringBuffer();
+
+ //insert the x-match key-value pair
+ if (xMatchString.equalsIgnoreCase("any"))
{
- ViewUtility.popupErrorMessage("Create New Binding", "Please enter a valid binding");
- return;
+ bindingValue.append("x-match=any");
+ }
+ else
+ {
+ bindingValue.append("x-match=all");
}
- String destQueue = destinationCombo.getItem(destinationCombo.getSelectionIndex()).toString();
+ //insert the other key-value pairs
+ for (Text keyText : headerBindingHashMap.keySet())
+ {
+
+ String key = keyText.getText();
+ if(key == null || key.length() == 0)
+ {
+ continue;
+ }
+
+ Text valueText = headerBindingHashMap.get(keyText);
+ String value = valueText.getText();
+
+ bindingValue.append(",");
+ bindingValue.append(key + "=");
+ //empty values are permitted, signalling only key-presence is required
+ if(value != null && value.length() > 0)
+ {
+ bindingValue.append(value);
+ }
+ }
shell.dispose();
try
{
- _emb.createNewBinding(destQueue, binding);
+ _emb.createNewBinding(destQueue, bindingValue.toString());
ViewUtility.operationResultFeedback(null, "Created new Binding", null);
}
catch (Exception e4)
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/ConfigurationFileTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/ConfigurationFileTabControl.java
index 6af90d117c..1b1d08aa67 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/ConfigurationFileTabControl.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/ConfigurationFileTabControl.java
@@ -30,6 +30,7 @@ import javax.management.openmbean.TabularDataSupport;
import static org.apache.qpid.management.ui.Constants.FONT_BOLD;
+import org.apache.qpid.management.ui.ApiVersion;
import org.apache.qpid.management.ui.ApplicationRegistry;
import org.apache.qpid.management.ui.ManagedBean;
import org.apache.qpid.management.common.mbeans.LoggingManagement;
@@ -80,6 +81,7 @@ public class ConfigurationFileTabControl extends TabControl
private String[] _availableLoggerLevels;
private TabularDataSupport _configFileLoggerLevels = null;
private LoggingManagement _lmmb;
+ private ApiVersion _ApiVersion;
static final String LOGGER_NAME = LoggingManagement.COMPOSITE_ITEM_NAMES[0];
static final String LOGGER_LEVEL = LoggingManagement.COMPOSITE_ITEM_NAMES[1];
@@ -91,6 +93,7 @@ public class ConfigurationFileTabControl extends TabControl
_lmmb = (LoggingManagement)
MBeanServerInvocationHandler.newProxyInstance(mbsc, mbean.getObjectName(),
LoggingManagement.class, false);
+ _ApiVersion = ApplicationRegistry.getServerRegistry(mbean).getManagementApiVersion();
_toolkit = new FormToolkit(_tabFolder.getDisplay());
_form = _toolkit.createScrolledForm(_tabFolder);
_form.getBody().setLayout(new GridLayout());
@@ -191,11 +194,15 @@ public class ConfigurationFileTabControl extends TabControl
}
Label noteLabel = _toolkit.createLabel(_headerComposite,
- "NOTE: These options modify the config file. " +
- "Changes take effect if LogWatch is enabled " +
- "or the broker is restarted.");
+ "NOTE: These options modify the configuration file. " +
+ "Changes only take effect automatically if LogWatch is enabled.");
+ Label noteLabel2 = _toolkit.createLabel(_headerComposite,
+ "A child Logger set to a non-inherited Level in the Runtime tab " +
+ "will retain that value after the file is reloaded.");
GridData gridData = new GridData(SWT.FILL, SWT.FILL, false, true);
noteLabel.setLayoutData(gridData);
+ gridData = new GridData(SWT.FILL, SWT.FILL, false, true);
+ noteLabel2.setLayoutData(gridData);
Group configFileLoggerLevelsGroup = new Group(_paramsComposite, SWT.SHADOW_NONE);
configFileLoggerLevelsGroup.setBackground(_paramsComposite.getBackground());
@@ -275,7 +282,7 @@ public class ConfigurationFileTabControl extends TabControl
public void mouseUp(MouseEvent e){}
});
- final Button logLevelEditButton = _toolkit.createButton(configFileLoggerLevelsGroup, "Edit Selected Logger...", SWT.PUSH);
+ final Button logLevelEditButton = _toolkit.createButton(configFileLoggerLevelsGroup, "Edit Selected Logger(s)...", SWT.PUSH);
logLevelEditButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
logLevelEditButton.setEnabled(false);
logLevelEditButton.addSelectionListener(new SelectionAdapter()
@@ -341,6 +348,43 @@ public class ConfigurationFileTabControl extends TabControl
_logWatchIntervalLabel = _toolkit.createLabel(logWatchIntervalGroup, "-");
_logWatchIntervalLabel.setFont(ApplicationRegistry.getFont(FONT_BOLD));
_logWatchIntervalLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, true));
+
+ if(_ApiVersion.greaterThanOrEqualTo(1, 3))
+ {
+ Group reloadConfigFileGroup = new Group(attributesComposite, SWT.SHADOW_NONE);
+ reloadConfigFileGroup.setBackground(attributesComposite.getBackground());
+ reloadConfigFileGroup.setText("Reload Configuration File");
+ gridData = new GridData(SWT.LEFT, SWT.TOP, true, false);
+ reloadConfigFileGroup.setLayoutData(gridData);
+ reloadConfigFileGroup.setLayout(new GridLayout());
+
+ final Button reloadConfigFileButton = _toolkit.createButton(reloadConfigFileGroup, "Reload Config File", SWT.PUSH);
+ reloadConfigFileButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false));
+ reloadConfigFileButton.addSelectionListener(new SelectionAdapter()
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ int response = ViewUtility.popupOkCancelConfirmationMessage("Reload",
+ "Reload Logging Configuration File?");
+ if (response == SWT.OK)
+ {
+ try
+ {
+ _lmmb.reloadConfigFile();
+ ViewUtility.operationResultFeedback(null, "Reloaded Logging Configuration File", null);
+
+ }
+ catch (Exception ex)
+ {
+ ViewUtility.operationFailedStatusBarMessage("Error Reloading Logging Configuration File");
+ MBeanUtility.handleException(_mbean, ex);
+ }
+
+ refresh(_mbean);;
+ }
+ }
+ });
+ }
}
@@ -362,7 +406,7 @@ public class ConfigurationFileTabControl extends TabControl
selectedLoggers.add(user);
}
- final Shell shell = ViewUtility.createModalDialogShell(parent, "Set Config File Logger Level");
+ final Shell shell = ViewUtility.createModalDialogShell(parent, "Set Config File Logger Level(s)");
_toolkit.createLabel(shell, "Logger(s): ").setBackground(shell.getBackground());
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/RuntimeTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/RuntimeTabControl.java
index 012c22e6de..9834cd729a 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/RuntimeTabControl.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/logging/RuntimeTabControl.java
@@ -26,6 +26,7 @@ import java.util.HashMap;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.TabularDataSupport;
import static org.apache.qpid.management.ui.Constants.FONT_BOLD;
@@ -37,7 +38,10 @@ import org.apache.qpid.management.ui.jmx.JMXManagedObject;
import org.apache.qpid.management.ui.jmx.MBeanUtility;
import org.apache.qpid.management.ui.views.TabControl;
import org.apache.qpid.management.ui.views.ViewUtility;
+import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
@@ -46,12 +50,15 @@ import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
@@ -78,10 +85,11 @@ public class RuntimeTabControl extends TabControl
private Label _runtimeRootLoggerLevelLabel = null;
private String[] _availableLoggerLevels;
private TabularDataSupport _runtimeLoggerLevels = null;
+ private ArrayList<String> _configFileLoggerNames = new ArrayList<String>();
private LoggingManagement _lmmb;
- static final String LOGGER_NAME = LoggingManagement.COMPOSITE_ITEM_NAMES[0];
- static final String LOGGER_LEVEL = LoggingManagement.COMPOSITE_ITEM_NAMES[1];
+ private static final String LOGGER_NAME = LoggingManagement.COMPOSITE_ITEM_NAMES[0];
+ private static final String LOGGER_LEVEL = LoggingManagement.COMPOSITE_ITEM_NAMES[1];
public RuntimeTabControl(TabFolder tabFolder, JMXManagedObject mbean, MBeanServerConnection mbsc)
{
@@ -148,6 +156,26 @@ public class RuntimeTabControl extends TabControl
MBeanUtility.handleException(_mbean, e2);
}
+
+ try
+ {
+ TabularDataSupport confLoggers = (TabularDataSupport) _lmmb.viewConfigFileLoggerLevels();
+ ArrayList<String> confLoggerNames = new ArrayList<String>();
+
+ for(Object obj : confLoggers.values())
+ {
+ CompositeData comp = (CompositeData) obj;
+ confLoggerNames.add((String) comp.get(LOGGER_NAME));
+ }
+
+ _configFileLoggerNames = confLoggerNames;
+ }
+ catch(Exception e2)
+ {
+ //dont signal the failure, just dont highlight the config file loggers, empty the existing list.
+ _configFileLoggerNames.clear();
+ }
+
_runtimeRootLoggerLevelLabel.setText(String.valueOf(runtimeRootLoggerLevel));
_tableViewer.setInput(_runtimeLoggerLevels);
@@ -174,8 +202,13 @@ public class RuntimeTabControl extends TabControl
Label noteLabel = _toolkit.createLabel(_headerComposite,
"NOTE: These options modify only the live runtime settings. " +
"Non-default values will be lost following broker restart.");
+ Label noteLabel2 = _toolkit.createLabel(_headerComposite,
+ "Loggers currently defined in the configuration file are " +
+ "highlighted. The other Loggers inherit a Level by default.");
GridData gridData = new GridData(SWT.FILL, SWT.FILL, false, true);
noteLabel.setLayoutData(gridData);
+ gridData = new GridData(SWT.FILL, SWT.FILL, false, true);
+ noteLabel2.setLayoutData(gridData);
Group effectiveRuntimeLoggerLevelsGroup = new Group(_paramsComposite, SWT.SHADOW_NONE);
effectiveRuntimeLoggerLevelsGroup.setBackground(_paramsComposite.getBackground());
@@ -239,7 +272,7 @@ public class RuntimeTabControl extends TabControl
}
_tableViewer.setContentProvider(new LoggingTableContentProvider());
- _tableViewer.setLabelProvider(new LoggingTableLabelProvider());
+ _tableViewer.setLabelProvider(new RuntimeLoggingTableLabelProvider());
_tableViewer.setSorter(tableSorter);
_table.setSortColumn(_table.getColumn(0));
_table.setSortDirection(SWT.UP);
@@ -254,7 +287,7 @@ public class RuntimeTabControl extends TabControl
public void mouseUp(MouseEvent e){}
});
- final Button logLevelEditButton = _toolkit.createButton(effectiveRuntimeLoggerLevelsGroup, "Edit Selected Logger...", SWT.PUSH);
+ final Button logLevelEditButton = _toolkit.createButton(effectiveRuntimeLoggerLevelsGroup, "Edit Selected Logger(s)...", SWT.PUSH);
logLevelEditButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
logLevelEditButton.setEnabled(false);
logLevelEditButton.addSelectionListener(new SelectionAdapter()
@@ -326,7 +359,7 @@ public class RuntimeTabControl extends TabControl
selectedLoggers.add(user);
}
- final Shell shell = ViewUtility.createModalDialogShell(parent, "Set Runtime Logger Level");
+ final Shell shell = ViewUtility.createModalDialogShell(parent, "Set Runtime Logger Level(s)");
_toolkit.createLabel(shell, "Logger(s): ").setBackground(shell.getBackground());
@@ -510,4 +543,53 @@ public class RuntimeTabControl extends TabControl
shell.open();
}
+
+ /**
+ * Label Provider class for the RuntimeLoggers table viewer
+ */
+ public class RuntimeLoggingTableLabelProvider extends LabelProvider implements ITableLabelProvider, IColorProvider
+ {
+
+ @Override
+ public String getColumnText(Object element, int columnIndex)
+ {
+ switch (columnIndex)
+ {
+ case 0 : // logger name column
+ return (String) ((CompositeDataSupport) element).get(LOGGER_NAME);
+ case 1 : // logger level column
+ return (String) ((CompositeDataSupport) element).get(LOGGER_LEVEL);
+ default :
+ return "-";
+ }
+ }
+
+ @Override
+ public Image getColumnImage(Object element, int columnIndex)
+ {
+ return null;
+ }
+
+ @Override
+ public Color getBackground(Object element)
+ {
+ return null;
+ }
+
+ @Override
+ public Color getForeground(Object element)
+ {
+ String loggerName = (String) ((CompositeData) element).get(LOGGER_NAME);
+ if(_configFileLoggerNames.contains(loggerName))
+ {
+ return Display.getCurrent().getSystemColor(SWT.COLOR_BLUE);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+
+ }
}
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/queue/QueueOperationsTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/queue/QueueOperationsTabControl.java
index c7c7a1791a..43d2cfe204 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/queue/QueueOperationsTabControl.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/queue/QueueOperationsTabControl.java
@@ -299,8 +299,8 @@ public class QueueOperationsTabControl extends TabControl
//message table
Composite tableAndButtonsComposite = _toolkit.createComposite(messagesGroup);
gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
- gridData.minimumHeight = 220;
- gridData.heightHint = 220;
+ gridData.minimumHeight = 180;
+ gridData.heightHint = 180;
tableAndButtonsComposite.setLayoutData(gridData);
tableAndButtonsComposite.setLayout(new GridLayout(2,false));
@@ -358,6 +358,8 @@ public class QueueOperationsTabControl extends TabControl
_tableViewer.setContentProvider(new ContentProviderImpl());
_tableViewer.setLabelProvider(new LabelProviderImpl());
_tableViewer.setSorter(tableSorter);
+ _table.setSortColumn(_table.getColumn(0));
+ _table.setSortDirection(SWT.UP);
//Side Buttons
Composite buttonsComposite = _toolkit.createComposite(tableAndButtonsComposite);
@@ -492,12 +494,12 @@ public class QueueOperationsTabControl extends TabControl
headerEtcComposite.setLayoutData(gridData);
headerEtcComposite.setLayout(new GridLayout());
- final Text headerText = new Text(headerEtcComposite, SWT.WRAP | SWT.BORDER );
+ final Text headerText = new Text(headerEtcComposite, SWT.WRAP | SWT.BORDER | SWT.V_SCROLL);
headerText.setText("Select a message to view its header.");
headerText.setEditable(false);
data = new GridData(SWT.LEFT, SWT.TOP, false, false);
- data.minimumHeight = 230;
- data.heightHint = 230;
+ data.minimumHeight = 210;
+ data.heightHint = 210;
data.minimumWidth = 500;
data.widthHint = 500;
headerText.setLayoutData(data);
@@ -570,10 +572,16 @@ public class QueueOperationsTabControl extends TabControl
String[] msgHeader = (String[]) selectedMsg.get(MSG_HEADER);
headerText.setText("");
String lineSeperator = System.getProperty("line.separator");
- for(String s: msgHeader)
+ int size = msgHeader.length;
+ for(int i=0; i < size; i++)
{
- headerText.append(s + lineSeperator);
+ headerText.append(msgHeader[i]);
+ if(!(i == size - 1))
+ {
+ headerText.append(lineSeperator);
+ }
}
+ headerText.setSelection(0);
}
if (_table.getSelectionCount() > 1)
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/MBeanTypeTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/MBeanTypeTabControl.java
index 37af1f8f31..cab9bc9f95 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/MBeanTypeTabControl.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/MBeanTypeTabControl.java
@@ -77,6 +77,8 @@ public abstract class MBeanTypeTabControl extends TabControl
protected String _virtualHost;
protected JMXServerRegistry _serverRegistry;
protected Composite _tableComposite;
+ protected Button _favouritesButton;
+ protected Button _openButton;
public MBeanTypeTabControl(TabFolder tabFolder, ManagedServer server, String virtualHost, String type)
{
@@ -191,8 +193,54 @@ public abstract class MBeanTypeTabControl extends TabControl
_tableViewer.setSorter(tableSorter);
_table.setSortColumn(_table.getColumn(0));
_table.setSortDirection(SWT.UP);
+
+ addTableListeners();
}
+ protected void addTableListeners()
+ {
+ _favouritesButton.setEnabled(false);
+ _openButton.setEnabled(false);
+
+ _tableViewer.addSelectionChangedListener(new ISelectionChangedListener(){
+ public void selectionChanged(SelectionChangedEvent evt)
+ {
+ int selectionIndex = _table.getSelectionIndex();
+
+ if (selectionIndex == -1)
+ {
+ _favouritesButton.setEnabled(false);
+ _openButton.setEnabled(false);
+ return;
+ }
+ else
+ {
+ _favouritesButton.setEnabled(true);
+ }
+
+ if(_table.getSelectionCount() > 1)
+ {
+ _openButton.setEnabled(false);
+ }
+ else
+ {
+ _openButton.setEnabled(true);
+ }
+ }
+ });
+
+ _table.addMouseListener(new MouseListener()
+ {
+ // MouseListener implementation
+ public void mouseDoubleClick(MouseEvent event)
+ {
+ openMBean();
+ }
+
+ public void mouseDown(MouseEvent e){}
+ public void mouseUp(MouseEvent e){}
+ });
+ }
private void createWidgets()
@@ -206,11 +254,11 @@ public abstract class MBeanTypeTabControl extends TabControl
buttonComposite.setLayoutData(gridData);
buttonComposite.setLayout(new GridLayout(2,true));
- final Button favouritesButton = _toolkit.createButton(buttonComposite,
+ _favouritesButton = _toolkit.createButton(buttonComposite,
"<-- Add " + _type + "(s) to favourites", SWT.PUSH);
gridData = new GridData(SWT.LEFT, SWT.CENTER, true, false);
- favouritesButton.setLayoutData(gridData);
- favouritesButton.addSelectionListener(new SelectionAdapter()
+ _favouritesButton.setLayoutData(gridData);
+ _favouritesButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent e)
{
@@ -218,10 +266,10 @@ public abstract class MBeanTypeTabControl extends TabControl
}
});
- final Button openButton = _toolkit.createButton(buttonComposite, "Open selected " + _type, SWT.PUSH);
+ _openButton = _toolkit.createButton(buttonComposite, "Open selected " + _type, SWT.PUSH);
gridData = new GridData(SWT.RIGHT, SWT.CENTER, true, false);
- openButton.setLayoutData(gridData);
- openButton.addSelectionListener(new SelectionAdapter()
+ _openButton.setLayoutData(gridData);
+ _openButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent e)
{
@@ -236,48 +284,6 @@ public abstract class MBeanTypeTabControl extends TabControl
createTable();
- favouritesButton.setEnabled(false);
- openButton.setEnabled(false);
-
- _tableViewer.addSelectionChangedListener(new ISelectionChangedListener(){
- public void selectionChanged(SelectionChangedEvent evt)
- {
- int selectionIndex = _table.getSelectionIndex();
-
- if (selectionIndex == -1)
- {
- favouritesButton.setEnabled(false);
- openButton.setEnabled(false);
- return;
- }
- else
- {
- favouritesButton.setEnabled(true);
- }
-
- if(_table.getSelectionCount() > 1)
- {
- openButton.setEnabled(false);
- }
- else
- {
- openButton.setEnabled(true);
- }
- }
- });
-
- _table.addMouseListener(new MouseListener()
- {
- // MouseListener implementation
- public void mouseDoubleClick(MouseEvent event)
- {
- openMBean();
- }
-
- public void mouseDown(MouseEvent e){}
- public void mouseUp(MouseEvent e){}
- });
-
createLowerAreaButton(mainComposite);
}
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/QueueTypeTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/QueueTypeTabControl.java
index 3e1f198606..df2a1eca59 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/QueueTypeTabControl.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/QueueTypeTabControl.java
@@ -357,6 +357,8 @@ public class QueueTypeTabControl extends MBeanTypeTabControl
_tableViewer.setSorter(tableSorter);
_table.setSortColumn(_table.getColumn(0));
_table.setSortDirection(SWT.UP);
+
+ addTableListeners();
}
protected void createLowerAreaButton(Composite parent)
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/vhost/VHostTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/vhost/VHostTabControl.java
index fef1e9f887..3b03aeaff1 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/vhost/VHostTabControl.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/vhost/VHostTabControl.java
@@ -205,6 +205,8 @@ public class VHostTabControl extends TabControl
_queueTableViewer.setContentProvider(new ContentProviderImpl());
_queueTableViewer.setLabelProvider(new LabelProviderImpl());
_queueTableViewer.setSorter(tableSorter);
+ _queueTable.setSortColumn(_queueTable.getColumn(0));
+ _queueTable.setSortDirection(SWT.UP);
Composite queuesRightComposite = _toolkit.createComposite(queuesGroup);
gridData = new GridData(SWT.FILL, SWT.FILL, false, true);
@@ -314,6 +316,8 @@ public class VHostTabControl extends TabControl
_exchangeTableViewer.setContentProvider(new ContentProviderImpl());
_exchangeTableViewer.setLabelProvider(new LabelProviderImpl());
_exchangeTableViewer.setSorter(exchangeTableSorter);
+ _exchangeTable.setSortColumn(_exchangeTable.getColumn(0));
+ _exchangeTable.setSortDirection(SWT.UP);
Composite exchangesRightComposite = _toolkit.createComposite(exchangesGroup);
gridData = new GridData(SWT.FILL, SWT.FILL, false, true);
diff --git a/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini b/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini
index 9e3de042d5..312580769e 100644
--- a/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini
+++ b/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini
@@ -23,14 +23,14 @@
-XX:MaxPermSize=256m
-Dosgi.requiredJavaVersion=1.5
-Declipse.consoleLog=true
-
-#===============================================
-# SSL trust store configuration options.
-#===============================================
-
-# Uncomment lines below to specify custom truststore for server SSL
-# certificate verification, eg when using self-signed server certs.
-#
-#-Djavax.net.ssl.trustStore=<path.to.truststore>
-#-Djavax.net.ssl.trustStorePassword=<truststore.password>
-
+
+#===============================================
+# SSL trust store configuration options.
+#===============================================
+
+# Uncomment lines below to specify custom truststore for server SSL
+# certificate verification, eg when using self-signed server certs.
+#
+#-Djavax.net.ssl.trustStore=<path.to.truststore>
+#-Djavax.net.ssl.trustStorePassword=<truststore.password>
+
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/BrokerStartupTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/BrokerStartupTest.java
new file mode 100644
index 0000000000..52120019f5
--- /dev/null
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/BrokerStartupTest.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.server;
+
+import org.apache.qpid.server.logging.AbstractTestLogging;
+import org.apache.qpid.util.LogMonitor;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Level;
+
+import java.util.List;
+
+import junit.framework.AssertionFailedError;
+
+import javax.jms.Connection;
+import javax.jms.Session;
+import javax.jms.Queue;
+
+/**
+ * Series of tests to validate the external Java broker starts up as expected.
+ */
+public class BrokerStartupTest extends AbstractTestLogging
+{
+ public void setUp() throws Exception
+ {
+ // We either do this here or have a null check in tearDown.
+ // As when this test is run against profiles other than java it will NPE
+ _monitor = new LogMonitor(_outputFile);
+ //We explicitly do not call super.setUp as starting up the broker is
+ //part of the test case.
+ }
+
+
+ /**
+ * Description:
+ * Test that providing an invalid broker logging configuration file does not
+ * cause the broker to enable DEBUG logging that will seriously impair
+ * performance
+ * Input:
+ * -l value that does not exist
+ * <p/>
+ * Output:
+ * <p/>
+ * No DEBUG output
+ * <p/>
+ * Validation Steps:
+ * <p/>
+ * 1. Start the broker and verify no DEBUG output exists
+ *
+ * @throws Exception caused by broker startup
+ */
+ public void testInvalidLog4jConfigurationFile() throws Exception
+ {
+ // This logging startup code only occurs when you run a Java broker,
+ // that broker must be started via Main so not an InVM broker.
+ if (isJavaBroker() && isExternalBroker())
+ {
+ //Remove test Log4j config from the commandline
+ _broker = _broker.substring(0, _broker.indexOf("-l"));
+
+ // Add an invalid value
+ _broker += " -l invalid";
+
+ // The release-bin build of the broker uses this log4j configuration
+ // so set up the broker environment to use it for this test.
+ // Also include -Dlog4j.debug so we can validate that it picked up this config
+ setBrokerEnvironment("QPID_OPTS", "-Dlog4j.debug -Dlog4j.configuration=file:" + System.getProperty(QPID_HOME) + "/../broker/src/main/java/log4j.properties");
+
+ // Disable all client logging so we can test for broker DEBUG only.
+ Logger.getRootLogger().setLevel(Level.WARN);
+ Logger.getLogger("qpid.protocol").setLevel(Level.WARN);
+ Logger.getLogger("org.apache.qpid").setLevel(Level.WARN);
+
+ startBroker();
+
+ assertEquals("Log4j could not load desired configruation.",
+ 0, _monitor.findMatches("log4j:ERROR Could not read configuration file from URL").size());
+
+ assertEquals("Logging did not error as expected",
+ 1, _monitor.findMatches("Logging configuration error: unable to read file ").size());
+
+
+ // Perfom some action on the broker to ensure that we hit the DEBUG
+ // messages that we know are there. Though the current xml parsing
+ // will generate a LOT of DEBUG on startup.
+ Connection connection = getConnection();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ Queue queue = session.createQueue(getTestQueueName());
+ session.createConsumer(queue).close();
+
+ int COUNT = 10;
+ sendMessage(session, queue, COUNT);
+
+ assertEquals(COUNT,drainQueue(queue));
+
+ List<String> results = _monitor.findMatches("DEBUG");
+ try
+ {
+ // Validation
+
+ assertEquals("DEBUG messages should not be logged", 0, results.size());
+ }
+ catch (AssertionFailedError afe)
+ {
+ System.err.println("Log Dump:");
+ for (String log : results)
+ {
+ System.err.println(log);
+ }
+
+ if (results.size() == 0)
+ {
+ System.err.println("Monitored file contents:");
+ System.err.println(_monitor.readFile());
+ }
+
+ throw afe;
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AlertingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AlertingTest.java
index 14eec8daff..620b2a5161 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AlertingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AlertingTest.java
@@ -65,6 +65,15 @@ public class AlertingTest extends AbstractTestLogging
setupConnection();
}
+ @Override
+ public void tearDown() throws Exception
+ {
+ // Ensure queue is clean for next run.
+ drainQueue(_destination);
+ super.tearDown();
+ }
+
+
/**
* Create a new connection and ensure taht our destination queue is created
* and bound.
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java
index d97ed71607..033c0533f8 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java
@@ -327,7 +327,7 @@ public class SubscriptionLoggingTest extends AbstractTestLogging
int PREFETCH = 15;
//Create new session with small prefetch
- _session = ((AMQConnection) _connection).createSession(false, Session.AUTO_ACKNOWLEDGE, PREFETCH);
+ _session = ((AMQConnection) _connection).createSession(true, Session.AUTO_ACKNOWLEDGE, PREFETCH);
MessageConsumer consumer = _session.createConsumer(_queue);
@@ -336,27 +336,35 @@ public class SubscriptionLoggingTest extends AbstractTestLogging
//Fill the prefetch and two extra so that our receive bellow allows the
// subscription to become active then return to a suspended state.
sendMessage(_session, _queue, 17);
-
+ _session.commit();
// Retreive the first message, and start the flow of messages
assertNotNull("First message not retreived", consumer.receive(1000));
-
- //Give the internal broker time to respond to the ack that the above
- // receive will perform.
- if (!isExternalBroker())
- {
- Thread.sleep(1000);
- }
+ _session.commit();
+
- _connection.close();
-
//Validate
List<String> results = _monitor.findMatches("SUB-1003");
+
+ int i = 0;
+ while (results.size() != 3 && i < 10)
+ {
+ try
+ {
+ Thread.sleep(1500);
+ }
+ catch (InterruptedException e)
+ {
+
+ }
+ results = _monitor.findMatches("SUB-1003");
+ i++;
+ }
try
{
// Validation expects three messages.
// The first will be logged by the QueueActor as part of the processQueue thread
-// INFO - MESSAGE [vh(/test)/qu(example.queue)] [sub:6(qu(example.queue))] SUB-1003 : State : SUSPENDED
+// INFO - MESSAGE [vh(/test)/qu(example.queue)] [sub:6(qu(example.queue))] SUB-1003 : State : SUSPENDED
// The second will be by the connnection as it acknowledges and activates the subscription
// INFO - MESSAGE [con:6(guest@anonymous(26562441)/test)/ch:3] [sub:6(qu(example.queue))] SUB-1003 : State : ACTIVE
// The final one can be the subscription suspending as part of the SubFlushRunner or the processQueue thread
@@ -365,7 +373,7 @@ public class SubscriptionLoggingTest extends AbstractTestLogging
// INFO - MESSAGE [sub:6(vh(test)/qu(example.queue))] [sub:6(qu(example.queue))] SUB-1003 : State : SUSPENDED
// INFO - MESSAGE [vh(/test)/qu(example.queue)] [sub:6(qu(example.queue))] SUB-1003 : State : SUSPENDED
- assertEquals("Result set larger than expected.", 3, results.size());
+ assertEquals("Result set not expected size:", 3, results.size());
// Validate Initial Suspension
String expectedState = "SUSPENDED";
@@ -401,6 +409,7 @@ public class SubscriptionLoggingTest extends AbstractTestLogging
}
throw afe;
}
+ _connection.close();
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java
index 9bca1ec39f..dfc3bb7b42 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java
@@ -333,8 +333,9 @@ public class FailoverTest extends FailoverBaseCase implements ConnectionListener
}
int iterations = Integer.getInteger("profile.failoverIterations",0);
- boolean b = true;
- int failingPort = getFailingPort();
+ boolean useAltPort = false;
+ int altPort = FAILING_PORT;
+ int stdPort = DEFAULT_PORT;
init(false, Session.AUTO_ACKNOWLEDGE);
for (int i=0; i < iterations; i++)
{
@@ -343,25 +344,25 @@ public class FailoverTest extends FailoverBaseCase implements ConnectionListener
_logger.debug("===================================================================");
runP2PFailover(numMessages, false,false, false);
- startBroker(failingPort);
- if (b)
+ startBroker(getFailingPort());
+ if (useAltPort)
{
- failingPort = getFailingPort()-1;
- b = false;
+ setFailingPort(altPort);
+ useAltPort = false;
}
else
{
- failingPort = getFailingPort()+1;
- b = true;
+ setFailingPort(stdPort);
+ useAltPort = true;
}
- setFailingPort(failingPort);
+
}
- //To prevent any failover logic being initiaed when we shutdown the brokers.
+ //To prevent any failover logic being initiated when we shutdown the brokers.
connection.close();
// Shutdown the brokers
- stopBroker(getFailingPort());
- stopBroker(b?getFailingPort()+1 : getFailingPort()-1);
+ stopBroker(altPort);
+ stopBroker(stdPort);
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/SelectorTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/SelectorTest.java
index af11a94ca3..5a5e23baa5 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/SelectorTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/SelectorTest.java
@@ -20,7 +20,7 @@ public class SelectorTest extends QpidTestCase
{
Connection conn = getConnection();
conn.start();
- Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Session session = conn.createSession(true, Session.AUTO_ACKNOWLEDGE);
Destination dest = session.createQueue("SelectorQueue");
@@ -32,6 +32,7 @@ public class SelectorTest extends QpidTestCase
Message msg = session.createTextMessage("Msg" + String.valueOf(i));
prod.send(msg);
}
+ session.commit();
Message msg1 = consumer.receive(1000);
Message msg2 = consumer.receive(1000);
@@ -39,6 +40,8 @@ public class SelectorTest extends QpidTestCase
Assert.assertNotNull("Msg1 should not be null", msg1);
Assert.assertNotNull("Msg2 should not be null", msg2);
+ session.commit();
+
prod.setDisableMessageID(true);
for (int i=0; i<2; i++)
@@ -47,14 +50,15 @@ public class SelectorTest extends QpidTestCase
prod.send(msg);
}
+ session.commit();
Message msg3 = consumer.receive(1000);
Assert.assertNull("Msg3 should be null", msg3);
-
+ session.commit();
consumer = session.createConsumer(dest,"JMSMessageID IS NULL");
Message msg4 = consumer.receive(1000);
Message msg5 = consumer.receive(1000);
-
+ session.commit();
Assert.assertNotNull("Msg4 should not be null", msg4);
Assert.assertNotNull("Msg5 should not be null", msg5);
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/forwardall/Client.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/forwardall/Client.java
index 0be11011b4..d911bb33d7 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/forwardall/Client.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/forwardall/Client.java
@@ -29,6 +29,7 @@ import org.apache.qpid.test.utils.QpidTestCase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
@@ -62,7 +63,7 @@ public class Client implements MessageListener
{
_connection = connection;
_expected = expected;
- _session = (AMQSession) _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ _session = (AMQSession) _connection.createSession(true, AMQSession.NO_ACKNOWLEDGE);
AMQQueue response =
new AMQQueue(_connection.getDefaultQueueExchangeName(), new AMQShortString("ResponseQueue"), true);
_session.createConsumer(response).setMessageListener(this);
@@ -73,6 +74,7 @@ public class Client implements MessageListener
request.setJMSReplyTo(response);
MessageProducer prod = _session.createProducer(service);
prod.send(request);
+ _session.commit();
}
void shutdownWhenComplete() throws Exception
@@ -90,6 +92,14 @@ public class Client implements MessageListener
notifyAll();
}
+ try
+ {
+ _session.commit();
+ }
+ catch (JMSException e)
+ {
+
+ }
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/forwardall/Service.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/forwardall/Service.java
index 9cd8b183af..ce50ceae19 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/forwardall/Service.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/forwardall/Service.java
@@ -55,7 +55,7 @@ public class Service implements MessageListener
{
_connection = connection;
//AMQQueue queue = new SpecialQueue(connection, "ServiceQueue");
- _session = (AMQSession) _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ _session = (AMQSession) _connection.createSession(true, AMQSession.NO_ACKNOWLEDGE);
AMQQueue queue = (AMQQueue) _session.createQueue("ServiceQueue") ;
_session.createConsumer(queue).setMessageListener(this);
_connection.start();
@@ -68,6 +68,7 @@ public class Service implements MessageListener
Message response = _session.createTextMessage("Response!");
Destination replyTo = request.getJMSReplyTo();
_session.createProducer(replyTo).send(response);
+ _session.commit();
}
catch (Exception e)
{
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
index c5cdb83bbf..2a44413ac8 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
@@ -51,7 +51,13 @@ import javax.jms.TopicSubscriber;
public class DurableSubscriptionTest extends QpidTestCase
{
private static final Logger _logger = LoggerFactory.getLogger(DurableSubscriptionTest.class);
-
+
+ /** Timeout for receive() if we are expecting a message */
+ private static final long POSITIVE_RECEIVE_TIMEOUT = 2000;
+
+ /** Timeout for receive() if we are not expecting a message */
+ private static final long NEGATIVE_RECEIVE_TIMEOUT = 1000;
+
public void testUnsubscribe() throws Exception
{
AMQConnection con = (AMQConnection) getConnection("guest", "guest");
@@ -76,16 +82,18 @@ public class DurableSubscriptionTest extends QpidTestCase
Message msg;
_logger.info("Receive message on consumer 1:expecting A");
- msg = consumer1.receive();
+ msg = consumer1.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Message should have been received",msg);
assertEquals("A", ((TextMessage) msg).getText());
_logger.info("Receive message on consumer 1 :expecting null");
- msg = consumer1.receive(1000);
+ msg = consumer1.receive(NEGATIVE_RECEIVE_TIMEOUT);
assertEquals(null, msg);
- _logger.info("Receive message on consumer 1:expecting A");
- msg = consumer2.receive();
+ _logger.info("Receive message on consumer 2:expecting A");
+ msg = consumer2.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Message should have been received",msg);
assertEquals("A", ((TextMessage) msg).getText());
- msg = consumer2.receive(1000);
+ msg = consumer2.receive(NEGATIVE_RECEIVE_TIMEOUT);
_logger.info("Receive message on consumer 1 :expecting null");
assertEquals(null, msg);
@@ -96,14 +104,15 @@ public class DurableSubscriptionTest extends QpidTestCase
producer.send(session1.createTextMessage("B"));
_logger.info("Receive message on consumer 1 :expecting B");
- msg = consumer1.receive();
+ msg = consumer1.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Message should have been received",msg);
assertEquals("B", ((TextMessage) msg).getText());
_logger.info("Receive message on consumer 1 :expecting null");
- msg = consumer1.receive(1000);
+ msg = consumer1.receive(NEGATIVE_RECEIVE_TIMEOUT);
assertEquals(null, msg);
_logger.info("Receive message on consumer 2 :expecting null");
- msg = consumer2.receive(1000);
+ msg = consumer2.receive(NEGATIVE_RECEIVE_TIMEOUT);
assertEquals(null, msg);
_logger.info("Close connection");
@@ -143,14 +152,16 @@ public class DurableSubscriptionTest extends QpidTestCase
producer.send(session1.createTextMessage("A"));
Message msg;
- msg = consumer1.receive();
+ msg = consumer1.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Message should have been received",msg);
assertEquals("A", ((TextMessage) msg).getText());
- msg = consumer1.receive(1000);
+ msg = consumer1.receive(NEGATIVE_RECEIVE_TIMEOUT);
assertEquals(null, msg);
- msg = consumer2.receive();
+ msg = consumer2.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Message should have been received",msg);
assertEquals("A", ((TextMessage) msg).getText());
- msg = consumer2.receive(1000);
+ msg = consumer2.receive(NEGATIVE_RECEIVE_TIMEOUT);
assertEquals(null, msg);
consumer2.close();
@@ -220,8 +231,8 @@ public class DurableSubscriptionTest extends QpidTestCase
msg = consumer1.receive(500);
assertNull("There should be no more messages for consumption on consumer1.", msg);
- msg = consumer2.receive();
- assertNotNull(msg);
+ msg = consumer2.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Message should have been received",msg);
assertEquals("Consumer 2 should also received the first msg.", "A", ((TextMessage) msg).getText());
msg = consumer2.receive(500);
assertNull("There should be no more messages for consumption on consumer2.", msg);
@@ -235,10 +246,10 @@ public class DurableSubscriptionTest extends QpidTestCase
producer.send(session0.createTextMessage("B"));
_logger.info("Receive message on consumer 1 :expecting B");
- msg = consumer1.receive(1000);
+ msg = consumer1.receive(NEGATIVE_RECEIVE_TIMEOUT);
assertEquals("B", ((TextMessage) msg).getText());
_logger.info("Receive message on consumer 1 :expecting null");
- msg = consumer1.receive(1000);
+ msg = consumer1.receive(NEGATIVE_RECEIVE_TIMEOUT);
assertEquals(null, msg);
// Re-attach a new consumer to the durable subscription, and check that it gets the message that it missed.
@@ -296,7 +307,7 @@ public class DurableSubscriptionTest extends QpidTestCase
producer.send(session.createTextMessage("testDurableWithInvalidSelector2"));
- Message msg = liveSubscriber.receive();
+ Message msg = liveSubscriber.receive(POSITIVE_RECEIVE_TIMEOUT);
assertNotNull ("Message should have been received", msg);
assertEquals ("testDurableWithInvalidSelector2", ((TextMessage) msg).getText());
assertNull("Should not receive subsequent message", liveSubscriber.receive(200));
@@ -331,7 +342,7 @@ public class DurableSubscriptionTest extends QpidTestCase
assertNotNull("Subscriber should have been created", liveSubscriber);
producer.send(session.createTextMessage("testDurableWithInvalidSelector2"));
- Message msg = liveSubscriber.receive();
+ Message msg = liveSubscriber.receive(POSITIVE_RECEIVE_TIMEOUT);
assertNotNull ("Message should have been received", msg);
assertEquals ("testDurableWithInvalidSelector2", ((TextMessage) msg).getText());
assertNull("Should not receive subsequent message", liveSubscriber.receive(200));
@@ -360,13 +371,13 @@ public class DurableSubscriptionTest extends QpidTestCase
// Send 1 matching message and 1 non-matching message
sendMatchingAndNonMatchingMessage(session, producer);
- Message rMsg = subA.receive(1000);
+ Message rMsg = subA.receive(NEGATIVE_RECEIVE_TIMEOUT);
assertNotNull(rMsg);
assertEquals("Content was wrong",
"testResubscribeWithChangedSelector1",
((TextMessage) rMsg).getText());
- rMsg = subA.receive(1000);
+ rMsg = subA.receive(NEGATIVE_RECEIVE_TIMEOUT);
assertNull(rMsg);
// Disconnect subscriber
@@ -379,13 +390,13 @@ public class DurableSubscriptionTest extends QpidTestCase
// Check messages are recieved properly
sendMatchingAndNonMatchingMessage(session, producer);
- rMsg = subB.receive(1000);
+ rMsg = subB.receive(NEGATIVE_RECEIVE_TIMEOUT);
assertNotNull(rMsg);
assertEquals("Content was wrong",
"testResubscribeWithChangedSelector2",
((TextMessage) rMsg).getText());
- rMsg = subB.receive(1000);
+ rMsg = subB.receive(NEGATIVE_RECEIVE_TIMEOUT);
assertNull(rMsg);
session.unsubscribe("testResubscribeWithChangedSelector");
}
@@ -429,5 +440,5 @@ public class DurableSubscriptionTest extends QpidTestCase
public static junit.framework.Test suite()
{
return new junit.framework.TestSuite(DurableSubscriptionTest.class);
- }
+ }
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java
index 95b90481c7..f8ba7060a9 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java
@@ -57,7 +57,7 @@ public class TopicSessionTest extends QpidTestCase
AMQConnection con = (AMQConnection) getConnection("guest", "guest");
AMQTopic topic = new AMQTopic(con.getDefaultTopicExchangeName(), "MyTopic");
- TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ TopicSession session1 = con.createTopicSession(true, AMQSession.NO_ACKNOWLEDGE);
TopicSubscriber sub = session1.createDurableSubscriber(topic, "subscription0");
TopicPublisher publisher = session1.createPublisher(topic);
@@ -65,10 +65,11 @@ public class TopicSessionTest extends QpidTestCase
TextMessage tm = session1.createTextMessage("Hello");
publisher.publish(tm);
+ session1.commit();
tm = (TextMessage) sub.receive(2000);
assertNotNull(tm);
-
+ session1.commit();
session1.unsubscribe("subscription0");
try
@@ -104,15 +105,17 @@ public class TopicSessionTest extends QpidTestCase
AMQTopic topic = new AMQTopic(con, "MyTopic1" + String.valueOf(shutdown));
AMQTopic topic2 = new AMQTopic(con, "MyOtherTopic1" + String.valueOf(shutdown));
- TopicSession session1 = con.createTopicSession(false, AMQSession.AUTO_ACKNOWLEDGE);
+ TopicSession session1 = con.createTopicSession(true, AMQSession.AUTO_ACKNOWLEDGE);
TopicSubscriber sub = session1.createDurableSubscriber(topic, "subscription0");
TopicPublisher publisher = session1.createPublisher(null);
con.start();
publisher.publish(topic, session1.createTextMessage("hello"));
+ session1.commit();
TextMessage m = (TextMessage) sub.receive(2000);
assertNotNull(m);
+ session1.commit();
if (shutdown)
{
@@ -120,17 +123,20 @@ public class TopicSessionTest extends QpidTestCase
con.close();
con = (AMQConnection) getConnection("guest", "guest");
con.start();
- session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ session1 = con.createTopicSession(true, AMQSession.NO_ACKNOWLEDGE);
publisher = session1.createPublisher(null);
}
TopicSubscriber sub2 = session1.createDurableSubscriber(topic2, "subscription0");
publisher.publish(topic, session1.createTextMessage("hello"));
+ session1.commit();
if (!shutdown)
{
m = (TextMessage) sub.receive(2000);
assertNull(m);
+ session1.commit();
}
publisher.publish(topic2, session1.createTextMessage("goodbye"));
+ session1.commit();
m = (TextMessage) sub2.receive(2000);
assertNotNull(m);
assertEquals("goodbye", m.getText());
@@ -143,25 +149,29 @@ public class TopicSessionTest extends QpidTestCase
AMQConnection con1 = (AMQConnection) getConnection("guest", "guest", "clientid");
AMQTopic topic = new AMQTopic(con1, "MyTopic3");
- TopicSession session1 = con1.createTopicSession(false, AMQSession.AUTO_ACKNOWLEDGE);
+ TopicSession session1 = con1.createTopicSession(true, AMQSession.AUTO_ACKNOWLEDGE);
TopicPublisher publisher = session1.createPublisher(topic);
AMQConnection con2 = (AMQConnection) getConnection("guest", "guest", "clientid");
- TopicSession session2 = con2.createTopicSession(false, AMQSession.AUTO_ACKNOWLEDGE);
+ TopicSession session2 = con2.createTopicSession(true, AMQSession.AUTO_ACKNOWLEDGE);
TopicSubscriber sub = session2.createDurableSubscriber(topic, "subscription0");
con2.start();
publisher.publish(session1.createTextMessage("Hello"));
+ session1.commit();
TextMessage tm = (TextMessage) sub.receive(2000);
+ session2.commit();
assertNotNull(tm);
con2.close();
publisher.publish(session1.createTextMessage("Hello2"));
+ session1.commit();
con2 = (AMQConnection) getConnection("guest", "guest", "clientid");
- session2 = con2.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ session2 = con2.createTopicSession(true, AMQSession.NO_ACKNOWLEDGE);
sub = session2.createDurableSubscriber(topic, "subscription0");
con2.start();
tm = (TextMessage) sub.receive(2000);
+ session2.commit();
assertNotNull(tm);
assertEquals("Hello2", tm.getText());
session2.unsubscribe("subscription0");
@@ -174,12 +184,13 @@ public class TopicSessionTest extends QpidTestCase
AMQConnection con = (AMQConnection) getConnection("guest", "guest");
AMQTopic topic = new AMQTopic(con, "MyTopic4");
- TopicSession session1 = con.createTopicSession(false, AMQSession.AUTO_ACKNOWLEDGE);
+ TopicSession session1 = con.createTopicSession(true, AMQSession.AUTO_ACKNOWLEDGE);
TopicPublisher publisher = session1.createPublisher(topic);
MessageConsumer consumer1 = session1.createConsumer(topic);
con.start();
TextMessage tm = session1.createTextMessage("Hello");
publisher.publish(tm);
+ session1.commit();
tm = (TextMessage) consumer1.receive(10000L);
assertNotNull(tm);
String msgText = tm.getText();
@@ -188,15 +199,19 @@ public class TopicSessionTest extends QpidTestCase
msgText = tm.getText();
assertNull(msgText);
publisher.publish(tm);
+ session1.commit();
tm = (TextMessage) consumer1.receive(10000L);
assertNotNull(tm);
+ session1.commit();
msgText = tm.getText();
assertNull(msgText);
tm.clearBody();
tm.setText("Now we are not null");
publisher.publish(tm);
+ session1.commit();
tm = (TextMessage) consumer1.receive(2000);
assertNotNull(tm);
+ session1.commit();
msgText = tm.getText();
assertEquals("Now we are not null", msgText);
@@ -204,7 +219,9 @@ public class TopicSessionTest extends QpidTestCase
msgText = tm.getText();
assertEquals("Empty string not returned", "", msgText);
publisher.publish(tm);
+ session1.commit();
tm = (TextMessage) consumer1.receive(2000);
+ session1.commit();
assertNotNull(tm);
assertEquals("Empty string not returned", "", msgText);
con.close();
@@ -213,7 +230,7 @@ public class TopicSessionTest extends QpidTestCase
public void testSendingSameMessage() throws Exception
{
AMQConnection conn = (AMQConnection) getConnection("guest", "guest");
- TopicSession session = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicSession session = conn.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
TemporaryTopic topic = session.createTemporaryTopic();
assertNotNull(topic);
TopicPublisher producer = session.createPublisher(topic);
@@ -221,14 +238,16 @@ public class TopicSessionTest extends QpidTestCase
conn.start();
TextMessage sentMessage = session.createTextMessage("Test Message");
producer.send(sentMessage);
+ session.commit();
TextMessage receivedMessage = (TextMessage) consumer.receive(2000);
assertNotNull(receivedMessage);
assertEquals(sentMessage.getText(), receivedMessage.getText());
producer.send(sentMessage);
+ session.commit();
receivedMessage = (TextMessage) consumer.receive(2000);
assertNotNull(receivedMessage);
assertEquals(sentMessage.getText(), receivedMessage.getText());
-
+ session.commit();
conn.close();
}
@@ -236,17 +255,18 @@ public class TopicSessionTest extends QpidTestCase
public void testTemporaryTopic() throws Exception
{
AMQConnection conn = (AMQConnection) getConnection("guest", "guest");
- TopicSession session = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicSession session = conn.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
TemporaryTopic topic = session.createTemporaryTopic();
assertNotNull(topic);
TopicPublisher producer = session.createPublisher(topic);
MessageConsumer consumer = session.createConsumer(topic);
conn.start();
producer.send(session.createTextMessage("hello"));
+ session.commit();
TextMessage tm = (TextMessage) consumer.receive(2000);
assertNotNull(tm);
assertEquals("hello", tm.getText());
-
+ session.commit();
try
{
topic.delete();
@@ -291,7 +311,7 @@ public class TopicSessionTest extends QpidTestCase
AMQTopic topic = new AMQTopic(con, "testNoLocal");
- TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ TopicSession session1 = con.createTopicSession(true, AMQSession.NO_ACKNOWLEDGE);
TopicSubscriber noLocal = session1.createSubscriber(topic, "", true);
TopicSubscriber select = session1.createSubscriber(topic, "Selector = 'select'", false);
TopicSubscriber normal = session1.createSubscriber(topic);
@@ -304,15 +324,17 @@ public class TopicSessionTest extends QpidTestCase
//send message to all consumers
publisher.publish(session1.createTextMessage("hello-new2"));
-
+ session1.commit();
//test normal subscriber gets message
m = (TextMessage) normal.receive(1000);
assertNotNull(m);
-
+ session1.commit();
+
//test selector subscriber doesn't message
m = (TextMessage) select.receive(1000);
assertNull(m);
-
+ session1.commit();
+
//test nolocal subscriber doesn't message
m = (TextMessage) noLocal.receive(1000);
if (m != null)
@@ -326,21 +348,24 @@ public class TopicSessionTest extends QpidTestCase
message.setStringProperty("Selector", "select");
publisher.publish(message);
-
+ session1.commit();
+
//test normal subscriber gets message
m = (TextMessage) normal.receive(1000);
assertNotNull(m);
-
+ session1.commit();
+
//test selector subscriber does get message
m = (TextMessage) select.receive(1000);
assertNotNull(m);
+ session1.commit();
//test nolocal subscriber doesn't message
m = (TextMessage) noLocal.receive(100);
assertNull(m);
AMQConnection con2 = (AMQConnection) getConnection("guest", "guest", "foo");
- TopicSession session2 = con2.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ TopicSession session2 = con2.createTopicSession(true, AMQSession.NO_ACKNOWLEDGE);
TopicPublisher publisher2 = session2.createPublisher(topic);
@@ -348,14 +373,17 @@ public class TopicSessionTest extends QpidTestCase
message.setStringProperty("Selector", "select");
publisher2.publish(message);
+ session2.commit();
//test normal subscriber gets message
m = (TextMessage) normal.receive(1000);
assertNotNull(m);
+ session1.commit();
//test selector subscriber does get message
m = (TextMessage) select.receive(1000);
assertNotNull(m);
+ session1.commit();
//test nolocal subscriber does message
m = (TextMessage) noLocal.receive(100);
@@ -378,7 +406,7 @@ public class TopicSessionTest extends QpidTestCase
// Setup Topic
AMQTopic topic = new AMQTopic(con, "testNoLocal");
- TopicSession session = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ TopicSession session = con.createTopicSession(true, AMQSession.NO_ACKNOWLEDGE);
// Setup subscriber with selector
TopicSubscriber selector = session.createSubscriber(topic, "Selector = 'select'", false);
@@ -391,13 +419,15 @@ public class TopicSessionTest extends QpidTestCase
// Send non-matching message
message = session.createTextMessage("non-matching 1");
publisher.publish(message);
+ session.commit();
// Send and consume matching message
message = session.createTextMessage("hello");
message.setStringProperty("Selector", "select");
publisher.publish(message);
-
+ session.commit();
+
m = (TextMessage) selector.receive(1000);
assertNotNull("should have received message", m);
assertEquals("Message contents were wrong", "hello", m.getText());
@@ -405,7 +435,8 @@ public class TopicSessionTest extends QpidTestCase
// Send non-matching message
message = session.createTextMessage("non-matching 2");
publisher.publish(message);
-
+ session.commit();
+
// Assert queue count is 0
long depth = ((AMQTopicSessionAdaptor) session).getSession().getQueueDepth(topic);
assertEquals("Queue depth was wrong", 0, depth);
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java
index f7d8152c83..1bef07fcd5 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java
@@ -28,7 +28,7 @@ public class FailoverBaseCase extends QpidTestCase
{
public static int FAILING_VM_PORT = 2;
- public static int FAILING_PORT = DEFAULT_PORT + 1;
+ public static int FAILING_PORT = DEFAULT_PORT + 100;
protected int failingPort;
@@ -42,7 +42,7 @@ public class FailoverBaseCase extends QpidTestCase
}
else
{
- failingPort = FAILING_PORT;
+ failingPort = FAILING_PORT;
}
}
@@ -55,7 +55,7 @@ public class FailoverBaseCase extends QpidTestCase
{
super.setUp();
setSystemProperty("QPID_WORK", System.getProperty("java.io.tmpdir")+"/"+getFailingPort());
- startBroker(getFailingPort());
+ startBroker(failingPort);
}
/**
@@ -67,14 +67,16 @@ public class FailoverBaseCase extends QpidTestCase
public Connection getConnection() throws Exception
{
Connection conn =
- getConnectionFactory("failover").createConnection("guest", "guest");
+ (Boolean.getBoolean("profile.use_ssl"))?
+ getConnectionFactory("failover.ssl").createConnection("guest", "guest"):
+ getConnectionFactory("failover").createConnection("guest", "guest");
_connections.add(conn);
return conn;
}
public void tearDown() throws Exception
{
- stopBroker(getFailingPort());
+ stopBroker(_broker.equals(VM)?FAILING_PORT:FAILING_PORT);
super.tearDown();
FileUtils.deleteDirectory(System.getProperty("java.io.tmpdir")+"/"+getFailingPort());
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java
index e7218d6975..db096710dc 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java
@@ -157,7 +157,7 @@ public class QpidTestCase extends TestCase
private static final String VERSION_08 = "0-8";
private static final String VERSION_010 = "0-10";
- private static final String QPID_HOME = "QPID_HOME";
+ protected static final String QPID_HOME = "QPID_HOME";
protected static int DEFAULT_VM_PORT = 1;
protected static int DEFAULT_PORT = Integer.getInteger("test.port", 5672);
@@ -182,6 +182,8 @@ public class QpidTestCase extends TestCase
protected List<Connection> _connections = new ArrayList<Connection>();
public static final String QUEUE = "queue";
public static final String TOPIC = "topic";
+ /** Map to hold test defined environment properties */
+ private Map<String,String> _env;
public QpidTestCase(String name)
@@ -199,6 +201,9 @@ public class QpidTestCase extends TestCase
_testName = getClass().getSimpleName() + "." + getName();
String qname = getClass().getName() + "." + getName();
+ // Initalise this for each test run
+ _env = new HashMap<String, String>();
+
PrintStream oldOut = System.out;
PrintStream oldErr = System.err;
PrintStream out = null;
@@ -435,6 +440,16 @@ public class QpidTestCase extends TestCase
//Add the test name to the broker run.
env.put("QPID_PNAME", "-DPNAME=\"" + _testName + "\"");
env.put("QPID_WORK", System.getProperty("QPID_WORK"));
+
+ // Add all the environment settings the test requested
+ if (!_env.isEmpty())
+ {
+ for(Map.Entry<String,String> entry : _env.entrySet())
+ {
+ env.put(entry.getKey() ,entry.getValue());
+ }
+ }
+
process = pb.start();
Piper p = new Piper(process.getInputStream(),
@@ -677,6 +692,18 @@ public class QpidTestCase extends TestCase
}
/**
+ * Add an environtmen variable for the external broker environment
+ *
+ * @param property the property to set
+ * @param value the value to set it to
+ */
+ protected void setBrokerEnvironment(String property, String value)
+ {
+ _env.put(property,value);
+ }
+
+
+ /**
* Check whether the broker is an 0.8
*
* @return true if the broker is an 0_8 version, false otherwise.
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitor.java b/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitor.java
index df8dd0b85b..44ac5b4838 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitor.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitor.java
@@ -31,6 +31,7 @@ import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -118,7 +119,7 @@ public class LogMonitor
* @throws java.io.FileNotFoundException if the Log file can nolonger be found
* @throws IOException thrown when reading the log file
*/
- public boolean waitForMessage(String message, long wait)
+ public boolean waitForMessage(String message, long wait, boolean printFileOnFailure)
throws FileNotFoundException, IOException
{
// Loop through alerts until we're done or wait ms seconds have passed,
@@ -126,20 +127,35 @@ public class LogMonitor
BufferedReader reader = new BufferedReader(new FileReader(_logfile));
boolean found = false;
long endtime = System.currentTimeMillis() + wait;
+ ArrayList<String> contents = new ArrayList<String>();
while (!found && System.currentTimeMillis() < endtime)
{
while (reader.ready())
{
String line = reader.readLine();
+ contents.add(line);
if (line.contains(message))
{
found = true;
}
}
}
-
+ if (!found && printFileOnFailure)
+ {
+ for (String line : contents)
+ {
+ System.out.println(line);
+ }
+ }
return found;
}
+
+
+ public boolean waitForMessage(String messageCountAlert, long alertLogWaitPeriod) throws FileNotFoundException, IOException
+ {
+ return waitForMessage(messageCountAlert, alertLogWaitPeriod, true);
+ }
+
/**
* Read the log file in to memory as a String
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java
index d1a0df30a9..b721e27726 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java
@@ -30,25 +30,25 @@ import java.util.List;
public class LogMonitorTest extends TestCase
{
+ private LogMonitor _monitor;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ _monitor = new LogMonitor();
+ _monitor.getMonitoredFile().deleteOnExit(); // Make sure we clean up
+ }
+
/**
* Test that a new file is created when attempting to set up a monitor with
* the default constructor.
*/
public void testMonitor()
{
- // Validate that a NPE is thrown with null input
- try
- {
- LogMonitor montior = new LogMonitor();
- //Validte that the monitor is now running on a new file
- assertTrue("New file does not have correct name:" + montior.
- getMonitoredFile().getName(),
- montior.getMonitoredFile().getName().contains("LogMonitor"));
- }
- catch (IOException ioe)
- {
- fail("IOE thrown:" + ioe);
- }
+ //Validate that the monitor is now running on a new file
+ assertTrue("New file does not have correct name:" + _monitor.
+ getMonitoredFile().getName(),
+ _monitor.getMonitoredFile().getName().contains("LogMonitor"));
}
/**
@@ -63,13 +63,11 @@ public class LogMonitorTest extends TestCase
File testFile = File.createTempFile("testMonitorFile", ".log");
testFile.deleteOnExit();
- LogMonitor monitor;
-
//Ensure that we can create a monitor on a file
try
{
- monitor = new LogMonitor(testFile);
- assertEquals(testFile, monitor.getMonitoredFile());
+ _monitor = new LogMonitor(testFile);
+ assertEquals(testFile, _monitor.getMonitoredFile());
}
catch (IOException ioe)
{
@@ -136,13 +134,12 @@ public class LogMonitorTest extends TestCase
*/
public void testFindMatches_Match() throws IOException
{
- LogMonitor monitor = new LogMonitor();
String message = getName() + ": Test Message";
Logger.getRootLogger().warn(message);
- validateLogContainsMessage(monitor, message);
+ validateLogContainsMessage(_monitor, message);
}
/**
@@ -152,21 +149,17 @@ public class LogMonitorTest extends TestCase
*/
public void testFindMatches_NoMatch() throws IOException
{
- LogMonitor monitor = new LogMonitor();
-
String message = getName() + ": Test Message";
Logger.getRootLogger().warn(message);
String notLogged = "This text was not logged";
- validateLogDoesNotContainsMessage(monitor, notLogged);
+ validateLogDoesNotContainsMessage(_monitor, notLogged);
}
public void testWaitForMessage_Found() throws IOException
{
- LogMonitor monitor = new LogMonitor();
-
String message = getName() + ": Test Message";
long TIME_OUT = 2000;
@@ -174,13 +167,11 @@ public class LogMonitorTest extends TestCase
logMessageWithDelay(message, TIME_OUT / 2);
assertTrue("Message was not logged ",
- monitor.waitForMessage(message, TIME_OUT));
+ _monitor.waitForMessage(message, TIME_OUT));
}
public void testWaitForMessage_Timeout() throws IOException
{
- LogMonitor monitor = new LogMonitor();
-
String message = getName() + ": Test Message";
long TIME_OUT = 2000;
@@ -189,41 +180,37 @@ public class LogMonitorTest extends TestCase
// Verify that we can time out waiting for a message
assertFalse("Message was logged ",
- monitor.waitForMessage(message, TIME_OUT / 2));
+ _monitor.waitForMessage(message, TIME_OUT / 2, false));
// Verify that the message did eventually get logged.
assertTrue("Message was never logged.",
- monitor.waitForMessage(message, TIME_OUT));
+ _monitor.waitForMessage(message, TIME_OUT));
}
public void testReset() throws IOException
{
- LogMonitor monitor = new LogMonitor();
-
String message = getName() + ": Test Message";
Logger.getRootLogger().warn(message);
- validateLogContainsMessage(monitor, message);
+ validateLogContainsMessage(_monitor, message);
String LOG_RESET_TEXT = "Log Monitor Reset";
- validateLogDoesNotContainsMessage(monitor, LOG_RESET_TEXT);
+ validateLogDoesNotContainsMessage(_monitor, LOG_RESET_TEXT);
- monitor.reset();
+ _monitor.reset();
- assertEquals("", monitor.readFile());
+ assertEquals("", _monitor.readFile());
}
public void testRead() throws IOException
{
- LogMonitor monitor = new LogMonitor();
-
String message = getName() + ": Test Message";
Logger.getRootLogger().warn(message);
- String fileContents = monitor.readFile();
+ String fileContents = _monitor.readFile();
assertTrue("Logged message not found when reading file.",
fileContents.contains(message));
diff --git a/qpid/java/test-profiles/010Excludes b/qpid/java/test-profiles/010Excludes
index 7577ad8da1..e238934124 100644
--- a/qpid/java/test-profiles/010Excludes
+++ b/qpid/java/test-profiles/010Excludes
@@ -4,6 +4,7 @@ org.apache.qpid.client.ResetMessageListenerTest#*
//These tests are for the java broker
org.apache.qpid.server.security.acl.SimpleACLTest#*
org.apache.qpid.server.plugins.PluginTest#*
+org.apache.qpid.server.BrokerStartupTest#*
// This test is not finished
org.apache.qpid.test.testcases.TTLTest#*
diff --git a/qpid/java/test-profiles/cpp.ssl.excludes b/qpid/java/test-profiles/cpp.ssl.excludes
index bf21ab9cc7..1828581d55 100644
--- a/qpid/java/test-profiles/cpp.ssl.excludes
+++ b/qpid/java/test-profiles/cpp.ssl.excludes
@@ -1 +1 @@
-org.apache.qpid.test.client.failover.FailoverTest#*
+#org.apache.qpid.test.client.failover.FailoverTest#*
diff --git a/qpid/java/test-profiles/default.testprofile b/qpid/java/test-profiles/default.testprofile
index 864cf149d4..49d4a25b82 100644
--- a/qpid/java/test-profiles/default.testprofile
+++ b/qpid/java/test-profiles/default.testprofile
@@ -19,7 +19,8 @@ log4j.debug=false
test.port=15672
test.mport=18999
test.port.ssl=15671
-test.port.alt=15673
+test.port.alt=15772
+test.port.alt.ssl=15771
test.exclude=true
profile.excludes=08TransientExcludes
diff --git a/qpid/java/test-profiles/test-provider.properties b/qpid/java/test-profiles/test-provider.properties
index 2479b5a5f0..e5cb18da0b 100644
--- a/qpid/java/test-profiles/test-provider.properties
+++ b/qpid/java/test-profiles/test-provider.properties
@@ -21,13 +21,17 @@
test.port=5672
test.port.ssl=5671
-test.port.alt=5673
+test.port.alt=5772
+test.port.alt.ssl=5771
connectionfactory.default = amqp://username:password@clientid/test?brokerlist='tcp://localhost:${test.port}'
connectionfactory.default.vm = amqp://username:password@clientid/test?brokerlist='vm://:1'
connectionfactory.ssl = amqp://username:password@clientid/test?brokerlist='tcp://localhost:${test.port.ssl}?ssl='true''
connectionfactory.failover = amqp://username:password@clientid/test?brokerlist='tcp://localhost:${test.port.alt};tcp://localhost:${test.port}'&sync_ack='true'&sync_publish='all'&failover='roundrobin?cyclecount='20''
+
+connectionfactory.failover.ssl = amqp://username:password@clientid/test?brokerlist='tcp://localhost:${test.port.alt.ssl}?ssl='true';tcp://localhost:${test.port.ssl}?ssl='true''&sync_ack='true'&sync_publish='all'&failover='roundrobin?cyclecount='20''
+
connectionfactory.failover.vm = amqp://username:password@clientid/test?brokerlist='vm://:2;vm://:1'
connectionfactory.connection1 = amqp://username:password@clientid/test?brokerlist='tcp://localhost:${test.port}'
connectionfactory.connection2 = amqp://username:password@clientid/test?brokerlist='tcp://localhost:${test.port.alt}'
diff --git a/qpid/python/Makefile b/qpid/python/Makefile
new file mode 100644
index 0000000000..380115db41
--- /dev/null
+++ b/qpid/python/Makefile
@@ -0,0 +1,92 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+PREFIX=/usr/local
+EXEC_PREFIX=$(PREFIX)/bin
+DATA_DIR=$(PREFIX)/share
+
+PYTHON_LIB=$(shell python -c "from distutils.sysconfig import get_python_lib; print get_python_lib(prefix='$(PREFIX)')")
+PYTHON_VERSION=$(shell python -c "from distutils.sysconfig import get_python_version; print get_python_version()")
+
+ddfirst=$(shell ddir=$(DATA_DIR) && echo $${ddir:0:1})
+ifeq ($(ddfirst),/)
+AMQP_SPEC_DIR=$(DATA_DIR)/amqp
+else
+AMQP_SPEC_DIR=$(PWD)/$(DATA_DIR)/amqp
+endif
+
+DIRS=qmf qpid mllib models examples tests tests_0-8 tests_0-9 tests_0-10
+SRCS=$(shell find $(DIRS) -name "*.py") qpid_config.py
+BUILD=build
+TARGETS=$(SRCS:%.py=$(BUILD)/%.py)
+
+PYCC=python -c "import compileall, sys; compileall.compile_dir(sys.argv[1])"
+
+all: build
+
+$(BUILD)/%.py: %.py
+ @mkdir -p $(shell dirname $@)
+ ./preppy $(PYTHON_VERSION) < $< > $@
+
+build: $(TARGETS)
+
+install: build
+ install -d $(PYTHON_LIB)
+
+ install -d $(PYTHON_LIB)/mllib
+ install -pm 0644 LICENSE.txt NOTICE.txt $(BUILD)/mllib/*.* $(PYTHON_LIB)/mllib
+ $(PYCC) $(PYTHON_LIB)/mllib
+
+ install -d $(PYTHON_LIB)/qpid
+ install -pm 0644 LICENSE.txt NOTICE.txt README.txt $(BUILD)/qpid/*.* $(PYTHON_LIB)/qpid
+ TDIR=$(shell mktemp -d) && \
+ sed s@AMQP_SPEC_DIR=.*@AMQP_SPEC_DIR='"$(AMQP_SPEC_DIR)"'@ \
+ $(BUILD)/qpid_config.py > $${TDIR}/qpid_config.py && \
+ install -pm 0644 $${TDIR}/qpid_config.py $(PYTHON_LIB) && \
+ rm -rf $${TDIR}
+
+ install -d $(PYTHON_LIB)/qpid/tests
+ install -pm 0644 $(BUILD)/qpid/tests/*.* $(PYTHON_LIB)/qpid/tests
+ $(PYCC) $(PYTHON_LIB)/qpid
+
+ install -d $(PYTHON_LIB)/qmf
+ install -pm 0644 LICENSE.txt NOTICE.txt qmf/*.* $(PYTHON_LIB)/qmf
+ $(PYCC) $(PYTHON_LIB)/qmf
+
+ install -d $(PYTHON_LIB)/tests
+ install -pm 0644 $(BUILD)/tests/*.* $(PYTHON_LIB)/tests
+ $(PYCC) $(PYTHON_LIB)/tests
+
+ install -d $(PYTHON_LIB)/tests_0-8
+ install -pm 0644 $(BUILD)/tests_0-8/*.* $(PYTHON_LIB)/tests_0-8
+ $(PYCC) $(PYTHON_LIB)/tests_0-8
+
+ install -d $(PYTHON_LIB)/tests_0-9
+ install -pm 0644 $(BUILD)/tests_0-9/*.* $(PYTHON_LIB)/tests_0-9
+ $(PYCC) $(PYTHON_LIB)/tests_0-9
+
+ install -d $(PYTHON_LIB)/tests_0-10
+ install -pm 0644 $(BUILD)/tests_0-10/*.* $(PYTHON_LIB)/tests_0-10
+ $(PYCC) $(PYTHON_LIB)/tests_0-10
+
+ install -d $(EXEC_PREFIX)
+ install -pm 0755 qpid-python-test commands/* $(EXEC_PREFIX)
+
+clean:
+ rm -rf $(BUILD)
diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config
index c4ea5c5f2d..4cf9505c58 100755
--- a/qpid/python/commands/qpid-config
+++ b/qpid/python/commands/qpid-config
@@ -110,6 +110,12 @@ def Usage ():
print " --force-if-not-empty Force delete of queue even if it's not empty"
print " --force-if-used Force delete of queue even if it's currently used"
print
+ print "Add Exchange <type> values:"
+ print " direct Direct exchange for point-to-point communication"
+ print " fanout Fanout exchange for broadcast communication"
+ print " topic Topic exchange that routes messages using binding keys with wildcards"
+ print " headers Headers exchange that matches header fields against the binding keys"
+ print
print "Add Exchange Options:"
print " --alternate-exchange [name of the alternate exchange]"
print " In the event that a message cannot be routed, this is the name of the exchange to"
@@ -190,6 +196,8 @@ class BrokerManager:
if ex.durable: print "--durable",
if MSG_SEQUENCE in args and args[MSG_SEQUENCE] == 1: print "--sequence",
if IVE in args and args[IVE] == 1: print "--ive",
+ if ex.altExchange:
+ print "--alternate-exchange=%s" % ex._altExchange_.name,
print
def ExchangeListRecurse (self, filter):
diff --git a/qpid/python/preppy b/qpid/python/preppy
new file mode 100755
index 0000000000..22893dad03
--- /dev/null
+++ b/qpid/python/preppy
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import os, re, sys
+
+ann = re.compile(r"([ \t]*)@([_a-zA-Z][_a-zA-Z0-9]*)([ \t\n\r]+def[ \t]+)([_a-zA-Z][_a-zA-Z0-9]*)")
+line = re.compile(r"\n([ \t]*)[^ \t\n#]+")
+
+if len(sys.argv) == 2:
+ major, minor = [int(p) for p in sys.argv[1].split(".")]
+elif len(sys.argv) == 1:
+ major, minor = sys.version_info[0:2]
+else:
+ print "usage: %s [ version ] < input.py > output.py" % sys.argv[0]
+ sys.exit(-1)
+
+if major <= 2 and minor <= 3:
+ def process(input):
+ output = ""
+ pos = 0
+ while True:
+ m = ann.search(input, pos)
+ if m:
+ indent, decorator, idef, function = m.groups()
+ output += input[pos:m.start()]
+ output += "%s#@%s%s%s" % (indent, decorator, idef, function)
+ pos = m.end()
+
+ subst = "\n%s%s = %s(%s)\n" % (indent, function, decorator, function)
+ npos = pos
+ while True:
+ n = line.search(input, npos)
+ if not n:
+ input += subst
+ break
+ if len(n.group(1)) <= len(indent):
+ idx = n.start()
+ input = input[:idx] + subst + input[idx:]
+ break
+ npos = n.end()
+ else:
+ break
+
+ output += input[pos:]
+ return output
+else:
+ def process(input):
+ return input
+
+sys.stdout.write(process(sys.stdin.read()))
diff --git a/qpid/python/qpid/compat.py b/qpid/python/qpid/compat.py
index 26f60fb8aa..49273193df 100644
--- a/qpid/python/qpid/compat.py
+++ b/qpid/python/qpid/compat.py
@@ -26,3 +26,10 @@ try:
from socket import SHUT_RDWR
except ImportError:
SHUT_RDWR = 2
+
+try:
+ from traceback import format_exc
+except ImportError:
+ import sys, traceback
+ def format_exc():
+ return "".join(traceback.format_exception(*sys.exc_info()))
diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py
index bba3f5b9ab..f832ddae34 100644
--- a/qpid/python/qpid/datatypes.py
+++ b/qpid/python/qpid/datatypes.py
@@ -132,7 +132,7 @@ class Serial:
return hash(self.value)
def __cmp__(self, other):
- if other is None:
+ if other.__class__ not in (int, long, Serial):
return 1
other = serial(other)
@@ -150,7 +150,10 @@ class Serial:
return Serial(self.value + other)
def __sub__(self, other):
- return Serial(self.value - other)
+ if isinstance(other, Serial):
+ return self.value - other.value
+ else:
+ return Serial(self.value - other)
def __repr__(self):
return "serial(%s)" % self.value
@@ -169,7 +172,7 @@ class Range:
def __contains__(self, n):
return self.lower <= n and n <= self.upper
-
+
def __iter__(self):
i = self.lower
while i <= self.upper:
@@ -230,7 +233,7 @@ class RangedSet:
def add(self, lower, upper = None):
self.add_range(Range(lower, upper))
-
+
def __iter__(self):
return iter(self.ranges)
diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py
index c006da76f5..65807548e6 100644
--- a/qpid/python/qpid/management.py
+++ b/qpid/python/qpid/management.py
@@ -234,8 +234,7 @@ class managementClient:
#========================================================
# User API - interacts with the class's user
#========================================================
- def __init__ (self, amqpSpec, ctrlCb=None, configCb=None, instCb=None, methodCb=None, closeCb=None):
- self.spec = amqpSpec
+ def __init__ (self, unused=None, ctrlCb=None, configCb=None, instCb=None, methodCb=None, closeCb=None):
self.ctrlCb = ctrlCb
self.configCb = configCb
self.instCb = instCb
@@ -268,7 +267,7 @@ class managementClient:
self.channels.append (mch)
self.incOutstanding (mch)
- codec = Codec (self.spec)
+ codec = Codec ()
self.setHeader (codec, ord ('B'))
msg = mch.message(codec.encoded)
mch.send ("qpid.management", msg)
@@ -285,7 +284,7 @@ class managementClient:
def getObjects (self, channel, userSequence, className, bank=0):
""" Request immediate content from broker """
- codec = Codec (self.spec)
+ codec = Codec ()
self.setHeader (codec, ord ('G'), userSequence)
ft = {}
ft["_class"] = className
@@ -353,7 +352,7 @@ class managementClient:
#========================================================
def topicCb (self, ch, msg):
""" Receive messages via the topic queue of a particular channel. """
- codec = Codec (self.spec, msg.body)
+ codec = Codec (msg.body)
while True:
hdr = self.checkHeader (codec)
if hdr == None:
@@ -372,7 +371,7 @@ class managementClient:
def replyCb (self, ch, msg):
""" Receive messages via the reply queue of a particular channel. """
- codec = Codec (self.spec, msg.body)
+ codec = Codec (msg.body)
hdr = self.checkHeader (codec)
if hdr == None:
return
@@ -498,7 +497,7 @@ class managementClient:
data = codec.read_uuid ()
elif typecode == 15: # FTABLE
data = {}
- sc = Codec(codec.spec, codec.read_vbin32())
+ sc = Codec(codec.read_vbin32())
if sc.encoded:
count = sc.read_uint32()
while count > 0:
@@ -599,7 +598,7 @@ class managementClient:
self.ctrlCb (ch.context, self.CTRL_BROKER_INFO, ch.brokerInfo)
# Send a package request
- sendCodec = Codec (self.spec)
+ sendCodec = Codec ()
seq = self.seqMgr.reserve ("outstanding")
self.setHeader (sendCodec, ord ('P'), seq)
smsg = ch.message(sendCodec.encoded)
@@ -611,7 +610,7 @@ class managementClient:
self.packages[pname] = {}
# Send a class request
- sendCodec = Codec (self.spec)
+ sendCodec = Codec ()
seq = self.seqMgr.reserve ("outstanding")
self.setHeader (sendCodec, ord ('Q'), seq)
self.incOutstanding (ch)
@@ -631,7 +630,7 @@ class managementClient:
if (cname, hash) not in self.packages[pname]:
# Send a schema request
- sendCodec = Codec (self.spec)
+ sendCodec = Codec ()
seq = self.seqMgr.reserve ("outstanding")
self.setHeader (sendCodec, ord ('S'), seq)
self.incOutstanding (ch)
@@ -885,7 +884,7 @@ class managementClient:
def method (self, channel, userSequence, objId, classId, methodName, args):
""" Invoke a method on an object """
- codec = Codec (self.spec)
+ codec = Codec ()
sequence = self.seqMgr.reserve ((userSequence, classId, methodName))
self.setHeader (codec, ord ('M'), sequence)
objId.encode(codec)
diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py
index c0d32d46cf..655ee13464 100644
--- a/qpid/python/qpid/managementdata.py
+++ b/qpid/python/qpid/managementdata.py
@@ -212,7 +212,6 @@ class ManagementData:
sock.settimeout(10)
self.conn = Connection (sock,
username=self.broker.username, password=self.broker.password)
- self.spec = self.conn.spec
def aborted():
raise Timeout("Waiting for connection to be established with broker")
oldAborted = self.conn.aborted
@@ -223,7 +222,7 @@ class ManagementData:
sock.settimeout(oldTimeout)
self.conn.aborted = oldAborted
- self.mclient = managementClient (self.spec, self.ctrlHandler, self.configHandler,
+ self.mclient = managementClient ("unused", self.ctrlHandler, self.configHandler,
self.instHandler, self.methodReply, self.closeHandler)
self.mclient.schemaListener (self.schemaHandler)
self.mch = self.mclient.addChannel (self.conn.session(self.sessionId))
diff --git a/qpid/python/qpid/messaging.py b/qpid/python/qpid/messaging.py
index 9b3fecbf9b..3c41a2c417 100644
--- a/qpid/python/qpid/messaging.py
+++ b/qpid/python/qpid/messaging.py
@@ -30,12 +30,13 @@ Areas that still need work:
- protocol negotiation/multiprotocol impl
"""
-import connection, time, socket, sys, traceback
+import connection, time, socket, sys, compat
from codec010 import StringCodec
-from datatypes import timestamp, uuid4, RangedSet, Message as Message010
+from datatypes import timestamp, uuid4, RangedSet, Message as Message010, Serial
+from exceptions import Timeout
from logging import getLogger
-from ops import PRIMITIVE
-from session import Client, INCOMPLETE
+from ops import PRIMITIVE, delivery_mode
+from session import Client, INCOMPLETE, SessionDetached
from threading import Thread, RLock, Condition
from util import connect
@@ -101,7 +102,10 @@ class Constant:
UNLIMITED = Constant("UNLIMITED", 0xFFFFFFFFL)
-class ConnectError(Exception):
+class ConnectionError(Exception):
+ pass
+
+class ConnectError(ConnectionError):
pass
class Connection(Lockable):
@@ -142,12 +146,35 @@ class Connection(Lockable):
self.host = host
self.port = default(port, AMQP_PORT)
self.started = False
- self._conn = None
self.id = str(uuid4())
self.session_counter = 0
self.sessions = {}
+ self.reconnect = False
+ self._connected = False
self._lock = RLock()
self._condition = Condition(self._lock)
+ self._modcount = Serial(0)
+ self.error = None
+ self._driver = Driver(self)
+ self._driver.start()
+
+ def wakeup(self):
+ self._modcount += 1
+ self._driver.wakeup()
+
+ def catchup(self, exc=ConnectionError):
+ mc = self._modcount
+ self.wait(lambda: not self._driver._modcount < mc)
+ self.check_error(exc)
+
+ def check_error(self, exc=ConnectionError):
+ if self.error:
+ raise exc(*self.error)
+
+ def ewait(self, predicate, timeout=None, exc=ConnectionError):
+ result = self.wait(lambda: self.error or predicate(), timeout)
+ self.check_error(exc)
+ return result
@synchronized
def session(self, name=None, transactional=False):
@@ -173,8 +200,7 @@ class Connection(Lockable):
else:
ssn = Session(self, name, self.started, transactional=transactional)
self.sessions[name] = ssn
- if self._conn is not None:
- ssn._attach()
+ self.wakeup()
return ssn
@synchronized
@@ -186,38 +212,25 @@ class Connection(Lockable):
"""
Connect to the remote endpoint.
"""
- if self._conn is not None:
- return
- try:
- self._socket = connect(self.host, self.port)
- except socket.error, e:
- raise ConnectError(e)
- self._conn = connection.Connection(self._socket)
- try:
- self._conn.start()
- except connection.VersionError, e:
- raise ConnectError(e)
-
- for ssn in self.sessions.values():
- ssn._attach()
+ self._connected = True
+ self.wakeup()
+ self.ewait(lambda: self._driver._connected, exc=ConnectError)
@synchronized
def disconnect(self):
"""
Disconnect from the remote endpoint.
"""
- if self._conn is not None:
- self._conn.close()
- self._conn = None
- for ssn in self.sessions.values():
- ssn._disconnected()
+ self._connected = False
+ self.wakeup()
+ self.ewait(lambda: not self._driver._connected)
@synchronized
def connected(self):
"""
Return true if the connection is connected, false otherwise.
"""
- return self._conn is not None
+ return self._connected
@synchronized
def start(self):
@@ -255,6 +268,7 @@ class Pattern:
def __init__(self, value):
self.value = value
+ # XXX: this should become part of the driver
def _bind(self, ssn, exchange, queue):
ssn.exchange_bind(exchange=exchange, queue=queue,
binding_key=self.value.replace("*", "#"))
@@ -263,13 +277,33 @@ FILTER_DEFAULTS = {
"topic": Pattern("*")
}
-def delegate(session):
+def delegate(handler, session):
class Delegate(Client):
def message_transfer(self, cmd):
- session._message_transfer(cmd)
+ handler._message_transfer(session, cmd)
return Delegate
+class SessionError(Exception):
+ pass
+
+class Disconnected(SessionError):
+ """
+ Exception raised when an operation is attempted that is illegal when
+ disconnected.
+ """
+ pass
+
+class NontransactionalSession(SessionError):
+ """
+ Exception raised when commit or rollback is attempted on a non
+ transactional session.
+ """
+ pass
+
+class TransactionAborted(SessionError):
+ pass
+
class Session(Lockable):
"""
@@ -281,18 +315,26 @@ class Session(Lockable):
self.connection = connection
self.name = name
self.started = started
+
self.transactional = transactional
- self._ssn = None
+
+ self.committing = False
+ self.committed = True
+ self.aborting = False
+ self.aborted = False
+
self.senders = []
self.receivers = []
- self.closing = False
+ self.outgoing = []
self.incoming = []
- self.closed = False
self.unacked = []
- if self.transactional:
- self.acked = []
- self._lock = RLock()
- self._condition = Condition(self._lock)
+ self.acked = []
+
+ self.closing = False
+ self.closed = False
+
+ self._lock = connection._lock
+ self._condition = connection._condition
self.thread = Thread(target = self.run)
self.thread.setDaemon(True)
self.thread.start()
@@ -300,60 +342,17 @@ class Session(Lockable):
def __repr__(self):
return "<Session %s>" % self.name
- def _attach(self):
- self._ssn = self.connection._conn.session(self.name, delegate=delegate(self))
- self._ssn.auto_sync = False
- self._ssn.invoke_lock = self._lock
- self._ssn.lock = self._lock
- self._ssn.condition = self._condition
- if self.transactional:
- self._ssn.tx_select()
- for link in self.senders + self.receivers:
- link._link()
-
- def _disconnected(self):
- self._ssn = None
- for link in self.senders + self.receivers:
- link._disconnected()
+ def wakeup(self):
+ self.connection.wakeup()
- @synchronized
- def _message_transfer(self, cmd):
- m = Message010(cmd.payload)
- m.headers = cmd.headers
- m.id = cmd.id
- msg = self._decode(m)
- rcv = self.receivers[int(cmd.destination)]
- msg._receiver = rcv
- log.debug("RECV [%s] %s", self, msg)
- self.incoming.append(msg)
- self.notifyAll()
- return INCOMPLETE
+ def catchup(self, exc=SessionError):
+ self.connection.catchup(exc)
- def _decode(self, message):
- dp = message.get("delivery_properties")
- mp = message.get("message_properties")
- ap = mp.application_headers
- enc, dec = get_codec(mp.content_type)
- content = dec(message.body)
- msg = Message(content)
- msg.id = mp.message_id
- if ap is not None:
- msg.to = ap.get("to")
- msg.subject = ap.get("subject")
- msg.user_id = mp.user_id
- if mp.reply_to is not None:
- msg.reply_to = reply_to2addr(mp.reply_to)
- msg.correlation_id = mp.correlation_id
- msg.properties = mp.application_headers
- msg.content_type = mp.content_type
- msg._transfer_id = message.id
- return msg
+ def check_error(self, exc=SessionError):
+ self.connection.check_error(exc)
- def _exchange_query(self, address):
- # XXX: auto sync hack is to avoid deadlock on future
- result = self._ssn.exchange_query(name=address, sync=True)
- self._ssn.sync()
- return result.get()
+ def ewait(self, predicate, timeout=None, exc=SessionError):
+ return self.connection.ewait(predicate, timeout, exc)
@synchronized
def sender(self, target):
@@ -368,8 +367,11 @@ class Session(Lockable):
"""
sender = Sender(self, len(self.senders), target)
self.senders.append(sender)
- if self._ssn is not None:
- sender._link()
+ self.wakeup()
+ # XXX: because of the lack of waiting here we can end up getting
+ # into the driver loop with messages sent for senders that haven't
+ # been linked yet, something similar can probably happen for
+ # receivers
return sender
@synchronized
@@ -386,8 +388,7 @@ class Session(Lockable):
receiver = Receiver(self, len(self.receivers), source, filter,
self.started)
self.receivers.append(receiver)
- if self._ssn is not None:
- receiver._link()
+ self.wakeup()
return receiver
@synchronized
@@ -419,6 +420,7 @@ class Session(Lockable):
timeout):
msg = self._pop(predicate)
if msg is not None:
+ msg._receiver.returned += 1
self.unacked.append(msg)
log.debug("RETR [%s] %s", self, msg)
return msg
@@ -438,20 +440,13 @@ class Session(Lockable):
else:
messages = [message]
- ids = RangedSet(*[m._transfer_id for m in messages])
- for range in ids:
- self._ssn.receiver._completed.add_range(range)
- self._ssn.channel.session_completed(self._ssn.receiver._completed)
- self._ssn.message_accept(ids, sync=True)
- self._ssn.sync()
-
for m in messages:
- try:
- self.unacked.remove(m)
- except ValueError:
- pass
- if self.transactional:
- self.acked.append(m)
+ self.unacked.remove(m)
+ self.acked.append(m)
+
+ self.wakeup()
+ self.wait(lambda: self.connection.error or not [m for m in messages if m in self.acked])
+ self.check_error()
@synchronized
def commit(self):
@@ -461,11 +456,12 @@ class Session(Lockable):
"""
if not self.transactional:
raise NontransactionalSession()
- if self._ssn is None:
- raise Disconnected()
- self._ssn.tx_commit(sync=True)
- del self.acked[:]
- self._ssn.sync()
+ self.committing = True
+ self.wakeup()
+ self.ewait(lambda: not self.committing)
+ if self.aborted:
+ raise TransactionAborted()
+ assert self.committed
@synchronized
def rollback(self):
@@ -475,21 +471,10 @@ class Session(Lockable):
"""
if not self.transactional:
raise NontransactionalSession()
- if self._ssn is None:
- raise Disconnected()
-
- ids = RangedSet(*[m._transfer_id for m in self.acked + self.unacked + self.incoming])
- for range in ids:
- self._ssn.receiver._completed.add_range(range)
- self._ssn.channel.session_completed(self._ssn.receiver._completed)
- self._ssn.message_release(ids)
- self._ssn.tx_rollback(sync=True)
-
- del self.incoming[:]
- del self.unacked[:]
- del self.acked[:]
-
- self._ssn.sync()
+ self.aborting = True
+ self.wakeup()
+ self.ewait(lambda: not self.aborting)
+ assert self.aborted
@synchronized
def start(self):
@@ -538,13 +523,12 @@ class Session(Lockable):
link.close()
self.closing = True
- self.notifyAll()
+ self.wakeup()
+ self.catchup()
self.wait(lambda: self.closed)
while self.thread.isAlive():
self.thread.join(3)
self.thread = None
- self._ssn.close()
- self._ssn = None
self.connection._remove_session(self)
def parse_addr(address):
@@ -562,18 +546,7 @@ def reply_to2addr(reply_to):
else:
return "%s/%s" % (reply_to.exchange, reply_to.routing_key)
-class Disconnected(Exception):
- """
- Exception raised when an operation is attempted that is illegal when
- disconnected.
- """
- pass
-
-class NontransactionalSession(Exception):
- """
- Exception raised when commit or rollback is attempted on a non
- transactional session.
- """
+class SendError(SessionError):
pass
class Sender(Lockable):
@@ -587,29 +560,20 @@ class Sender(Lockable):
self.index = index
self.target = target
self.closed = False
- self._ssn = None
- self._exchange = None
- self._routing_key = None
- self._subject = None
self._lock = self.session._lock
self._condition = self.session._condition
- def _link(self):
- self._ssn = self.session._ssn
- node, self._subject = parse_addr(self.target)
- result = self.session._exchange_query(node)
- if result.not_found:
- # XXX: should check 'create' option
- self._ssn.queue_declare(queue=node, durable=False, sync=True)
- self._ssn.sync()
- self._exchange = ""
- self._routing_key = node
- else:
- self._exchange = node
- self._routing_key = self._subject
+ def wakeup(self):
+ self.session.wakeup()
- def _disconnected(self):
- self._ssn = None
+ def catchup(self, exc=SendError):
+ self.session.catchup(exc)
+
+ def check_error(self, exc=SendError):
+ self.session.check_error(exc)
+
+ def ewait(self, predicate, timeout=None, exc=SendError):
+ return self.session.ewait(predicate, timeout, exc)
@synchronized
def send(self, object):
@@ -623,56 +587,35 @@ class Sender(Lockable):
@param object: the message or content to send
"""
- if self._ssn is None:
+ if not self.session.connection._connected or self.session.closing:
raise Disconnected()
if isinstance(object, Message):
message = object
else:
message = Message(object)
- # XXX: what if subject is specified for a normal queue?
- if self._routing_key is None:
- rk = message.subject
- else:
- rk = self._routing_key
- # XXX: do we need to query to figure out how to create the reply-to interoperably?
- if message.reply_to:
- rt = self._ssn.reply_to(*parse_addr(message.reply_to))
- else:
- rt = None
- dp = self._ssn.delivery_properties(routing_key=rk)
- mp = self._ssn.message_properties(message_id=message.id,
- user_id=message.user_id,
- reply_to=rt,
- correlation_id=message.correlation_id,
- content_type=message.content_type,
- application_headers=message.properties)
- if message.subject is not None:
- if mp.application_headers is None:
- mp.application_headers = {}
- mp.application_headers["subject"] = message.subject
- if message.to is not None:
- if mp.application_headers is None:
- mp.application_headers = {}
- mp.application_headers["to"] = message.to
- enc, dec = get_codec(message.content_type)
- body = enc(message.content)
- self._ssn.message_transfer(destination=self._exchange,
- message=Message010(dp, mp, body),
- sync=True)
- log.debug("SENT [%s] %s", self.session, message)
- self._ssn.sync()
+
+ # XXX: what if we send the same message to multiple senders?
+ message._sender = self
+ self.session.outgoing.append(message)
+
+ self.wakeup()
+ self.ewait(lambda: message not in self.session.outgoing)
@synchronized
def close(self):
"""
Close the Sender.
"""
+ # XXX: should make driver do something here
if not self.closed:
self.session.senders.remove(self)
self.closed = True
-class Empty(Exception):
+class ReceiveError(SessionError):
+ pass
+
+class Empty(ReceiveError):
"""
Exception raised by L{Receiver.fetch} when there is no message
available within the alloted time.
@@ -693,43 +636,36 @@ class Receiver(Lockable):
self.destination = str(self.index)
self.source = source
self.filter = filter
+
self.started = started
self.capacity = UNLIMITED
+ self.granted = Serial(0)
+ self.drain = False
+ self.impending = Serial(0)
+ self.received = Serial(0)
+ self.returned = Serial(0)
+
+ self.closing = False
self.closed = False
self.listener = None
- self._ssn = None
- self._queue = None
self._lock = self.session._lock
self._condition = self.session._condition
- def _link(self):
- self._ssn = self.session._ssn
- result = self.session._exchange_query(self.source)
- if result.not_found:
- self._queue = self.source
- # XXX: should check 'create' option
- self._ssn.queue_declare(queue=self._queue, durable=False)
- else:
- self._queue = "%s.%s" % (self.session.name, self.destination)
- self._ssn.queue_declare(queue=self._queue, durable=False, exclusive=True, auto_delete=True)
- if self.filter is None:
- f = FILTER_DEFAULTS[result.type]
- else:
- f = self.filter
- f._bind(self._ssn, self.source, self._queue)
- self._ssn.message_subscribe(queue=self._queue, destination=self.destination,
- sync=True)
- self._ssn.message_set_flow_mode(self.destination, self._ssn.flow_mode.credit)
- self._ssn.sync()
- if self.started:
- self._start()
+ def wakeup(self):
+ self.session.wakeup()
- def _disconnected(self):
- self._ssn = None
+ def catchup(self, exc=ReceiveError):
+ self.session.catchup()
+
+ def check_error(self, exc=ReceiveError):
+ self.session.check_error(exc)
+
+ def ewait(self, predicate, timeout=None, exc=ReceiveError):
+ return self.session.ewait(predicate, timeout, exc)
@synchronized
def pending(self):
- return self.session._count(self._pred)
+ return self.received - self.returned
def _capacity(self):
if not self.started:
@@ -762,23 +698,36 @@ class Receiver(Lockable):
@type timeout: float
@param timeout: the time to wait for a message to be available
"""
- if self.capacity is not UNLIMITED or not self.started:
- self._ssn.message_flow(self.destination, self._ssn.credit_unit.byte,
- UNLIMITED.value)
- self._ssn.message_flow(self.destination, self._ssn.credit_unit.message, 1)
+ if self._capacity() == 0:
+ self.granted = self.returned + 1
+ self.wakeup()
+ self.ewait(lambda: self.impending == self.granted)
msg = self.session._get(self._pred, timeout=timeout)
if msg is None:
- self._ssn.message_flush(self.destination)
- self._start()
- self._ssn.sync()
+ self.drain = True
+ self.granted = self.received
+ self.wakeup()
+ self.ewait(lambda: self.impending == self.received)
+ self.drain = False
+ self._grant()
+ self.wakeup()
msg = self.session._get(self._pred, timeout=0)
if msg is None:
raise Empty()
+ elif self._capacity() not in (0, UNLIMITED.value):
+ self.granted += 1
+ self.wakeup()
return msg
- def _start(self):
- self._ssn.message_flow(self.destination, self._ssn.credit_unit.byte, UNLIMITED.value)
- self._ssn.message_flow(self.destination, self._ssn.credit_unit.message, self._capacity())
+ def _grant(self):
+ if self.started:
+ if self.capacity is UNLIMITED:
+ self.granted = UNLIMITED
+ else:
+ self.granted = self.received + self._capacity()
+ else:
+ self.granted = self.received
+
@synchronized
def start(self):
@@ -786,34 +735,31 @@ class Receiver(Lockable):
Start incoming message delivery for this receiver.
"""
self.started = True
- if self._ssn is not None:
- self._start()
-
- def _stop(self):
- self._ssn.message_stop(self.destination)
+ self._grant()
+ self.wakeup()
@synchronized
def stop(self):
"""
Stop incoming message delivery for this receiver.
"""
- if self._ssn is not None:
- self._stop()
self.started = False
+ self._grant()
+ self.wakeup()
+ self.ewait(lambda: self.impending == self.received)
@synchronized
def close(self):
"""
Close the receiver.
"""
- if not self.closed:
- self.closed = True
- self._ssn.message_cancel(self.destination, sync=True)
- self._ssn.sync()
+ self.closing = True
+ self.wakeup()
+ try:
+ self.ewait(lambda: self.closed)
+ finally:
self.session.receivers.remove(self)
-
-
def codec(name):
type = PRIMITIVE[name]
@@ -889,6 +835,7 @@ class Message:
self.to = None
self.reply_to = None
self.correlation_id = None
+ self.durable = False
self.properties = {}
self.content_type = get_type(content)
self.content = content
@@ -896,5 +843,391 @@ class Message:
def __repr__(self):
return "Message(%r)" % self.content
+class Attachment:
+
+ def __init__(self, target):
+ self.target = target
+
+DURABLE_DEFAULT=True
+
+class Driver(Lockable):
+
+ def __init__(self, connection):
+ self.connection = connection
+ self._lock = self.connection._lock
+ self._condition = self.connection._condition
+ self._wakeup_cond = Condition()
+ self._socket = None
+ self._conn = None
+ self._connected = False
+ self._attachments = {}
+ self._modcount = self.connection._modcount
+ self.thread = Thread(target=self.run)
+ self.thread.setDaemon(True)
+ # XXX: need to figure out how to join on this thread
+
+ def start(self):
+ self.thread.start()
+
+ def wakeup(self):
+ self._wakeup_cond.acquire()
+ try:
+ self._wakeup_cond.notifyAll()
+ finally:
+ self._wakeup_cond.release()
+
+ def start(self):
+ self.thread.start()
+
+ def run(self):
+ while True:
+ self._wakeup_cond.acquire()
+ try:
+ if self.connection._modcount <= self._modcount:
+ self._wakeup_cond.wait(10)
+ finally:
+ self._wakeup_cond.release()
+ self.dispatch(self.connection._modcount)
+
+ @synchronized
+ def dispatch(self, modcount):
+ try:
+ if self._conn is None and self.connection._connected:
+ self.connect()
+ elif self._conn is not None and not self.connection._connected:
+ self.disconnect()
+
+ if self._conn is not None:
+ for ssn in self.connection.sessions.values():
+ self.attach(ssn)
+ self.process(ssn)
+
+ exi = None
+ except:
+ exi = sys.exc_info()
+
+ if exi:
+ msg = compat.format_exc()
+ recoverable = ["aborted", "Connection refused", "SessionDetached", "Connection reset by peer",
+ "Bad file descriptor", "start timed out", "Broken pipe"]
+ for r in recoverable:
+ if self.connection.reconnect and r in msg:
+ print "waiting to retry"
+ self.reset()
+ time.sleep(3)
+ print "retrying..."
+ return
+ else:
+ self.connection.error = (msg,)
+
+ self._modcount = modcount
+ self.notifyAll()
+
+ def connect(self):
+ if self._conn is not None:
+ return
+ try:
+ self._socket = connect(self.connection.host, self.connection.port)
+ except socket.error, e:
+ raise ConnectError(e)
+ self._conn = connection.Connection(self._socket)
+ try:
+ self._conn.start(timeout=10)
+ self._connected = True
+ except connection.VersionError, e:
+ raise ConnectError(e)
+ except Timeout:
+ print "start timed out"
+ raise ConnectError("start timed out")
+
+ def disconnect(self):
+ self._conn.close()
+ self.reset()
+
+ def reset(self):
+ self._conn = None
+ self._connected = False
+ self._attachments.clear()
+ for ssn in self.connection.sessions.values():
+ for m in ssn.acked + ssn.unacked + ssn.incoming:
+ m._transfer_id = None
+ for rcv in ssn.receivers:
+ rcv.impending = rcv.received
+
+ def connected(self):
+ return self._conn is not None
+
+ def attach(self, ssn):
+ _ssn = self._attachments.get(ssn)
+ if _ssn is None:
+ _ssn = self._conn.session(ssn.name, delegate=delegate(self, ssn))
+ _ssn.auto_sync = False
+ _ssn.invoke_lock = self._lock
+ _ssn.lock = self._lock
+ _ssn.condition = self._condition
+ if ssn.transactional:
+ # XXX: adding an attribute to qpid.session.Session
+ _ssn.acked = []
+ _ssn.tx_select()
+ self._attachments[ssn] = _ssn
+
+ for snd in ssn.senders:
+ self.link_out(snd)
+ for rcv in ssn.receivers:
+ self.link_in(rcv)
+
+ if ssn.closing:
+ _ssn.close()
+ del self._attachments[ssn]
+
+ def _exchange_query(self, ssn, address):
+ # XXX: auto sync hack is to avoid deadlock on future
+ result = ssn.exchange_query(name=address, sync=True)
+ ssn.sync()
+ return result.get()
+
+ def link_out(self, snd):
+ _ssn = self._attachments[snd.session]
+ _snd = self._attachments.get(snd)
+ if _snd is None:
+ _snd = Attachment(snd)
+ node, _snd._subject = parse_addr(snd.target)
+ result = self._exchange_query(_ssn, node)
+ if result.not_found:
+ # XXX: should check 'create' option
+ _ssn.queue_declare(queue=node, durable=DURABLE_DEFAULT, sync=True)
+ _ssn.sync()
+ _snd._exchange = ""
+ _snd._routing_key = node
+ else:
+ _snd._exchange = node
+ _snd._routing_key = _snd._subject
+ self._attachments[snd] = _snd
+
+ if snd.closed:
+ del self._attachments[snd]
+ return None
+ else:
+ return _snd
+
+ def link_in(self, rcv):
+ _ssn = self._attachments[rcv.session]
+ _rcv = self._attachments.get(rcv)
+ if _rcv is None:
+ _rcv = Attachment(rcv)
+ result = self._exchange_query(_ssn, rcv.source)
+ if result.not_found:
+ _rcv._queue = rcv.source
+ # XXX: should check 'create' option
+ _ssn.queue_declare(queue=_rcv._queue, durable=DURABLE_DEFAULT)
+ else:
+ _rcv._queue = "%s.%s" % (rcv.session.name, rcv.destination)
+ _ssn.queue_declare(queue=_rcv._queue, durable=DURABLE_DEFAULT, exclusive=True, auto_delete=True)
+ if rcv.filter is None:
+ f = FILTER_DEFAULTS[result.type]
+ else:
+ f = rcv.filter
+ f._bind(_ssn, rcv.source, _rcv._queue)
+ _ssn.message_subscribe(queue=_rcv._queue, destination=rcv.destination)
+ _ssn.message_set_flow_mode(rcv.destination, _ssn.flow_mode.credit, sync=True)
+ self._attachments[rcv] = _rcv
+ # XXX: need to kill syncs
+ _ssn.sync()
+
+ if rcv.closing:
+ _ssn.message_cancel(rcv.destination, sync=True)
+ # XXX: need to kill syncs
+ _ssn.sync()
+ del self._attachments[rcv]
+ rcv.closed = True
+ return None
+ else:
+ return _rcv
+
+ def process(self, ssn):
+ if ssn.closing: return
+
+ _ssn = self._attachments[ssn]
+
+ while ssn.outgoing:
+ msg = ssn.outgoing[0]
+ snd = msg._sender
+ self.send(snd, msg)
+ ssn.outgoing.pop(0)
+
+ for rcv in ssn.receivers:
+ self.process_receiver(rcv)
+
+ if ssn.acked:
+ messages = ssn.acked[:]
+ ids = RangedSet(*[m._transfer_id for m in messages if m._transfer_id is not None])
+ for range in ids:
+ _ssn.receiver._completed.add_range(range)
+ ch = _ssn.channel
+ if ch is None:
+ raise SessionDetached()
+ ch.session_completed(_ssn.receiver._completed)
+ _ssn.message_accept(ids, sync=True)
+ # XXX: really need to make this async so that we don't give up the lock
+ _ssn.sync()
+
+ for m in messages:
+ ssn.acked.remove(m)
+ if ssn.transactional:
+ _ssn.acked.append(m)
+
+ if ssn.committing:
+ _ssn.tx_commit(sync=True)
+ # XXX: need to kill syncs
+ _ssn.sync()
+ del _ssn.acked[:]
+ ssn.committing = False
+ ssn.committed = True
+ ssn.aborting = False
+ ssn.aborted = False
+
+ if ssn.aborting:
+ for rcv in ssn.receivers:
+ _ssn.message_stop(rcv.destination)
+ _ssn.sync()
+
+ messages = _ssn.acked + ssn.unacked + ssn.incoming
+ ids = RangedSet(*[m._transfer_id for m in messages])
+ for range in ids:
+ _ssn.receiver._completed.add_range(range)
+ _ssn.channel.session_completed(_ssn.receiver._completed)
+ _ssn.message_release(ids)
+ _ssn.tx_rollback(sync=True)
+ _ssn.sync()
+
+ del ssn.incoming[:]
+ del ssn.unacked[:]
+ del _ssn.acked[:]
+
+ for rcv in ssn.receivers:
+ rcv.impending = rcv.received
+ rcv.returned = rcv.received
+ # XXX: do we need to update granted here as well?
+
+ for rcv in ssn.receivers:
+ self.process_receiver(rcv)
+
+ ssn.aborting = False
+ ssn.aborted = True
+ ssn.committing = False
+ ssn.committed = False
+
+ def grant(self, rcv):
+ _ssn = self._attachments[rcv.session]
+ _rcv = self.link_in(rcv)
+
+ if rcv.granted is UNLIMITED:
+ if rcv.impending is UNLIMITED:
+ delta = 0
+ else:
+ delta = UNLIMITED
+ elif rcv.impending is UNLIMITED:
+ delta = -1
+ else:
+ delta = max(rcv.granted, rcv.received) - rcv.impending
+
+ if delta is UNLIMITED:
+ _ssn.message_flow(rcv.destination, _ssn.credit_unit.byte, UNLIMITED.value)
+ _ssn.message_flow(rcv.destination, _ssn.credit_unit.message, UNLIMITED.value)
+ rcv.impending = UNLIMITED
+ elif delta > 0:
+ _ssn.message_flow(rcv.destination, _ssn.credit_unit.byte, UNLIMITED.value)
+ _ssn.message_flow(rcv.destination, _ssn.credit_unit.message, delta)
+ rcv.impending += delta
+ elif delta < 0:
+ if rcv.drain:
+ _ssn.message_flush(rcv.destination, sync=True)
+ else:
+ _ssn.message_stop(rcv.destination, sync=True)
+ # XXX: need to kill syncs
+ _ssn.sync()
+ rcv.impending = rcv.received
+ self.grant(rcv)
+
+ def process_receiver(self, rcv):
+ if rcv.closed: return
+ self.grant(rcv)
+
+ def send(self, snd, msg):
+ _ssn = self._attachments[snd.session]
+ _snd = self.link_out(snd)
+
+ # XXX: what if subject is specified for a normal queue?
+ if _snd._routing_key is None:
+ rk = msg.subject
+ else:
+ rk = _snd._routing_key
+ # XXX: do we need to query to figure out how to create the reply-to interoperably?
+ if msg.reply_to:
+ rt = _ssn.reply_to(*parse_addr(msg.reply_to))
+ else:
+ rt = None
+ dp = _ssn.delivery_properties(routing_key=rk)
+ mp = _ssn.message_properties(message_id=msg.id,
+ user_id=msg.user_id,
+ reply_to=rt,
+ correlation_id=msg.correlation_id,
+ content_type=msg.content_type,
+ application_headers=msg.properties)
+ if msg.subject is not None:
+ if mp.application_headers is None:
+ mp.application_headers = {}
+ mp.application_headers["subject"] = msg.subject
+ if msg.to is not None:
+ if mp.application_headers is None:
+ mp.application_headers = {}
+ mp.application_headers["to"] = msg.to
+ if msg.durable:
+ dp.delivery_mode = delivery_mode.persistent
+ enc, dec = get_codec(msg.content_type)
+ body = enc(msg.content)
+ _ssn.message_transfer(destination=_snd._exchange,
+ message=Message010(dp, mp, body),
+ sync=True)
+ log.debug("SENT [%s] %s", snd.session, msg)
+ # XXX: really need to make this async so that we don't give up the lock
+ _ssn.sync()
+ # XXX: should we log the ack somehow too?
+
+ @synchronized
+ def _message_transfer(self, ssn, cmd):
+ m = Message010(cmd.payload)
+ m.headers = cmd.headers
+ m.id = cmd.id
+ msg = self._decode(m)
+ rcv = ssn.receivers[int(cmd.destination)]
+ msg._receiver = rcv
+ rcv.received += 1
+ log.debug("RECV [%s] %s", ssn, msg)
+ ssn.incoming.append(msg)
+ self.notifyAll()
+ return INCOMPLETE
+
+ def _decode(self, message):
+ dp = message.get("delivery_properties")
+ mp = message.get("message_properties")
+ ap = mp.application_headers
+ enc, dec = get_codec(mp.content_type)
+ content = dec(message.body)
+ msg = Message(content)
+ msg.id = mp.message_id
+ if ap is not None:
+ msg.to = ap.get("to")
+ msg.subject = ap.get("subject")
+ msg.user_id = mp.user_id
+ if mp.reply_to is not None:
+ msg.reply_to = reply_to2addr(mp.reply_to)
+ msg.correlation_id = mp.correlation_id
+ msg.durable = dp.delivery_mode == delivery_mode.persistent
+ msg.properties = mp.application_headers
+ msg.content_type = mp.content_type
+ msg._transfer_id = message.id
+ return msg
+
__all__ = ["Connection", "Pattern", "Session", "Sender", "Receiver", "Message",
"Empty", "timestamp", "uuid4"]
diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py
index 4413a22899..2f1bd81bd4 100644
--- a/qpid/python/qpid/session.py
+++ b/qpid/python/qpid/session.py
@@ -146,7 +146,8 @@ class Session(command_invoker()):
if self._closing:
raise SessionClosed()
- if self.channel == None:
+ ch = self.channel
+ if ch == None:
raise SessionDetached()
if op == MessageTransfer:
@@ -162,14 +163,12 @@ class Session(command_invoker()):
cmd = op(*args, **kwargs)
cmd.sync = self.auto_sync or cmd.sync
self.need_sync = not cmd.sync
- cmd.channel = self.channel.id
+ cmd.channel = ch.id
if op.RESULT:
result = Future(exception=SessionException)
self.results[self.sender.next_id] = result
- log.debug("SENDING %s", cmd)
-
self.send(cmd)
log.debug("SENT %s", cmd)
@@ -245,13 +244,16 @@ class Sender:
self._completed = RangedSet()
def send(self, cmd):
+ ch = self.session.channel
+ if ch is None:
+ raise SessionDetached()
cmd.id = self.next_id
self.next_id += 1
if self.session.send_id:
self.session.send_id = False
- self.session.channel.session_command_point(cmd.id, 0)
+ ch.session_command_point(cmd.id, 0)
self.commands.append(cmd)
- self.session.channel.connection.write_op(cmd)
+ ch.connection.write_op(cmd)
def completed(self, commands):
idx = 0
diff --git a/qpid/python/qpid/tests/messaging.py b/qpid/python/qpid/tests/messaging.py
index 7706ebbabe..6062895519 100644
--- a/qpid/python/qpid/tests/messaging.py
+++ b/qpid/python/qpid/tests/messaging.py
@@ -84,6 +84,10 @@ class Base(Test):
contents = self.drain(rcv)
assert len(contents) == 0, "%s is supposed to be empty: %s" % (rcv, contents)
+ def assertPending(self, rcv, expected):
+ p = rcv.pending()
+ assert p == expected, "expected %s, got %s" % (expected, p)
+
def sleep(self):
time.sleep(self.delay())
@@ -107,7 +111,8 @@ class SetupTests(Base):
try:
self.conn = Connection.open("localhost", 0)
assert False, "connect succeeded"
- except ConnectError:
+ except ConnectError, e:
+ # XXX: should verify that e includes appropriate diagnostic info
pass
class ConnectionTests(Base):
@@ -237,7 +242,8 @@ class SessionTests(Base):
# were requeued, and ack this time before closing
self.ssn = self.conn.session()
rcv = self.ssn.receiver("test-ack-queue")
- assert contents == self.drain(rcv)
+ drained = self.drain(rcv)
+ assert contents == drained, "expected %s, got %s" % (contents, drained)
self.ssn.acknowledge()
self.ssn.close()
@@ -319,7 +325,8 @@ class SessionTests(Base):
txssn.acknowledge()
else:
txssn.rollback()
- assert contents == self.drain(txrcv)
+ drained = self.drain(txrcv)
+ assert contents == drained, "expected %s, got %s" % (contents, drained)
txssn.acknowledge()
txssn.rollback()
assert contents == self.drain(txrcv)
@@ -401,9 +408,9 @@ class ReceiverTests(Base):
elapsed = time.time() - start
assert elapsed >= self.delay()
- one = self.send("testListen", 1)
- two = self.send("testListen", 2)
- three = self.send("testListen", 3)
+ one = self.send("testFetch", 1)
+ two = self.send("testFetch", 2)
+ three = self.send("testFetch", 3)
msg = self.rcv.fetch(0)
assert msg.content == one
msg = self.rcv.fetch(self.delay())
@@ -467,34 +474,35 @@ class ReceiverTests(Base):
def testCapacity(self):
self.rcv.capacity = 5
self.rcv.start()
- assert self.rcv.pending() == 0
+ self.assertPending(self.rcv, 0)
for i in range(15):
self.send("testCapacity", i)
self.sleep()
- assert self.rcv.pending() == 5
+ self.assertPending(self.rcv, 5)
self.drain(self.rcv, limit = 5)
self.sleep()
- assert self.rcv.pending() == 5
+ self.assertPending(self.rcv, 5)
- self.drain(self.rcv)
- assert self.rcv.pending() == 0
+ drained = self.drain(self.rcv)
+ assert len(drained) == 10
+ self.assertPending(self.rcv, 0)
self.ssn.acknowledge()
def testCapacityUNLIMITED(self):
self.rcv.capacity = UNLIMITED
self.rcv.start()
- assert self.rcv.pending() == 0
+ self.assertPending(self.rcv, 0)
for i in range(10):
self.send("testCapacityUNLIMITED", i)
self.sleep()
- assert self.rcv.pending() == 10
+ self.assertPending(self.rcv, 10)
self.drain(self.rcv)
- assert self.rcv.pending() == 0
+ self.assertPending(self.rcv, 0)
self.ssn.acknowledge()
diff --git a/qpid/python/qpid_config.py b/qpid/python/qpid_config.py
index 3cf6b69b7e..d740a53dfe 100644
--- a/qpid/python/qpid_config.py
+++ b/qpid/python/qpid_config.py
@@ -19,7 +19,7 @@
import os
-qpid_home = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-amqp_spec = os.path.join(qpid_home, "specs", "amqp.0-10-qpid-errata.xml")
-amqp_spec_0_8 = os.path.join(qpid_home, "specs", "amqp.0-8.xml")
-amqp_spec_0_9 = os.path.join(qpid_home, "specs", "amqp.0-9.xml")
+AMQP_SPEC_DIR=os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "specs")
+amqp_spec = os.path.join(AMQP_SPEC_DIR, "amqp.0-10-qpid-errata.xml")
+amqp_spec_0_8 = os.path.join(AMQP_SPEC_DIR, "amqp.0-8.xml")
+amqp_spec_0_9 = os.path.join(AMQP_SPEC_DIR, "amqp.0-9.xml")
diff --git a/qpid/python/tests/datatypes.py b/qpid/python/tests/datatypes.py
index 1a60bb4107..b00e5e78f8 100644
--- a/qpid/python/tests/datatypes.py
+++ b/qpid/python/tests/datatypes.py
@@ -54,6 +54,19 @@ class SerialTest(TestCase):
d[serial(0)] = "zero"
assert d[0] == "zero"
+ def testAdd(self):
+ assert serial(2) + 2 == serial(4)
+ assert serial(2) + 2 == 4
+
+ def testSub(self):
+ delta = serial(4) - serial(2)
+ assert isinstance(delta, int) or isinstance(delta, long)
+ assert delta == 2
+
+ delta = serial(4) - 2
+ assert isinstance(delta, Serial)
+ assert delta == serial(2)
+
class RangedSetTest(TestCase):
def check(self, ranges):
diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py
index 5cd0caba40..51c2a687cb 100644
--- a/qpid/python/tests_0-10/management.py
+++ b/qpid/python/tests_0-10/management.py
@@ -29,13 +29,13 @@ class ManagementTest (TestBase010):
Tests for the management hooks
"""
- def disabled_test_broker_connectivity_oldAPI (self):
+ def test_broker_connectivity_oldAPI (self):
"""
Call the "echo" method on the broker to verify it is alive and talking.
"""
session = self.session
- mc = managementClient (session.spec)
+ mc = managementClient ()
mch = mc.addChannel (session)
mc.syncWaitForStable (mch)
diff --git a/qpid/specs/management-schema.xml b/qpid/specs/management-schema.xml
index bc38350a45..e72ba1cdd7 100644
--- a/qpid/specs/management-schema.xml
+++ b/qpid/specs/management-schema.xml
@@ -164,11 +164,13 @@
===============================================================
-->
<class name="Exchange">
- <property name="vhostRef" type="objId" references="Vhost" access="RC" index="y" parentRef="y"/>
- <property name="name" type="sstr" access="RC" index="y"/>
- <property name="type" type="sstr" access="RO"/>
- <property name="durable" type="bool" access="RC"/>
- <property name="arguments" type="map" access="RO" desc="Arguments supplied in exchange.declare"/>
+ <property name="vhostRef" type="objId" references="Vhost" access="RC" index="y" parentRef="y"/>
+ <property name="name" type="sstr" access="RC" index="y"/>
+ <property name="type" type="sstr" access="RO"/>
+ <property name="durable" type="bool" access="RO"/>
+ <property name="autoDelete" type="bool" access="RO"/>
+ <property name="altExchange" type="objId" access="RO" optional="y"/>
+ <property name="arguments" type="map" access="RO" desc="Arguments supplied in exchange.declare"/>
<statistic name="producerCount" type="hilo32" desc="Current producers on exchange"/>
<statistic name="bindingCount" type="hilo32" desc="Current bindings"/>