diff options
author | Robert Godfrey <rgodfrey@apache.org> | 2009-10-05 12:51:57 +0000 |
---|---|---|
committer | Robert Godfrey <rgodfrey@apache.org> | 2009-10-05 12:51:57 +0000 |
commit | f67026b148b3429ca9731bce94a0bd3ff2981ce5 (patch) | |
tree | 95e7dfcfff6640d83b4171520bffb4d4b2304b9a | |
parent | 69e05a6b0979d2e12588635a4ed46a09a87cdec0 (diff) | |
download | qpid-python-f67026b148b3429ca9731bce94a0bd3ff2981ce5.tar.gz |
Merged from trunk up to r816580
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-broker-0-10@821779 13f79535-47bb-0310-9956-ffa450edef68
383 files changed, 22999 insertions, 5767 deletions
diff --git a/qpid/cpp/bindings/qmf/Makefile.am b/qpid/cpp/bindings/qmf/Makefile.am index 68312a4208..eebb4b94de 100644 --- a/qpid/cpp/bindings/qmf/Makefile.am +++ b/qpid/cpp/bindings/qmf/Makefile.am @@ -20,6 +20,14 @@ if HAVE_SWIG EXTRA_DIST = qmfengine.i -SUBDIRS = ruby tests +SUBDIRS = tests + +if HAVE_RUBY_DEVEL +SUBDIRS += ruby +endif + +if HAVE_PYTHON_DEVEL +SUBDIRS += python +endif endif diff --git a/qpid/cpp/bindings/qmf/python/Makefile.am b/qpid/cpp/bindings/qmf/python/Makefile.am index 7b3f4d3be7..55d9079fb7 100644 --- a/qpid/cpp/bindings/qmf/python/Makefile.am +++ b/qpid/cpp/bindings/qmf/python/Makefile.am @@ -29,17 +29,18 @@ EXTRA_DIST = python.i BUILT_SOURCES = $(generated_file_list) $(generated_file_list): $(srcdir)/python.i $(srcdir)/../qmfengine.i - swig -python -c++ -Wall -I/usr/include $(INCLUDES) $(QPID_CXXFLAGS) -I$(top_srcdir)/src/qmf -o qmfengine.cpp $(srcdir)/python.i + swig -c++ -python -Wall -I/usr/include $(INCLUDES) $(QPID_CXXFLAGS) -I$(top_srcdir)/src/qmf -o qmfengine.cpp $(srcdir)/python.i pylibdir = $(PYTHON_LIB) lib_LTLIBRARIES = _qmfengine.la -_qmfengine_la_LDFLAGS = -avoid-version -module -shrext "$(PYTHON_SO)" -_qmfengine_la_LIBADD = $(PYTHON_LIBS) -L$(top_builddir)/src/.libs -lqpidclient $(top_builddir)/src/libqmfcommon.la +#_qmfengine_la_LDFLAGS = -avoid-version -module -shrext "$(PYTHON_SO)" +#_qmfengine_la_LDFLAGS = -avoid-version -module -shrext ".so" +_qmfengine_la_LDFLAGS = -avoid-version -module -shared +_qmfengine_la_LIBADD = $(PYTHON_LIBS) -L$(top_builddir)/src/.libs -lqpidclient $(top_builddir)/src/libqmfagent.la _qmfengine_la_CXXFLAGS = $(INCLUDES) -I$(srcdir)/qmf -I$(PYTHON_INC) -_qmfengine_la_SOURCES = \ - qmfengine.cpp +nodist__qmfengine_la_SOURCES = qmfengine.cpp CLEANFILES = $(generated_file_list) diff --git a/qpid/cpp/bindings/qmf/python/python.i b/qpid/cpp/bindings/qmf/python/python.i index ea14efd4dd..5e25d155f9 100644 --- a/qpid/cpp/bindings/qmf/python/python.i +++ b/qpid/cpp/bindings/qmf/python/python.i @@ -19,17 +19,125 @@ %module qmfengine -// These are probably wrong.. just to get it to compile for now. -%typemap (in) void * -{ - $1 = (void *) $input; + +/* unsigned32 Convert from Python --> C */ +%typemap(in) uint32_t { + if (PyInt_Check($input)) { + $1 = (uint32_t) PyInt_AsUnsignedLongMask($input); + } else if (PyLong_Check($input)) { + $1 = (uint32_t) PyLong_AsUnsignedLong($input); + } else { + SWIG_exception_fail(SWIG_ValueError, "unknown integer type"); + } +} + +/* unsinged32 Convert from C --> Python */ +%typemap(out) uint32_t { + $result = PyInt_FromLong((long)$1); +} + + +/* unsigned16 Convert from Python --> C */ +%typemap(in) uint16_t { + if (PyInt_Check($input)) { + $1 = (uint16_t) PyInt_AsUnsignedLongMask($input); + } else if (PyLong_Check($input)) { + $1 = (uint16_t) PyLong_AsUnsignedLong($input); + } else { + SWIG_exception_fail(SWIG_ValueError, "unknown integer type"); + } +} + +/* unsigned16 Convert from C --> Python */ +%typemap(out) uint16_t { + $result = PyInt_FromLong((long)$1); +} + + +/* signed32 Convert from Python --> C */ +%typemap(in) int32_t { + if (PyInt_Check($input)) { + $1 = (int32_t) PyInt_AsLong($input); + } else if (PyLong_Check($input)) { + $1 = (int32_t) PyLong_AsLong($input); + } else { + SWIG_exception_fail(SWIG_ValueError, "unknown integer type"); + } +} + +/* signed32 Convert from C --> Python */ +%typemap(out) int32_t { + $result = PyInt_FromLong((long)$1); +} + + +/* unsigned64 Convert from Python --> C */ +%typemap(in) uint64_t { +%#ifdef HAVE_LONG_LONG + if (PyLong_Check($input)) { + $1 = (uint64_t)PyLong_AsUnsignedLongLong($input); + } else if (PyInt_Check($input)) { + $1 = (uint64_t)PyInt_AsUnsignedLongLongMask($input); + } else +%#endif + { + SWIG_exception_fail(SWIG_ValueError, "unsupported integer size - uint64_t input too large"); + } +} + +/* unsigned64 Convert from C --> Python */ +%typemap(out) uint64_t { +%#ifdef HAVE_LONG_LONG + $result = PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)$1); +%#else + SWIG_exception_fail(SWIG_ValueError, "unsupported integer size - uint64_t output too large"); +%#endif +} + +/* signed64 Convert from Python --> C */ +%typemap(in) int64_t { +%#ifdef HAVE_LONG_LONG + if (PyLong_Check($input)) { + $1 = (int64_t)PyLong_AsLongLong($input); + } else if (PyInt_Check($input)) { + $1 = (int64_t)PyInt_AsLong($input); + } else +%#endif + { + SWIG_exception_fail(SWIG_ValueError, "unsupported integer size - int64_t input too large"); + } +} + +/* signed64 Convert from C --> Python */ +%typemap(out) int64_t { +%#ifdef HAVE_LONG_LONG + $result = PyLong_FromLongLong((PY_LONG_LONG)$1); +%#else + SWIG_exception_fail(SWIG_ValueError, "unsupported integer size - int64_t output too large"); +%#endif } -%typemap (out) void * -{ + +/* Convert from Python --> C */ +%typemap(in) void * { + $1 = (void *)$input; +} + +/* Convert from C --> Python */ +%typemap(out) void * { $result = (PyObject *) $1; + Py_INCREF($result); +} + +%typemap (typecheck, precedence=SWIG_TYPECHECK_UINT64) uint64_t { + $1 = PyLong_Check($input) ? 1 : 0; } +%typemap (typecheck, precedence=SWIG_TYPECHECK_UINT32) uint32_t { + $1 = PyInt_Check($input) ? 1 : 0; +} + + %include "../qmfengine.i" diff --git a/qpid/cpp/bindings/qmf/python/qmf.py b/qpid/cpp/bindings/qmf/python/qmf.py new file mode 100644 index 0000000000..4800b327f1 --- /dev/null +++ b/qpid/cpp/bindings/qmf/python/qmf.py @@ -0,0 +1,858 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +import sys +import socket +import os +from threading import Thread +from threading import RLock +import qmfengine +from qmfengine import (ACCESS_READ_CREATE, ACCESS_READ_ONLY, ACCESS_READ_WRITE) +from qmfengine import (CLASS_EVENT, CLASS_OBJECT) +from qmfengine import (DIR_IN, DIR_IN_OUT, DIR_OUT) +from qmfengine import (TYPE_ABSTIME, TYPE_ARRAY, TYPE_BOOL, TYPE_DELTATIME, + TYPE_DOUBLE, TYPE_FLOAT, TYPE_INT16, TYPE_INT32, TYPE_INT64, + TYPE_INT8, TYPE_LIST, TYPE_LSTR, TYPE_MAP, TYPE_OBJECT, + TYPE_REF, TYPE_SSTR, TYPE_UINT16, TYPE_UINT32, TYPE_UINT64, + TYPE_UINT8, TYPE_UUID) +from qmfengine import (O_EQ, O_NE, O_LT, O_LE, O_GT, O_GE, O_RE_MATCH, O_RE_NOMATCH, + E_NOT, E_AND, E_OR, E_XOR) + + + ##============================================================================== + ## CONNECTION + ##============================================================================== + +class ConnectionSettings: + #attr_reader :impl + def __init__(self, url=None): + if url: + self.impl = qmfengine.ConnectionSettings(url) + else: + self.impl = qmfengine.ConnectionSettings() + + + def set_attr(self, key, val): + if type(val) == str: + _v = qmfengine.Value(TYPE_LSTR) + _v.setString(val) + elif type(val) == bool: + _v = qmfengine.Value(TYPE_BOOL) + _v.setBool(val) + elif type(val) == int: + _v = qmfengine.Value(TYPE_UINT32) + _v.setUint(val) + else: + raise ArgumentError("Value for attribute '%s' has unsupported type: %s" % ( key, type(val))) + + self.impl.setAttr(key, _v) + + + +class ConnectionHandler: + def conn_event_connected(self): None + def conn_event_disconnected(self, error): None + def sess_event_session_closed(self, context, error): None + def sess_event_recv(self, context, message): None + + + +class Connection(Thread): + def __init__(self, settings): + Thread.__init__(self) + self._lock = RLock() + self.impl = qmfengine.ResilientConnection(settings.impl) + self._sockEngine, self._sock = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM) + self.impl.setNotifyFd(self._sockEngine.fileno()) + self._new_conn_handlers = [] + self._conn_handlers = [] + self.start() + + + def add_conn_handler(self, handler): + self._lock.acquire() + try: + self._new_conn_handlers.append(handler) + finally: + self._lock.release() + self._sockEngine.send("x") + + + def run(self): + eventImpl = qmfengine.ResilientConnectionEvent() + connected = False + new_handlers = [] + bt_count = 0 + + while True: + # print "Waiting for socket data" + self._sock.recv(1) + + self._lock.acquire() + try: + new_handlers = self._new_conn_handlers + self._new_conn_handlers = [] + finally: + self._lock.release() + + for nh in new_handlers: + self._conn_handlers.append(nh) + if connected: + nh.conn_event_connected() + + new_handlers = [] + + valid = self.impl.getEvent(eventImpl) + while valid: + try: + if eventImpl.kind == qmfengine.ResilientConnectionEvent.CONNECTED: + connected = True + for h in self._conn_handlers: + h.conn_event_connected() + + elif eventImpl.kind == qmfengine.ResilientConnectionEvent.DISCONNECTED: + connected = False + for h in self._conn_handlers: + h.conn_event_disconnected(eventImpl.errorText) + + elif eventImpl.kind == qmfengine.ResilientConnectionEvent.SESSION_CLOSED: + eventImpl.sessionContext.handler.sess_event_session_closed(eventImpl.sessionContext, eventImpl.errorText) + + elif eventImpl.kind == qmfengine.ResilientConnectionEvent.RECV: + eventImpl.sessionContext.handler.sess_event_recv(eventImpl.sessionContext, eventImpl.message) + + except: + import traceback + print "Event Exception:", sys.exc_info() + if bt_count < 2: + traceback.print_exc() + traceback.print_stack() + bt_count += 1 + + self.impl.popEvent() + valid = self.impl.getEvent(eventImpl) + + + +class Session: + def __init__(self, conn, label, handler): + self._conn = conn + self._label = label + self.handler = handler + self.handle = qmfengine.SessionHandle() + result = self._conn.impl.createSession(label, self, self.handle) + + + def __del__(self): + self._conn.impl.destroySession(self.handle) + + + + ##============================================================================== + ## OBJECTS + ##============================================================================== + +class QmfObject: + # attr_reader :impl, :object_class + def __init__(self, cls): + self.object_class = cls + self.impl = qmfengine.Object(self.object_class.impl) + + + def __del__(self): + self.impl.destroy() + + + def object_id(self): + return ObjectId(self.impl.getObjectId()) + + + def set_object_id(self, oid): + self.impl.setObjectId(oid.impl) + + + def get_attr(self, name): + val = self._value(name) + vType = val.getType() + if vType == TYPE_UINT8: return val.asUint() + elif vType == TYPE_UINT16: return val.asUint() + elif vType == TYPE_UINT32: return val.asUint() + elif vType == TYPE_UINT64: return val.asUint64() + elif vType == TYPE_SSTR: return val.asString() + elif vType == TYPE_LSTR: return val.asString() + elif vType == TYPE_ABSTIME: return val.asInt64() + elif vType == TYPE_DELTATIME: return val.asUint64() + elif vType == TYPE_REF: return val.asObjectId() + elif vType == TYPE_BOOL: return val.asBool() + elif vType == TYPE_FLOAT: return val.asFloat() + elif vType == TYPE_DOUBLE: return val.asDouble() + elif vType == TYPE_UUID: return val.asUuid() + elif vType == TYPE_INT8: return val.asInt() + elif vType == TYPE_INT16: return val.asInt() + elif vType == TYPE_INT32: return val.asInt() + elif vType == TYPE_INT64: return val.asInt64() + else: + # when TYPE_MAP + # when TYPE_OBJECT + # when TYPE_LIST + # when TYPE_ARRAY + print "Unsupported type for get_attr?", val.getType() + return None + + + def set_attr(self, name, v): + val = self._value(name) + vType = val.getType() + if vType == TYPE_UINT8: return val.setUint(v) + elif vType == TYPE_UINT16: return val.setUint(v) + elif vType == TYPE_UINT32: return val.setUint(v) + elif vType == TYPE_UINT64: return val.setUint64(v) + elif vType == TYPE_SSTR: + if v: return val.setString(v) + else: return val.setString('') + elif vType == TYPE_LSTR: + if v: return val.setString(v) + else: return val.setString('') + elif vType == TYPE_ABSTIME: return val.setInt64(v) + elif vType == TYPE_DELTATIME: return val.setUint64(v) + elif vType == TYPE_REF: return val.setObjectId(v.impl) + elif vType == TYPE_BOOL: return val.setBool(v) + elif vType == TYPE_FLOAT: return val.setFloat(v) + elif vType == TYPE_DOUBLE: return val.setDouble(v) + elif vType == TYPE_UUID: return val.setUuid(v) + elif vType == TYPE_INT8: return val.setInt(v) + elif vType == TYPE_INT16: return val.setInt(v) + elif vType == TYPE_INT32: return val.setInt(v) + elif vType == TYPE_INT64: return val.setInt64(v) + else: + # when TYPE_MAP + # when TYPE_OBJECT + # when TYPE_LIST + # when TYPE_ARRAY + print "Unsupported type for get_attr?", val.getType() + return None + + + def __getitem__(self, name): + return self.get_attr(name) + + + def __setitem__(self, name, value): + self.set_attr(name, value) + + + def inc_attr(self, name, by=1): + self.set_attr(name, self.get_attr(name) + by) + + + def dec_attr(self, name, by=1): + self.set_attr(name, self.get_attr(name) - by) + + + def _value(self, name): + val = self.impl.getValue(name) + if not val: + raise ArgumentError("Attribute '%s' not defined for class %s" % (name, self.object_class.impl.getName())) + return val + + + +class ConsoleObject(QmfObject): + # attr_reader :current_time, :create_time, :delete_time + def __init__(self, cls): + QmfObject.__init__(self, cls) + + + def update(self): pass + def mergeUpdate(self, newObject): pass + def is_deleted(self): + return self.delete_time > 0 + def index(self): pass + def method_missing(self, name, *args): pass + + + +class ObjectId: + def __init__(self, impl=None): + if impl: + self.impl = impl + else: + self.impl = qmfengine.ObjectId() + + + def object_num_high(self): + return self.impl.getObjectNumHi() + + + def object_num_low(self): + return self.impl.getObjectNumLo() + + + def __eq__(self, other): + if self.__class__ != other.__class__: return False + return (self.impl.getObjectNumHi() == other.impl.getObjectNumHi() and + self.impl.getObjectNumLo() == other.impl.getObjectNumLo()) + + + def __ne__(self, other): + return not self.__eq__(other) + + + +class Arguments: + def __init__(self, map): + self.map = map + self._by_hash = {} + key_count = self.map.keyCount() + a = 0 + while a < key_count: + self._by_hash[self.map.key(a)] = self.by_key(self.map.key(a)) + a += 1 + + + def __getitem__(self, key): + return self._by_hash[key] + + + def __setitem__(self, key, value): + self._by_hash[key] = value + self.set(key, value) + + + def __iter__(self): + return _by_hash.__iter__ + + + def by_key(self, key): + val = self.map.byKey(key) + vType = val.getType() + if vType == TYPE_UINT8: return val.asUint() + elif vType == TYPE_UINT16: return val.asUint() + elif vType == TYPE_UINT32: return val.asUint() + elif vType == TYPE_UINT64: return val.asUint64() + elif vType == TYPE_SSTR: return val.asString() + elif vType == TYPE_LSTR: return val.asString() + elif vType == TYPE_ABSTIME: return val.asInt64() + elif vType == TYPE_DELTATIME: return val.asUint64() + elif vType == TYPE_REF: return val.asObjectId() + elif vType == TYPE_BOOL: return val.asBool() + elif vType == TYPE_FLOAT: return val.asFloat() + elif vType == TYPE_DOUBLE: return val.asDouble() + elif vType == TYPE_UUID: return val.asUuid() + elif vType == TYPE_INT8: return val.asInt() + elif vType == TYPE_INT16: return val.asInt() + elif vType == TYPE_INT32: return val.asInt() + elif vType == TYPE_INT64: return val.asInt64() + else: + # when TYPE_MAP + # when TYPE_OBJECT + # when TYPE_LIST + # when TYPE_ARRAY + print "Unsupported Type for Get?", val.getType() + return None + + + def set(self, key, value): + val = self.map.byKey(key) + vType = val.getType() + if vType == TYPE_UINT8: return val.setUint(value) + elif vType == TYPE_UINT16: return val.setUint(value) + elif vType == TYPE_UINT32: return val.setUint(value) + elif vType == TYPE_UINT64: return val.setUint64(value) + elif vType == TYPE_SSTR: + if value: + return val.setString(value) + else: + return val.setString('') + elif vType == TYPE_LSTR: + if value: + return val.setString(value) + else: + return val.setString('') + elif vType == TYPE_ABSTIME: return val.setInt64(value) + elif vType == TYPE_DELTATIME: return val.setUint64(value) + elif vType == TYPE_REF: return val.setObjectId(value.impl) + elif vType == TYPE_BOOL: return val.setBool(value) + elif vType == TYPE_FLOAT: return val.setFloat(value) + elif vType == TYPE_DOUBLE: return val.setDouble(value) + elif vType == TYPE_UUID: return val.setUuid(value) + elif vType == TYPE_INT8: return val.setInt(value) + elif vType == TYPE_INT16: return val.setInt(value) + elif vType == TYPE_INT32: return val.setInt(value) + elif vType == TYPE_INT64: return val.setInt64(value) + else: + # when TYPE_MAP + # when TYPE_OBJECT + # when TYPE_LIST + # when TYPE_ARRAY + print "Unsupported Type for Set?", val.getType() + return None + + + +class Query: + def __init__(self, i=None, package="", cls=None, oid=None): + if i: + self.impl = i + else: + if cls: + self.impl = qmfengine.Query(cls, package) + elif oid: + self.impl = qmfengine.Query(oid) + else: + raise "Argument error" + + + def package_name(self): return self.impl.getPackage() + def class_name(self): return self.impl.getClass() + def object_id(self): + _objid = self.impl.getObjectId() + if _objid: + return ObjectId(_objid) + else: + return None + + + ##============================================================================== + ## SCHEMA + ##============================================================================== + + + +class SchemaArgument: + #attr_reader :impl + def __init__(self, name, typecode, kwargs={}): + self.impl = qmfengine.SchemaArgument(name, typecode) + if kwargs.has_key("dir"): self.impl.setDirection(kwargs["dir"]) + if kwargs.has_key("unit"): self.impl.setUnit(kwargs["unit"]) + if kwargs.has_key("desc"): self.impl.setDesc(kwargs["desc"]) + + + +class SchemaMethod: + # attr_reader :impl + def __init__(self, name, kwargs={}): + self.impl = qmfengine.SchemaMethod(name) + if kwargs.has_key("desc"): self.impl.setDesc(kwargs["desc"]) + self._arguments = [] + + + def add_argument(self, arg): + self._arguments.append(arg) + self.impl.addArgument(arg.impl) + + +class SchemaProperty: + #attr_reader :impl + def __init__(self, name, typecode, kwargs={}): + self.impl = qmfengine.SchemaProperty(name, typecode) + if kwargs.has_key("access"): self.impl.setAccess(kwargs["access"]) + if kwargs.has_key("index"): self.impl.setIndex(kwargs["index"]) + if kwargs.has_key("optional"): self.impl.setOptional(kwargs["optional"]) + if kwargs.has_key("unit"): self.impl.setUnit(kwargs["unit"]) + if kwargs.has_key("desc"): self.impl.setDesc(kwargs["desc"]) + + + def name(self): + return self.impl.getName() + + + +class SchemaStatistic: + # attr_reader :impl + def __init__(self, name, typecode, kwargs={}): + self.impl = qmfengine.SchemaStatistic(name, typecode) + if kwargs.has_key("unit"): self.impl.setUnit(kwargs["unit"]) + if kwargs.has_key("desc"): self.impl.setDesc(kwargs["desc"]) + + + +class SchemaClassKey: + #attr_reader :impl + def __init__(self, i): + self.impl = i + + + def get_package(self): + self.impl.getPackageName() + + + def get_class(self): + self.impl.getClassName() + + + +class SchemaObjectClass: + # attr_reader :impl + def __init__(self, package, name, kwargs={}): + self.impl = qmfengine.SchemaObjectClass(package, name) + self._properties = [] + self._statistics = [] + self._methods = [] + + + def add_property(self, prop): + self._properties.append(prop) + self.impl.addProperty(prop.impl) + + + def add_statistic(self, stat): + self._statistics.append(stat) + self.impl.addStatistic(stat.impl) + + + def add_method(self, meth): + self._methods.append(meth) + self.impl.addMethod(meth.impl) + + + def name(self): + return self.impl.getName() + + + def properties(self): + return self._properties + + +class SchemaEventClass: + # attr_reader :impl + def __init__(self, package, name, kwargs={}): + self.impl = qmfengine.SchemaEventClass(package, name) + if kwargs.has_key("desc"): self.impl.setDesc(kwargs["desc"]) + self._arguments = [] + + + def add_argument(self, arg): + self._arguments.append(arg) + self.impl.addArgument(arg.impl) + + + + ##============================================================================== + ## CONSOLE + ##============================================================================== + + + +class ConsoleHandler: + def agent_added(self, agent): pass + def agent_deleted(self, agent): pass + def new_package(self, package): pass + def new_class(self, class_key): pass + def object_update(self, obj, hasProps, hasStats): pass + def event_received(self, event): pass + def agent_heartbeat(self, agent, timestamp): pass + def method_response(self, resp): pass + def broker_info(self, broker): pass + + + +class Console: + # attr_reader :impl + def initialize(handler=None, kwargs={}): + self._handler = handler + self.impl = qmfengine.ConsoleEngine() + self._event = qmfengine.ConsoleEvent() + self._broker_list = [] + + + def add_connection(self, conn): + broker = Broker(self, conn) + self._broker_list.append(broker) + return broker + + + def del_connection(self, broker): pass + + + def get_packages(self): pass + + + def get_classes(self, package): pass + + + def get_schema(self, class_key): pass + + + def bind_package(self, package): pass + + + def bind_class(self, kwargs = {}): pass + + + def get_agents(self, broker=None): pass + + + def get_objects(self, query, kwargs = {}): pass + + + def start_sync(self, query): pass + + + def touch_sync(self, sync): pass + + + def end_sync(self, sync): pass + + + def do_console_events(self): + count = 0 + valid = self.impl.getEvent(self._event) + while valid: + count += 1 + print "Console Event:", self._event.kind + if self._event.kind == qmfengine.ConsoleEvent.AGENT_ADDED: + pass + elif self._event.kind == qmfengine.ConsoleEvent.AGENT_DELETED: + pass + elif self._event.kind == qmfengine.ConsoleEvent.NEW_PACKAGE: + pass + elif self._event.kind == qmfengine.ConsoleEvent.NEW_CLASS: + pass + elif self._event.kind == qmfengine.ConsoleEvent.OBJECT_UPDATE: + pass + elif self._event.kind == qmfengine.ConsoleEvent.EVENT_RECEIVED: + pass + elif self._event.kind == qmfengine.ConsoleEvent.AGENT_HEARTBEAT: + pass + elif self._event.kind == qmfengine.ConsoleEvent.METHOD_RESPONSE: + pass + + self.impl.popEvent() + valid = self.impl.getEvent(self._event) + return count + + + +class Broker(ConnectionHandler): + # attr_reader :impl + def __init__(self, console, conn): + self._console = console + self._conn = conn + self._session = None + self._event = qmfengine.BrokerEvent() + self._xmtMessage = qmfengine.Message() + self.impl = qmfengine.BrokerProxy(self._console.impl) + self._console.impl.addConnection(self.impl, self) + self._conn.add_conn_handler(self) + + + def do_broker_events(self): + count = 0 + valid = self.impl.getEvent(self._event) + while valid: + count += 1 + print "Broker Event: ", self._event.kind + if self._event.kind == qmfengine.BrokerEvent.BROKER_INFO: + pass + elif self._event.kind == qmfengine.BrokerEvent.DECLARE_QUEUE: + self._conn.impl.declareQueue(self._session.handle, self._event.name) + elif self._event.kind == qmfengine.BrokerEvent.DELETE_QUEUE: + self._conn.impl.deleteQueue(self._session.handle, self._event.name) + elif self._event.kind == qmfengine.BrokerEvent.BIND: + self._conn.impl.bind(self._session.handle, self._event.exchange, self._event.name, self._event.bindingKey) + elif self._event.kind == qmfengine.BrokerEvent.UNBIND: + self._conn.impl.unbind(self._session.handle, self._event.exchange, self._event.name, self._event.bindingKey) + elif self._event.kind == qmfengine.BrokerEvent.SETUP_COMPLETE: + self.impl.startProtocol() + + self.impl.popEvent() + valid = self.impl.getEvent(self._event) + + return count + + + def do_broker_messages(self): + count = 0 + valid = self.impl.getXmtMessage(self._xmtMessage) + while valid: + count += 1 + self._conn.impl.sendMessage(self._session.handle, self._xmtMessage) + self.impl.popXmt() + valid = self.impl.getXmtMessage(self._xmtMessage) + + return count + + + def do_events(self): + while True: + ccnt = self._console.do_console_events() + bcnt = do_broker_events() + mcnt = do_broker_messages() + if ccnt == 0 and bcnt == 0 and mcnt == 0: + break; + + + def conn_event_connected(self): + print "Console Connection Established..." + self._session = Session(self._conn, "qmfc-%s.%d" % (socket.gethostname(), os.getpid()), self) + self.impl.sessionOpened(self._session.handle) + self.do_events() + + + def conn_event_disconnected(self, error): + print "Console Connection Lost" + pass + + + def sess_event_session_closed(self, context, error): + print "Console Session Lost" + self.impl.sessionClosed() + + + def sess_event_recv(self, context, message): + self.impl.handleRcvMessage(message) + self.do_events() + + + + ##============================================================================== + ## AGENT + ##============================================================================== + + + +class AgentHandler: + def get_query(self, context, query, userId): None + def method_call(self, context, name, object_id, args, userId): None + + + +class Agent(ConnectionHandler): + def __init__(self, handler, label=""): + if label == "": + self._agentLabel = "rb-%s.%d" % (socket.gethostname(), os.getpid()) + else: + self._agentLabel = label + self._conn = None + self._handler = handler + self.impl = qmfengine.AgentEngine(self._agentLabel) + self._event = qmfengine.AgentEvent() + self._xmtMessage = qmfengine.Message() + + + def set_connection(self, conn): + self._conn = conn + self._conn.add_conn_handler(self) + + + def register_class(self, cls): + self.impl.registerClass(cls.impl) + + + def alloc_object_id(self, low = 0, high = 0): + return ObjectId(self.impl.allocObjectId(low, high)) + + + def query_response(self, context, obj): + self.impl.queryResponse(context, obj.impl) + + + def query_complete(self, context): + self.impl.queryComplete(context) + + + def method_response(self, context, status, text, arguments): + self.impl.methodResponse(context, status, text, arguments.map) + + + def do_agent_events(self): + count = 0 + valid = self.impl.getEvent(self._event) + while valid: + count += 1 + if self._event.kind == qmfengine.AgentEvent.GET_QUERY: + self._handler.get_query(self._event.sequence, + Query(self._event.query), + self._event.authUserId) + + elif self._event.kind == qmfengine.AgentEvent.START_SYNC: + pass + elif self._event.kind == qmfengine.AgentEvent.END_SYNC: + pass + elif self._event.kind == qmfengine.AgentEvent.METHOD_CALL: + args = Arguments(self._event.arguments) + self._handler.method_call(self._event.sequence, self._event.name, + ObjectId(self._event.objectId), + args, self._event.authUserId) + + elif self._event.kind == qmfengine.AgentEvent.DECLARE_QUEUE: + self._conn.impl.declareQueue(self._session.handle, self._event.name) + + elif self._event.kind == qmfengine.AgentEvent.DELETE_QUEUE: + self._conn.impl.deleteQueue(self._session.handle, self._event.name) + + elif self._event.kind == qmfengine.AgentEvent.BIND: + self._conn.impl.bind(self._session.handle, self._event.exchange, + self._event.name, self._event.bindingKey) + + elif self._event.kind == qmfengine.AgentEvent.UNBIND: + self._conn.impl.unbind(self._session.handle, self._event.exchange, + self._event.name, self._event.bindingKey) + + elif self._event.kind == qmfengine.AgentEvent.SETUP_COMPLETE: + self.impl.startProtocol() + + self.impl.popEvent() + valid = self.impl.getEvent(self._event) + return count + + + def do_agent_messages(self): + count = 0 + valid = self.impl.getXmtMessage(self._xmtMessage) + while valid: + count += 1 + self._conn.impl.sendMessage(self._session.handle, self._xmtMessage) + self.impl.popXmt() + valid = self.impl.getXmtMessage(self._xmtMessage) + return count + + + def do_events(self): + while True: + ecnt = self.do_agent_events() + mcnt = self.do_agent_messages() + if ecnt == 0 and mcnt == 0: + break + + + def conn_event_connected(self): + print "Agent Connection Established..." + self._session = Session(self._conn, + "qmfa-%s.%d" % (socket.gethostname(), os.getpid()), + self) + self.impl.newSession() + self.do_events() + + + def conn_event_disconnected(self, error): + print "Agent Connection Lost" + pass + + + def sess_event_session_closed(self, context, error): + print "Agent Session Lost" + pass + + + def sess_event_recv(self, context, message): + self.impl.handleRcvMessage(message) + self.do_events() + + diff --git a/qpid/cpp/bindings/qmf/qmfengine.i b/qpid/cpp/bindings/qmf/qmfengine.i index 8ae28730e5..d3500c9b8f 100644 --- a/qpid/cpp/bindings/qmf/qmfengine.i +++ b/qpid/cpp/bindings/qmf/qmfengine.i @@ -20,7 +20,8 @@ %{ #include "qmf/AgentEngine.h" -#include <qmf/ResilientConnection.h> +#include "qmf/ConsoleEngine.h" +#include "qmf/ResilientConnection.h" %} @@ -28,6 +29,7 @@ %include <qmf/Query.h> %include <qmf/Message.h> %include <qmf/AgentEngine.h> +%include <qmf/ConsoleEngine.h> %include <qmf/ConnectionSettings.h> %include <qmf/ResilientConnection.h> %include <qmf/Typecode.h> diff --git a/qpid/cpp/bindings/qmf/ruby/qmf.rb b/qpid/cpp/bindings/qmf/ruby/qmf.rb index 79b18423db..12741b45f2 100644 --- a/qpid/cpp/bindings/qmf/ruby/qmf.rb +++ b/qpid/cpp/bindings/qmf/ruby/qmf.rb @@ -20,364 +20,565 @@ require 'qmfengine' require 'thread' require 'socket' +require 'monitor' module Qmf - # Pull all the TYPE_* constants into Qmf namespace. Maybe there's an easier way? - Qmfengine.constants.each do |c| - if c.index('TYPE_') == 0 or c.index('ACCESS_') == 0 or c.index('DIR_') == 0 - const_set(c, Qmfengine.const_get(c)) - end + # Pull all the TYPE_* constants into Qmf namespace. Maybe there's an easier way? + Qmfengine.constants.each do |c| + if c.index('TYPE_') == 0 or c.index('ACCESS_') == 0 or c.index('DIR_') == 0 or c.index('CLASS_') == 0 + const_set(c, Qmfengine.const_get(c)) end + end - class ConnectionSettings - attr_reader :impl + ##============================================================================== + ## CONNECTION + ##============================================================================== - def initialize(url = nil) - if url - @impl = Qmfengine::ConnectionSettings.new(url) - else - @impl = Qmfengine::ConnectionSettings.new() - end + 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 + 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) + @impl.setAttr(key, v) + end + end + + class ConnectionHandler + def conn_event_connected(); end + def conn_event_disconnected(error); end + def conn_event_visit(); end + def sess_event_session_closed(context, error); end + def sess_event_recv(context, message); end + end + + class Connection + include MonitorMixin + + attr_reader :impl + + def initialize(settings) + super() + @impl = Qmfengine::ResilientConnection.new(settings.impl) + @sockEngine, @sock = Socket::socketpair(Socket::PF_UNIX, Socket::SOCK_STREAM, 0) + @impl.setNotifyFd(@sockEngine.fileno) + @new_conn_handlers = [] + @conn_handlers_to_delete = [] + @conn_handlers = [] + + @thread = Thread.new do + run end end - class ConnectionHandler - def conn_event_connected(); end - def conn_event_disconnected(error); end - def sess_event_session_closed(context, error); end - def sess_event_recv(context, message); end + def kick + @sockEngine.write(".") + @sockEngine.flush end - class Query - attr_reader :impl - def initialize(i) - @impl = i + def add_conn_handler(handler) + synchronize do + @new_conn_handlers << handler end + kick + end - def package_name - @impl.getPackage + def del_conn_handler(handler) + synchronize do + @conn_handlers_to_delete << handler end + kick + end - def class_name - @impl.getClass - end + def run() + eventImpl = Qmfengine::ResilientConnectionEvent.new + connected = nil + new_handlers = nil + del_handlers = nil + bt_count = 0 + + while :true + @sock.read(1) + + synchronize do + new_handlers = @new_conn_handlers + del_handlers = @conn_handlers_to_delete + @new_conn_handlers = [] + @conn_handlers_to_delete = [] + end + + new_handlers.each do |nh| + @conn_handlers << nh + nh.conn_event_connected() if connected + end + new_handlers = nil - def object_id - objid = @impl.getObjectId - if objid.class == NilClass - return nil + del_handlers.each do |dh| + d = @conn_handlers.delete(dh) end - return ObjectId.new(objid) + del_handlers = nil + + valid = @impl.getEvent(eventImpl) + while valid + begin + case eventImpl.kind + when Qmfengine::ResilientConnectionEvent::CONNECTED + connected = :true + @conn_handlers.each { |h| h.conn_event_connected() } + when Qmfengine::ResilientConnectionEvent::DISCONNECTED + connected = nil + @conn_handlers.each { |h| h.conn_event_disconnected(eventImpl.errorText) } + when Qmfengine::ResilientConnectionEvent::SESSION_CLOSED + eventImpl.sessionContext.handler.sess_event_session_closed(eventImpl.sessionContext, eventImpl.errorText) + when Qmfengine::ResilientConnectionEvent::RECV + eventImpl.sessionContext.handler.sess_event_recv(eventImpl.sessionContext, eventImpl.message) + end + rescue Exception => ex + puts "Event Exception: #{ex}" + if bt_count < 2 + puts ex.backtrace + bt_count += 1 + end + end + @impl.popEvent + valid = @impl.getEvent(eventImpl) + end + @conn_handlers.each { |h| h.conn_event_visit } end end + end - class Connection - attr_reader :impl + class Session + attr_reader :handle, :handler - 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 - @conn_handlers = Array.new - @sess_handlers = Array.new + def initialize(conn, label, handler) + @conn = conn + @label = label + @handler = handler + @handle = Qmfengine::SessionHandle.new + result = @conn.impl.createSession(label, self, @handle) + end - @thread = Thread.new do - run - end + def destroy() + @conn.impl.destroySession(@handle) + end + end + + ##============================================================================== + ## OBJECTS + ##============================================================================== + + class QmfObject + include MonitorMixin + attr_reader :impl, :object_class + def initialize(cls, kwargs={}) + super() + @cv = new_cond + @sync_count = 0 + @sync_result = nil + @allow_sets = :false + @broker = kwargs[:broker] if kwargs.include?(:broker) + + if cls: + @object_class = cls + @impl = Qmfengine::Object.new(@object_class.impl) + elsif kwargs.include?(:impl) + @impl = Qmfengine::Object.new(kwargs[:impl]) + @object_class = SchemaObjectClass.new(nil, nil, :impl => @impl.getClass) end + end + + def object_id + return ObjectId.new(@impl.getObjectId) + end - def add_conn_handler(handler) - @new_conn_handlers.push(handler) - @sockEngine.write("x") + def get_attr(name) + val = value(name) + case val.getType + when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.asUint + when TYPE_UINT64 then val.asUint64 + when TYPE_SSTR, TYPE_LSTR then val.asString + when TYPE_ABSTIME then val.asInt64 + when TYPE_DELTATIME then val.asUint64 + when TYPE_REF then val.asObjectId + when TYPE_BOOL then val.asBool + when TYPE_FLOAT then val.asFloat + when TYPE_DOUBLE then val.asDouble + when TYPE_UUID then val.asUuid + when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.asInt + when TYPE_INT64 then val.asInt64 + when TYPE_MAP + when TYPE_OBJECT + when TYPE_LIST + when TYPE_ARRAY end + end - def add_sess_handler(handler) - @sess_handlers.push(handler) + def set_attr(name, v) + val = value(name) + case val.getType + when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.setUint(v) + when TYPE_UINT64 then val.setUint64(v) + when TYPE_SSTR, TYPE_LSTR then v ? val.setString(v) : val.setString('') + when TYPE_ABSTIME then val.setInt64(v) + when TYPE_DELTATIME then val.setUint64(v) + when TYPE_REF then val.setObjectId(v.impl) + when TYPE_BOOL then v ? val.setBool(v) : val.setBool(0) + when TYPE_FLOAT then val.setFloat(v) + when TYPE_DOUBLE then val.setDouble(v) + when TYPE_UUID then val.setUuid(v) + when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.setInt(v) + when TYPE_INT64 then val.setInt64(v) + when TYPE_MAP + when TYPE_OBJECT + when TYPE_LIST + when TYPE_ARRAY end + end - def run() - event = Qmfengine::ResilientConnectionEvent.new - connected = nil - while :true - @sock.read(1) + def [](name) + get_attr(name) + end - @new_conn_handlers.each do |nh| - @conn_handlers.push(nh) - nh.conn_event_connected() if connected - end - @new_conn_handlers = Array.new - - valid = @impl.getEvent(event) - while valid - begin - case event.kind - when Qmfengine::ResilientConnectionEvent::CONNECTED - connected = :true - @conn_handlers.each { |h| h.conn_event_connected() } - when Qmfengine::ResilientConnectionEvent::DISCONNECTED - connected = nil - @conn_handlers.each { |h| h.conn_event_disconnected(event.errorText) } - when Qmfengine::ResilientConnectionEvent::SESSION_CLOSED - event.sessionContext.handler.sess_event_session_closed(event.sessionContext, event.errorText) - when Qmfengine::ResilientConnectionEvent::RECV - event.sessionContext.handler.sess_event_recv(event.sessionContext, event.message) - end - rescue Exception => ex - puts "Event Exception: #{ex}" - puts ex.backtrace - end - @impl.popEvent - valid = @impl.getEvent(event) - end - end - end + def []=(name, value) + set_attr(name, value) end - class Session - attr_reader :handle, :handler + def inc_attr(name, by=1) + set_attr(name, get_attr(name) + by) + end - def initialize(conn, label, handler) - @conn = conn - @label = label - @handler = handler - @handle = Qmfengine::SessionHandle.new - @conn.add_sess_handler(@handler) - result = @conn.impl.createSession(label, self, @handle) - end + def dec_attr(name, by=1) + set_attr(name, get_attr(name) - by) end - class ObjectId - attr_reader :impl - def initialize(impl=nil) - if impl - @impl = impl - else - @impl = Qmfengine::ObjectId.new + def method_missing(name_in, *args) + # + # Convert the name to a string and determine if it represents an + # attribute assignment (i.e. "attr=") + # + name = name_in.to_s + attr_set = (name[name.length - 1] == 61) + name = name[0..name.length - 2] if attr_set + raise "Sets not permitted on this object" if attr_set && !@allow_sets + + # + # If the name matches a property name, set or return the value of the property. + # + @object_class.properties.each do |prop| + if prop.name == name + if attr_set + return set_attr(name, args[0]) + else + return get_attr(name) + end end end - def object_num_high - return @impl.getObjectNumHi + # + # Do the same for statistics + # + @object_class.statistics.each do |stat| + if stat.name == name + if attr_set + return set_attr(name, args[0]) + else + return get_attr(name) + end + end end - def object_num_low - return @impl.getObjectNumLo + # + # If we still haven't found a match for the name, check to see if + # it matches a method name. If so, marshall the arguments and invoke + # the method. + # + @object_class.methods.each do |method| + if method.name == name + raise "Sets not permitted on methods" if attr_set + timeout = 30 + synchronize do + @sync_count = 1 + @impl.invokeMethod(name, _marshall(method, args), self) + @broker.conn.kick if @broker + unless @cv.wait(timeout) { @sync_count == 0 } + raise "Timed out waiting for response" + end + end + + return @sync_result + end end - def ==(other) - return (@impl.getObjectNumHi == other.impl.getObjectNumHi) && - (@impl.getObjectNumLo == other.impl.getObjectNumLo) + # + # This name means nothing to us, pass it up the line to the parent + # class's handler. + # + super.method_missing(name_in, args) + end + + def _method_result(result) + synchronize do + @sync_result = result + @sync_count -= 1 + @cv.signal end end - class Arguments - attr_reader :map - def initialize(map) - @map = map - @by_hash = {} - key_count = @map.keyCount - a = 0 - while a < key_count - @by_hash[@map.key(a)] = by_key(@map.key(a)) - a += 1 + # + # Convert a Ruby array of arguments (positional) into a Value object of type "map". + # + private + def _marshall(schema, args) + map = Qmfengine::Value.new(TYPE_MAP) + schema.arguments.each do |arg| + if arg.direction == DIR_IN || arg.direction == DIR_IN_OUT + map.insert(arg.name, Qmfengine::Value.new(arg.typecode)) end end - def [] (key) - return @by_hash[key] + marshalled = Arguments.new(map) + idx = 0 + schema.arguments.each do |arg| + if arg.direction == DIR_IN || arg.direction == DIR_IN_OUT + marshalled[arg.name] = args[idx] unless args[idx] == nil + idx += 1 + end end - def []= (key, value) - @by_hash[key] = value - set(key, value) - end + return marshalled.map + end - def each - @by_hash.each { |k, v| yield(k, v) } + private + def value(name) + val = @impl.getValue(name.to_s) + if val.nil? + raise ArgumentError, "Attribute '#{name}' not defined for class #{@object_class.impl.getClassKey.getPackageName}:#{@object_class.impl.getClassKey.getClassName}" end + return val + end + end - def by_key(key) - val = @map.byKey(key) - case val.getType - when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.asUint - when TYPE_UINT64 then val.asUint64 - when TYPE_SSTR, TYPE_LSTR then val.asString - when TYPE_ABSTIME then val.asInt64 - when TYPE_DELTATIME then val.asUint64 - when TYPE_REF then val.asObjectId - when TYPE_BOOL then val.asBool - when TYPE_FLOAT then val.asFloat - when TYPE_DOUBLE then val.asDouble - when TYPE_UUID then val.asUuid - when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.asInt - when TYPE_INT64 then val.asInt64 - when TYPE_MAP - when TYPE_OBJECT - when TYPE_LIST - when TYPE_ARRAY - end - end + class AgentObject < QmfObject + def initialize(cls, kwargs={}) + super(cls, kwargs) + @allow_sets = :true + end - def set(key, value) - val = @map.byKey(key) - case val.getType - when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.setUint(value) - when TYPE_UINT64 then val.setUint64(value) - when TYPE_SSTR, TYPE_LSTR then value ? val.setString(value) : val.setString('') - when TYPE_ABSTIME then val.setInt64(value) - when TYPE_DELTATIME then val.setUint64(value) - when TYPE_REF then val.setObjectId(value.impl) - when TYPE_BOOL then value ? val.setBool(value) : val.setBool(0) - when TYPE_FLOAT then val.setFloat(value) - when TYPE_DOUBLE then val.setDouble(value) - when TYPE_UUID then val.setUuid(value) - when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.setInt(value) - when TYPE_INT64 then val.setInt64(value) - when TYPE_MAP - when TYPE_OBJECT - when TYPE_LIST - when TYPE_ARRAY - end - end + def destroy + @impl.destroy end - class AgentHandler - def get_query(context, query, userId); end - def method_call(context, name, object_id, args, userId); end + def set_object_id(oid) + @impl.setObjectId(oid.impl) end + end - class Agent < ConnectionHandler - def initialize(handler, label="") - if label == "" - @agentLabel = "rb-%s.%d" % [Socket.gethostname, Process::pid] - else - @agentLabel = label - end - @conn = nil - @handler = handler - @impl = Qmfengine::AgentEngine.new(@agentLabel) - @event = Qmfengine::AgentEvent.new - @xmtMessage = Qmfengine::Message.new - end + class ConsoleObject < QmfObject + attr_reader :current_time, :create_time, :delete_time - def set_connection(conn) - @conn = conn - @conn.add_conn_handler(self) - end + def initialize(cls, kwargs={}) + super(cls, kwargs) + end - def register_class(cls) - @impl.registerClass(cls.impl) - end + def update() + end - def alloc_object_id(low = 0, high = 0) - ObjectId.new(@impl.allocObjectId(low, high)) - end + def mergeUpdate(newObject) + end - def query_response(context, object) - @impl.queryResponse(context, object.impl) - end + def deleted?() + @delete_time > 0 + end - def query_complete(context) - @impl.queryComplete(context) + def index() + end + end + + class ObjectId + attr_reader :impl + def initialize(impl=nil) + if impl + @impl = impl + else + @impl = Qmfengine::ObjectId.new end + end - def method_response(context, status, text, arguments) - @impl.methodResponse(context, status, text, arguments.map) - end + def object_num_high + return @impl.getObjectNumHi + end - def do_agent_events() - count = 0 - valid = @impl.getEvent(@event) - while valid - count += 1 - case @event.kind - when Qmfengine::AgentEvent::GET_QUERY - @handler.get_query(@event.sequence, Query.new(@event.query), @event.authUserId) - when Qmfengine::AgentEvent::START_SYNC - when Qmfengine::AgentEvent::END_SYNC - when Qmfengine::AgentEvent::METHOD_CALL - args = Arguments.new(@event.arguments) - @handler.method_call(@event.sequence, @event.name, ObjectId.new(@event.objectId), - args, @event.authUserId) - when Qmfengine::AgentEvent::DECLARE_QUEUE - @conn.impl.declareQueue(@session.handle, @event.name) - when Qmfengine::AgentEvent::DELETE_QUEUE - @conn.impl.deleteQueue(@session.handle, @event.name) - when Qmfengine::AgentEvent::BIND - @conn.impl.bind(@session.handle, @event.exchange, @event.name, @event.bindingKey) - when Qmfengine::AgentEvent::UNBIND - @conn.impl.unbind(@session.handle, @event.exchange, @event.name, @event.bindingKey) - when Qmfengine::AgentEvent::SETUP_COMPLETE - @impl.startProtocol() - end - @impl.popEvent - valid = @impl.getEvent(@event) - end - return count - end + def object_num_low + return @impl.getObjectNumLo + end - def do_agent_messages() - count = 0 - valid = @impl.getXmtMessage(@xmtMessage) - while valid - count += 1 - @conn.impl.sendMessage(@session.handle, @xmtMessage) - @impl.popXmt - valid = @impl.getXmtMessage(@xmtMessage) - end - return count + def ==(other) + return (@impl.getObjectNumHi == other.impl.getObjectNumHi) && + (@impl.getObjectNumLo == other.impl.getObjectNumLo) + end + end + + class Arguments + attr_reader :map + def initialize(map) + @map = map + @by_hash = {} + key_count = @map.keyCount + a = 0 + while a < key_count + @by_hash[@map.key(a)] = by_key(@map.key(a)) + a += 1 end + end - def do_events() - begin - ecnt = do_agent_events - mcnt = do_agent_messages - end until ecnt == 0 and mcnt == 0 - end + def [] (key) + return @by_hash[key] + end - def conn_event_connected() - puts "Agent Connection Established..." - @session = Session.new(@conn, "qmfa-%s.%d" % [Socket.gethostname, Process::pid], self) - @impl.newSession - do_events - end + def []= (key, value) + @by_hash[key] = value + set(key, value) + end - def conn_event_disconnected(error) - puts "Agent Connection Lost" + def each + @by_hash.each { |k, v| yield(k, v) } + end + + def by_key(key) + val = @map.byKey(key) + case val.getType + when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.asUint + when TYPE_UINT64 then val.asUint64 + when TYPE_SSTR, TYPE_LSTR then val.asString + when TYPE_ABSTIME then val.asInt64 + when TYPE_DELTATIME then val.asUint64 + when TYPE_REF then val.asObjectId + when TYPE_BOOL then val.asBool + when TYPE_FLOAT then val.asFloat + when TYPE_DOUBLE then val.asDouble + when TYPE_UUID then val.asUuid + when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.asInt + when TYPE_INT64 then val.asInt64 + when TYPE_MAP + when TYPE_OBJECT + when TYPE_LIST + when TYPE_ARRAY end + end - def sess_event_session_closed(context, error) - puts "Agent Session Lost" + def set(key, value) + val = @map.byKey(key) + case val.getType + when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.setUint(value) + when TYPE_UINT64 then val.setUint64(value) + when TYPE_SSTR, TYPE_LSTR then value ? val.setString(value) : val.setString('') + when TYPE_ABSTIME then val.setInt64(value) + when TYPE_DELTATIME then val.setUint64(value) + when TYPE_REF then val.setObjectId(value.impl) + when TYPE_BOOL then value ? val.setBool(value) : val.setBool(0) + when TYPE_FLOAT then val.setFloat(value) + when TYPE_DOUBLE then val.setDouble(value) + when TYPE_UUID then val.setUuid(value) + when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.setInt(value) + when TYPE_INT64 then val.setInt64(value) + when TYPE_MAP + when TYPE_OBJECT + when TYPE_LIST + when TYPE_ARRAY end + end + end + + class MethodResponse + def initialize(impl) + puts "start copying..." + @impl = Qmfengine::MethodResponse.new(impl) + puts "done copying..." + end + + def status + @impl.getStatus + end - def sess_event_recv(context, message) - @impl.handleRcvMessage(message) - do_events + def exception + @impl.getException + end + end + + ##============================================================================== + ## QUERY + ##============================================================================== + + class Query + attr_reader :impl + def initialize(kwargs = {}) + if kwargs.include?(:impl) + @impl = kwargs[:impl] + else + package = '' + if kwargs.include?(:key) + @impl = Qmfengine::Query.new(kwargs[:key]) + elsif kwargs.include?(:object_id) + @impl = Qmfengine::Query.new(kwargs[:object_id]) + else + package = kwargs[:package] if kwargs.include?(:package) + if kwargs.include?(:class) + @impl = Qmfengine::Query.new(kwargs[:class], package) + else + raise ArgumentError, "Invalid arguments, use :key or :class[,:package]" + end + end end end - class SchemaArgument - attr_reader :impl - def initialize(name, typecode, kwargs={}) + def package_name + @impl.getPackage + end + + def class_name + @impl.getClass + end + + def object_id + objid = @impl.getObjectId + if objid.class == NilClass + return nil + end + return ObjectId.new(objid) + end + end + + ##============================================================================== + ## SCHEMA + ##============================================================================== + + class SchemaArgument + attr_reader :impl + def initialize(name, typecode, kwargs={}) + if kwargs.include?(:impl) + @impl = kwargs[:impl] + else @impl = Qmfengine::SchemaArgument.new(name, typecode) @impl.setDirection(kwargs[:dir]) if kwargs.include?(:dir) @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) @@ -385,23 +586,51 @@ module Qmf end end - class SchemaMethod - attr_reader :impl - def initialize(name, kwargs={}) + def name + @impl.getName + end + + def direction + @impl.getDirection + end + + def typecode + @impl.getType + end + end + + class SchemaMethod + attr_reader :impl, :arguments + def initialize(name, kwargs={}) + @arguments = [] + if kwargs.include?(:impl) + @impl = kwargs[:impl] + arg_count = @impl.getArgumentCount + for i in 0...arg_count + @arguments << SchemaArgument.new(nil, nil, :impl => @impl.getArgument(i)) + end + else @impl = Qmfengine::SchemaMethod.new(name) @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) - @arguments = [] end + end - def add_argument(arg) - @arguments << arg - @impl.addArgument(arg.impl) - end + def add_argument(arg) + @arguments << arg + @impl.addArgument(arg.impl) end - class SchemaProperty - attr_reader :impl - def initialize(name, typecode, kwargs={}) + def name + @impl.getName + end + end + + class SchemaProperty + attr_reader :impl + def initialize(name, typecode, kwargs={}) + if kwargs.include?(:impl) + @impl = kwargs[:impl] + else @impl = Qmfengine::SchemaProperty.new(name, typecode) @impl.setAccess(kwargs[:access]) if kwargs.include?(:access) @impl.setIndex(kwargs[:index]) if kwargs.include?(:index) @@ -409,161 +638,547 @@ module Qmf @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) end - - def name - @impl.getName - end end - class SchemaStatistic - attr_reader :impl - def initialize(name, typecode, kwargs={}) + def name + @impl.getName + end + end + + class SchemaStatistic + attr_reader :impl + def initialize(name, typecode, kwargs={}) + if kwargs.include?(:impl) + @impl = kwargs[:impl] + else @impl = Qmfengine::SchemaStatistic.new(name, typecode) @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) end end - class SchemaObjectClass - attr_reader :impl - def initialize(package, name, kwargs={}) + def name + @impl.getName + end + end + + class SchemaClassKey + attr_reader :impl + def initialize(i) + @impl = i + end + + def get_package() + @impl.getPackageName() + end + + def get_class() + @impl.getClassName() + end + end + + class SchemaObjectClass + attr_reader :impl, :properties, :statistics, :methods + def initialize(package, name, kwargs={}) + @properties = [] + @statistics = [] + @methods = [] + if kwargs.include?(:impl) + @impl = kwargs[:impl] + + @impl.getPropertyCount.times do |i| + @properties << SchemaProperty.new(nil, nil, :impl => @impl.getProperty(i)) + end + + @impl.getStatisticCount.times do |i| + @statistics << SchemaStatistic.new(nil, nil, :impl => @impl.getStatistic(i)) + end + + @impl.getMethodCount.times do |i| + @methods << SchemaMethod.new(nil, :impl => @impl.getMethod(i)) + end + else @impl = Qmfengine::SchemaObjectClass.new(package, name) - @properties = [] - @statistics = [] - @methods = [] end + end - def add_property(prop) - @properties << prop - @impl.addProperty(prop.impl) - end + def add_property(prop) + @properties << prop + @impl.addProperty(prop.impl) + end - def add_statistic(stat) - @statistics << stat - @impl.addStatistic(stat.impl) - end + def add_statistic(stat) + @statistics << stat + @impl.addStatistic(stat.impl) + end - def add_method(meth) - @methods << meth - @impl.addMethod(meth.impl) + def add_method(meth) + @methods << meth + @impl.addMethod(meth.impl) + end + + def name + @impl.getClassKey.getClassName + end + end + + class SchemaEventClass + attr_reader :impl, :arguments + def initialize(package, name, kwargs={}) + @arguments = [] + if kwargs.include?(:impl) + @impl = kwargs[:impl] + @impl.getArgumentCount.times do |i| + @arguments << SchemaArgument.new(nil, nil, :impl => @impl.getArgument(i)) + end + else + @impl = Qmfengine::SchemaEventClass.new(package, name) + @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) end + end - def name - @impl.getName + def add_argument(arg) + @arguments << arg + @impl.addArgument(arg.impl) + end + + def name + @impl.getClassKey.getClassName + end + end + + ##============================================================================== + ## CONSOLE + ##============================================================================== + + class ConsoleHandler + def agent_added(agent); end + def agent_deleted(agent); end + def new_package(package); end + def new_class(class_key); end + def object_update(object, hasProps, hasStats); end + def event_received(event); end + def agent_heartbeat(agent, timestamp); end + def method_response(resp); end + def broker_info(broker); end + end + + class Console + include MonitorMixin + attr_reader :impl + + def initialize(handler = nil, kwargs={}) + super() + @handler = handler + @impl = Qmfengine::ConsoleEngine.new + @event = Qmfengine::ConsoleEvent.new + @broker_list = [] + @cv = new_cond + @sync_count = nil + @sync_result = nil + end + + def add_connection(conn) + broker = Broker.new(self, conn) + @broker_list << broker + return broker + end + + def del_connection(broker) + broker.shutdown + @broker_list.delete(broker) + end + + def get_packages() + plist = [] + count = @impl.packageCount + for i in 0...count + plist << @impl.getPackageName(i) end + return plist + end - def properties - unless @properties - @properties = [] - @impl.getPropertyCount.times do |i| - @properties << @impl.getProperty(i) + def get_classes(package, kind=CLASS_OBJECT) + clist = [] + count = @impl.classCount(package) + for i in 0...count + key = @impl.getClass(package, i) + class_kind = @impl.getClassKind(key) + if class_kind == kind + if kind == CLASS_OBJECT + clist << SchemaObjectClass.new(nil, nil, :impl => @impl.getObjectClass(key)) + elsif kind == CLASS_EVENT + clist << SchemaEventClass.new(nil, nil, :impl => @impl.getEventClass(key)) end end - return @properties end + + return clist end - class SchemaEventClass - attr_reader :impl - def initialize(package, name, kwargs={}) - @impl = Qmfengine::SchemaEventClass.new(package, name) - @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) - @arguments = [] - end + def bind_package(package) + @impl.bindPackage(package) + end - def add_argument(arg) - @arguments << arg - @impl.addArgument(arg.impl) + def bind_class(kwargs = {}) + if kwargs.include?(:key) + @impl.bindClass(kwargs[:key]) + elsif kwargs.include?(:package) + package = kwargs[:package] + if kwargs.include?(:class) + @impl.bindClass(package, kwargs[:class]) + else + @impl.bindClass(package) + end + else + raise ArgumentError, "Invalid arguments, use :key or :package[,:class]" end end - class QmfObject - attr_reader :impl, :object_class - def initialize(cls) - @object_class = cls - @impl = Qmfengine::Object.new(@object_class.impl) + def get_agents(broker = nil) + blist = [] + if broker + blist << broker + else + blist = @broker_list end - def destroy - @impl.destroy + agents = [] + blist.each do |b| + count = b.impl.agentCount + for idx in 0...count + agents << AgentProxy.new(b.impl.getAgent(idx), b) + end end - def object_id - return ObjectId.new(@impl.getObjectId) + return agents + end + + def get_objects(query, kwargs = {}) + timeout = 30 + if kwargs.include?(:timeout) + timeout = kwargs[:timeout] end + synchronize do + @sync_count = 1 + @sync_result = [] + broker = @broker_list[0] + broker.send_query(query.impl, nil) + unless @cv.wait(timeout) { @sync_count == 0 } + raise "Timed out waiting for response" + end - def set_object_id(oid) - @impl.setObjectId(oid.impl) + return @sync_result end + end - def get_attr(name) - val = value(name) - case val.getType - when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.asUint - when TYPE_UINT64 then val.asUint64 - when TYPE_SSTR, TYPE_LSTR then val.asString - when TYPE_ABSTIME then val.asInt64 - when TYPE_DELTATIME then val.asUint64 - when TYPE_REF then val.asObjectId - when TYPE_BOOL then val.asBool - when TYPE_FLOAT then val.asFloat - when TYPE_DOUBLE then val.asDouble - when TYPE_UUID then val.asUuid - when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.asInt - when TYPE_INT64 then val.asInt64 - when TYPE_MAP - when TYPE_OBJECT - when TYPE_LIST - when TYPE_ARRAY + def _get_result(list, context) + synchronize do + list.each do |item| + @sync_result << item end + @sync_count -= 1 + @cv.signal end + end + + def start_sync(query) + end + + def touch_sync(sync) + end + + def end_sync(sync) + end - def set_attr(name, v) - val = value(name) - case val.getType - when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.setUint(v) - when TYPE_UINT64 then val.setUint64(v) - when TYPE_SSTR, TYPE_LSTR then v ? val.setString(v) : val.setString('') - when TYPE_ABSTIME then val.setInt64(v) - when TYPE_DELTATIME then val.setUint64(v) - when TYPE_REF then val.setObjectId(v.impl) - when TYPE_BOOL then v ? val.setBool(v) : val.setBool(0) - when TYPE_FLOAT then val.setFloat(v) - when TYPE_DOUBLE then val.setDouble(v) - when TYPE_UUID then val.setUuid(v) - when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.setInt(v) - when TYPE_INT64 then val.setInt64(v) - when TYPE_MAP - when TYPE_OBJECT - when TYPE_LIST - when TYPE_ARRAY + def do_console_events() + count = 0 + valid = @impl.getEvent(@event) + while valid + count += 1 + puts "Console Event: #{@event.kind}" + case @event.kind + when Qmfengine::ConsoleEvent::AGENT_ADDED + when Qmfengine::ConsoleEvent::AGENT_DELETED + when Qmfengine::ConsoleEvent::NEW_PACKAGE + when Qmfengine::ConsoleEvent::NEW_CLASS + when Qmfengine::ConsoleEvent::OBJECT_UPDATE + when Qmfengine::ConsoleEvent::EVENT_RECEIVED + when Qmfengine::ConsoleEvent::AGENT_HEARTBEAT + when Qmfengine::ConsoleEvent::METHOD_RESPONSE end + @impl.popEvent + valid = @impl.getEvent(@event) end + return count + end + end - def [](name) - get_attr(name) + class AgentProxy + attr_reader :broker + + def initialize(impl, broker) + @impl = impl + @broker = broker + end + + def label + @impl.getLabel + end + end + + class Broker < ConnectionHandler + include MonitorMixin + attr_reader :impl, :conn + + def initialize(console, conn) + super() + @console = console + @conn = conn + @session = nil + @cv = new_cond + @stable = nil + @event = Qmfengine::BrokerEvent.new + @xmtMessage = Qmfengine::Message.new + @impl = Qmfengine::BrokerProxy.new(@console.impl) + @console.impl.addConnection(@impl, self) + @conn.add_conn_handler(self) + @operational = :true + end + + def shutdown() + @console.impl.delConnection(@impl) + @conn.del_conn_handler(self) + @operational = :false + end + + def waitForStable(timeout = nil) + synchronize do + return if @stable + if timeout + unless @cv.wait(timeout) { @stable } + raise "Timed out waiting for broker connection to become stable" + end + else + while not @stable + @cv.wait + end + end end + end - def []=(name, value) - set_attr(name, value) + def send_query(query, ctx) + @impl.sendQuery(query, ctx) + @conn.kick + end + + def do_broker_events() + count = 0 + valid = @impl.getEvent(@event) + while valid + count += 1 + puts "Broker Event: #{@event.kind}" + case @event.kind + when Qmfengine::BrokerEvent::BROKER_INFO + when Qmfengine::BrokerEvent::DECLARE_QUEUE + @conn.impl.declareQueue(@session.handle, @event.name) + when Qmfengine::BrokerEvent::DELETE_QUEUE + @conn.impl.deleteQueue(@session.handle, @event.name) + when Qmfengine::BrokerEvent::BIND + @conn.impl.bind(@session.handle, @event.exchange, @event.name, @event.bindingKey) + when Qmfengine::BrokerEvent::UNBIND + @conn.impl.unbind(@session.handle, @event.exchange, @event.name, @event.bindingKey) + when Qmfengine::BrokerEvent::SETUP_COMPLETE + @impl.startProtocol + when Qmfengine::BrokerEvent::STABLE + synchronize do + @stable = :true + @cv.signal + end + when Qmfengine::BrokerEvent::QUERY_COMPLETE + result = [] + for idx in 0...@event.queryResponse.getObjectCount + result << ConsoleObject.new(nil, :impl => @event.queryResponse.getObject(idx), :broker => self) + end + @console._get_result(result, @event.context) + when Qmfengine::BrokerEvent::METHOD_RESPONSE + obj = @event.context + obj._method_result(MethodResponse.new(@event.methodResponse)) + end + @impl.popEvent + valid = @impl.getEvent(@event) end + return count + end - def inc_attr(name, by=1) - set_attr(name, get_attr(name) + by) + def do_broker_messages() + count = 0 + valid = @impl.getXmtMessage(@xmtMessage) + while valid + count += 1 + @conn.impl.sendMessage(@session.handle, @xmtMessage) + @impl.popXmt + valid = @impl.getXmtMessage(@xmtMessage) end + return count + end + + def do_events() + begin + ccnt = @console.do_console_events + bcnt = do_broker_events + mcnt = do_broker_messages + end until ccnt == 0 and bcnt == 0 and mcnt == 0 + end + + def conn_event_connected() + puts "Console Connection Established..." + @session = Session.new(@conn, "qmfc-%s.%d" % [Socket.gethostname, Process::pid], self) + @impl.sessionOpened(@session.handle) + do_events + end + + def conn_event_disconnected(error) + puts "Console Connection Lost" + end - def dec_attr(name, by=1) - set_attr(name, get_attr(name) - by) + def conn_event_visit + do_events + end + + def sess_event_session_closed(context, error) + puts "Console Session Lost" + @impl.sessionClosed() + end + + def sess_event_recv(context, message) + puts "Unexpected RECV Event" if not @operational + @impl.handleRcvMessage(message) + do_events + end + end + + ##============================================================================== + ## 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] + else + @agentLabel = label end + @conn = nil + @handler = handler + @impl = Qmfengine::AgentEngine.new(@agentLabel) + @event = Qmfengine::AgentEvent.new + @xmtMessage = Qmfengine::Message.new + end - private - def value(name) - val = @impl.getValue(name.to_s) - if val.nil? - raise ArgumentError, "Attribute '#{name}' not defined for class #{@object_class.impl.getName}" - end - return val + def set_connection(conn) + @conn = conn + @conn.add_conn_handler(self) + end + + def register_class(cls) + @impl.registerClass(cls.impl) + end + + def alloc_object_id(low = 0, high = 0) + ObjectId.new(@impl.allocObjectId(low, high)) + end + + def query_response(context, object) + @impl.queryResponse(context, object.impl) + end + + def query_complete(context) + @impl.queryComplete(context) + end + + def method_response(context, status, text, arguments) + @impl.methodResponse(context, status, text, arguments.map) + end + + def do_agent_events() + count = 0 + valid = @impl.getEvent(@event) + while valid + count += 1 + case @event.kind + when Qmfengine::AgentEvent::GET_QUERY + @handler.get_query(@event.sequence, Query.new(:impl => @event.query), @event.authUserId) + when Qmfengine::AgentEvent::START_SYNC + when Qmfengine::AgentEvent::END_SYNC + when Qmfengine::AgentEvent::METHOD_CALL + args = Arguments.new(@event.arguments) + @handler.method_call(@event.sequence, @event.name, ObjectId.new(@event.objectId), + args, @event.authUserId) + when Qmfengine::AgentEvent::DECLARE_QUEUE + @conn.impl.declareQueue(@session.handle, @event.name) + when Qmfengine::AgentEvent::DELETE_QUEUE + @conn.impl.deleteQueue(@session.handle, @event.name) + when Qmfengine::AgentEvent::BIND + @conn.impl.bind(@session.handle, @event.exchange, @event.name, @event.bindingKey) + when Qmfengine::AgentEvent::UNBIND + @conn.impl.unbind(@session.handle, @event.exchange, @event.name, @event.bindingKey) + when Qmfengine::AgentEvent::SETUP_COMPLETE + @impl.startProtocol() + end + @impl.popEvent + valid = @impl.getEvent(@event) + end + return count + end + + def do_agent_messages() + count = 0 + valid = @impl.getXmtMessage(@xmtMessage) + while valid + count += 1 + @conn.impl.sendMessage(@session.handle, @xmtMessage) + @impl.popXmt + valid = @impl.getXmtMessage(@xmtMessage) end + return count + end + + def do_events() + begin + ecnt = do_agent_events + mcnt = do_agent_messages + end until ecnt == 0 and mcnt == 0 + end + + def conn_event_connected() + puts "Agent Connection Established..." + @session = Session.new(@conn, "qmfa-%s.%d" % [Socket.gethostname, Process::pid], self) + @impl.newSession + do_events + end + + def conn_event_disconnected(error) + puts "Agent Connection Lost" + end + + def conn_event_visit + do_events + end + + def sess_event_session_closed(context, error) + puts "Agent Session Lost" + end + + def sess_event_recv(context, message) + @impl.handleRcvMessage(message) + do_events end + end end diff --git a/qpid/cpp/bindings/qmf/tests/Makefile.am b/qpid/cpp/bindings/qmf/tests/Makefile.am index 1ff8a64bed..182771e16b 100644 --- a/qpid/cpp/bindings/qmf/tests/Makefile.am +++ b/qpid/cpp/bindings/qmf/tests/Makefile.am @@ -18,4 +18,10 @@ # TESTS = run_interop_tests -EXTRA_DIST = run_interop_tests + +EXTRA_DIST = \ + agent_ruby.rb \ + python_agent.py \ + python_console.py \ + ruby_console.rb \ + run_interop_tests diff --git a/qpid/cpp/bindings/qmf/tests/agent_ruby.rb b/qpid/cpp/bindings/qmf/tests/agent_ruby.rb index 0f85c90f37..2f0869ad73 100755 --- a/qpid/cpp/bindings/qmf/tests/agent_ruby.rb +++ b/qpid/cpp/bindings/qmf/tests/agent_ruby.rb @@ -53,7 +53,10 @@ class Model 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)) + @parent_class.add_method(method) + method = Qmf::SchemaMethod.new("probe_userid", :desc => "Return the user-id for this method call") + method.add_argument(Qmf::SchemaArgument.new("userid", Qmf::TYPE_SSTR, :dir => Qmf::DIR_OUT)) @parent_class.add_method(method) @child_class = Qmf::SchemaObjectClass.new("org.apache.qpid.qmf", "child") @@ -69,8 +72,7 @@ end class App < Qmf::AgentHandler def get_query(context, query, userId) -# puts "Query: user=#{userId} context=#{context} class=#{query.class_name} object_num=#{query.object_id.object_num_low if query.object_id}" - #@parent.inc_attr("queryCount") +# puts "Query: user=#{userId} context=#{context} class=#{query.class_name} object_num=#{query.object_id.object_num_low if query.object_id}" if query.class_name == 'parent' @agent.query_response(context, @parent) elsif query.object_id == @parent_oid @@ -90,37 +92,37 @@ class App < Qmf::AgentHandler 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.uint64val = 0x9494949449494949 + @parent.uint32val = 0xa5a55a5a + @parent.uint16val = 0xb66b + @parent.uint8val = 0xc7 - @parent.set_attr("int64val", 1000000000000000000) - @parent.set_attr("int32val", 1000000000) - @parent.set_attr("int16val", 10000) - @parent.set_attr("int8val", 100) + @parent.int64val = 1000000000000000000 + @parent.int32val = 1000000000 + @parent.int16val = 10000 + @parent.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.uint64val = 4 + @parent.uint32val = 5 + @parent.uint16val = 6 + @parent.uint8val = 7 - @parent.set_attr("int64val", 8) - @parent.set_attr("int32val", 9) - @parent.set_attr("int16val", 10) - @parent.set_attr("int8val", 11) + @parent.int64val = 8 + @parent.int32val = 9 + @parent.int16val = 10 + @parent.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.uint64val = 0 + @parent.uint32val = 0 + @parent.uint16val = 0 + @parent.uint8val = 0 - @parent.set_attr("int64val", -10000000000) - @parent.set_attr("int32val", -100000) - @parent.set_attr("int16val", -1000) - @parent.set_attr("int8val", -100) + @parent.int64val = -10000000000 + @parent.int32val = -100000 + @parent.int16val = -1000 + @parent.int8val = -100 else retCode = 1 @@ -132,10 +134,17 @@ class App < Qmf::AgentHandler 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 = Qmf::AgentObject.new(@model.child_class) + @child.name = args.by_key("child_name") @child.set_object_id(oid) @agent.method_response(context, 0, "OK", args) + + elsif name == "probe_userid" + args['userid'] = userId + @agent.method_response(context, 0, "OK", args) + + else + @agent.method_response(context, 1, "Unimplemented Method: #{name}", args) end end @@ -151,19 +160,19 @@ class App < Qmf::AgentHandler @agent.set_connection(@connection) - @parent = Qmf::QmfObject.new(@model.parent_class) - @parent.set_attr("name", "Parent One") - @parent.set_attr("state", "OPERATIONAL") + @parent = Qmf::AgentObject.new(@model.parent_class) + @parent.name = "Parent One" + @parent.state = "OPERATIONAL" - @parent.set_attr("uint64val", 0) - @parent.set_attr("uint32val", 0) - @parent.set_attr("uint16val", 0) - @parent.set_attr("uint8val", 0) + @parent.uint64val = 0 + @parent.uint32val = 0 + @parent.uint16val = 0 + @parent.uint8val = 0 - @parent.set_attr("int64val", 0) - @parent.set_attr("int32val", 0) - @parent.set_attr("int16val", 0) - @parent.set_attr("int8val", 0) + @parent.int64val = 0 + @parent.int32val = 0 + @parent.int16val = 0 + @parent.int8val = 0 @parent_oid = @agent.alloc_object_id(1) @parent.set_object_id(@parent_oid) diff --git a/qpid/cpp/bindings/qmf/tests/python_agent.py b/qpid/cpp/bindings/qmf/tests/python_agent.py new file mode 100644 index 0000000000..d4373d3bb8 --- /dev/null +++ b/qpid/cpp/bindings/qmf/tests/python_agent.py @@ -0,0 +1,194 @@ +#!/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 qmf +import sys +import time + + +class Model: + # attr_reader :parent_class, :child_class + def __init__(self): + self.parent_class = qmf.SchemaObjectClass("org.apache.qpid.qmf", "parent") + self.parent_class.add_property(qmf.SchemaProperty("name", qmf.TYPE_SSTR, {"index":True})) + self.parent_class.add_property(qmf.SchemaProperty("state", qmf.TYPE_SSTR)) + + self.parent_class.add_property(qmf.SchemaProperty("uint64val", qmf.TYPE_UINT64)) + self.parent_class.add_property(qmf.SchemaProperty("uint32val", qmf.TYPE_UINT32)) + self.parent_class.add_property(qmf.SchemaProperty("uint16val", qmf.TYPE_UINT16)) + self.parent_class.add_property(qmf.SchemaProperty("uint8val", qmf.TYPE_UINT8)) + + self.parent_class.add_property(qmf.SchemaProperty("int64val", qmf.TYPE_INT64)) + self.parent_class.add_property(qmf.SchemaProperty("int32val", qmf.TYPE_INT32)) + self.parent_class.add_property(qmf.SchemaProperty("int16val", qmf.TYPE_INT16)) + self.parent_class.add_property(qmf.SchemaProperty("int8val", qmf.TYPE_INT8)) + + self.parent_class.add_statistic(qmf.SchemaStatistic("queryCount", qmf.TYPE_UINT32, {"unit":"query", "desc":"Query count"})) + + _method = qmf.SchemaMethod("echo", {"desc":"Check responsiveness of the agent object"}) + _method.add_argument(qmf.SchemaArgument("sequence", qmf.TYPE_UINT32, {"dir":qmf.DIR_IN_OUT})) + self.parent_class.add_method(_method) + + _method = qmf.SchemaMethod("set_numerics", {"desc":"Set the numeric values in the object"}) + _method.add_argument(qmf.SchemaArgument("test", qmf.TYPE_SSTR, {"dir":qmf.DIR_IN})) + self.parent_class.add_method(_method) + + _method = qmf.SchemaMethod("create_child", {"desc":"Create a new child object"}) + _method.add_argument(qmf.SchemaArgument("child_name", qmf.TYPE_LSTR, {"dir":qmf.DIR_IN})) + _method.add_argument(qmf.SchemaArgument("child_ref", qmf.TYPE_REF, {"dir":qmf.DIR_OUT})) + self.parent_class.add_method(_method) + + _method = qmf.SchemaMethod("probe_userid", {"desc":"Return the user-id for this method call"}) + _method.add_argument(qmf.SchemaArgument("userid", qmf.TYPE_SSTR, {"dir":qmf.DIR_OUT})) + self.parent_class.add_method(_method) + + self.child_class = qmf.SchemaObjectClass("org.apache.qpid.qmf", "child") + self.child_class.add_property(qmf.SchemaProperty("name", qmf.TYPE_SSTR, {"index":True})) + + + def register(self, agent): + agent.register_class(self.parent_class) + agent.register_class(self.child_class) + + + +class App(qmf.AgentHandler): + def get_query(self, context, query, userId): + #print "Query: user=%s context=%d class=%s" % (userId, context, query.class_name()) + #if query.object_id(): + # print query.object_id().object_num_low() + self._parent.inc_attr("queryCount") + if query.class_name() == 'parent': + self._agent.query_response(context, self._parent) + elif query.object_id() == self._parent_oid: + self._agent.query_response(context, self._parent) + self._agent.query_complete(context) + + + def method_call(self, 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 = self._agent.alloc_object_id(2) + # args['child_ref'] = oid + # self._child = qmf.QmfObject(self._model.child_class) + # self._child.set_attr("name", args.by_key("child_name")) + # self._child.set_object_id(oid) + # self._agent.method_response(context, 0, "OK", args) + if name == "echo": + self._agent.method_response(context, 0, "OK", args) + + elif name == "set_numerics": + _retCode = 0 + _retText = "OK" + + if args['test'] == "big": + self._parent.set_attr("uint64val", 0x9494949449494949) + self._parent.set_attr("uint32val", 0xa5a55a5a) + self._parent.set_attr("uint16val", 0xb66b) + self._parent.set_attr("uint8val", 0xc7) + + self._parent.set_attr("int64val", 1000000000000000000) + self._parent.set_attr("int32val", 1000000000) + self._parent.set_attr("int16val", 10000) + self._parent.set_attr("int8val", 100) + + elif args['test'] == "small": + self._parent.set_attr("uint64val", 4) + self._parent.set_attr("uint32val", 5) + self._parent.set_attr("uint16val", 6) + self._parent.set_attr("uint8val", 7) + + self._parent.set_attr("int64val", 8) + self._parent.set_attr("int32val", 9) + self._parent.set_attr("int16val", 10) + self._parent.set_attr("int8val", 11) + + elif args['test'] == "negative": + self._parent.set_attr("uint64val", 0) + self._parent.set_attr("uint32val", 0) + self._parent.set_attr("uint16val", 0) + self._parent.set_attr("uint8val", 0) + + self._parent.set_attr("int64val", -10000000000) + self._parent.set_attr("int32val", -100000) + self._parent.set_attr("int16val", -1000) + self._parent.set_attr("int8val", -100) + + else: + _retCode = 1 + _retText = "Invalid argument value for test" + + self._agent.method_response(context, _retCode, _retText, args) + + elif name == "create_child": + _oid = self._agent.alloc_object_id(2) + args['child_ref'] = _oid + self._child = qmf.QmfObject(self._model.child_class) + self._child.set_attr("name", args["child_name"]) + self._child.set_object_id(_oid) + self._agent.method_response(context, 0, "OK", args) + + elif name == "probe_userid": + args['userid'] = userId + self._agent.method_response(context, 0, "OK", args) + + else: + self._agent.method_response(context, 1, "Unimplemented Method: %s" % name, args) + + + def main(self): + self._settings = qmf.ConnectionSettings() + if len(sys.argv) > 1: + self._settings.set_attr("host", sys.argv[1]) + if len(sys.argv) > 2: + self._settings.set_attr("port", int(sys.argv[2])) + self._connection = qmf.Connection(self._settings) + self._agent = qmf.Agent(self) + + self._model = Model() + self._model.register(self._agent) + + self._agent.set_connection(self._connection) + + self._parent = qmf.QmfObject(self._model.parent_class) + self._parent.set_attr("name", "Parent One") + self._parent.set_attr("state", "OPERATIONAL") + + self._parent.set_attr("uint64val", 0) + self._parent.set_attr("uint32val", 0) + self._parent.set_attr("uint16val", 0) + self._parent.set_attr("uint8val", 0) + + self._parent.set_attr("int64val", 0) + self._parent.set_attr("int32val", 0) + self._parent.set_attr("int16val", 0) + self._parent.set_attr("int8val", 0) + + self._parent_oid = self._agent.alloc_object_id(1) + self._parent.set_object_id(self._parent_oid) + + while True: # there may be a better way, but + time.sleep(1000) # I'm a python noob... + + + +app = App() +app.main() + diff --git a/qpid/cpp/bindings/qmf/tests/python_console.py b/qpid/cpp/bindings/qmf/tests/python_console.py index 365f2ac33a..bcd3063fe3 100755 --- a/qpid/cpp/bindings/qmf/tests/python_console.py +++ b/qpid/cpp/bindings/qmf/tests/python_console.py @@ -128,6 +128,18 @@ class QmfInteropTests(TestBase010): self.assertEqual(parent.int16val, -1000) self.assertEqual(parent.int8val, -100) + def test_D_userid_for_method(self): + self.startQmf(); + qmf = self.qmf + + parents = qmf.getObjects(_class="parent") + self.assertEqual(len(parents), 1) + parent = parents[0] + + result = parent.probe_userid() + self.assertEqual(result.status, 0) + self.assertEqual(result.userid, "guest") + def getProperty(self, msg, name): for h in msg.headers: if hasattr(h, name): return getattr(h, name) diff --git a/qpid/cpp/bindings/qmf/tests/ruby_console.rb b/qpid/cpp/bindings/qmf/tests/ruby_console.rb new file mode 100755 index 0000000000..2f8f633d16 --- /dev/null +++ b/qpid/cpp/bindings/qmf/tests/ruby_console.rb @@ -0,0 +1,133 @@ +#!/usr/bin/ruby + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'qmf' +require 'socket' + +class App < Qmf::ConsoleHandler + + def dump_schema + packages = @qmfc.get_packages + puts "----- Packages -----" + packages.each do |p| + puts p + puts " ----- Object Classes -----" + classes = @qmfc.get_classes(p) + classes.each do |c| + puts " #{c.name}" + + puts " ---- Properties ----" + props = c.properties + props.each do |prop| + puts " #{prop.name}" + end + + puts " ---- Statistics ----" + stats = c.statistics + stats.each do |stat| + puts " #{stat.name}" + end + + puts " ---- Methods ----" + methods = c.methods + methods.each do |method| + puts " #{method.name}" + puts " ---- Args ----" + args = method.arguments + args.each do |arg| + puts " #{arg.name}" + end + end + end + + puts " ----- Event Classes -----" + classes = @qmfc.get_classes(p, Qmf::CLASS_EVENT) + classes.each do |c| + puts " #{c.name}" + puts " ---- Args ----" + args = c.arguments + args.each do |arg| + puts " #{arg.name}" + end + end + end + puts "-----" + end + + def main + @settings = Qmf::ConnectionSettings.new + @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) + @qmfc = Qmf::Console.new + + @broker = @qmfc.add_connection(@connection) + @broker.waitForStable + + dump_schema + + agents = @qmfc.get_agents() + puts "---- Agents ----" + agents.each do |a| + puts " => #{a.label}" + end + puts "----" + + for idx in 0...20 + blist = @qmfc.get_objects(Qmf::Query.new(:class => "broker")) + puts "---- Brokers ----" + blist.each do |b| + puts " ---- Broker ----" + puts " systemRef: #{b.systemRef}" + puts " port : #{b.port}" + puts " uptime : #{b.uptime / 1000000000}" + + for rep in 0...0 + puts " Pinging..." + ret = b.echo(45, 'text string') + puts " ret=#{ret}" + end + end + puts "----" + + qlist = @qmfc.get_objects(Qmf::Query.new(:package => "org.apache.qpid.broker", + :class => "queue")) + puts "---- Queues ----" + qlist.each do |q| + puts " ---- Queue ----" + puts " name : #{q.name}" + end + puts "----" + sleep(5) + end + + sleep(5) + puts "Deleting connection..." + @qmfc.del_connection(@broker) + puts " done" + sleep + end +end + +app = App.new +app.main + + diff --git a/qpid/cpp/bindings/qmf/tests/run_interop_tests b/qpid/cpp/bindings/qmf/tests/run_interop_tests index e6fc872dbb..01d7221ac6 100755 --- a/qpid/cpp/bindings/qmf/tests/run_interop_tests +++ b/qpid/cpp/bindings/qmf/tests/run_interop_tests @@ -28,6 +28,9 @@ BROKER_DIR=${BUILD_DIR}/src API_DIR=${BUILD_DIR}/bindings/qmf SPEC_DIR=${QPID_DIR}/specs +RUBY_LIB_DIR=${API_DIR}/ruby/.libs +PYTHON_LIB_DIR=${API_DIR}/python/.libs + trap stop_broker INT TERM QUIT start_broker() { @@ -41,7 +44,7 @@ stop_broker() { } start_ruby_agent() { - ruby -I${MY_DIR}/../ruby -I${API_DIR}/ruby/.libs ${MY_DIR}/agent_ruby.rb localhost $BROKER_PORT & + ruby -I${MY_DIR}/../ruby -I${RUBY_LIB_DIR} ${MY_DIR}/agent_ruby.rb localhost $BROKER_PORT & AGENT_PID=$! } @@ -49,19 +52,62 @@ stop_ruby_agent() { kill $AGENT_PID } +start_python_agent() { + PYTHONPATH="${MY_DIR}/../python:${API_DIR}/python:${PYTHON_LIB_DIR}" python ${MY_DIR}/python_agent.py localhost $BROKER_PORT & + PY_AGENT_PID=$! +} + +stop_python_agent() { + kill $PY_AGENT_PID +} + +TESTS_FAILED=0 + if test -d ${PYTHON_DIR} ; then start_broker echo "Running qmf interop tests using broker on port $BROKER_PORT" PYTHONPATH=${PYTHON_DIR}:${MY_DIR} export PYTHONPATH - echo " Ruby Agent vs. Pure-Python Console" - start_ruby_agent - echo " Ruby agent started at pid $AGENT_PID" - ${PYTHON_DIR}/qpid-python-test -m python_console -b localhost:$BROKER_PORT $@ - RETCODE=$? - stop_ruby_agent + + if test -d ${PYTHON_LIB_DIR} ; then + echo " Python Agent (external storage) vs. Pure-Python Console" + start_python_agent + echo " Python agent started at pid $PY_AGENT_PID" + ${PYTHON_DIR}/qpid-python-test -m python_console -b localhost:$BROKER_PORT $@ + RETCODE=$? + stop_python_agent + if test x$RETCODE != x0; then + echo "FAIL qmf interop tests (Python Agent)"; + TESTS_FAILED=1 + fi + fi + + if test -d ${RUBY_LIB_DIR} ; then + echo " Ruby Agent (external storage) vs. Pure-Python Console" + start_ruby_agent + echo " Ruby agent started at pid $AGENT_PID" + ${PYTHON_DIR}/qpid-python-test -m python_console -b localhost:$BROKER_PORT $@ + RETCODE=$? + stop_ruby_agent + if test x$RETCODE != x0; then + echo "FAIL qmf interop tests (Ruby Agent)"; + TESTS_FAILED=1 + fi + fi + + # Also against the Pure-Python console: + # Ruby agent (internal storage) + # Python agent (external and internal) + # C++ agent (external and internal) + # + # Other consoles against the same set of agents: + # Wrapped Python console + # Ruby console + # C++ console + stop_broker - if test x$RETCODE != x0; then - echo "FAIL qmf interop tests"; exit 1; + if test x$TESTS_FAILED != x0; then + echo "TEST FAILED!" + exit 1 fi fi diff --git a/qpid/cpp/configure.ac b/qpid/cpp/configure.ac index a4e45ad19b..7eb1e99159 100644 --- a/qpid/cpp/configure.ac +++ b/qpid/cpp/configure.ac @@ -515,6 +515,7 @@ AC_CONFIG_FILES([ examples/messaging/Makefile bindings/qmf/Makefile bindings/qmf/ruby/Makefile + bindings/qmf/python/Makefile bindings/qmf/tests/Makefile managementgen/Makefile etc/Makefile diff --git a/qpid/cpp/examples/messaging/server.cpp b/qpid/cpp/examples/messaging/server.cpp index 6f0f2af02d..38f4601ff6 100644 --- a/qpid/cpp/examples/messaging/server.cpp +++ b/qpid/cpp/examples/messaging/server.cpp @@ -44,9 +44,7 @@ int main(int argc, char** argv) { try { Connection connection = Connection::open(url); Session session = connection.newSession(); - VariantMap options; - options["auto_acknowledge"] = 0; - Receiver receiver = session.createReceiver("service_queue", options); + Receiver receiver = session.createReceiver("service_queue"); while (true) { Message request = receiver.fetch(); diff --git a/qpid/cpp/include/qmf/QmfImportExport.h b/qpid/cpp/include/qmf/QmfImportExport.h index 8353a3cc16..f5e1d9127c 100644 --- a/qpid/cpp/include/qmf/QmfImportExport.h +++ b/qpid/cpp/include/qmf/QmfImportExport.h @@ -21,7 +21,7 @@ */ #if defined(WIN32) && !defined(QPID_DECLARE_STATIC) -# if defined(QMF_EXPORT) || defined (qmf_EXPORTS) +# if defined(QMF_EXPORT) || defined (qmfcommon_EXPORTS) # define QMF_EXTERN __declspec(dllexport) # else # define QMF_EXTERN __declspec(dllimport) diff --git a/qpid/cpp/include/qpid/framing/FieldTable.h b/qpid/cpp/include/qpid/framing/FieldTable.h index b2331cd4e1..fd09cfc6f6 100644 --- a/qpid/cpp/include/qpid/framing/FieldTable.h +++ b/qpid/cpp/include/qpid/framing/FieldTable.h @@ -52,6 +52,7 @@ class FieldTable typedef std::map<std::string, ValuePtr> ValueMap; typedef ValueMap::iterator iterator; typedef ValueMap::const_reference const_reference; + typedef ValueMap::reference reference; typedef ValueMap::value_type value_type; QPID_COMMON_EXTERN FieldTable() {}; @@ -108,7 +109,7 @@ class FieldTable 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&); + QPID_COMMON_EXTERN ValueMap::iterator insert(ValueMap::iterator, const ValueMap::value_type&); void clear() { values.clear(); } // ### Hack Alert diff --git a/qpid/cpp/include/qpid/framing/List.h b/qpid/cpp/include/qpid/framing/List.h index cb1129ebf8..0f17c7884c 100644 --- a/qpid/cpp/include/qpid/framing/List.h +++ b/qpid/cpp/include/qpid/framing/List.h @@ -44,6 +44,7 @@ class List typedef Values::const_iterator const_iterator; typedef Values::iterator iterator; typedef Values::const_reference const_reference; + typedef Values::reference reference; QPID_COMMON_EXTERN uint32_t encodedSize() const; QPID_COMMON_EXTERN void encode(Buffer& buffer) const; diff --git a/qpid/cpp/include/qpid/framing/Uuid.h b/qpid/cpp/include/qpid/framing/Uuid.h index 0dfa7a58e7..618515622d 100644 --- a/qpid/cpp/include/qpid/framing/Uuid.h +++ b/qpid/cpp/include/qpid/framing/Uuid.h @@ -20,7 +20,6 @@ */ #include "qpid/CommonImportExport.h" -#include "qpid/sys/uuid.h" #include "qpid/sys/IntegerTypes.h" #include <boost/array.hpp> @@ -38,33 +37,31 @@ class Buffer; * * Full value semantics, operators ==, < etc. are provided by * boost::array so Uuid can be the key type in a map etc. + * + * TODO: change this implementation as it leaks boost into the + * client API */ struct Uuid : public boost::array<uint8_t, 16> { /** If unique is true, generate a unique ID else a null ID. */ - Uuid(bool unique=false) { if (unique) generate(); else clear(); } + QPID_COMMON_EXTERN Uuid(bool unique=false); /** Copy from 16 bytes of data. */ - Uuid(const uint8_t* data) { assign(data); } + QPID_COMMON_EXTERN Uuid(const uint8_t* data); + + // Default op= and copy ctor are fine. + // boost::array gives us ==, < etc. /** Copy from 16 bytes of data. */ - void assign(const uint8_t* data) { - uuid_copy(c_array(), data); - } + void assign(const uint8_t* data); /** Set to a new unique identifier. */ - void generate() { uuid_generate(c_array()); } + QPID_COMMON_EXTERN void generate(); /** Set to all zeros. */ - void clear() { uuid_clear(c_array()); } + void clear(); /** Test for null (all zeros). */ - // Force int 0/!0 to false/true; avoids compile warnings. - bool isNull() { - return !!uuid_is_null(data()); - } - - // Default op= and copy ctor are fine. - // boost::array gives us ==, < etc. + bool isNull(); QPID_COMMON_EXTERN void encode(framing::Buffer& buf) const; QPID_COMMON_EXTERN void decode(framing::Buffer& buf); diff --git a/qpid/cpp/include/qpid/management/Manageable.h b/qpid/cpp/include/qpid/management/Manageable.h index 8062479ac6..7a72cc1592 100644 --- a/qpid/cpp/include/qpid/management/Manageable.h +++ b/qpid/cpp/include/qpid/management/Manageable.h @@ -43,7 +43,7 @@ class QPID_COMMON_EXTERN Manageable static const status_t STATUS_UNKNOWN_OBJECT = 1; static const status_t STATUS_UNKNOWN_METHOD = 2; static const status_t STATUS_NOT_IMPLEMENTED = 3; - static const status_t STATUS_INVALID_PARAMETER = 4; + static const status_t STATUS_PARAMETER_INVALID = 4; static const status_t STATUS_FEATURE_NOT_IMPLEMENTED = 5; static const status_t STATUS_FORBIDDEN = 6; static const status_t STATUS_EXCEPTION = 7; diff --git a/qpid/cpp/include/qpid/messaging/Connection.h b/qpid/cpp/include/qpid/messaging/Connection.h index 5517e45af9..19dae586a4 100644 --- a/qpid/cpp/include/qpid/messaging/Connection.h +++ b/qpid/cpp/include/qpid/messaging/Connection.h @@ -41,7 +41,7 @@ class Session; class Connection : public qpid::client::Handle<ConnectionImpl> { public: - static Connection open(const std::string& url, const Variant::Map& options = Variant::Map()); + static QPID_CLIENT_EXTERN 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&); diff --git a/qpid/cpp/include/qpid/messaging/Message.h b/qpid/cpp/include/qpid/messaging/Message.h index 39edae3637..e68d8a1141 100644 --- a/qpid/cpp/include/qpid/messaging/Message.h +++ b/qpid/cpp/include/qpid/messaging/Message.h @@ -33,9 +33,9 @@ namespace client { namespace messaging { -class Address; +struct Address; class Codec; -class MessageImpl; +struct MessageImpl; /** * Representation of a message. @@ -79,11 +79,9 @@ class Message 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; + friend struct MessageImplAccess; }; }} // namespace qpid::messaging diff --git a/qpid/cpp/include/qpid/messaging/Receiver.h b/qpid/cpp/include/qpid/messaging/Receiver.h index e51e1093d1..a4fdd7a34b 100644 --- a/qpid/cpp/include/qpid/messaging/Receiver.h +++ b/qpid/cpp/include/qpid/messaging/Receiver.h @@ -40,7 +40,7 @@ class MessageListener; class ReceiverImpl; /** - * A pull style interface for message retrieval. + * Interface through which messages are received. */ class Receiver : public qpid::client::Handle<ReceiverImpl> { @@ -75,7 +75,7 @@ class Receiver : public qpid::client::Handle<ReceiverImpl> 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 + * for up to 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. @@ -87,8 +87,8 @@ class Receiver : public qpid::client::Handle<ReceiverImpl> */ QPID_CLIENT_EXTERN void start(); /** - * Stops the message flow for this receiver (without actually - * cancelling the subscription). + * Stops the message flow for this receiver (but does not cancel + * the subscription). */ QPID_CLIENT_EXTERN void stop(); /** @@ -97,14 +97,35 @@ class Receiver : public qpid::client::Handle<ReceiverImpl> * requested by a client via fetch() (or pushed to a listener). */ QPID_CLIENT_EXTERN void setCapacity(uint32_t); + /** + * Returns the capacity of 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 uint32_t getCapacity(); + /** + * Returns the number of messages received and waiting to be + * fetched. + */ + QPID_CLIENT_EXTERN uint32_t available(); + /** + * Returns a count of the number of messages received on this + * receiver that have been acknowledged, but for which that + * acknowledgement has not yet been confirmed as processed by the + * server. + */ + QPID_CLIENT_EXTERN uint32_t pendingAck(); /** - * Cancels this receiver + * Cancels this receiver. */ QPID_CLIENT_EXTERN void cancel(); /** - * Set a message listener for receiving messages asynchronously. + * Set a message listener for this receiver. + * + * @see Session::dispatch() */ QPID_CLIENT_EXTERN void setListener(MessageListener* listener); private: diff --git a/qpid/cpp/include/qpid/messaging/Sender.h b/qpid/cpp/include/qpid/messaging/Sender.h index 657c4b8cfe..9b83a04d60 100644 --- a/qpid/cpp/include/qpid/messaging/Sender.h +++ b/qpid/cpp/include/qpid/messaging/Sender.h @@ -23,6 +23,7 @@ */ #include "qpid/client/ClientImportExport.h" #include "qpid/client/Handle.h" +#include "qpid/sys/IntegerTypes.h" namespace qpid { namespace client { @@ -47,8 +48,26 @@ class Sender : public qpid::client::Handle<SenderImpl> QPID_CLIENT_EXTERN ~Sender(); QPID_CLIENT_EXTERN Sender& operator=(const Sender&); - QPID_CLIENT_EXTERN void send(Message& message); + QPID_CLIENT_EXTERN void send(const Message& message); QPID_CLIENT_EXTERN void cancel(); + + /** + * Sets the capacity for the sender. The capacity determines how + * many outgoing messages can be held pending confirmation of + * receipt by the broker. + */ + QPID_CLIENT_EXTERN void setCapacity(uint32_t); + /** + * Returns the capacity of the sender. + * @see setCapacity + */ + QPID_CLIENT_EXTERN uint32_t getCapacity(); + /** + * Returns the number of sent messages pending confirmation of + * receipt by the broker. (These are the 'in-doubt' messages). + */ + QPID_CLIENT_EXTERN uint32_t pending(); + private: friend class qpid::client::PrivateImplRef<Sender>; }; diff --git a/qpid/cpp/include/qpid/messaging/Session.h b/qpid/cpp/include/qpid/messaging/Session.h index 3a354c009f..979e27adae 100644 --- a/qpid/cpp/include/qpid/messaging/Session.h +++ b/qpid/cpp/include/qpid/messaging/Session.h @@ -35,8 +35,8 @@ template <class> class PrivateImplRef; namespace messaging { -class Address; -class Filter; +struct Address; +struct Filter; class Message; class MessageListener; class Sender; @@ -75,6 +75,17 @@ class Session : public qpid::client::Handle<SessionImpl> QPID_CLIENT_EXTERN void sync(); QPID_CLIENT_EXTERN void flush(); + /** + * Returns the number of messages received and waiting to be + * fetched. + */ + QPID_CLIENT_EXTERN uint32_t available(); + /** + * Returns a count of the number of messages received this session + * that have been acknowledged, but for which that acknowledgement + * has not yet been confirmed as processed by the server. + */ + QPID_CLIENT_EXTERN uint32_t pendingAck(); 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); @@ -88,9 +99,6 @@ class Session : public qpid::client::Handle<SessionImpl> 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>; }; diff --git a/qpid/cpp/include/qpid/messaging/Variant.h b/qpid/cpp/include/qpid/messaging/Variant.h index ac000244c2..1e51914794 100644 --- a/qpid/cpp/include/qpid/messaging/Variant.h +++ b/qpid/cpp/include/qpid/messaging/Variant.h @@ -44,21 +44,21 @@ struct InvalidConversion : public qpid::Exception }; enum VariantType { - VOID = 0, - BOOL, - UINT8, - UINT16, - UINT32, - UINT64, - INT8, - INT16, - INT32, - INT64, - FLOAT, - DOUBLE, - STRING, - MAP, - LIST + VAR_VOID = 0, + VAR_BOOL, + VAR_UINT8, + VAR_UINT16, + VAR_UINT32, + VAR_UINT64, + VAR_INT8, + VAR_INT16, + VAR_INT32, + VAR_INT64, + VAR_FLOAT, + VAR_DOUBLE, + VAR_STRING, + VAR_MAP, + VAR_LIST }; class VariantImpl; diff --git a/qpid/cpp/include/qpid/sys/windows/Condition.h b/qpid/cpp/include/qpid/sys/windows/Condition.h index c31f7b4823..979fae9b0a 100755 --- a/qpid/cpp/include/qpid/sys/windows/Condition.h +++ b/qpid/cpp/include/qpid/sys/windows/Condition.h @@ -30,7 +30,6 @@ #include <boost/thread/condition.hpp> #include <boost/thread/thread_time.hpp> #include <windows.h> -#undef STATUS_INVALID_PARAMETER // Hack for windows.h namespace pollution namespace qpid { namespace sys { diff --git a/qpid/cpp/include/qpid/sys/windows/Mutex.h b/qpid/cpp/include/qpid/sys/windows/Mutex.h index 12768640d5..5dcc69e836 100755 --- a/qpid/cpp/include/qpid/sys/windows/Mutex.h +++ b/qpid/cpp/include/qpid/sys/windows/Mutex.h @@ -31,7 +31,6 @@ #include <boost/thread/shared_mutex.hpp> #include <boost/thread/thread_time.hpp> #include <boost/thread/tss.hpp> -#undef STATUS_INVALID_PARAMETER // Hack for windows.h namespace pollution namespace qpid { namespace sys { diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt index 7456401618..786facced9 100644 --- a/qpid/cpp/src/CMakeLists.txt +++ b/qpid/cpp/src/CMakeLists.txt @@ -528,6 +528,8 @@ set (libqpidclient_SOURCES qpid/messaging/ConnectionImpl.h qpid/messaging/Filter.cpp qpid/messaging/Message.cpp + qpid/messaging/MessageImpl.h + qpid/messaging/MessageImpl.cpp qpid/messaging/Receiver.cpp qpid/messaging/ReceiverImpl.h qpid/messaging/Session.cpp @@ -535,17 +537,20 @@ set (libqpidclient_SOURCES qpid/messaging/Sender.cpp qpid/messaging/SenderImpl.h qpid/messaging/Variant.cpp + qpid/client/amqp0_10/AcceptTracker.h + qpid/client/amqp0_10/AcceptTracker.cpp qpid/client/amqp0_10/AddressResolution.h qpid/client/amqp0_10/AddressResolution.cpp qpid/client/amqp0_10/Codecs.cpp - qpid/client/amqp0_10/CompletionTracker.h - qpid/client/amqp0_10/CompletionTracker.cpp + qpid/client/amqp0_10/CodecsInternal.h qpid/client/amqp0_10/ConnectionImpl.h qpid/client/amqp0_10/ConnectionImpl.cpp qpid/client/amqp0_10/IncomingMessages.h qpid/client/amqp0_10/IncomingMessages.cpp qpid/client/amqp0_10/MessageSink.h qpid/client/amqp0_10/MessageSource.h + qpid/client/amqp0_10/OutgoingMessage.h + qpid/client/amqp0_10/OutgoingMessage.cpp qpid/client/amqp0_10/ReceiverImpl.h qpid/client/amqp0_10/ReceiverImpl.cpp qpid/client/amqp0_10/SessionImpl.h diff --git a/qpid/cpp/src/Makefile.am b/qpid/cpp/src/Makefile.am index 05b5efc5b5..4988f3f031 100644 --- a/qpid/cpp/src/Makefile.am +++ b/qpid/cpp/src/Makefile.am @@ -54,7 +54,7 @@ windows_dist = \ qpid/sys/windows/Time.cpp \ ../include/qpid/sys/windows/Time.h \ qpid/sys/windows/uuid.cpp \ - ../include/qpid/sys/windows/uuid.h \ + qpid/sys/windows/uuid.h \ windows/QpiddBroker.cpp \ qpid/broker/windows/BrokerDefaults.cpp \ qpid/broker/windows/SaslAuthenticator.cpp @@ -461,7 +461,8 @@ libqpidcommon_la_SOURCES += \ qpid/sys/Timer.cpp \ qpid/sys/Timer.h \ qpid/sys/Waitable.h \ - qpid/sys/alloca.h + qpid/sys/alloca.h \ + qpid/sys/uuid.h if HAVE_SASL libqpidcommon_la_SOURCES += qpid/sys/cyrus/CyrusSecurityLayer.h @@ -686,6 +687,8 @@ libqpidclient_la_SOURCES = \ qpid/messaging/Connection.cpp \ qpid/messaging/Filter.cpp \ qpid/messaging/Message.cpp \ + qpid/messaging/MessageImpl.h \ + qpid/messaging/MessageImpl.cpp \ qpid/messaging/Sender.cpp \ qpid/messaging/Receiver.cpp \ qpid/messaging/Session.cpp \ @@ -694,17 +697,20 @@ libqpidclient_la_SOURCES = \ qpid/messaging/SenderImpl.h \ qpid/messaging/ReceiverImpl.h \ qpid/messaging/SessionImpl.h \ + qpid/client/amqp0_10/AcceptTracker.h \ + qpid/client/amqp0_10/AcceptTracker.cpp \ qpid/client/amqp0_10/AddressResolution.h \ qpid/client/amqp0_10/AddressResolution.cpp \ qpid/client/amqp0_10/Codecs.cpp \ + qpid/client/amqp0_10/CodecsInternal.h \ 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/OutgoingMessage.h \ + qpid/client/amqp0_10/OutgoingMessage.cpp \ qpid/client/amqp0_10/ReceiverImpl.h \ qpid/client/amqp0_10/ReceiverImpl.cpp \ qpid/client/amqp0_10/SessionImpl.h \ @@ -780,7 +786,6 @@ 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/messaging/Address.h \ ../include/qpid/messaging/Connection.h \ ../include/qpid/messaging/Codec.h \ diff --git a/qpid/cpp/src/qmf.mk b/qpid/cpp/src/qmf.mk index 9d24709b6e..65caaedd5c 100644 --- a/qpid/cpp/src/qmf.mk +++ b/qpid/cpp/src/qmf.mk @@ -35,8 +35,12 @@ nobase_include_HEADERS += \ ../include/qmf/AgentObject.h libqmfcommon_la_SOURCES = \ + qmf/BrokerProxyImpl.cpp \ + qmf/BrokerProxyImpl.h \ qmf/ConnectionSettingsImpl.cpp \ qmf/ConnectionSettingsImpl.h \ + qmf/ConsoleEngineImpl.cpp \ + qmf/ConsoleEngineImpl.h \ qmf/ConsoleEngine.h \ qmf/Event.h \ qmf/Message.h \ @@ -48,11 +52,15 @@ libqmfcommon_la_SOURCES = \ qmf/ObjectIdImpl.h \ qmf/ObjectImpl.cpp \ qmf/ObjectImpl.h \ + qmf/Protocol.cpp \ + qmf/Protocol.h \ qmf/Query.h \ qmf/QueryImpl.cpp \ qmf/QueryImpl.h \ qmf/ResilientConnection.cpp \ qmf/ResilientConnection.h \ + qmf/SequenceManager.cpp \ + qmf/SequenceManager.h \ qmf/Schema.h \ qmf/SchemaImpl.cpp \ qmf/SchemaImpl.h \ diff --git a/qpid/cpp/src/qmf/AgentEngine.cpp b/qpid/cpp/src/qmf/AgentEngine.cpp index bef8b3d102..9ea3be5907 100644 --- a/qpid/cpp/src/qmf/AgentEngine.cpp +++ b/qpid/cpp/src/qmf/AgentEngine.cpp @@ -25,6 +25,7 @@ #include "qmf/ObjectIdImpl.h" #include "qmf/QueryImpl.h" #include "qmf/ValueImpl.h" +#include "qmf/Protocol.h" #include <qpid/framing/Buffer.h> #include <qpid/framing/Uuid.h> #include <qpid/framing/FieldTable.h> @@ -56,7 +57,7 @@ namespace qmf { string name; Object* object; boost::shared_ptr<ObjectId> objectId; - Query query; + boost::shared_ptr<Query> query; boost::shared_ptr<Value> arguments; string exchange; string bindingKey; @@ -85,9 +86,9 @@ namespace qmf { void setStoreDir(const char* path); void setTransferDir(const char* path); void handleRcvMessage(Message& message); - bool getXmtMessage(Message& item); + bool getXmtMessage(Message& item) const; void popXmt(); - bool getEvent(AgentEvent& event); + bool getEvent(AgentEvent& event) const; void popEvent(); void newSession(); void startProtocol(); @@ -103,7 +104,7 @@ namespace qmf { void raiseEvent(Event& event); private: - Mutex lock; + mutable Mutex lock; Mutex addLock; string label; string queueName; @@ -134,13 +135,13 @@ namespace qmf { # define MA_BUFFER_SIZE 65536 char outputBuffer[MA_BUFFER_SIZE]; - struct SchemaClassKey { + struct AgentClassKey { string name; uint8_t hash[16]; - SchemaClassKey(const string& n, const uint8_t* h) : name(n) { + AgentClassKey(const string& n, const uint8_t* h) : name(n) { memcpy(hash, h, 16); } - SchemaClassKey(Buffer& buffer) { + AgentClassKey(Buffer& buffer) { buffer.getShortString(name); buffer.getBin128(hash); } @@ -149,8 +150,8 @@ namespace qmf { } }; - struct SchemaClassKeyComp { - bool operator() (const SchemaClassKey& lhs, const SchemaClassKey& rhs) const + struct AgentClassKeyComp { + bool operator() (const AgentClassKey& lhs, const AgentClassKey& rhs) const { if (lhs.name != rhs.name) return lhs.name < rhs.name; @@ -162,8 +163,8 @@ namespace qmf { } }; - typedef map<SchemaClassKey, SchemaObjectClassImpl*, SchemaClassKeyComp> ObjectClassMap; - typedef map<SchemaClassKey, SchemaEventClassImpl*, SchemaClassKeyComp> EventClassMap; + typedef map<AgentClassKey, SchemaObjectClassImpl*, AgentClassKeyComp> ObjectClassMap; + typedef map<AgentClassKey, SchemaEventClassImpl*, AgentClassKeyComp> EventClassMap; struct ClassMaps { ObjectClassMap objectClasses; @@ -172,8 +173,6 @@ namespace qmf { map<string, ClassMaps> packages; - bool checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq); - void encodeHeader(Buffer& buf, uint8_t opcode, uint32_t seq = 0); AgentEventImpl::Ptr eventDeclareQueue(const string& queueName); AgentEventImpl::Ptr eventBind(const string& exchange, const string& queue, const string& key); AgentEventImpl::Ptr eventSetupComplete(); @@ -185,7 +184,7 @@ namespace qmf { void sendBufferLH(Buffer& buf, const string& destination, const string& routingKey); void sendPackageIndicationLH(const string& packageName); - void sendClassIndicationLH(ClassKind kind, const string& packageName, const SchemaClassKey& key); + void sendClassIndicationLH(ClassKind kind, const string& packageName, const AgentClassKey& key); void sendCommandCompleteLH(const string& exchange, const string& key, uint32_t seq, uint32_t code = 0, const string& text = "OK"); void sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t code, const string& text=""); @@ -215,7 +214,7 @@ AgentEvent AgentEventImpl::copy() item.sequence = sequence; item.object = object; item.objectId = objectId.get(); - item.query = &query; + item.query = query.get(); item.arguments = arguments.get(); item.objectClass = objectClass; @@ -268,16 +267,20 @@ void AgentEngineImpl::handleRcvMessage(Message& message) string replyToKey(message.replyKey ? message.replyKey : ""); string userId(message.userId ? message.userId : ""); - if (checkHeader(inBuffer, &opcode, &sequence)) { - if (opcode == 'a') handleAttachResponse(inBuffer); - else if (opcode == 'S') handleSchemaRequest(inBuffer, sequence, replyToExchange, replyToKey); - else if (opcode == 'x') handleConsoleAddedIndication(); - else if (opcode == 'G') handleGetQuery(inBuffer, sequence, replyToKey, userId); - else if (opcode == 'M') handleMethodRequest(inBuffer, sequence, replyToKey, userId); + while (Protocol::checkHeader(inBuffer, &opcode, &sequence)) { + if (opcode == Protocol::OP_ATTACH_RESPONSE) handleAttachResponse(inBuffer); + else if (opcode == Protocol::OP_SCHEMA_REQUEST) handleSchemaRequest(inBuffer, sequence, replyToExchange, replyToKey); + else if (opcode == Protocol::OP_CONSOLE_ADDED_INDICATION) handleConsoleAddedIndication(); + else if (opcode == Protocol::OP_GET_QUERY) handleGetQuery(inBuffer, sequence, replyToKey, userId); + else if (opcode == Protocol::OP_METHOD_REQUEST) handleMethodRequest(inBuffer, sequence, replyToKey, userId); + else { + QPID_LOG(error, "AgentEngineImpl::handleRcvMessage invalid opcode=" << opcode); + break; + } } } -bool AgentEngineImpl::getXmtMessage(Message& item) +bool AgentEngineImpl::getXmtMessage(Message& item) const { Mutex::ScopedLock _lock(lock); if (xmtQueue.empty()) @@ -293,7 +296,7 @@ void AgentEngineImpl::popXmt() xmtQueue.pop_front(); } -bool AgentEngineImpl::getEvent(AgentEvent& event) +bool AgentEngineImpl::getEvent(AgentEvent& event) const { Mutex::ScopedLock _lock(lock); if (eventQueue.empty()) @@ -325,7 +328,7 @@ void AgentEngineImpl::startProtocol() char rawbuffer[512]; Buffer buffer(rawbuffer, 512); - encodeHeader(buffer, 'A'); + Protocol::encodeHeader(buffer, Protocol::OP_ATTACH_REQUEST); buffer.putShortString("qmfa"); systemId.encode(buffer); buffer.putLong(requestedBrokerBank); @@ -340,7 +343,7 @@ void AgentEngineImpl::heartbeat() Mutex::ScopedLock _lock(lock); Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 'h'); + Protocol::encodeHeader(buffer, Protocol::OP_HEARTBEAT_INDICATION); buffer.putLongLong(uint64_t(Duration(now()))); stringstream key; key << "console.heartbeat." << assignedBrokerBank << "." << assignedAgentBank; @@ -349,7 +352,7 @@ void AgentEngineImpl::heartbeat() } void AgentEngineImpl::methodResponse(uint32_t sequence, uint32_t status, char* text, - const Value& argMap) + const Value& argMap) { Mutex::ScopedLock _lock(lock); map<uint32_t, AgentQueryContext::Ptr>::iterator iter = contextMap.find(sequence); @@ -359,7 +362,7 @@ void AgentEngineImpl::methodResponse(uint32_t sequence, uint32_t status, char* t contextMap.erase(iter); Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 'm', context->sequence); + Protocol::encodeHeader(buffer, Protocol::OP_METHOD_RESPONSE, context->sequence); buffer.putLong(status); buffer.putMediumString(text); if (status == 0) { @@ -378,7 +381,7 @@ void AgentEngineImpl::methodResponse(uint32_t sequence, uint32_t status, char* t } } sendBufferLH(buffer, context->exchange, context->key); - QPID_LOG(trace, "SENT MethodResponse"); + QPID_LOG(trace, "SENT MethodResponse seq=" << context->sequence << " status=" << status << " text=" << text); } void AgentEngineImpl::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat) @@ -390,7 +393,7 @@ void AgentEngineImpl::queryResponse(uint32_t sequence, Object& object, bool prop AgentQueryContext::Ptr context = iter->second; Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 'g', context->sequence); + Protocol::encodeHeader(buffer, Protocol::OP_OBJECT_INDICATION, context->sequence); object.impl->encodeSchemaKey(buffer); object.impl->encodeManagedObjectData(buffer); @@ -400,7 +403,7 @@ void AgentEngineImpl::queryResponse(uint32_t sequence, Object& object, bool prop object.impl->encodeStatistics(buffer); sendBufferLH(buffer, context->exchange, context->key); - QPID_LOG(trace, "SENT ContentIndication"); + QPID_LOG(trace, "SENT ContentIndication seq=" << context->sequence); } void AgentEngineImpl::queryComplete(uint32_t sequence) @@ -423,11 +426,11 @@ void AgentEngineImpl::registerClass(SchemaObjectClass* cls) map<string, ClassMaps>::iterator iter = packages.find(impl->package); if (iter == packages.end()) { packages[impl->package] = ClassMaps(); - iter = packages.find(impl->package); + iter = packages.find(impl->getClassKey()->getPackageName()); // TODO: Indicate this package if connected } - SchemaClassKey key(impl->name, impl->getHash()); + AgentClassKey key(impl->getClassKey()->getClassName(), impl->getClassKey()->getHash()); iter->second.objectClasses[key] = impl; // TODO: Indicate this schema if connected. @@ -441,11 +444,11 @@ void AgentEngineImpl::registerClass(SchemaEventClass* cls) map<string, ClassMaps>::iterator iter = packages.find(impl->package); if (iter == packages.end()) { packages[impl->package] = ClassMaps(); - iter = packages.find(impl->package); + iter = packages.find(impl->getClassKey()->getPackageName()); // TODO: Indicate this package if connected } - SchemaClassKey key(impl->name, impl->getHash()); + AgentClassKey key(impl->getClassKey()->getClassName(), impl->getClassKey()->getHash()); iter->second.eventClasses[key] = impl; // TODO: Indicate this schema if connected. @@ -477,30 +480,6 @@ void AgentEngineImpl::raiseEvent(Event&) Mutex::ScopedLock _lock(lock); } -void AgentEngineImpl::encodeHeader(Buffer& buf, uint8_t opcode, uint32_t seq) -{ - buf.putOctet('A'); - buf.putOctet('M'); - buf.putOctet('3'); - buf.putOctet(opcode); - buf.putLong (seq); -} - -bool AgentEngineImpl::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq) -{ - if (buf.getSize() < 8) - return false; - - uint8_t h1 = buf.getOctet(); - uint8_t h2 = buf.getOctet(); - uint8_t h3 = buf.getOctet(); - - *opcode = buf.getOctet(); - *seq = buf.getLong(); - - return h1 == 'A' && h2 == 'M' && h3 == '3'; -} - AgentEventImpl::Ptr AgentEngineImpl::eventDeclareQueue(const string& name) { AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::DECLARE_QUEUE)); @@ -532,9 +511,10 @@ AgentEventImpl::Ptr AgentEngineImpl::eventQuery(uint32_t num, const string& user AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::GET_QUERY)); event->sequence = num; event->authUserId = userId; - event->query.impl->packageName = package; - event->query.impl->className = cls; - event->query.impl->oid = oid; + if (oid.get()) + event->query.reset(new Query(oid.get())); + else + event->query.reset(new Query(cls.c_str(), package.c_str())); return event; } @@ -570,16 +550,16 @@ void AgentEngineImpl::sendBufferLH(Buffer& buf, const string& destination, const void AgentEngineImpl::sendPackageIndicationLH(const string& packageName) { Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 'p'); + Protocol::encodeHeader(buffer, Protocol::OP_PACKAGE_INDICATION); buffer.putShortString(packageName); sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); QPID_LOG(trace, "SENT PackageIndication: package_name=" << packageName); } -void AgentEngineImpl::sendClassIndicationLH(ClassKind kind, const string& packageName, const SchemaClassKey& key) +void AgentEngineImpl::sendClassIndicationLH(ClassKind kind, const string& packageName, const AgentClassKey& key) { Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 'q'); + Protocol::encodeHeader(buffer, Protocol::OP_CLASS_INDICATION); buffer.putOctet((int) kind); buffer.putShortString(packageName); buffer.putShortString(key.name); @@ -592,7 +572,7 @@ void AgentEngineImpl::sendCommandCompleteLH(const string& exchange, const string uint32_t sequence, uint32_t code, const string& text) { Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 'z', sequence); + Protocol::encodeHeader(buffer, Protocol::OP_COMMAND_COMPLETE, sequence); buffer.putLong(code); buffer.putShortString(text); sendBufferLH(buffer, exchange, replyToKey); @@ -602,7 +582,7 @@ void AgentEngineImpl::sendCommandCompleteLH(const string& exchange, const string 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); + Protocol::encodeHeader(buffer, Protocol::OP_METHOD_RESPONSE, sequence); buffer.putLong(code); string fulltext; @@ -690,7 +670,7 @@ void AgentEngineImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, string rKey(replyKey); string packageName; inBuffer.getShortString(packageName); - SchemaClassKey key(inBuffer); + AgentClassKey key(inBuffer); if (rExchange.empty()) rExchange = QMF_EXCHANGE; @@ -710,7 +690,7 @@ void AgentEngineImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, if (ocIter != cMap.objectClasses.end()) { SchemaObjectClassImpl* oImpl = ocIter->second; Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 's', sequence); + Protocol::encodeHeader(buffer, Protocol::OP_SCHEMA_RESPONSE, sequence); oImpl->encode(buffer); sendBufferLH(buffer, rExchange, rKey); QPID_LOG(trace, "SENT SchemaResponse: (object) package=" << packageName << " class=" << key.name); @@ -721,7 +701,7 @@ void AgentEngineImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, if (ecIter != cMap.eventClasses.end()) { SchemaEventClassImpl* eImpl = ecIter->second; Buffer buffer(outputBuffer, MA_BUFFER_SIZE); - encodeHeader(buffer, 's', sequence); + Protocol::encodeHeader(buffer, Protocol::OP_SCHEMA_RESPONSE, sequence); eImpl->encode(buffer); sendBufferLH(buffer, rExchange, rKey); QPID_LOG(trace, "SENT SchemaResponse: (event) package=" << packageName << " class=" << key.name); @@ -744,7 +724,7 @@ void AgentEngineImpl::handleGetQuery(Buffer& inBuffer, uint32_t sequence, const ft.decode(inBuffer); - QPID_LOG(trace, "RCVD GetQuery: map=" << ft); + QPID_LOG(trace, "RCVD GetQuery: seq=" << sequence << " map=" << ft); value = ft.get("_package"); if (value.get() && value->convertsTo<string>()) { @@ -791,9 +771,11 @@ void AgentEngineImpl::handleMethodRequest(Buffer& buffer, uint32_t sequence, con ObjectIdImpl* oidImpl = new ObjectIdImpl(buffer); boost::shared_ptr<ObjectId> oid(oidImpl->envelope); buffer.getShortString(pname); - SchemaClassKey classKey(buffer); + AgentClassKey classKey(buffer); buffer.getShortString(method); + QPID_LOG(trace, "RCVD MethodRequest seq=" << sequence << " method=" << method); + map<string, ClassMaps>::const_iterator pIter = packages.find(pname); if (pIter == packages.end()) { sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_PACKAGE, pname); @@ -851,108 +833,25 @@ void AgentEngineImpl::handleConsoleAddedIndication() // Wrappers //================================================================== -AgentEngine::AgentEngine(char* label, bool internalStore) -{ - impl = new AgentEngineImpl(label, internalStore); -} - -AgentEngine::~AgentEngine() -{ - delete impl; -} - -void AgentEngine::setStoreDir(const char* path) -{ - impl->setStoreDir(path); -} - -void AgentEngine::setTransferDir(const char* path) -{ - impl->setTransferDir(path); -} - -void AgentEngine::handleRcvMessage(Message& message) -{ - impl->handleRcvMessage(message); -} - -bool AgentEngine::getXmtMessage(Message& item) -{ - return impl->getXmtMessage(item); -} - -void AgentEngine::popXmt() -{ - impl->popXmt(); -} - -bool AgentEngine::getEvent(AgentEvent& event) -{ - return impl->getEvent(event); -} - -void AgentEngine::popEvent() -{ - impl->popEvent(); -} - -void AgentEngine::newSession() -{ - impl->newSession(); -} - -void AgentEngine::startProtocol() -{ - impl->startProtocol(); -} - -void AgentEngine::heartbeat() -{ - impl->heartbeat(); -} - -void AgentEngine::methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments) -{ - impl->methodResponse(sequence, status, text, arguments); -} - -void AgentEngine::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat) -{ - impl->queryResponse(sequence, object, prop, stat); -} - -void AgentEngine::queryComplete(uint32_t sequence) -{ - impl->queryComplete(sequence); -} - -void AgentEngine::registerClass(SchemaObjectClass* cls) -{ - impl->registerClass(cls); -} - -void AgentEngine::registerClass(SchemaEventClass* cls) -{ - impl->registerClass(cls); -} - -const ObjectId* AgentEngine::addObject(Object& obj, uint64_t persistId) -{ - return impl->addObject(obj, persistId); -} - -const ObjectId* AgentEngine::allocObjectId(uint64_t persistId) -{ - return impl->allocObjectId(persistId); -} - -const ObjectId* AgentEngine::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi) -{ - return impl->allocObjectId(persistIdLo, persistIdHi); -} - -void AgentEngine::raiseEvent(Event& event) -{ - impl->raiseEvent(event); -} +AgentEngine::AgentEngine(char* label, bool internalStore) { impl = new AgentEngineImpl(label, internalStore); } +AgentEngine::~AgentEngine() { delete impl; } +void AgentEngine::setStoreDir(const char* path) { impl->setStoreDir(path); } +void AgentEngine::setTransferDir(const char* path) { impl->setTransferDir(path); } +void AgentEngine::handleRcvMessage(Message& message) { impl->handleRcvMessage(message); } +bool AgentEngine::getXmtMessage(Message& item) const { return impl->getXmtMessage(item); } +void AgentEngine::popXmt() { impl->popXmt(); } +bool AgentEngine::getEvent(AgentEvent& event) const { return impl->getEvent(event); } +void AgentEngine::popEvent() { impl->popEvent(); } +void AgentEngine::newSession() { impl->newSession(); } +void AgentEngine::startProtocol() { impl->startProtocol(); } +void AgentEngine::heartbeat() { impl->heartbeat(); } +void AgentEngine::methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments) { impl->methodResponse(sequence, status, text, arguments); } +void AgentEngine::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat) { impl->queryResponse(sequence, object, prop, stat); } +void AgentEngine::queryComplete(uint32_t sequence) { impl->queryComplete(sequence); } +void AgentEngine::registerClass(SchemaObjectClass* cls) { impl->registerClass(cls); } +void AgentEngine::registerClass(SchemaEventClass* cls) { impl->registerClass(cls); } +const ObjectId* AgentEngine::addObject(Object& obj, uint64_t persistId) { return impl->addObject(obj, persistId); } +const ObjectId* AgentEngine::allocObjectId(uint64_t persistId) { return impl->allocObjectId(persistId); } +const ObjectId* AgentEngine::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi) { return impl->allocObjectId(persistIdLo, persistIdHi); } +void AgentEngine::raiseEvent(Event& event) { impl->raiseEvent(event); } diff --git a/qpid/cpp/src/qmf/AgentEngine.h b/qpid/cpp/src/qmf/AgentEngine.h index d18a104e96..c88ef33657 100644 --- a/qpid/cpp/src/qmf/AgentEngine.h +++ b/qpid/cpp/src/qmf/AgentEngine.h @@ -101,7 +101,7 @@ namespace qmf { *@param item The Message structure describing the message to be produced. *@return true if the Message is valid, false if there are no messages to send. */ - bool getXmtMessage(Message& item); + bool getXmtMessage(Message& item) const; /** * Remove and discard one message from the head of the transmit queue. @@ -113,7 +113,7 @@ namespace qmf { *@param event The event iff the return value is true *@return true if event is valid, false if there are no events to process */ - bool getEvent(AgentEvent& event); + bool getEvent(AgentEvent& event) const; /** * Remove and discard one event from the head of the event queue. @@ -182,7 +182,7 @@ 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); + // const ObjectId* addObject(Object& obj, uint32_t persistIdLo, uint32_t persistIdHi); /** * Allocate an object-id for an object that will be managed by the application. diff --git a/qpid/cpp/src/qmf/BrokerProxyImpl.cpp b/qpid/cpp/src/qmf/BrokerProxyImpl.cpp new file mode 100644 index 0000000000..47dca309d7 --- /dev/null +++ b/qpid/cpp/src/qmf/BrokerProxyImpl.cpp @@ -0,0 +1,663 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES 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/BrokerProxyImpl.h" +#include "qmf/ConsoleEngineImpl.h" +#include "qmf/Protocol.h" +#include "qpid/Address.h" +#include "qpid/sys/SystemInfo.h" +#include <qpid/log/Statement.h> +#include <string.h> +#include <iostream> +#include <fstream> + +using namespace std; +using namespace qmf; +using namespace qpid::framing; +using namespace qpid::sys; + +namespace { + const char* QMF_EXCHANGE = "qpid.management"; + const char* DIR_EXCHANGE = "amq.direct"; + const char* BROKER_KEY = "broker"; + const char* BROKER_PACKAGE = "org.apache.qpid.broker"; + const char* AGENT_CLASS = "agent"; + const char* BROKER_AGENT_KEY = "agent.1.0"; +} + +const Object* QueryResponseImpl::getObject(uint32_t idx) const +{ + vector<ObjectImpl::Ptr>::const_iterator iter = results.begin(); + + while (idx > 0) { + if (iter == results.end()) + return 0; + iter++; + idx--; + } + + return (*iter)->envelope; +} + +#define STRING_REF(s) {if (!s.empty()) item.s = const_cast<char*>(s.c_str());} + +BrokerEvent BrokerEventImpl::copy() +{ + BrokerEvent item; + + ::memset(&item, 0, sizeof(BrokerEvent)); + item.kind = kind; + + STRING_REF(name); + STRING_REF(exchange); + STRING_REF(bindingKey); + item.context = context; + item.queryResponse = queryResponse.get() ? queryResponse->envelope : 0; + item.methodResponse = methodResponse.get() ? methodResponse->envelope : 0; + + return item; +} + +BrokerProxyImpl::BrokerProxyImpl(BrokerProxy* e, ConsoleEngine& _console) : + envelope(e), console(_console.impl) +{ + stringstream qn; + qpid::TcpAddress addr; + + SystemInfo::getLocalHostname(addr); + qn << "qmfc-" << SystemInfo::getProcessName() << "-" << addr << "-" << SystemInfo::getProcessId(); + queueName = qn.str(); + + seqMgr.setUnsolicitedContext(SequenceContext::Ptr(new StaticContext(*this))); +} + +void BrokerProxyImpl::sessionOpened(SessionHandle& /*sh*/) +{ + Mutex::ScopedLock _lock(lock); + agentList.clear(); + eventQueue.clear(); + xmtQueue.clear(); + eventQueue.push_back(eventDeclareQueue(queueName)); + eventQueue.push_back(eventBind(DIR_EXCHANGE, queueName, queueName)); + eventQueue.push_back(eventSetupComplete()); + + // TODO: Store session handle +} + +void BrokerProxyImpl::sessionClosed() +{ + Mutex::ScopedLock _lock(lock); + agentList.clear(); + eventQueue.clear(); + xmtQueue.clear(); +} + +void BrokerProxyImpl::startProtocol() +{ + Mutex::ScopedLock _lock(lock); + char rawbuffer[512]; + Buffer buffer(rawbuffer, 512); + + agentList.push_back(AgentProxyImpl::Ptr(new AgentProxyImpl(console, this, 0, "Agent embedded in broker"))); + + requestsOutstanding = 1; + topicBound = false; + uint32_t sequence(seqMgr.reserve()); + Protocol::encodeHeader(buffer, Protocol::OP_BROKER_REQUEST, sequence); + sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT BrokerRequest seq=" << sequence); +} + +void BrokerProxyImpl::sendBufferLH(Buffer& buf, const string& destination, const string& routingKey) +{ + uint32_t length = buf.getPosition(); + MessageImpl::Ptr message(new MessageImpl); + + buf.reset(); + buf.getRawData(message->body, length); + message->destination = destination; + message->routingKey = routingKey; + message->replyExchange = DIR_EXCHANGE; + message->replyKey = queueName; + + xmtQueue.push_back(message); +} + +void BrokerProxyImpl::handleRcvMessage(Message& message) +{ + Buffer inBuffer(message.body, message.length); + uint8_t opcode; + uint32_t sequence; + + while (Protocol::checkHeader(inBuffer, &opcode, &sequence)) + seqMgr.dispatch(opcode, sequence, inBuffer); +} + +bool BrokerProxyImpl::getXmtMessage(Message& item) const +{ + Mutex::ScopedLock _lock(lock); + if (xmtQueue.empty()) + return false; + item = xmtQueue.front()->copy(); + return true; +} + +void BrokerProxyImpl::popXmt() +{ + Mutex::ScopedLock _lock(lock); + if (!xmtQueue.empty()) + xmtQueue.pop_front(); +} + +bool BrokerProxyImpl::getEvent(BrokerEvent& event) const +{ + Mutex::ScopedLock _lock(lock); + if (eventQueue.empty()) + return false; + event = eventQueue.front()->copy(); + return true; +} + +void BrokerProxyImpl::popEvent() +{ + Mutex::ScopedLock _lock(lock); + if (!eventQueue.empty()) + eventQueue.pop_front(); +} + +uint32_t BrokerProxyImpl::agentCount() const +{ + Mutex::ScopedLock _lock(lock); + return agentList.size(); +} + +const AgentProxy* BrokerProxyImpl::getAgent(uint32_t idx) const +{ + Mutex::ScopedLock _lock(lock); + for (vector<AgentProxyImpl::Ptr>::const_iterator iter = agentList.begin(); + iter != agentList.end(); iter++) + if (idx-- == 0) + return (*iter)->envelope; + return 0; +} + +void BrokerProxyImpl::sendQuery(const Query& query, void* context, const AgentProxy* agent) +{ + SequenceContext::Ptr queryContext(new QueryContext(*this, context)); + Mutex::ScopedLock _lock(lock); + if (agent != 0) { + sendGetRequestLH(queryContext, query, agent->impl); + } else { + // TODO (optimization) only send queries to agents that have the requested class+package + for (vector<AgentProxyImpl::Ptr>::const_iterator iter = agentList.begin(); + iter != agentList.end(); iter++) { + sendGetRequestLH(queryContext, query, (*iter).get()); + } + } +} + +void BrokerProxyImpl::sendGetRequestLH(SequenceContext::Ptr queryContext, const Query& query, const AgentProxyImpl* agent) +{ + stringstream key; + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve(queryContext)); + + Protocol::encodeHeader(outBuffer, Protocol::OP_GET_QUERY, sequence); + query.impl->encode(outBuffer); + key << "agent.1." << agent->agentBank; + sendBufferLH(outBuffer, QMF_EXCHANGE, key.str()); + QPID_LOG(trace, "SENT GetQuery seq=" << sequence << " key=" << key.str()); +} + +string BrokerProxyImpl::encodeMethodArguments(const SchemaMethod* schema, const Value* argmap, Buffer& buffer) +{ + int argCount = schema->getArgumentCount(); + + if (argmap == 0 || !argmap->isMap()) + return string("Arguments must be in a map value"); + + for (int aIdx = 0; aIdx < argCount; aIdx++) { + const SchemaArgument* arg(schema->getArgument(aIdx)); + if (arg->getDirection() == DIR_IN || arg->getDirection() == DIR_IN_OUT) { + if (argmap->keyInMap(arg->getName())) { + const Value* argVal(argmap->byKey(arg->getName())); + if (argVal->getType() != arg->getType()) + return string("Argument is the wrong type: ") + arg->getName(); + argVal->impl->encode(buffer); + } else { + Value defaultValue(arg->getType()); + defaultValue.impl->encode(buffer); + } + } + } + + return string(); +} + +void BrokerProxyImpl::sendMethodRequest(ObjectIdImpl* oid, const SchemaObjectClass* cls, + const string& methodName, const Value* args, void* userContext) +{ + int methodCount = cls->getMethodCount(); + int idx; + for (idx = 0; idx < methodCount; idx++) { + const SchemaMethod* method = cls->getMethod(idx); + if (string(method->getName()) == methodName) { + Mutex::ScopedLock _lock(lock); + SequenceContext::Ptr methodContext(new MethodContext(*this, userContext, method->impl)); + stringstream key; + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve(methodContext)); + + Protocol::encodeHeader(outBuffer, Protocol::OP_METHOD_REQUEST, sequence); + oid->encode(outBuffer); + cls->getClassKey()->impl->encode(outBuffer); + outBuffer.putShortString(methodName); + + string argErrorString = encodeMethodArguments(method, args, outBuffer); + if (argErrorString.empty()) { + key << "agent.1." << oid->getAgentBank(); + sendBufferLH(outBuffer, QMF_EXCHANGE, key.str()); + QPID_LOG(trace, "SENT MethodRequest seq=" << sequence << " method=" << methodName << " key=" << key.str()); + } else { + MethodResponseImpl::Ptr argError(new MethodResponseImpl(1, argErrorString)); + eventQueue.push_back(eventMethodResponse(userContext, argError)); + } + return; + } + } + + MethodResponseImpl::Ptr error(new MethodResponseImpl(1, string("Unknown method: ") + methodName)); + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(eventMethodResponse(userContext, error)); +} + +void BrokerProxyImpl::addBinding(const string& exchange, const string& key) +{ + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(eventBind(exchange, queueName, key)); +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventDeclareQueue(const string& queueName) +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::DECLARE_QUEUE)); + event->name = queueName; + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventBind(const string& exchange, const string& queue, const string& key) +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::BIND)); + event->name = queue; + event->exchange = exchange; + event->bindingKey = key; + + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventSetupComplete() +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::SETUP_COMPLETE)); + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventStable() +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::STABLE)); + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventQueryComplete(void* context, QueryResponseImpl::Ptr response) +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::QUERY_COMPLETE)); + event->context = context; + event->queryResponse = response; + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventMethodResponse(void* context, MethodResponseImpl::Ptr response) +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::METHOD_RESPONSE)); + event->context = context; + event->methodResponse = response; + return event; +} + +void BrokerProxyImpl::handleBrokerResponse(Buffer& inBuffer, uint32_t seq) +{ + brokerId.decode(inBuffer); + QPID_LOG(trace, "RCVD BrokerResponse seq=" << seq << " brokerId=" << brokerId); + Mutex::ScopedLock _lock(lock); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve()); + incOutstandingLH(); + Protocol::encodeHeader(outBuffer, Protocol::OP_PACKAGE_REQUEST, sequence); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT PackageRequest seq=" << sequence); +} + +void BrokerProxyImpl::handlePackageIndication(Buffer& inBuffer, uint32_t seq) +{ + string package; + + inBuffer.getShortString(package); + QPID_LOG(trace, "RCVD PackageIndication seq=" << seq << " package=" << package); + console->learnPackage(package); + + Mutex::ScopedLock _lock(lock); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve()); + incOutstandingLH(); + Protocol::encodeHeader(outBuffer, Protocol::OP_CLASS_QUERY, sequence); + outBuffer.putShortString(package); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT ClassQuery seq=" << sequence << " package=" << package); +} + +void BrokerProxyImpl::handleCommandComplete(Buffer& inBuffer, uint32_t seq) +{ + string text; + uint32_t code = inBuffer.getLong(); + inBuffer.getShortString(text); + QPID_LOG(trace, "RCVD CommandComplete seq=" << seq << " code=" << code << " text=" << text); +} + +void BrokerProxyImpl::handleClassIndication(Buffer& inBuffer, uint32_t seq) +{ + uint8_t kind = inBuffer.getOctet(); + SchemaClassKeyImpl classKey(inBuffer); + + QPID_LOG(trace, "RCVD ClassIndication seq=" << seq << " kind=" << (int) kind << " key=" << classKey.str()); + + if (!console->haveClass(classKey)) { + Mutex::ScopedLock _lock(lock); + incOutstandingLH(); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve()); + Protocol::encodeHeader(outBuffer, Protocol::OP_SCHEMA_REQUEST, sequence); + classKey.encode(outBuffer); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT SchemaRequest seq=" << sequence <<" key=" << classKey.str()); + } +} + +MethodResponseImpl::Ptr BrokerProxyImpl::handleMethodResponse(Buffer& inBuffer, uint32_t seq, SchemaMethodImpl* schema) +{ + MethodResponseImpl::Ptr response(new MethodResponseImpl(inBuffer, schema)); + + QPID_LOG(trace, "RCVD MethodResponse seq=" << seq << " status=" << response->getStatus() << " text=" << + response->getException()->asString()); + + return response; +} + +void BrokerProxyImpl::handleHeartbeatIndication(Buffer& /*inBuffer*/, uint32_t /*seq*/) +{ + // TODO +} + +void BrokerProxyImpl::handleEventIndication(Buffer& /*inBuffer*/, uint32_t /*seq*/) +{ + // TODO +} + +void BrokerProxyImpl::handleSchemaResponse(Buffer& inBuffer, uint32_t seq) +{ + SchemaObjectClassImpl::Ptr oClassPtr; + SchemaEventClassImpl::Ptr eClassPtr; + uint8_t kind = inBuffer.getOctet(); + const SchemaClassKeyImpl* key; + if (kind == CLASS_OBJECT) { + oClassPtr.reset(new SchemaObjectClassImpl(inBuffer)); + console->learnClass(oClassPtr); + key = oClassPtr->getClassKey()->impl; + QPID_LOG(trace, "RCVD SchemaResponse seq=" << seq << " kind=object key=" << key->str()); + + // + // If we have just learned about the org.apache.qpid.broker:agent class, send a get + // request for the current list of agents so we can have it on-hand before we declare + // this session "stable". + // + if (key->getClassName() == AGENT_CLASS && key->getPackageName() == BROKER_PACKAGE) { + Mutex::ScopedLock _lock(lock); + incOutstandingLH(); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve()); + Protocol::encodeHeader(outBuffer, Protocol::OP_GET_QUERY, sequence); + FieldTable ft; + ft.setString("_class", AGENT_CLASS); + ft.setString("_package", BROKER_PACKAGE); + ft.encode(outBuffer); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_AGENT_KEY); + QPID_LOG(trace, "SENT GetQuery seq=" << sequence << " key=" << BROKER_AGENT_KEY); + } + } else if (kind == CLASS_EVENT) { + eClassPtr.reset(new SchemaEventClassImpl(inBuffer)); + console->learnClass(eClassPtr); + key = eClassPtr->getClassKey()->impl; + QPID_LOG(trace, "RCVD SchemaResponse seq=" << seq << " kind=event key=" << key->str()); + } + else { + QPID_LOG(error, "BrokerProxyImpl::handleSchemaResponse received unknown class kind: " << (int) kind); + } +} + +ObjectImpl::Ptr BrokerProxyImpl::handleObjectIndication(Buffer& inBuffer, uint32_t seq, bool prop, bool stat) +{ + SchemaClassKeyImpl classKey(inBuffer); + QPID_LOG(trace, "RCVD ObjectIndication seq=" << seq << " key=" << classKey.str()); + + SchemaObjectClassImpl::Ptr schema = console->getSchema(classKey); + if (schema.get() == 0) { + QPID_LOG(trace, "No Schema Found for ObjectIndication. seq=" << seq << " key=" << classKey.str()); + return ObjectImpl::Ptr(); + } + + return ObjectImpl::Ptr(new ObjectImpl(schema->envelope, this, inBuffer, prop, stat, true)); +} + +void BrokerProxyImpl::incOutstandingLH() +{ + requestsOutstanding++; +} + +void BrokerProxyImpl::decOutstanding() +{ + Mutex::ScopedLock _lock(lock); + requestsOutstanding--; + if (requestsOutstanding == 0 && !topicBound) { + topicBound = true; + for (vector<pair<string, string> >::const_iterator iter = console->bindingList.begin(); + iter != console->bindingList.end(); iter++) { + string exchange(iter->first.empty() ? QMF_EXCHANGE : iter->first); + string key(iter->second); + eventQueue.push_back(eventBind(exchange, queueName, key)); + } + eventQueue.push_back(eventStable()); + } +} + +MethodResponseImpl::MethodResponseImpl(const MethodResponseImpl& from) : + envelope(from.envelope), // !!!! TODO !!!! this is not right + status(from.status), schema(from.schema), + exception(from.exception.get() ? new Value(*from.exception) : 0), + arguments(from.arguments.get() ? new Value(*from.arguments) : 0) +{ +} + +MethodResponseImpl::MethodResponseImpl(Buffer& buf, SchemaMethodImpl* s) : + envelope(new MethodResponse(this)), schema(s) +{ + string text; + + status = buf.getLong(); + buf.getMediumString(text); + exception.reset(new Value(TYPE_LSTR)); + exception->setString(text.c_str()); + + if (status != 0) + return; + + arguments.reset(new Value(TYPE_MAP)); + int argCount(schema->getArgumentCount()); + for (int idx = 0; idx < argCount; idx++) { + const SchemaArgument* arg = schema->getArgument(idx); + if (arg->getDirection() == DIR_OUT || arg->getDirection() == DIR_IN_OUT) { + ValueImpl* value(new ValueImpl(arg->getType(), buf)); + arguments->insert(arg->getName(), value->envelope); + } + } +} + +MethodResponseImpl::MethodResponseImpl(uint32_t s, const string& text) : envelope(new MethodResponse(this)), schema(0) +{ + status = s; + exception.reset(new Value(TYPE_LSTR)); + exception->setString(text.c_str()); +} + +bool StaticContext::handleMessage(uint8_t opcode, uint32_t sequence, Buffer& buffer) +{ + bool completeContext = false; + if (opcode == Protocol::OP_BROKER_RESPONSE) { + broker.handleBrokerResponse(buffer, sequence); + completeContext = true; + } + else if (opcode == Protocol::OP_COMMAND_COMPLETE) { + broker.handleCommandComplete(buffer, sequence); + completeContext = true; + } + else if (opcode == Protocol::OP_SCHEMA_RESPONSE) { + broker.handleSchemaResponse(buffer, sequence); + completeContext = true; + } + else if (opcode == Protocol::OP_PACKAGE_INDICATION) + broker.handlePackageIndication(buffer, sequence); + else if (opcode == Protocol::OP_CLASS_INDICATION) + broker.handleClassIndication(buffer, sequence); + else if (opcode == Protocol::OP_HEARTBEAT_INDICATION) + broker.handleHeartbeatIndication(buffer, sequence); + else if (opcode == Protocol::OP_EVENT_INDICATION) + broker.handleEventIndication(buffer, sequence); + else if (opcode == Protocol::OP_PROPERTY_INDICATION) + broker.handleObjectIndication(buffer, sequence, true, false); + else if (opcode == Protocol::OP_STATISTIC_INDICATION) + broker.handleObjectIndication(buffer, sequence, false, true); + else if (opcode == Protocol::OP_OBJECT_INDICATION) + broker.handleObjectIndication(buffer, sequence, true, true); + else { + QPID_LOG(trace, "StaticContext::handleMessage invalid opcode: " << opcode); + completeContext = true; + } + + return completeContext; +} + +void QueryContext::reserve() +{ + Mutex::ScopedLock _lock(lock); + requestsOutstanding++; +} + +void QueryContext::release() +{ + { + Mutex::ScopedLock _lock(lock); + if (--requestsOutstanding > 0) + return; + } + + Mutex::ScopedLock _block(broker.lock); + broker.eventQueue.push_back(broker.eventQueryComplete(userContext, queryResponse)); +} + +bool QueryContext::handleMessage(uint8_t opcode, uint32_t sequence, Buffer& buffer) +{ + bool completeContext = false; + ObjectImpl::Ptr object; + + if (opcode == Protocol::OP_COMMAND_COMPLETE) { + broker.handleCommandComplete(buffer, sequence); + completeContext = true; + } + else if (opcode == Protocol::OP_OBJECT_INDICATION) { + object = broker.handleObjectIndication(buffer, sequence, true, true); + if (object.get() != 0) + queryResponse->results.push_back(object); + } + else { + QPID_LOG(trace, "QueryContext::handleMessage invalid opcode: " << opcode); + completeContext = true; + } + + return completeContext; +} + +void MethodContext::release() +{ + Mutex::ScopedLock _block(broker.lock); + broker.eventQueue.push_back(broker.eventMethodResponse(userContext, methodResponse)); +} + +bool MethodContext::handleMessage(uint8_t opcode, uint32_t sequence, Buffer& buffer) +{ + if (opcode == Protocol::OP_METHOD_RESPONSE) + methodResponse = broker.handleMethodResponse(buffer, sequence, schema); + else + QPID_LOG(trace, "QueryContext::handleMessage invalid opcode: " << opcode); + + return true; +} + + +//================================================================== +// Wrappers +//================================================================== + +AgentProxy::AgentProxy(AgentProxyImpl* i) : impl(i) {} +AgentProxy::~AgentProxy() { delete impl; } +const char* AgentProxy::getLabel() const { return impl->getLabel().c_str(); } + +BrokerProxy::BrokerProxy(ConsoleEngine& console) : impl(new BrokerProxyImpl(this, console)) {} +BrokerProxy::~BrokerProxy() { delete impl; } +void BrokerProxy::sessionOpened(SessionHandle& sh) { impl->sessionOpened(sh); } +void BrokerProxy::sessionClosed() { impl->sessionClosed(); } +void BrokerProxy::startProtocol() { impl->startProtocol(); } +void BrokerProxy::handleRcvMessage(Message& message) { impl->handleRcvMessage(message); } +bool BrokerProxy::getXmtMessage(Message& item) const { return impl->getXmtMessage(item); } +void BrokerProxy::popXmt() { impl->popXmt(); } +bool BrokerProxy::getEvent(BrokerEvent& event) const { return impl->getEvent(event); } +void BrokerProxy::popEvent() { impl->popEvent(); } +uint32_t BrokerProxy::agentCount() const { return impl->agentCount(); } +const AgentProxy* BrokerProxy::getAgent(uint32_t idx) const { return impl->getAgent(idx); } +void BrokerProxy::sendQuery(const Query& query, void* context, const AgentProxy* agent) { impl->sendQuery(query, context, agent); } + +MethodResponse::MethodResponse(const MethodResponse& from) : impl(new MethodResponseImpl(*(from.impl))) {} +MethodResponse::MethodResponse(MethodResponseImpl* i) : impl(i) {} +MethodResponse::~MethodResponse() {} +uint32_t MethodResponse::getStatus() const { return impl->getStatus(); } +const Value* MethodResponse::getException() const { return impl->getException(); } +const Value* MethodResponse::getArgs() const { return impl->getArgs(); } + +QueryResponse::QueryResponse(QueryResponseImpl* i) : impl(i) {} +QueryResponse::~QueryResponse() {} +uint32_t QueryResponse::getStatus() const { return impl->getStatus(); } +const Value* QueryResponse::getException() const { return impl->getException(); } +uint32_t QueryResponse::getObjectCount() const { return impl->getObjectCount(); } +const Object* QueryResponse::getObject(uint32_t idx) const { return impl->getObject(idx); } + diff --git a/qpid/cpp/src/qmf/BrokerProxyImpl.h b/qpid/cpp/src/qmf/BrokerProxyImpl.h new file mode 100644 index 0000000000..930d8c8995 --- /dev/null +++ b/qpid/cpp/src/qmf/BrokerProxyImpl.h @@ -0,0 +1,222 @@ +#ifndef _QmfBrokerProxyImpl_ +#define _QmfBrokerProxyImpl_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES 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/ConsoleEngine.h" +#include "qmf/ObjectImpl.h" +#include "qmf/SchemaImpl.h" +#include "qmf/ValueImpl.h" +#include "qmf/QueryImpl.h" +#include "qmf/SequenceManager.h" +#include "qmf/MessageImpl.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/Uuid.h" +#include "qpid/sys/Mutex.h" +#include "boost/shared_ptr.hpp" +#include <memory> +#include <string> +#include <deque> +#include <map> +#include <vector> + +namespace qmf { + + struct MethodResponseImpl { + typedef boost::shared_ptr<MethodResponseImpl> Ptr; + MethodResponse* envelope; + uint32_t status; + SchemaMethodImpl* schema; + boost::shared_ptr<Value> exception; + boost::shared_ptr<Value> arguments; + + MethodResponseImpl(const MethodResponseImpl& from); + MethodResponseImpl(qpid::framing::Buffer& buf, SchemaMethodImpl* schema); + MethodResponseImpl(uint32_t status, const std::string& text); + ~MethodResponseImpl() { delete envelope; } + uint32_t getStatus() const { return status; } + const Value* getException() const { return exception.get(); } + const Value* getArgs() const { return arguments.get(); } + }; + + struct QueryResponseImpl { + typedef boost::shared_ptr<QueryResponseImpl> Ptr; + QueryResponse *envelope; + uint32_t status; + std::auto_ptr<Value> exception; + std::vector<ObjectImpl::Ptr> results; + + QueryResponseImpl() : envelope(new QueryResponse(this)), status(0) {} + ~QueryResponseImpl() { delete envelope; } + uint32_t getStatus() const { return status; } + const Value* getException() const { return exception.get(); } + uint32_t getObjectCount() const { return results.size(); } + const Object* getObject(uint32_t idx) const; + }; + + struct BrokerEventImpl { + typedef boost::shared_ptr<BrokerEventImpl> Ptr; + BrokerEvent::EventKind kind; + std::string name; + std::string exchange; + std::string bindingKey; + void* context; + QueryResponseImpl::Ptr queryResponse; + MethodResponseImpl::Ptr methodResponse; + + BrokerEventImpl(BrokerEvent::EventKind k) : kind(k), context(0) {} + ~BrokerEventImpl() {} + BrokerEvent copy(); + }; + + struct AgentProxyImpl { + typedef boost::shared_ptr<AgentProxyImpl> Ptr; + AgentProxy* envelope; + ConsoleEngineImpl* console; + BrokerProxyImpl* broker; + uint32_t agentBank; + std::string label; + + AgentProxyImpl(ConsoleEngineImpl* c, BrokerProxyImpl* b, uint32_t ab, const std::string& l) : + envelope(new AgentProxy(this)), console(c), broker(b), agentBank(ab), label(l) {} + ~AgentProxyImpl() {} + const std::string& getLabel() const { return label; } + }; + + class BrokerProxyImpl { + public: + typedef boost::shared_ptr<BrokerProxyImpl> Ptr; + + BrokerProxyImpl(BrokerProxy* e, ConsoleEngine& _console); + ~BrokerProxyImpl() {} + + void sessionOpened(SessionHandle& sh); + void sessionClosed(); + void startProtocol(); + + void sendBufferLH(qpid::framing::Buffer& buf, const std::string& destination, const std::string& routingKey); + void handleRcvMessage(Message& message); + bool getXmtMessage(Message& item) const; + void popXmt(); + + bool getEvent(BrokerEvent& event) const; + void popEvent(); + + uint32_t agentCount() const; + const AgentProxy* getAgent(uint32_t idx) const; + void sendQuery(const Query& query, void* context, const AgentProxy* agent); + void sendGetRequestLH(SequenceContext::Ptr queryContext, const Query& query, const AgentProxyImpl* agent); + std::string encodeMethodArguments(const SchemaMethod* schema, const Value* args, qpid::framing::Buffer& buffer); + void sendMethodRequest(ObjectIdImpl* oid, const SchemaObjectClass* cls, const std::string& method, const Value* args, void* context); + + void addBinding(const std::string& exchange, const std::string& key); + void staticRelease() { decOutstanding(); } + + private: + friend class StaticContext; + friend class QueryContext; + friend class MethodContext; + mutable qpid::sys::Mutex lock; + BrokerProxy* envelope; + ConsoleEngineImpl* console; + std::string queueName; + qpid::framing::Uuid brokerId; + SequenceManager seqMgr; + uint32_t requestsOutstanding; + bool topicBound; + std::vector<AgentProxyImpl::Ptr> agentList; + std::deque<MessageImpl::Ptr> xmtQueue; + std::deque<BrokerEventImpl::Ptr> eventQueue; + +# define MA_BUFFER_SIZE 65536 + char outputBuffer[MA_BUFFER_SIZE]; + + BrokerEventImpl::Ptr eventDeclareQueue(const std::string& queueName); + BrokerEventImpl::Ptr eventBind(const std::string& exchange, const std::string& queue, const std::string& key); + BrokerEventImpl::Ptr eventSetupComplete(); + BrokerEventImpl::Ptr eventStable(); + BrokerEventImpl::Ptr eventQueryComplete(void* context, QueryResponseImpl::Ptr response); + BrokerEventImpl::Ptr eventMethodResponse(void* context, MethodResponseImpl::Ptr response); + + void handleBrokerResponse(qpid::framing::Buffer& inBuffer, uint32_t seq); + void handlePackageIndication(qpid::framing::Buffer& inBuffer, uint32_t seq); + void handleCommandComplete(qpid::framing::Buffer& inBuffer, uint32_t seq); + void handleClassIndication(qpid::framing::Buffer& inBuffer, uint32_t seq); + MethodResponseImpl::Ptr handleMethodResponse(qpid::framing::Buffer& inBuffer, uint32_t seq, SchemaMethodImpl* schema); + void handleHeartbeatIndication(qpid::framing::Buffer& inBuffer, uint32_t seq); + void handleEventIndication(qpid::framing::Buffer& inBuffer, uint32_t seq); + void handleSchemaResponse(qpid::framing::Buffer& inBuffer, uint32_t seq); + ObjectImpl::Ptr handleObjectIndication(qpid::framing::Buffer& inBuffer, uint32_t seq, bool prop, bool stat); + void incOutstandingLH(); + void decOutstanding(); + }; + + // + // StaticContext is used to handle: + // + // 1) Responses to console-level requests (for schema info, etc.) + // 2) Unsolicited messages from agents (events, published updates, etc.) + // + struct StaticContext : public SequenceContext { + StaticContext(BrokerProxyImpl& b) : broker(b) {} + virtual ~StaticContext() {} + void reserve() {} + void release() { broker.staticRelease(); } + bool handleMessage(uint8_t opcode, uint32_t sequence, qpid::framing::Buffer& buffer); + BrokerProxyImpl& broker; + }; + + // + // QueryContext is used to track and handle responses associated with a single Get Query + // + struct QueryContext : public SequenceContext { + QueryContext(BrokerProxyImpl& b, void* u) : + broker(b), userContext(u), requestsOutstanding(0), queryResponse(new QueryResponseImpl()) {} + virtual ~QueryContext() {} + void reserve(); + void release(); + bool handleMessage(uint8_t opcode, uint32_t sequence, qpid::framing::Buffer& buffer); + + mutable qpid::sys::Mutex lock; + BrokerProxyImpl& broker; + void* userContext; + uint32_t requestsOutstanding; + QueryResponseImpl::Ptr queryResponse; + }; + + struct MethodContext : public SequenceContext { + MethodContext(BrokerProxyImpl& b, void* u, SchemaMethodImpl* s) : broker(b), userContext(u), schema(s) {} + virtual ~MethodContext() {} + void reserve() {} + void release(); + bool handleMessage(uint8_t opcode, uint32_t sequence, qpid::framing::Buffer& buffer); + + BrokerProxyImpl& broker; + void* userContext; + SchemaMethodImpl* schema; + MethodResponseImpl::Ptr methodResponse; + }; + + + +} + +#endif + diff --git a/qpid/cpp/src/qmf/ConsoleEngine.h b/qpid/cpp/src/qmf/ConsoleEngine.h index 823e281b14..f04bbcea47 100644 --- a/qpid/cpp/src/qmf/ConsoleEngine.h +++ b/qpid/cpp/src/qmf/ConsoleEngine.h @@ -20,62 +20,201 @@ * under the License. */ -#include <qmf/ManagedConnection.h> -#include <qmf/Broker.h> -#include <qmf/Package.h> -#include <qmf/SchemaClassTable.h> +#include <qmf/ResilientConnection.h> +#include <qmf/Schema.h> +#include <qmf/ObjectId.h> #include <qmf/Object.h> -#include <qmf/ConsoleHandler.h> -#include <set> -#include <vector> -#include <string> +#include <qmf/Event.h> +#include <qmf/Query.h> +#include <qmf/Value.h> +#include <qmf/Message.h> namespace qmf { + class ConsoleEngine; + class ConsoleEngineImpl; + class BrokerProxyImpl; + class AgentProxy; + class AgentProxyImpl; + class MethodResponseImpl; + class QueryResponseImpl; + class QueryContext; + + /** + * + */ + class MethodResponse { + public: + MethodResponse(MethodResponseImpl* impl); + MethodResponse(const MethodResponse& from); + ~MethodResponse(); + uint32_t getStatus() const; + const Value* getException() const; + const Value* getArgs() const; + + private: + friend class ConsoleEngineImpl; + MethodResponseImpl* impl; + }; + + /** + * + */ + class QueryResponse { + public: + QueryResponse(QueryResponseImpl* impl); + ~QueryResponse(); + uint32_t getStatus() const; + const Value* getException() const; + uint32_t getObjectCount() const; + const Object* getObject(uint32_t idx) const; + + private: + friend class QueryContext; + QueryResponseImpl *impl; + }; + + /** + * + */ + struct ConsoleEvent { + enum EventKind { + AGENT_ADDED = 1, + AGENT_DELETED = 2, + NEW_PACKAGE = 3, + NEW_CLASS = 4, + OBJECT_UPDATE = 5, + EVENT_RECEIVED = 7, + AGENT_HEARTBEAT = 8 + }; + + EventKind kind; + AgentProxy* agent; // (AGENT_[ADDED|DELETED|HEARTBEAT]) + char* name; // (NEW_PACKAGE) + SchemaClassKey* classKey; // (NEW_CLASS) + Object* object; // (OBJECT_UPDATE) + void* context; // (OBJECT_UPDATE) + Event* event; // (EVENT_RECEIVED) + uint64_t timestamp; // (AGENT_HEARTBEAT) + QueryResponse* queryResponse; // (QUERY_COMPLETE) + }; + + /** + * + */ + struct BrokerEvent { + enum EventKind { + BROKER_INFO = 10, + DECLARE_QUEUE = 11, + DELETE_QUEUE = 12, + BIND = 13, + UNBIND = 14, + SETUP_COMPLETE = 15, + STABLE = 16, + QUERY_COMPLETE = 17, + METHOD_RESPONSE = 18 + }; + + EventKind kind; + char* name; // ([DECLARE|DELETE]_QUEUE, [UN]BIND) + char* exchange; // ([UN]BIND) + char* bindingKey; // ([UN]BIND) + void* context; // (QUERY_COMPLETE, METHOD_RESPONSE) + QueryResponse* queryResponse; // (QUERY_COMPLETE) + MethodResponse* methodResponse; // (METHOD_RESPONSE) + }; + + /** + * + */ + class AgentProxy { + public: + AgentProxy(AgentProxyImpl* impl); + ~AgentProxy(); + const char* getLabel() const; + + private: + friend class BrokerProxyImpl; + AgentProxyImpl* impl; + }; + + /** + * + */ + class BrokerProxy { + public: + BrokerProxy(ConsoleEngine& console); + ~BrokerProxy(); + + void sessionOpened(SessionHandle& sh); + void sessionClosed(); + void startProtocol(); + + void handleRcvMessage(Message& message); + bool getXmtMessage(Message& item) const; + void popXmt(); + + bool getEvent(BrokerEvent& event) const; + void popEvent(); + + uint32_t agentCount() const; + const AgentProxy* getAgent(uint32_t idx) const; + void sendQuery(const Query& query, void* context, const AgentProxy* agent = 0); + + private: + friend class ConsoleEngineImpl; + BrokerProxyImpl* impl; + }; + + // TODO - move this to a public header struct ConsoleSettings { bool rcvObjects; bool rcvEvents; bool rcvHeartbeats; bool userBindings; - uint32_t methodTimeout; - uint32_t getTimeout; ConsoleSettings() : rcvObjects(true), rcvEvents(true), rcvHeartbeats(true), - userBindings(false), - methodTimeout(20), - getTimeout(20) {} + userBindings(false) {} }; class ConsoleEngine { public: - ConsoleEngine(ConsoleHandler* handler = 0, ConsoleSettings settings = ConsoleSettings()); + ConsoleEngine(const ConsoleSettings& settings = ConsoleSettings()); ~ConsoleEngine(); - Broker* addConnection(ManagedConnection& connection); - void delConnection(Broker* broker); - void delConnection(ManagedConnection& connection); + bool getEvent(ConsoleEvent& event) const; + void popEvent(); + + void addConnection(BrokerProxy& broker, void* context); + void delConnection(BrokerProxy& broker); - const PackageMap& getPackages() const; + uint32_t packageCount() const; + const char* getPackageName(uint32_t idx) const; - void bindPackage(const Package& package); - void bindPackage(const std::string& packageName); - void bindClass(const SchemaClass& otype); - void bindClass(const std::string& packageName, const std::string& className); + uint32_t classCount(const char* packageName) const; + const SchemaClassKey* getClass(const char* packageName, uint32_t idx) const; + + ClassKind getClassKind(const SchemaClassKey* key) const; + const SchemaObjectClass* getObjectClass(const SchemaClassKey* key) const; + const SchemaEventClass* getEventClass(const SchemaClassKey* key) const; + + void bindPackage(const char* packageName); + void bindClass(const SchemaClassKey* key); + void bindClass(const char* packageName, const char* className); /* - void getAgents(std::set<Agent>& agents, Broker* = 0); - void getObjects(std::vector<Object>& objects, const std::string& typeName, - const std::string& packageName = "", - Broker* broker = 0, - Agent* agent = 0); - void getObjects(std::vector<Object>& objects, - const std::map<std::string, std::string>& query, - Broker* broker = 0, - Agent* agent = 0); + void startSync(const Query& query, void* context, SyncQuery& sync); + void touchSync(SyncQuery& sync); + void endSync(SyncQuery& sync); */ + + private: + friend class BrokerProxyImpl; + friend class AgentProxyImpl; + ConsoleEngineImpl* impl; }; } diff --git a/qpid/cpp/src/qmf/ConsoleEngineImpl.cpp b/qpid/cpp/src/qmf/ConsoleEngineImpl.cpp new file mode 100644 index 0000000000..d71bf93105 --- /dev/null +++ b/qpid/cpp/src/qmf/ConsoleEngineImpl.cpp @@ -0,0 +1,361 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/ConsoleEngineImpl.h" +#include "qmf/MessageImpl.h" +#include "qmf/SchemaImpl.h" +#include "qmf/Typecode.h" +#include "qmf/ObjectImpl.h" +#include "qmf/ObjectIdImpl.h" +#include "qmf/QueryImpl.h" +#include "qmf/ValueImpl.h" +#include "qmf/Protocol.h" +#include "qmf/SequenceManager.h" +#include "qmf/BrokerProxyImpl.h" +#include <qpid/framing/Buffer.h> +#include <qpid/framing/Uuid.h> +#include <qpid/framing/FieldTable.h> +#include <qpid/framing/FieldValue.h> +#include <qpid/log/Statement.h> +#include <qpid/sys/Time.h> +#include <qpid/sys/SystemInfo.h> +#include <string.h> +#include <iostream> +#include <fstream> + +using namespace std; +using namespace qmf; +using namespace qpid::framing; +using namespace qpid::sys; + +namespace { + const char* QMF_EXCHANGE = "qpid.management"; +} + +#define STRING_REF(s) {if (!s.empty()) item.s = const_cast<char*>(s.c_str());} + +ConsoleEvent ConsoleEventImpl::copy() +{ + ConsoleEvent item; + + ::memset(&item, 0, sizeof(ConsoleEvent)); + item.kind = kind; + item.agent = agent.get() ? agent->envelope : 0; + item.classKey = classKey.get(); + item.object = object; + item.context = context; + item.event = event; + item.timestamp = timestamp; + + STRING_REF(name); + + return item; +} + +ConsoleEngineImpl::ConsoleEngineImpl(ConsoleEngine* e, const ConsoleSettings& s) : + envelope(e), settings(s) +{ + bindingList.push_back(pair<string, string>(string(), "schema.#")); + if (settings.rcvObjects && settings.rcvEvents && settings.rcvHeartbeats && !settings.userBindings) { + bindingList.push_back(pair<string, string>(string(), "console.#")); + } else { + if (settings.rcvObjects && !settings.userBindings) + bindingList.push_back(pair<string, string>(string(), "console.obj.#")); + else + bindingList.push_back(pair<string, string>(string(), "console.obj.*.*.org.apache.qpid.broker.agent")); + if (settings.rcvEvents) + bindingList.push_back(pair<string, string>(string(), "console.event.#")); + if (settings.rcvHeartbeats) + bindingList.push_back(pair<string, string>(string(), "console.heartbeat.#")); + } +} + +ConsoleEngineImpl::~ConsoleEngineImpl() +{ + // This function intentionally left blank. +} + +bool ConsoleEngineImpl::getEvent(ConsoleEvent& event) const +{ + Mutex::ScopedLock _lock(lock); + if (eventQueue.empty()) + return false; + event = eventQueue.front()->copy(); + return true; +} + +void ConsoleEngineImpl::popEvent() +{ + Mutex::ScopedLock _lock(lock); + if (!eventQueue.empty()) + eventQueue.pop_front(); +} + +void ConsoleEngineImpl::addConnection(BrokerProxy& broker, void* /*context*/) +{ + Mutex::ScopedLock _lock(lock); + brokerList.push_back(broker.impl); +} + +void ConsoleEngineImpl::delConnection(BrokerProxy& broker) +{ + Mutex::ScopedLock _lock(lock); + for (vector<BrokerProxyImpl*>::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + if (*iter == broker.impl) { + brokerList.erase(iter); + break; + } +} + +uint32_t ConsoleEngineImpl::packageCount() const +{ + Mutex::ScopedLock _lock(lock); + return packages.size(); +} + +const string& ConsoleEngineImpl::getPackageName(uint32_t idx) const +{ + const static string empty; + + Mutex::ScopedLock _lock(lock); + if (idx >= packages.size()) + return empty; + + PackageList::const_iterator iter = packages.begin(); + for (uint32_t i = 0; i < idx; i++) iter++; + return iter->first; +} + +uint32_t ConsoleEngineImpl::classCount(const char* packageName) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(packageName); + if (pIter == packages.end()) + return 0; + + const ObjectClassList& oList = pIter->second.first; + const EventClassList& eList = pIter->second.second; + + return oList.size() + eList.size(); +} + +const SchemaClassKey* ConsoleEngineImpl::getClass(const char* packageName, uint32_t idx) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(packageName); + if (pIter == packages.end()) + return 0; + + const ObjectClassList& oList = pIter->second.first; + const EventClassList& eList = pIter->second.second; + uint32_t count = 0; + + for (ObjectClassList::const_iterator oIter = oList.begin(); + oIter != oList.end(); oIter++) { + if (count == idx) + return oIter->second->getClassKey(); + count++; + } + + for (EventClassList::const_iterator eIter = eList.begin(); + eIter != eList.end(); eIter++) { + if (count == idx) + return eIter->second->getClassKey(); + count++; + } + + return 0; +} + +ClassKind ConsoleEngineImpl::getClassKind(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return CLASS_OBJECT; + + const EventClassList& eList = pIter->second.second; + if (eList.find(key->impl) != eList.end()) + return CLASS_EVENT; + return CLASS_OBJECT; +} + +const SchemaObjectClass* ConsoleEngineImpl::getObjectClass(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return 0; + + const ObjectClassList& oList = pIter->second.first; + ObjectClassList::const_iterator iter = oList.find(key->impl); + if (iter == oList.end()) + return 0; + return iter->second->envelope; +} + +const SchemaEventClass* ConsoleEngineImpl::getEventClass(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return 0; + + const EventClassList& eList = pIter->second.second; + EventClassList::const_iterator iter = eList.find(key->impl); + if (iter == eList.end()) + return 0; + return iter->second->envelope; +} + +void ConsoleEngineImpl::bindPackage(const char* packageName) +{ + stringstream key; + key << "console.obj.*.*." << packageName << ".#"; + Mutex::ScopedLock _lock(lock); + bindingList.push_back(pair<string, string>(string(), key.str())); + for (vector<BrokerProxyImpl*>::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + (*iter)->addBinding(QMF_EXCHANGE, key.str()); +} + +void ConsoleEngineImpl::bindClass(const SchemaClassKey* classKey) +{ + stringstream key; + key << "console.obj.*.*." << classKey->getPackageName() << "." << classKey->getClassName() << ".#"; + Mutex::ScopedLock _lock(lock); + bindingList.push_back(pair<string, string>(string(), key.str())); + for (vector<BrokerProxyImpl*>::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + (*iter)->addBinding(QMF_EXCHANGE, key.str()); +} + +void ConsoleEngineImpl::bindClass(const char* packageName, const char* className) +{ + stringstream key; + key << "console.obj.*.*." << packageName << "." << className << ".#"; + Mutex::ScopedLock _lock(lock); + bindingList.push_back(pair<string, string>(string(), key.str())); + for (vector<BrokerProxyImpl*>::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + (*iter)->addBinding(QMF_EXCHANGE, key.str()); +} + +/* +void ConsoleEngineImpl::startSync(const Query& query, void* context, SyncQuery& sync) +{ +} + +void ConsoleEngineImpl::touchSync(SyncQuery& sync) +{ +} + +void ConsoleEngineImpl::endSync(SyncQuery& sync) +{ +} +*/ + +void ConsoleEngineImpl::learnPackage(const string& packageName) +{ + Mutex::ScopedLock _lock(lock); + if (packages.find(packageName) == packages.end()) + packages.insert(pair<string, pair<ObjectClassList, EventClassList> > + (packageName, pair<ObjectClassList, EventClassList>(ObjectClassList(), EventClassList()))); +} + +void ConsoleEngineImpl::learnClass(SchemaObjectClassImpl::Ptr cls) +{ + Mutex::ScopedLock _lock(lock); + const SchemaClassKey* key = cls->getClassKey(); + PackageList::iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return; + + ObjectClassList& list = pIter->second.first; + if (list.find(key->impl) == list.end()) + list[key->impl] = cls; +} + +void ConsoleEngineImpl::learnClass(SchemaEventClassImpl::Ptr cls) +{ + Mutex::ScopedLock _lock(lock); + const SchemaClassKey* key = cls->getClassKey(); + PackageList::iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return; + + EventClassList& list = pIter->second.second; + if (list.find(key->impl) == list.end()) + list[key->impl] = cls; +} + +bool ConsoleEngineImpl::haveClass(const SchemaClassKeyImpl& key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key.getPackageName()); + if (pIter == packages.end()) + return false; + + const ObjectClassList& oList = pIter->second.first; + const EventClassList& eList = pIter->second.second; + + return oList.find(&key) != oList.end() || eList.find(&key) != eList.end(); +} + +SchemaObjectClassImpl::Ptr ConsoleEngineImpl::getSchema(const SchemaClassKeyImpl& key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key.getPackageName()); + if (pIter == packages.end()) + return SchemaObjectClassImpl::Ptr(); + + const ObjectClassList& oList = pIter->second.first; + ObjectClassList::const_iterator iter = oList.find(&key); + if (iter == oList.end()) + return SchemaObjectClassImpl::Ptr(); + + return iter->second; +} + +//================================================================== +// Wrappers +//================================================================== + +ConsoleEngine::ConsoleEngine(const ConsoleSettings& settings) : impl(new ConsoleEngineImpl(this, settings)) {} +ConsoleEngine::~ConsoleEngine() { delete impl; } +bool ConsoleEngine::getEvent(ConsoleEvent& event) const { return impl->getEvent(event); } +void ConsoleEngine::popEvent() { impl->popEvent(); } +void ConsoleEngine::addConnection(BrokerProxy& broker, void* context) { impl->addConnection(broker, context); } +void ConsoleEngine::delConnection(BrokerProxy& broker) { impl->delConnection(broker); } +uint32_t ConsoleEngine::packageCount() const { return impl->packageCount(); } +const char* ConsoleEngine::getPackageName(uint32_t idx) const { return impl->getPackageName(idx).c_str(); } +uint32_t ConsoleEngine::classCount(const char* packageName) const { return impl->classCount(packageName); } +const SchemaClassKey* ConsoleEngine::getClass(const char* packageName, uint32_t idx) const { return impl->getClass(packageName, idx); } +ClassKind ConsoleEngine::getClassKind(const SchemaClassKey* key) const { return impl->getClassKind(key); } +const SchemaObjectClass* ConsoleEngine::getObjectClass(const SchemaClassKey* key) const { return impl->getObjectClass(key); } +const SchemaEventClass* ConsoleEngine::getEventClass(const SchemaClassKey* key) const { return impl->getEventClass(key); } +void ConsoleEngine::bindPackage(const char* packageName) { impl->bindPackage(packageName); } +void ConsoleEngine::bindClass(const SchemaClassKey* key) { impl->bindClass(key); } +void ConsoleEngine::bindClass(const char* packageName, const char* className) { impl->bindClass(packageName, className); } +//void ConsoleEngine::startSync(const Query& query, void* context, SyncQuery& sync) { impl->startSync(query, context, sync); } +//void ConsoleEngine::touchSync(SyncQuery& sync) { impl->touchSync(sync); } +//void ConsoleEngine::endSync(SyncQuery& sync) { impl->endSync(sync); } + + diff --git a/qpid/cpp/src/qmf/ConsoleEngineImpl.h b/qpid/cpp/src/qmf/ConsoleEngineImpl.h new file mode 100644 index 0000000000..b95cb7523a --- /dev/null +++ b/qpid/cpp/src/qmf/ConsoleEngineImpl.h @@ -0,0 +1,133 @@ +#ifndef _QmfConsoleEngineImpl_ +#define _QmfConsoleEngineImpl_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES 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/ConsoleEngine.h" +#include "qmf/MessageImpl.h" +#include "qmf/SchemaImpl.h" +#include "qmf/Typecode.h" +#include "qmf/ObjectImpl.h" +#include "qmf/ObjectIdImpl.h" +#include "qmf/QueryImpl.h" +#include "qmf/ValueImpl.h" +#include "qmf/Protocol.h" +#include "qmf/SequenceManager.h" +#include "qmf/BrokerProxyImpl.h" +#include <qpid/framing/Buffer.h> +#include <qpid/framing/Uuid.h> +#include <qpid/framing/FieldTable.h> +#include <qpid/framing/FieldValue.h> +#include <qpid/sys/Mutex.h> +#include <qpid/sys/Time.h> +#include <qpid/sys/SystemInfo.h> +#include <string.h> +#include <string> +#include <deque> +#include <map> +#include <vector> +#include <iostream> +#include <fstream> +#include <boost/shared_ptr.hpp> + +namespace qmf { + + struct ConsoleEventImpl { + typedef boost::shared_ptr<ConsoleEventImpl> Ptr; + ConsoleEvent::EventKind kind; + boost::shared_ptr<AgentProxyImpl> agent; + std::string name; + boost::shared_ptr<SchemaClassKey> classKey; + Object* object; + void* context; + Event* event; + uint64_t timestamp; + + ConsoleEventImpl(ConsoleEvent::EventKind k) : + kind(k), object(0), context(0), event(0), timestamp(0) {} + ~ConsoleEventImpl() {} + ConsoleEvent copy(); + }; + + class ConsoleEngineImpl { + public: + ConsoleEngineImpl(ConsoleEngine* e, const ConsoleSettings& settings = ConsoleSettings()); + ~ConsoleEngineImpl(); + + bool getEvent(ConsoleEvent& event) const; + void popEvent(); + + void addConnection(BrokerProxy& broker, void* context); + void delConnection(BrokerProxy& broker); + + uint32_t packageCount() const; + const std::string& getPackageName(uint32_t idx) const; + + uint32_t classCount(const char* packageName) const; + const SchemaClassKey* getClass(const char* packageName, uint32_t idx) const; + + ClassKind getClassKind(const SchemaClassKey* key) const; + const SchemaObjectClass* getObjectClass(const SchemaClassKey* key) const; + const SchemaEventClass* getEventClass(const SchemaClassKey* key) const; + + void bindPackage(const char* packageName); + void bindClass(const SchemaClassKey* key); + void bindClass(const char* packageName, const char* className); + + /* + void startSync(const Query& query, void* context, SyncQuery& sync); + void touchSync(SyncQuery& sync); + void endSync(SyncQuery& sync); + */ + + private: + friend class BrokerProxyImpl; + ConsoleEngine* envelope; + const ConsoleSettings& settings; + mutable qpid::sys::Mutex lock; + std::deque<ConsoleEventImpl::Ptr> eventQueue; + std::vector<BrokerProxyImpl*> brokerList; + std::vector<std::pair<std::string, std::string> > bindingList; // exchange/key (empty exchange => QMF_EXCHANGE) + + // Declare a compare class for the class maps that compares the dereferenced + // class key pointers. The default behavior would be to compare the pointer + // addresses themselves. + struct KeyCompare { + bool operator()(const SchemaClassKeyImpl* left, const SchemaClassKeyImpl* right) const { + return *left < *right; + } + }; + + typedef std::map<const SchemaClassKeyImpl*, SchemaObjectClassImpl::Ptr, KeyCompare> ObjectClassList; + typedef std::map<const SchemaClassKeyImpl*, SchemaEventClassImpl::Ptr, KeyCompare> EventClassList; + typedef std::map<std::string, std::pair<ObjectClassList, EventClassList> > PackageList; + + PackageList packages; + + void learnPackage(const std::string& packageName); + void learnClass(SchemaObjectClassImpl::Ptr cls); + void learnClass(SchemaEventClassImpl::Ptr cls); + bool haveClass(const SchemaClassKeyImpl& key) const; + SchemaObjectClassImpl::Ptr getSchema(const SchemaClassKeyImpl& key) const; + }; +} + +#endif + diff --git a/qpid/cpp/src/qmf/Object.h b/qpid/cpp/src/qmf/Object.h index eb92cbbe45..db89dbb700 100644 --- a/qpid/cpp/src/qmf/Object.h +++ b/qpid/cpp/src/qmf/Object.h @@ -31,13 +31,15 @@ namespace qmf { public: Object(const SchemaObjectClass* type); Object(ObjectImpl* impl); + Object(const Object& from); virtual ~Object(); void destroy(); const ObjectId* getObjectId() const; void setObjectId(ObjectId* oid); const SchemaObjectClass* getClass() const; - Value* getValue(char* key); + Value* getValue(char* key) const; + void invokeMethod(const char* methodName, const Value* inArgs, void* context) const; ObjectImpl* impl; }; diff --git a/qpid/cpp/src/qmf/ObjectId.h b/qpid/cpp/src/qmf/ObjectId.h index ffd1b6978b..e894e0b39c 100644 --- a/qpid/cpp/src/qmf/ObjectId.h +++ b/qpid/cpp/src/qmf/ObjectId.h @@ -30,6 +30,7 @@ namespace qmf { class ObjectId { public: ObjectId(); + ObjectId(const ObjectId& from); ObjectId(ObjectIdImpl* impl); ~ObjectId(); diff --git a/qpid/cpp/src/qmf/ObjectIdImpl.cpp b/qpid/cpp/src/qmf/ObjectIdImpl.cpp index 75661fdb47..c0618ccc49 100644 --- a/qpid/cpp/src/qmf/ObjectIdImpl.cpp +++ b/qpid/cpp/src/qmf/ObjectIdImpl.cpp @@ -100,6 +100,15 @@ void ObjectIdImpl::fromString(const std::string& repr) agent = 0; } +std::string ObjectIdImpl::asString() const +{ + stringstream val; + + val << getFlags() << "-" << getSequence() << "-" << getBrokerBank() << "-" << + getAgentBank() << "-" << getObjectNum(); + return val.str(); +} + bool ObjectIdImpl::operator==(const ObjectIdImpl& other) const { uint64_t otherFirst = agent == 0 ? other.first : other.first & 0xffff000000000000LL; @@ -126,15 +135,11 @@ bool ObjectIdImpl::operator>(const ObjectIdImpl& other) const // Wrappers //================================================================== -ObjectId::ObjectId() -{ - impl = new ObjectIdImpl(this); -} +ObjectId::ObjectId() : impl(new ObjectIdImpl(this)) {} -ObjectId::ObjectId(ObjectIdImpl* i) -{ - impl = i; -} +ObjectId::ObjectId(const ObjectId& from) : impl(new ObjectIdImpl(*(from.impl))) {} + +ObjectId::ObjectId(ObjectIdImpl* i) : impl(i) {} ObjectId::~ObjectId() { diff --git a/qpid/cpp/src/qmf/ObjectIdImpl.h b/qpid/cpp/src/qmf/ObjectIdImpl.h index 5d8ee59aee..38d231237f 100644 --- a/qpid/cpp/src/qmf/ObjectIdImpl.h +++ b/qpid/cpp/src/qmf/ObjectIdImpl.h @@ -39,13 +39,14 @@ namespace qmf { uint64_t first; uint64_t second; - ObjectIdImpl(ObjectId* e) : envelope(e), agent(0) {} + ObjectIdImpl(ObjectId* e) : envelope(e), agent(0), first(0), second(0) {} ObjectIdImpl(qpid::framing::Buffer& buffer); ObjectIdImpl(AgentAttachment* agent, uint8_t flags, uint16_t seq, uint64_t object); void decode(qpid::framing::Buffer& buffer); void encode(qpid::framing::Buffer& buffer) const; void fromString(const std::string& repr); + std::string asString() const; uint8_t getFlags() const { return (first & 0xF000000000000000LL) >> 60; } uint16_t getSequence() const { return (first & 0x0FFF000000000000LL) >> 48; } uint32_t getBrokerBank() const { return (first & 0x0000FFFFF0000000LL) >> 28; } diff --git a/qpid/cpp/src/qmf/ObjectImpl.cpp b/qpid/cpp/src/qmf/ObjectImpl.cpp index d3882935e4..216a9d883e 100644 --- a/qpid/cpp/src/qmf/ObjectImpl.cpp +++ b/qpid/cpp/src/qmf/ObjectImpl.cpp @@ -19,6 +19,7 @@ #include "qmf/ObjectImpl.h" #include "qmf/ValueImpl.h" +#include "qmf/BrokerProxyImpl.h" #include <qpid/sys/Time.h> using namespace std; @@ -27,7 +28,7 @@ using namespace qpid::sys; using qpid::framing::Buffer; ObjectImpl::ObjectImpl(Object* e, const SchemaObjectClass* type) : - envelope(e), objectClass(type), createTime(uint64_t(Duration(now()))), + envelope(e), objectClass(type), broker(0), createTime(uint64_t(Duration(now()))), destroyTime(0), lastUpdatedTime(createTime) { int propCount = objectClass->getPropertyCount(); @@ -45,30 +46,40 @@ ObjectImpl::ObjectImpl(Object* e, const SchemaObjectClass* type) : } } -ObjectImpl::ObjectImpl(const SchemaObjectClass* type, Buffer& buffer) : - envelope(new Object(this)), objectClass(type), createTime(uint64_t(Duration(now()))), - destroyTime(0), lastUpdatedTime(createTime) +ObjectImpl::ObjectImpl(const SchemaObjectClass* type, BrokerProxyImpl* b, Buffer& buffer, bool prop, bool stat, bool managed) : + envelope(new Object(this)), objectClass(type), broker(b), createTime(0), destroyTime(0), lastUpdatedTime(0) { - int propCount = objectClass->getPropertyCount(); - int statCount = objectClass->getStatisticCount(); int idx; - set<string> excludes; - parsePresenceMasks(buffer, excludes); - for (idx = 0; idx < propCount; idx++) { - const SchemaProperty* prop = objectClass->getProperty(idx); - if (excludes.count(prop->getName()) != 0) { - properties[prop->getName()] = ValuePtr(new Value(prop->getType())); - } else { - ValueImpl* pval = new ValueImpl(prop->getType(), buffer); - properties[prop->getName()] = ValuePtr(pval->envelope); + if (managed) { + lastUpdatedTime = buffer.getLongLong(); + createTime = buffer.getLongLong(); + destroyTime = buffer.getLongLong(); + objectId.reset(new ObjectIdImpl(buffer)); + } + + if (prop) { + int propCount = objectClass->getPropertyCount(); + set<string> excludes; + parsePresenceMasks(buffer, excludes); + for (idx = 0; idx < propCount; idx++) { + const SchemaProperty* prop = objectClass->getProperty(idx); + if (excludes.count(prop->getName()) != 0) { + properties[prop->getName()] = ValuePtr(new Value(prop->getType())); + } else { + ValueImpl* pval = new ValueImpl(prop->getType(), buffer); + properties[prop->getName()] = ValuePtr(pval->envelope); + } } } - for (idx = 0; idx < statCount; idx++) { - const SchemaStatistic* stat = objectClass->getStatistic(idx); - ValueImpl* sval = new ValueImpl(stat->getType(), buffer); - statistics[stat->getName()] = ValuePtr(sval->envelope); + if (stat) { + int statCount = objectClass->getStatisticCount(); + for (idx = 0; idx < statCount; idx++) { + const SchemaStatistic* stat = objectClass->getStatistic(idx); + ValueImpl* sval = new ValueImpl(stat->getType(), buffer); + statistics[stat->getName()] = ValuePtr(sval->envelope); + } } } @@ -82,7 +93,7 @@ void ObjectImpl::destroy() // TODO - flag deletion } -Value* ObjectImpl::getValue(const string& key) +Value* ObjectImpl::getValue(const string& key) const { map<string, ValuePtr>::const_iterator iter; @@ -97,6 +108,12 @@ Value* ObjectImpl::getValue(const string& key) return 0; } +void ObjectImpl::invokeMethod(const string& methodName, const Value* inArgs, void* context) const +{ + if (broker != 0 && objectId.get() != 0) + broker->sendMethodRequest(objectId.get(), objectClass, methodName, inArgs, context); +} + void ObjectImpl::parsePresenceMasks(Buffer& buffer, set<string>& excludeList) { int propCount = objectClass->getPropertyCount(); @@ -123,9 +140,9 @@ void ObjectImpl::parsePresenceMasks(Buffer& buffer, set<string>& excludeList) void ObjectImpl::encodeSchemaKey(qpid::framing::Buffer& buffer) const { - buffer.putShortString(objectClass->getPackage()); - buffer.putShortString(objectClass->getName()); - buffer.putBin128(const_cast<uint8_t*>(objectClass->getHash())); + buffer.putShortString(objectClass->getClassKey()->getPackageName()); + buffer.putShortString(objectClass->getClassKey()->getClassName()); + buffer.putBin128(const_cast<uint8_t*>(objectClass->getClassKey()->getHash())); } void ObjectImpl::encodeManagedObjectData(qpid::framing::Buffer& buffer) const @@ -133,7 +150,7 @@ void ObjectImpl::encodeManagedObjectData(qpid::framing::Buffer& buffer) const buffer.putLongLong(lastUpdatedTime); buffer.putLongLong(createTime); buffer.putLongLong(destroyTime); - objectId->impl->encode(buffer); + objectId->encode(buffer); } void ObjectImpl::encodeProperties(qpid::framing::Buffer& buffer) const @@ -187,36 +204,13 @@ void ObjectImpl::encodeStatistics(qpid::framing::Buffer& buffer) const //================================================================== Object::Object(const SchemaObjectClass* type) : impl(new ObjectImpl(this, type)) {} - Object::Object(ObjectImpl* i) : impl(i) {} - -Object::~Object() -{ - delete impl; -} - -void Object::destroy() -{ - impl->destroy(); -} - -const ObjectId* Object::getObjectId() const -{ - return impl->getObjectId(); -} - -void Object::setObjectId(ObjectId* oid) -{ - impl->setObjectId(oid); -} - -const SchemaObjectClass* Object::getClass() const -{ - return impl->getClass(); -} - -Value* Object::getValue(char* key) -{ - return impl->getValue(key); -} +Object::Object(const Object& from) : impl(new ObjectImpl(*(from.impl))) {} +Object::~Object() { delete impl; } +void Object::destroy() { impl->destroy(); } +const ObjectId* Object::getObjectId() const { return impl->getObjectId(); } +void Object::setObjectId(ObjectId* oid) { impl->setObjectId(oid); } +const SchemaObjectClass* Object::getClass() const { return impl->getClass(); } +Value* Object::getValue(char* key) const { return impl->getValue(key); } +void Object::invokeMethod(const char* m, const Value* a, void* c) const { impl->invokeMethod(m, a, c); } diff --git a/qpid/cpp/src/qmf/ObjectImpl.h b/qpid/cpp/src/qmf/ObjectImpl.h index 4dc2170bfc..4ac5a31b54 100644 --- a/qpid/cpp/src/qmf/ObjectImpl.h +++ b/qpid/cpp/src/qmf/ObjectImpl.h @@ -21,19 +21,25 @@ */ #include <qmf/Object.h> +#include <qmf/ObjectIdImpl.h> #include <map> #include <set> #include <string> #include <qpid/framing/Buffer.h> #include <boost/shared_ptr.hpp> +#include <qpid/sys/Mutex.h> namespace qmf { + class BrokerProxyImpl; + struct ObjectImpl { + typedef boost::shared_ptr<ObjectImpl> Ptr; typedef boost::shared_ptr<Value> ValuePtr; Object* envelope; const SchemaObjectClass* objectClass; - boost::shared_ptr<ObjectId> objectId; + BrokerProxyImpl* broker; + boost::shared_ptr<ObjectIdImpl> objectId; uint64_t createTime; uint64_t destroyTime; uint64_t lastUpdatedTime; @@ -41,14 +47,16 @@ namespace qmf { mutable std::map<std::string, ValuePtr> statistics; ObjectImpl(Object* e, const SchemaObjectClass* type); - ObjectImpl(const SchemaObjectClass* type, qpid::framing::Buffer& buffer); + ObjectImpl(const SchemaObjectClass* type, BrokerProxyImpl* b, qpid::framing::Buffer& buffer, + bool prop, bool stat, bool managed); ~ObjectImpl(); void destroy(); - const ObjectId* getObjectId() const { return objectId.get(); } - void setObjectId(ObjectId* oid) { objectId.reset(oid); } + const ObjectId* getObjectId() const { return objectId.get() ? objectId->envelope : 0; } + void setObjectId(ObjectId* oid) { objectId.reset(oid->impl); } const SchemaObjectClass* getClass() const { return objectClass; } - Value* getValue(const std::string& key); + Value* getValue(const std::string& key) const; + void invokeMethod(const std::string& methodName, const Value* inArgs, void* context) const; void parsePresenceMasks(qpid::framing::Buffer& buffer, std::set<std::string>& excludeList); void encodeSchemaKey(qpid::framing::Buffer& buffer) const; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/network/FirewallFactory.java b/qpid/cpp/src/qmf/Protocol.cpp index a1a399e5bf..0a3beeb276 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/network/FirewallFactory.java +++ b/qpid/cpp/src/qmf/Protocol.cpp @@ -1,5 +1,4 @@ /* - * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information @@ -7,39 +6,47 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT 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.security.access.plugins.network; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.qpid.server.security.access.ACLPlugin; -import org.apache.qpid.server.security.access.ACLPluginFactory; +#include "qmf/Protocol.h" +#include "qpid/framing/Buffer.h" -public class FirewallFactory implements ACLPluginFactory +using namespace std; +using namespace qmf; +using namespace qpid::framing; + + +bool Protocol::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq) { + if (buf.available() < 8) + return false; + + uint8_t h1 = buf.getOctet(); + uint8_t h2 = buf.getOctet(); + uint8_t h3 = buf.getOctet(); - @Override - public ACLPlugin newInstance(Configuration config) throws ConfigurationException - { - FirewallPlugin plugin = new FirewallPlugin(); - plugin.setConfiguration(config); - return plugin; - } - - @Override - public boolean supportsTag(String name) - { - return name.equals("firewall"); - } - + *opcode = buf.getOctet(); + *seq = buf.getLong(); + + return h1 == 'A' && h2 == 'M' && h3 == '3'; } + +void Protocol::encodeHeader(qpid::framing::Buffer& buf, uint8_t opcode, uint32_t seq) +{ + buf.putOctet('A'); + buf.putOctet('M'); + buf.putOctet('3'); + buf.putOctet(opcode); + buf.putLong (seq); +} + + diff --git a/qpid/cpp/src/qmf/Protocol.h b/qpid/cpp/src/qmf/Protocol.h new file mode 100644 index 0000000000..d5da08c1db --- /dev/null +++ b/qpid/cpp/src/qmf/Protocol.h @@ -0,0 +1,67 @@ +#ifndef _QmfProtocol_ +#define _QmfProtocol_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <qpid/sys/IntegerTypes.h> + +namespace qpid { + namespace framing { + class Buffer; + } +} + +namespace qmf { + + class Protocol { + public: + static bool checkHeader(qpid::framing::Buffer& buf, uint8_t *opcode, uint32_t *seq); + static void encodeHeader(qpid::framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0); + + const static uint8_t OP_ATTACH_REQUEST = 'A'; + const static uint8_t OP_ATTACH_RESPONSE = 'a'; + + const static uint8_t OP_BROKER_REQUEST = 'B'; + const static uint8_t OP_BROKER_RESPONSE = 'b'; + + const static uint8_t OP_CONSOLE_ADDED_INDICATION = 'x'; + const static uint8_t OP_COMMAND_COMPLETE = 'z'; + const static uint8_t OP_HEARTBEAT_INDICATION = 'h'; + + const static uint8_t OP_PACKAGE_REQUEST = 'P'; + const static uint8_t OP_PACKAGE_INDICATION = 'p'; + const static uint8_t OP_CLASS_QUERY = 'Q'; + const static uint8_t OP_CLASS_INDICATION = 'q'; + const static uint8_t OP_SCHEMA_REQUEST = 'S'; + const static uint8_t OP_SCHEMA_RESPONSE = 's'; + + const static uint8_t OP_METHOD_REQUEST = 'M'; + const static uint8_t OP_METHOD_RESPONSE = 'm'; + const static uint8_t OP_GET_QUERY = 'G'; + const static uint8_t OP_OBJECT_INDICATION = 'g'; + const static uint8_t OP_PROPERTY_INDICATION = 'c'; + const static uint8_t OP_STATISTIC_INDICATION = 'i'; + const static uint8_t OP_EVENT_INDICATION = 'e'; + }; + +} + +#endif + diff --git a/qpid/cpp/src/qmf/Query.h b/qpid/cpp/src/qmf/Query.h index 78bc6f4ae2..875749862e 100644 --- a/qpid/cpp/src/qmf/Query.h +++ b/qpid/cpp/src/qmf/Query.h @@ -25,26 +25,76 @@ namespace qmf { + struct Object; + struct QueryElementImpl; struct QueryImpl; + struct QueryExpressionImpl; + struct SchemaClassKey; + + enum ValueOper { + O_EQ = 1, + O_NE = 2, + O_LT = 3, + O_LE = 4, + O_GT = 5, + O_GE = 6, + O_RE_MATCH = 7, + O_RE_NOMATCH = 8 + }; + + struct QueryOperand { + virtual ~QueryOperand() {} + virtual bool evaluate(const Object* object) const = 0; + }; + + struct QueryElement : public QueryOperand { + QueryElement(const char* attrName, const Value* value, ValueOper oper); + QueryElement(QueryElementImpl* impl); + virtual ~QueryElement(); + bool evaluate(const Object* object) const; + + QueryElementImpl* impl; + }; + + enum ExprOper { + E_NOT = 1, + E_AND = 2, + E_OR = 3, + E_XOR = 4 + }; + + struct QueryExpression : public QueryOperand { + QueryExpression(ExprOper oper, const QueryOperand* operand1, const QueryOperand* operand2); + QueryExpression(QueryExpressionImpl* impl); + virtual ~QueryExpression(); + bool evaluate(const Object* object) const; + + QueryExpressionImpl* impl; + }; + class Query { public: - Query(); + Query(const char* className, const char* packageName); + Query(const SchemaClassKey* key); + Query(const ObjectId* oid); Query(QueryImpl* impl); ~Query(); + void setSelect(const QueryOperand* criterion); + void setLimit(uint32_t maxResults); + void setOrderBy(const char* attrName, bool decreasing); + const char* getPackage() const; const char* getClass() const; const ObjectId* getObjectId() const; - enum Oper { - OPER_AND = 1, - OPER_OR = 2 - }; - - int whereCount() const; - Oper whereOper() const; - const char* whereKey() const; - const Value* whereValue() const; + bool haveSelect() const; + bool haveLimit() const; + bool haveOrderBy() const; + const QueryOperand* getSelect() const; + uint32_t getLimit() const; + const char* getOrderBy() const; + bool getDecreasing() const; QueryImpl* impl; }; diff --git a/qpid/cpp/src/qmf/QueryImpl.cpp b/qpid/cpp/src/qmf/QueryImpl.cpp index 7e827796bb..f75a9aa5d5 100644 --- a/qpid/cpp/src/qmf/QueryImpl.cpp +++ b/qpid/cpp/src/qmf/QueryImpl.cpp @@ -18,54 +18,77 @@ */ #include "qmf/QueryImpl.h" +#include "qmf/ObjectIdImpl.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/FieldTable.h" using namespace std; using namespace qmf; +using namespace qpid::framing; -//================================================================== -// Wrappers -//================================================================== - -Query::Query() : impl(new QueryImpl(this)) {} -Query::Query(QueryImpl* i) : impl(i) {} - -Query::~Query() +bool QueryElementImpl::evaluate(const Object* /*object*/) const { - delete impl; + // TODO: Implement this + return false; } -const char* Query::getPackage() const +bool QueryExpressionImpl::evaluate(const Object* /*object*/) const { - return impl->getPackage(); + // TODO: Implement this + return false; } -const char* Query::getClass() const +QueryImpl::QueryImpl(Buffer& buffer) { - return impl->getClass(); + FieldTable ft; + ft.decode(buffer); + // TODO } -const ObjectId* Query::getObjectId() const +void QueryImpl::encode(Buffer& buffer) const { - return impl->getObjectId(); -} + FieldTable ft; -int Query::whereCount() const -{ - return impl->whereCount(); -} + if (oid.get() != 0) { + ft.setString("_objectid", oid->impl->asString()); + } else { + if (!packageName.empty()) + ft.setString("_package", packageName); + ft.setString("_class", className); + } -Query::Oper Query::whereOper() const -{ - return impl->whereOper(); + ft.encode(buffer); } -const char* Query::whereKey() const -{ - return impl->whereKey(); -} -const Value* Query::whereValue() const -{ - return impl->whereValue(); -} +//================================================================== +// Wrappers +//================================================================== + +QueryElement::QueryElement(const char* attrName, const Value* value, ValueOper oper) : impl(new QueryElementImpl(attrName, value, oper)) {} +QueryElement::QueryElement(QueryElementImpl* i) : impl(i) {} +QueryElement::~QueryElement() { delete impl; } +bool QueryElement::evaluate(const Object* object) const { return impl->evaluate(object); } +QueryExpression::QueryExpression(ExprOper oper, const QueryOperand* operand1, const QueryOperand* operand2) : impl(new QueryExpressionImpl(oper, operand1, operand2)) {} +QueryExpression::QueryExpression(QueryExpressionImpl* i) : impl(i) {} +QueryExpression::~QueryExpression() { delete impl; } +bool QueryExpression::evaluate(const Object* object) const { return impl->evaluate(object); } +Query::Query(const char* className, const char* packageName) : impl(new QueryImpl(className, packageName)) {} +Query::Query(const SchemaClassKey* key) : impl(new QueryImpl(key)) {} +Query::Query(const ObjectId* oid) : impl(new QueryImpl(oid)) {} +Query::Query(QueryImpl* i) : impl(i) {} +Query::~Query() { delete impl; } +void Query::setSelect(const QueryOperand* criterion) { impl->setSelect(criterion); } +void Query::setLimit(uint32_t maxResults) { impl->setLimit(maxResults); } +void Query::setOrderBy(const char* attrName, bool decreasing) { impl->setOrderBy(attrName, decreasing); } +const char* Query::getPackage() const { return impl->getPackage().c_str(); } +const char* Query::getClass() const { return impl->getClass().c_str(); } +const ObjectId* Query::getObjectId() const { return impl->getObjectId(); } +bool Query::haveSelect() const { return impl->haveSelect(); } +bool Query::haveLimit() const { return impl->haveLimit(); } +bool Query::haveOrderBy() const { return impl->haveOrderBy(); } +const QueryOperand* Query::getSelect() const { return impl->getSelect(); } +uint32_t Query::getLimit() const { return impl->getLimit(); } +const char* Query::getOrderBy() const { return impl->getOrderBy().c_str(); } +bool Query::getDecreasing() const { return impl->getDecreasing(); } diff --git a/qpid/cpp/src/qmf/QueryImpl.h b/qpid/cpp/src/qmf/QueryImpl.h index 1cb9bfe554..4a56a457c0 100644 --- a/qpid/cpp/src/qmf/QueryImpl.h +++ b/qpid/cpp/src/qmf/QueryImpl.h @@ -20,28 +20,82 @@ * under the License. */ -#include <qmf/Query.h> +#include "qmf/Query.h" +#include "qmf/Schema.h" #include <string> #include <boost/shared_ptr.hpp> +namespace qpid { + namespace framing { + class Buffer; + } +} + namespace qmf { + struct QueryElementImpl { + QueryElementImpl(const std::string& a, const Value* v, ValueOper o) : + envelope(new QueryElement(this)), attrName(a), value(v), oper(o) {} + ~QueryElementImpl() {} + bool evaluate(const Object* object) const; + + QueryElement* envelope; + std::string attrName; + const Value* value; + ValueOper oper; + }; + + struct QueryExpressionImpl { + QueryExpressionImpl(ExprOper o, const QueryOperand* operand1, const QueryOperand* operand2) : + envelope(new QueryExpression(this)), oper(o), left(operand1), right(operand2) {} + ~QueryExpressionImpl() {} + bool evaluate(const Object* object) const; + + QueryExpression* envelope; + ExprOper oper; + const QueryOperand* left; + const QueryOperand* right; + }; + struct QueryImpl { - Query* envelope; - std::string packageName; - std::string className; - boost::shared_ptr<ObjectId> oid; + QueryImpl(Query* e) : envelope(e), select(0) {} + QueryImpl(const std::string& c, const std::string& p) : + envelope(new Query(this)), packageName(p), className(c) {} + QueryImpl(const SchemaClassKey* key) : + envelope(new Query(this)), packageName(key->getPackageName()), className(key->getClassName()) {} + QueryImpl(const ObjectId* oid) : + envelope(new Query(this)), oid(new ObjectId(*oid)) {} + QueryImpl(qpid::framing::Buffer& buffer); + ~QueryImpl() {}; - QueryImpl(Query* e) : envelope(e) {} + void setSelect(const QueryOperand* criterion) { select = criterion; } + void setLimit(uint32_t maxResults) { resultLimit = maxResults; } + void setOrderBy(const std::string& attrName, bool decreasing) { + orderBy = attrName; orderDecreasing = decreasing; + } - const char* getPackage() const { return packageName.empty() ? 0 : packageName.c_str(); } - const char* getClass() const { return className.empty() ? 0 : className.c_str(); } + const std::string& getPackage() const { return packageName; } + const std::string& getClass() const { return className; } const ObjectId* getObjectId() const { return oid.get(); } - int whereCount() const { return 0;} - Query::Oper whereOper() const { return Query::OPER_AND; } - const char* whereKey() const { return 0; } - const Value* whereValue() const { return 0; } + bool haveSelect() const { return select != 0; } + bool haveLimit() const { return resultLimit > 0; } + bool haveOrderBy() const { return !orderBy.empty(); } + const QueryOperand* getSelect() const { return select; } + uint32_t getLimit() const { return resultLimit; } + const std::string& getOrderBy() const { return orderBy; } + bool getDecreasing() const { return orderDecreasing; } + + void encode(qpid::framing::Buffer& buffer) const; + + Query* envelope; + std::string packageName; + std::string className; + boost::shared_ptr<ObjectId> oid; + const QueryOperand* select; + uint32_t resultLimit; + std::string orderBy; + bool orderDecreasing; }; } diff --git a/qpid/cpp/src/qmf/ResilientConnection.cpp b/qpid/cpp/src/qmf/ResilientConnection.cpp index 3531e104b0..7ec03cf4da 100644 --- a/qpid/cpp/src/qmf/ResilientConnection.cpp +++ b/qpid/cpp/src/qmf/ResilientConnection.cpp @@ -41,7 +41,7 @@ using namespace std; using namespace qmf; -using namespace qpid::client; +using namespace qpid; using qpid::sys::Mutex; namespace qmf { @@ -57,29 +57,29 @@ namespace qmf { ResilientConnectionEvent copy(); }; - struct RCSession : public MessageListener, public qpid::sys::Runnable, public qpid::RefCounted { + struct RCSession : public client::MessageListener, public qpid::sys::Runnable, public qpid::RefCounted { typedef boost::intrusive_ptr<RCSession> Ptr; ResilientConnectionImpl& connImpl; string name; - Connection& connection; - Session session; - SubscriptionManager* subscriptions; + client::Connection& connection; + client::Session session; + client::SubscriptionManager* subscriptions; void* userContext; vector<string> dests; qpid::sys::Thread thread; - RCSession(ResilientConnectionImpl& ci, const string& n, Connection& c, void* uc) : + RCSession(ResilientConnectionImpl& ci, const string& n, client::Connection& c, void* uc) : connImpl(ci), name(n), connection(c), session(connection.newSession(name)), - subscriptions(new SubscriptionManager(session)), userContext(uc), thread(*this) {} + subscriptions(new client::SubscriptionManager(session)), userContext(uc), thread(*this) {} ~RCSession(); - void received(qpid::client::Message& msg); + void received(client::Message& msg); void run(); void stop(); }; class ResilientConnectionImpl : public qpid::sys::Runnable { public: - ResilientConnectionImpl(ConnectionSettings& settings); + ResilientConnectionImpl(const ConnectionSettings& settings); ~ResilientConnectionImpl(); bool isConnected() const; @@ -108,8 +108,8 @@ namespace qmf { bool connected; bool shutdown; string lastError; - ConnectionSettings settings; - Connection connection; + const ConnectionSettings settings; + client::Connection connection; mutable qpid::sys::Mutex lock; int delayMin; int delayMax; @@ -156,7 +156,7 @@ void RCSession::stop() subscriptions->stop(); } -void RCSession::received(qpid::client::Message& msg) +void RCSession::received(client::Message& msg) { qmf::MessageImpl qmsg; qmsg.body = msg.getData(); @@ -175,8 +175,8 @@ void RCSession::received(qpid::client::Message& msg) connImpl.EnqueueEvent(ResilientConnectionEvent::RECV, userContext, qmsg); } -ResilientConnectionImpl::ResilientConnectionImpl(ConnectionSettings& _settings) : - notifyFd(-1), connected(false), shutdown(false), settings(_settings), connThread(*this) +ResilientConnectionImpl::ResilientConnectionImpl(const ConnectionSettings& _settings) : + notifyFd(-1), connected(false), shutdown(false), settings(_settings), delayMin(1), connThread(*this) { connection.registerFailureCallback(boost::bind(&ResilientConnectionImpl::failure, this)); settings.impl->getRetrySettings(&delayMin, &delayMax, &delayFactor); @@ -222,7 +222,7 @@ bool ResilientConnectionImpl::createSession(const char* name, void* sessionConte RCSession::Ptr sess = RCSession::Ptr(new RCSession(*this, name, connection, sessionContext)); - handle.handle = (void*) sess.get(); + handle.impl = (void*) sess.get(); sessions.insert(sess); return true; @@ -231,7 +231,7 @@ bool ResilientConnectionImpl::createSession(const char* name, void* sessionConte void ResilientConnectionImpl::destroySession(SessionHandle handle) { Mutex::ScopedLock _lock(lock); - RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.handle); + RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.impl); set<RCSession::Ptr>::iterator iter = sessions.find(sess); if (iter != sessions.end()) { for (vector<string>::iterator dIter = sess->dests.begin(); dIter != sess->dests.end(); dIter++) @@ -247,7 +247,7 @@ void ResilientConnectionImpl::destroySession(SessionHandle handle) void ResilientConnectionImpl::sendMessage(SessionHandle handle, qmf::Message& message) { Mutex::ScopedLock _lock(lock); - RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.handle); + RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.impl); set<RCSession::Ptr>::iterator iter = sessions.find(sess); qpid::client::Message msg; string data(message.body, message.length); @@ -256,7 +256,7 @@ void ResilientConnectionImpl::sendMessage(SessionHandle handle, qmf::Message& me msg.setData(data); try { - sess->session.messageTransfer(arg::content=msg, arg::destination=message.destination); + sess->session.messageTransfer(client::arg::content=msg, client::arg::destination=message.destination); } catch(exception& e) { QPID_LOG(error, "Session Exception during message-transfer: " << e.what()); sessions.erase(iter); @@ -267,19 +267,22 @@ void ResilientConnectionImpl::sendMessage(SessionHandle handle, qmf::Message& me void ResilientConnectionImpl::declareQueue(SessionHandle handle, char* queue) { Mutex::ScopedLock _lock(lock); - RCSession* sess = (RCSession*) handle.handle; + RCSession* sess = (RCSession*) handle.impl; - sess->session.queueDeclare(arg::queue=queue, arg::autoDelete=true, arg::exclusive=true); + sess->session.queueDeclare(client::arg::queue=queue, client::arg::autoDelete=true, client::arg::exclusive=true); + sess->subscriptions->setAcceptMode(client::ACCEPT_MODE_NONE); + sess->subscriptions->setAcquireMode(client::ACQUIRE_MODE_PRE_ACQUIRED); sess->subscriptions->subscribe(*sess, queue, queue); + sess->subscriptions->setFlowControl(queue, client::FlowControl::unlimited()); sess->dests.push_back(string(queue)); } void ResilientConnectionImpl::deleteQueue(SessionHandle handle, char* queue) { Mutex::ScopedLock _lock(lock); - RCSession* sess = (RCSession*) handle.handle; + RCSession* sess = (RCSession*) handle.impl; - sess->session.queueDelete(arg::queue=queue); + sess->session.queueDelete(client::arg::queue=queue); for (vector<string>::iterator iter = sess->dests.begin(); iter != sess->dests.end(); iter++) if (*iter == queue) { @@ -293,18 +296,18 @@ void ResilientConnectionImpl::bind(SessionHandle handle, char* exchange, char* queue, char* key) { Mutex::ScopedLock _lock(lock); - RCSession* sess = (RCSession*) handle.handle; + RCSession* sess = (RCSession*) handle.impl; - sess->session.exchangeBind(arg::exchange=exchange, arg::queue=queue, arg::bindingKey=key); + sess->session.exchangeBind(client::arg::exchange=exchange, client::arg::queue=queue, client::arg::bindingKey=key); } void ResilientConnectionImpl::unbind(SessionHandle handle, char* exchange, char* queue, char* key) { Mutex::ScopedLock _lock(lock); - RCSession* sess = (RCSession*) handle.handle; + RCSession* sess = (RCSession*) handle.impl; - sess->session.exchangeUnbind(arg::exchange=exchange, arg::queue=queue, arg::bindingKey=key); + sess->session.exchangeUnbind(client::arg::exchange=exchange, client::arg::queue=queue, client::arg::bindingKey=key); } void ResilientConnectionImpl::setNotifyFd(int fd) @@ -318,6 +321,7 @@ void ResilientConnectionImpl::run() while (true) { try { + QPID_LOG(trace, "Trying to open connection..."); connection.open(settings.impl->getClientSettings()); { Mutex::ScopedLock _lock(lock); @@ -326,6 +330,7 @@ void ResilientConnectionImpl::run() while (connected) cond.wait(lock); + delay = delayMin; while (!sessions.empty()) { set<RCSession::Ptr>::iterator iter = sessions.begin(); @@ -334,6 +339,11 @@ void ResilientConnectionImpl::run() EnqueueEvent(ResilientConnectionEvent::SESSION_CLOSED, sess->userContext); Mutex::ScopedUnlock _u(lock); sess->stop(); + + // Nullify the intrusive pointer within the scoped unlock, otherwise, + // the reference is held until overwritted above (under lock) which causes + // the session destructor to be called with the lock held. + sess = 0; } EnqueueEvent(ResilientConnectionEvent::DISCONNECTED); @@ -341,7 +351,6 @@ void ResilientConnectionImpl::run() if (shutdown) return; } - delay = delayMin; connection.close(); } catch (exception &e) { QPID_LOG(debug, "connection.open exception: " << e.what()); @@ -396,7 +405,7 @@ void ResilientConnectionImpl::EnqueueEvent(ResilientConnectionEvent::EventKind k // Wrappers //================================================================== -ResilientConnection::ResilientConnection(ConnectionSettings& settings) +ResilientConnection::ResilientConnection(const ConnectionSettings& settings) { impl = new ResilientConnectionImpl(settings); } diff --git a/qpid/cpp/src/qmf/ResilientConnection.h b/qpid/cpp/src/qmf/ResilientConnection.h index 6e05541253..03f1b9c0d5 100644 --- a/qpid/cpp/src/qmf/ResilientConnection.h +++ b/qpid/cpp/src/qmf/ResilientConnection.h @@ -26,6 +26,8 @@ namespace qmf { + class ResilientConnectionImpl; + /** * Represents events that occur, unsolicited, from ResilientConnection. */ @@ -43,12 +45,11 @@ namespace qmf { Message message; // RECV }; - struct SessionHandle { - void* handle; + class SessionHandle { + friend class ResilientConnectionImpl; + void* impl; }; - class ResilientConnectionImpl; - /** * ResilientConnection represents a Qpid connection that is resilient. * @@ -67,7 +68,7 @@ namespace qmf { *@param delayMax Maximum delay (in seconds) between retries. *@param delayFactor Factor to multiply retry delay by after each failure. */ - ResilientConnection(ConnectionSettings& settings); + ResilientConnection(const ConnectionSettings& settings); ~ResilientConnection(); /** diff --git a/qpid/cpp/src/qmf/Schema.h b/qpid/cpp/src/qmf/Schema.h index e3ab90e3e3..1123acc3b8 100644 --- a/qpid/cpp/src/qmf/Schema.h +++ b/qpid/cpp/src/qmf/Schema.h @@ -35,6 +35,7 @@ namespace qmf { struct SchemaStatisticImpl; struct SchemaObjectClassImpl; struct SchemaEventClassImpl; + struct SchemaClassKeyImpl; /** */ @@ -114,6 +115,20 @@ namespace qmf { /** */ + class SchemaClassKey { + public: + SchemaClassKey(SchemaClassKeyImpl* impl); + ~SchemaClassKey(); + + const char* getPackageName() const; + const char* getClassName() const; + const uint8_t* getHash() const; + + SchemaClassKeyImpl* impl; + }; + + /** + */ class SchemaObjectClass { public: SchemaObjectClass(const char* package, const char* name); @@ -123,9 +138,7 @@ namespace qmf { void addStatistic(const SchemaStatistic& statistic); void addMethod(const SchemaMethod& method); - const char* getPackage() const; - const char* getName() const; - const uint8_t* getHash() const; + const SchemaClassKey* getClassKey() const; int getPropertyCount() const; int getStatisticCount() const; int getMethodCount() const; @@ -146,9 +159,7 @@ namespace qmf { void addArgument(const SchemaArgument& argument); void setDesc(const char* desc); - const char* getPackage() const; - const char* getName() const; - const uint8_t* getHash() const; + const SchemaClassKey* getClassKey() const; int getArgumentCount() const; const SchemaArgument* getArgument(int idx) const; diff --git a/qpid/cpp/src/qmf/SchemaImpl.cpp b/qpid/cpp/src/qmf/SchemaImpl.cpp index 665c94f2a1..3eb14c3952 100644 --- a/qpid/cpp/src/qmf/SchemaImpl.cpp +++ b/qpid/cpp/src/qmf/SchemaImpl.cpp @@ -20,6 +20,8 @@ #include "qmf/SchemaImpl.h" #include <qpid/framing/Buffer.h> #include <qpid/framing/FieldTable.h> +#include <qpid/framing/Uuid.h> +#include <string.h> #include <string> #include <vector> @@ -27,6 +29,7 @@ using namespace std; using namespace qmf; using qpid::framing::Buffer; using qpid::framing::FieldTable; +using qpid::framing::Uuid; SchemaHash::SchemaHash() { @@ -34,7 +37,7 @@ SchemaHash::SchemaHash() hash[idx] = 0x5A; } -void SchemaHash::encode(Buffer& buffer) +void SchemaHash::encode(Buffer& buffer) const { buffer.putBin128(hash); } @@ -63,6 +66,21 @@ void SchemaHash::update(const char* data, uint32_t len) } } +bool SchemaHash::operator==(const SchemaHash& other) const +{ + return ::memcmp(&hash, &other.hash, 16) == 0; +} + +bool SchemaHash::operator<(const SchemaHash& other) const +{ + return ::memcmp(&hash, &other.hash, 16) < 0; +} + +bool SchemaHash::operator>(const SchemaHash& other) const +{ + return ::memcmp(&hash, &other.hash, 16) > 0; +} + SchemaArgumentImpl::SchemaArgumentImpl(Buffer& buffer) : envelope(new SchemaArgument(this)) { FieldTable map; @@ -240,15 +258,59 @@ void SchemaStatisticImpl::updateHash(SchemaHash& hash) const hash.update(description); } -SchemaObjectClassImpl::SchemaObjectClassImpl(Buffer& buffer) : envelope(new SchemaObjectClass(this)), hasHash(true) +SchemaClassKeyImpl::SchemaClassKeyImpl(const string& p, const string& n, const SchemaHash& h) : + envelope(new SchemaClassKey(this)), package(p), name(n), hash(h) {} + +SchemaClassKeyImpl::SchemaClassKeyImpl(Buffer& buffer) : + envelope(new SchemaClassKey(this)), package(packageContainer), name(nameContainer), hash(hashContainer) +{ + buffer.getShortString(packageContainer); + buffer.getShortString(nameContainer); + hashContainer.decode(buffer); +} + +void SchemaClassKeyImpl::encode(Buffer& buffer) const +{ + buffer.putShortString(package); + buffer.putShortString(name); + hash.encode(buffer); +} + +bool SchemaClassKeyImpl::operator==(const SchemaClassKeyImpl& other) const +{ + return package == other.package && + name == other.name && + hash == other.hash; +} + +bool SchemaClassKeyImpl::operator<(const SchemaClassKeyImpl& other) const +{ + if (package < other.package) return true; + if (package > other.package) return false; + if (name < other.name) return true; + if (name > other.name) return false; + return hash < other.hash; +} + +string SchemaClassKeyImpl::str() const +{ + Uuid printableHash(hash.get()); + stringstream str; + str << package << ":" << name << "(" << printableHash << ")"; + return str.str(); +} + +SchemaObjectClassImpl::SchemaObjectClassImpl(Buffer& buffer) : + envelope(new SchemaObjectClass(this)), hasHash(true), classKey(package, name, hash) { buffer.getShortString(package); buffer.getShortString(name); hash.decode(buffer); - uint16_t propCount = buffer.getShort(); - uint16_t statCount = buffer.getShort(); - uint16_t methodCount = buffer.getShort(); + /*uint8_t hasParentClass =*/ buffer.getOctet(); // TODO: Parse parent-class indicator + uint16_t propCount = buffer.getShort(); + uint16_t statCount = buffer.getShort(); + uint16_t methodCount = buffer.getShort(); for (uint16_t idx = 0; idx < propCount; idx++) { SchemaPropertyImpl* property = new SchemaPropertyImpl(buffer); @@ -288,7 +350,7 @@ void SchemaObjectClassImpl::encode(Buffer& buffer) const (*iter)->encode(buffer); } -const uint8_t* SchemaObjectClassImpl::getHash() const +const SchemaClassKey* SchemaObjectClassImpl::getClassKey() const { if (!hasHash) { hasHash = true; @@ -305,7 +367,7 @@ const uint8_t* SchemaObjectClassImpl::getHash() const (*iter)->updateHash(hash); } - return hash.get(); + return classKey.envelope; } void SchemaObjectClassImpl::addProperty(const SchemaProperty& property) @@ -353,13 +415,15 @@ const SchemaMethod* SchemaObjectClassImpl::getMethod(int idx) const return 0; } -SchemaEventClassImpl::SchemaEventClassImpl(Buffer& buffer) : envelope(new SchemaEventClass(this)), hasHash(true) +SchemaEventClassImpl::SchemaEventClassImpl(Buffer& buffer) : + envelope(new SchemaEventClass(this)), hasHash(true), classKey(package, name, hash) { buffer.getShortString(package); buffer.getShortString(name); hash.decode(buffer); + buffer.putOctet(0); // No parent class - uint16_t argCount = buffer.getShort(); + uint16_t argCount = buffer.getShort(); for (uint16_t idx = 0; idx < argCount; idx++) { SchemaArgumentImpl* argument = new SchemaArgumentImpl(buffer); @@ -380,7 +444,7 @@ void SchemaEventClassImpl::encode(Buffer& buffer) const (*iter)->encode(buffer); } -const uint8_t* SchemaEventClassImpl::getHash() const +const SchemaClassKey* SchemaEventClassImpl::getClassKey() const { if (!hasHash) { hasHash = true; @@ -390,7 +454,7 @@ const uint8_t* SchemaEventClassImpl::getHash() const iter != arguments.end(); iter++) (*iter)->updateHash(hash); } - return hash.get(); + return classKey.envelope; } void SchemaEventClassImpl::addArgument(const SchemaArgument& argument) @@ -408,334 +472,79 @@ const SchemaArgument* SchemaEventClassImpl::getArgument(int idx) const return 0; } + //================================================================== // Wrappers //================================================================== -SchemaArgument::SchemaArgument(const char* name, Typecode typecode) -{ - impl = new SchemaArgumentImpl(this, name, typecode); -} - +SchemaArgument::SchemaArgument(const char* name, Typecode typecode) { impl = new SchemaArgumentImpl(this, name, typecode); } SchemaArgument::SchemaArgument(SchemaArgumentImpl* i) : impl(i) {} - -SchemaArgument::~SchemaArgument() -{ - delete impl; -} - -void SchemaArgument::setDirection(Direction dir) -{ - impl->setDirection(dir); -} - -void SchemaArgument::setUnit(const char* val) -{ - impl->setUnit(val); -} - -void SchemaArgument::setDesc(const char* desc) -{ - impl->setDesc(desc); -} - -const char* SchemaArgument::getName() const -{ - return impl->getName().c_str(); -} - -Typecode SchemaArgument::getType() const -{ - return impl->getType(); -} - -Direction SchemaArgument::getDirection() const -{ - return impl->getDirection(); -} - -const char* SchemaArgument::getUnit() const -{ - return impl->getUnit().c_str(); -} - -const char* SchemaArgument::getDesc() const -{ - return impl->getDesc().c_str(); -} - -SchemaMethod::SchemaMethod(const char* name) -{ - impl = new SchemaMethodImpl(this, name); -} - +SchemaArgument::~SchemaArgument() { delete impl; } +void SchemaArgument::setDirection(Direction dir) { impl->setDirection(dir); } +void SchemaArgument::setUnit(const char* val) { impl->setUnit(val); } +void SchemaArgument::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaArgument::getName() const { return impl->getName().c_str(); } +Typecode SchemaArgument::getType() const { return impl->getType(); } +Direction SchemaArgument::getDirection() const { return impl->getDirection(); } +const char* SchemaArgument::getUnit() const { return impl->getUnit().c_str(); } +const char* SchemaArgument::getDesc() const { return impl->getDesc().c_str(); } +SchemaMethod::SchemaMethod(const char* name) { impl = new SchemaMethodImpl(this, name); } SchemaMethod::SchemaMethod(SchemaMethodImpl* i) : impl(i) {} - -SchemaMethod::~SchemaMethod() -{ - delete impl; -} - -void SchemaMethod::addArgument(const SchemaArgument& argument) -{ - impl->addArgument(argument); -} - -void SchemaMethod::setDesc(const char* desc) -{ - impl->setDesc(desc); -} - -const char* SchemaMethod::getName() const -{ - return impl->getName().c_str(); -} - -const char* SchemaMethod::getDesc() const -{ - return impl->getDesc().c_str(); -} - -int SchemaMethod::getArgumentCount() const -{ - return impl->getArgumentCount(); -} - -const SchemaArgument* SchemaMethod::getArgument(int idx) const -{ - return impl->getArgument(idx); -} - -SchemaProperty::SchemaProperty(const char* name, Typecode typecode) -{ - impl = new SchemaPropertyImpl(this, name, typecode); -} - +SchemaMethod::~SchemaMethod() { delete impl; } +void SchemaMethod::addArgument(const SchemaArgument& argument) { impl->addArgument(argument); } +void SchemaMethod::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaMethod::getName() const { return impl->getName().c_str(); } +const char* SchemaMethod::getDesc() const { return impl->getDesc().c_str(); } +int SchemaMethod::getArgumentCount() const { return impl->getArgumentCount(); } +const SchemaArgument* SchemaMethod::getArgument(int idx) const { return impl->getArgument(idx); } +SchemaProperty::SchemaProperty(const char* name, Typecode typecode) { impl = new SchemaPropertyImpl(this, name, typecode); } SchemaProperty::SchemaProperty(SchemaPropertyImpl* i) : impl(i) {} - -SchemaProperty::~SchemaProperty() -{ - delete impl; -} - -void SchemaProperty::setAccess(Access access) -{ - impl->setAccess(access); -} - -void SchemaProperty::setIndex(bool val) -{ - impl->setIndex(val); -} - -void SchemaProperty::setOptional(bool val) -{ - impl->setOptional(val); -} - -void SchemaProperty::setUnit(const char* val) -{ - impl->setUnit(val); -} - -void SchemaProperty::setDesc(const char* desc) -{ - impl->setDesc(desc); -} - -const char* SchemaProperty::getName() const -{ - return impl->getName().c_str(); -} - -Typecode SchemaProperty::getType() const -{ - return impl->getType(); -} - -Access SchemaProperty::getAccess() const -{ - return impl->getAccess(); -} - -bool SchemaProperty::isIndex() const -{ - return impl->isIndex(); -} - -bool SchemaProperty::isOptional() const -{ - return impl->isOptional(); -} - -const char* SchemaProperty::getUnit() const -{ - return impl->getUnit().c_str(); -} - -const char* SchemaProperty::getDesc() const -{ - return impl->getDesc().c_str(); -} - -SchemaStatistic::SchemaStatistic(const char* name, Typecode typecode) -{ - impl = new SchemaStatisticImpl(this, name, typecode); -} - +SchemaProperty::~SchemaProperty() { delete impl; } +void SchemaProperty::setAccess(Access access) { impl->setAccess(access); } +void SchemaProperty::setIndex(bool val) { impl->setIndex(val); } +void SchemaProperty::setOptional(bool val) { impl->setOptional(val); } +void SchemaProperty::setUnit(const char* val) { impl->setUnit(val); } +void SchemaProperty::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaProperty::getName() const { return impl->getName().c_str(); } +Typecode SchemaProperty::getType() const { return impl->getType(); } +Access SchemaProperty::getAccess() const { return impl->getAccess(); } +bool SchemaProperty::isIndex() const { return impl->isIndex(); } +bool SchemaProperty::isOptional() const { return impl->isOptional(); } +const char* SchemaProperty::getUnit() const { return impl->getUnit().c_str(); } +const char* SchemaProperty::getDesc() const { return impl->getDesc().c_str(); } +SchemaStatistic::SchemaStatistic(const char* name, Typecode typecode) { impl = new SchemaStatisticImpl(this, name, typecode); } SchemaStatistic::SchemaStatistic(SchemaStatisticImpl* i) : impl(i) {} - -SchemaStatistic::~SchemaStatistic() -{ - delete impl; -} - -void SchemaStatistic::setUnit(const char* val) -{ - impl->setUnit(val); -} - -void SchemaStatistic::setDesc(const char* desc) -{ - impl->setDesc(desc); -} - -const char* SchemaStatistic::getName() const -{ - return impl->getName().c_str(); -} - -Typecode SchemaStatistic::getType() const -{ - return impl->getType(); -} - -const char* SchemaStatistic::getUnit() const -{ - return impl->getUnit().c_str(); -} - -const char* SchemaStatistic::getDesc() const -{ - return impl->getDesc().c_str(); -} - -SchemaObjectClass::SchemaObjectClass(const char* package, const char* name) -{ - impl = new SchemaObjectClassImpl(this, package, name); -} - +SchemaStatistic::~SchemaStatistic() { delete impl; } +void SchemaStatistic::setUnit(const char* val) { impl->setUnit(val); } +void SchemaStatistic::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaStatistic::getName() const { return impl->getName().c_str(); } +Typecode SchemaStatistic::getType() const { return impl->getType(); } +const char* SchemaStatistic::getUnit() const { return impl->getUnit().c_str(); } +const char* SchemaStatistic::getDesc() const { return impl->getDesc().c_str(); } +SchemaClassKey::SchemaClassKey(SchemaClassKeyImpl* i) : impl(i) {} +SchemaClassKey::~SchemaClassKey() { delete impl; } +const char* SchemaClassKey::getPackageName() const { return impl->getPackageName().c_str(); } +const char* SchemaClassKey::getClassName() const { return impl->getClassName().c_str(); } +const uint8_t* SchemaClassKey::getHash() const { return impl->getHash(); } +SchemaObjectClass::SchemaObjectClass(const char* package, const char* name) { impl = new SchemaObjectClassImpl(this, package, name); } SchemaObjectClass::SchemaObjectClass(SchemaObjectClassImpl* i) : impl(i) {} - -SchemaObjectClass::~SchemaObjectClass() -{ - delete impl; -} - -void SchemaObjectClass::addProperty(const SchemaProperty& property) -{ - impl->addProperty(property); -} - -void SchemaObjectClass::addStatistic(const SchemaStatistic& statistic) -{ - impl->addStatistic(statistic); -} - -void SchemaObjectClass::addMethod(const SchemaMethod& method) -{ - impl->addMethod(method); -} - -const char* SchemaObjectClass::getPackage() const -{ - return impl->getPackage().c_str(); -} - -const char* SchemaObjectClass::getName() const -{ - return impl->getName().c_str(); -} - -const uint8_t* SchemaObjectClass::getHash() const -{ - return impl->getHash(); -} - -int SchemaObjectClass::getPropertyCount() const -{ - return impl->getPropertyCount(); -} - -int SchemaObjectClass::getStatisticCount() const -{ - return impl->getStatisticCount(); -} - -int SchemaObjectClass::getMethodCount() const -{ - return impl->getMethodCount(); -} - -const SchemaProperty* SchemaObjectClass::getProperty(int idx) const -{ - return impl->getProperty(idx); -} - -const SchemaStatistic* SchemaObjectClass::getStatistic(int idx) const -{ - return impl->getStatistic(idx); -} - -const SchemaMethod* SchemaObjectClass::getMethod(int idx) const -{ - return impl->getMethod(idx); -} - -SchemaEventClass::SchemaEventClass(const char* package, const char* name) -{ - impl = new SchemaEventClassImpl(this, package, name); -} - +SchemaObjectClass::~SchemaObjectClass() { delete impl; } +void SchemaObjectClass::addProperty(const SchemaProperty& property) { impl->addProperty(property); } +void SchemaObjectClass::addStatistic(const SchemaStatistic& statistic) { impl->addStatistic(statistic); } +void SchemaObjectClass::addMethod(const SchemaMethod& method) { impl->addMethod(method); } +const SchemaClassKey* SchemaObjectClass::getClassKey() const { return impl->getClassKey(); } +int SchemaObjectClass::getPropertyCount() const { return impl->getPropertyCount(); } +int SchemaObjectClass::getStatisticCount() const { return impl->getStatisticCount(); } +int SchemaObjectClass::getMethodCount() const { return impl->getMethodCount(); } +const SchemaProperty* SchemaObjectClass::getProperty(int idx) const { return impl->getProperty(idx); } +const SchemaStatistic* SchemaObjectClass::getStatistic(int idx) const { return impl->getStatistic(idx); } +const SchemaMethod* SchemaObjectClass::getMethod(int idx) const { return impl->getMethod(idx); } +SchemaEventClass::SchemaEventClass(const char* package, const char* name) { impl = new SchemaEventClassImpl(this, package, name); } SchemaEventClass::SchemaEventClass(SchemaEventClassImpl* i) : impl(i) {} - -SchemaEventClass::~SchemaEventClass() -{ - delete impl; -} - -void SchemaEventClass::addArgument(const SchemaArgument& argument) -{ - impl->addArgument(argument); -} - -void SchemaEventClass::setDesc(const char* desc) -{ - impl->setDesc(desc); -} - -const char* SchemaEventClass::getPackage() const -{ - return impl->getPackage().c_str(); -} - -const char* SchemaEventClass::getName() const -{ - return impl->getName().c_str(); -} - -const uint8_t* SchemaEventClass::getHash() const -{ - return impl->getHash(); -} - -int SchemaEventClass::getArgumentCount() const -{ - return impl->getArgumentCount(); -} - -const SchemaArgument* SchemaEventClass::getArgument(int idx) const -{ - return impl->getArgument(idx); -} +SchemaEventClass::~SchemaEventClass() { delete impl; } +void SchemaEventClass::addArgument(const SchemaArgument& argument) { impl->addArgument(argument); } +void SchemaEventClass::setDesc(const char* desc) { impl->setDesc(desc); } +const SchemaClassKey* SchemaEventClass::getClassKey() const { return impl->getClassKey(); } +int SchemaEventClass::getArgumentCount() const { return impl->getArgumentCount(); } +const SchemaArgument* SchemaEventClass::getArgument(int idx) const { return impl->getArgument(idx); } diff --git a/qpid/cpp/src/qmf/SchemaImpl.h b/qpid/cpp/src/qmf/SchemaImpl.h index 2c30a8851f..035d99aecd 100644 --- a/qpid/cpp/src/qmf/SchemaImpl.h +++ b/qpid/cpp/src/qmf/SchemaImpl.h @@ -21,6 +21,7 @@ */ #include "qmf/Schema.h" +#include <boost/shared_ptr.hpp> #include <string> #include <vector> #include <qpid/framing/Buffer.h> @@ -35,7 +36,7 @@ namespace qmf { uint8_t hash[16]; public: SchemaHash(); - void encode(qpid::framing::Buffer& buffer); + void encode(qpid::framing::Buffer& buffer) const; void decode(qpid::framing::Buffer& buffer); void update(const char* data, uint32_t len); void update(uint8_t data); @@ -45,6 +46,9 @@ namespace qmf { void update(Access a) { update((uint8_t) a); } void update(bool b) { update((uint8_t) (b ? 1 : 0)); } const uint8_t* get() const { return hash; } + bool operator==(const SchemaHash& other) const; + bool operator<(const SchemaHash& other) const; + bool operator>(const SchemaHash& other) const; }; struct SchemaArgumentImpl { @@ -138,27 +142,52 @@ namespace qmf { void updateHash(SchemaHash& hash) const; }; + struct SchemaClassKeyImpl { + const SchemaClassKey* envelope; + const std::string& package; + const std::string& name; + const SchemaHash& hash; + + // The *Container elements are only used if there isn't an external place to + // store these values. + std::string packageContainer; + std::string nameContainer; + SchemaHash hashContainer; + + SchemaClassKeyImpl(const std::string& package, const std::string& name, const SchemaHash& hash); + SchemaClassKeyImpl(qpid::framing::Buffer& buffer); + + const std::string& getPackageName() const { return package; } + const std::string& getClassName() const { return name; } + const uint8_t* getHash() const { return hash.get(); } + + void encode(qpid::framing::Buffer& buffer) const; + bool operator==(const SchemaClassKeyImpl& other) const; + bool operator<(const SchemaClassKeyImpl& other) const; + std::string str() const; + }; + struct SchemaObjectClassImpl { + typedef boost::shared_ptr<SchemaObjectClassImpl> Ptr; SchemaObjectClass* envelope; std::string package; std::string name; mutable SchemaHash hash; mutable bool hasHash; + SchemaClassKeyImpl classKey; std::vector<SchemaPropertyImpl*> properties; std::vector<SchemaStatisticImpl*> statistics; std::vector<SchemaMethodImpl*> methods; SchemaObjectClassImpl(SchemaObjectClass* e, const char* p, const char* n) : - envelope(e), package(p), name(n), hasHash(false) {} + envelope(e), package(p), name(n), hasHash(false), classKey(package, name, hash) {} SchemaObjectClassImpl(qpid::framing::Buffer& buffer); void encode(qpid::framing::Buffer& buffer) const; void addProperty(const SchemaProperty& property); void addStatistic(const SchemaStatistic& statistic); void addMethod(const SchemaMethod& method); - const std::string& getPackage() const { return package; } - const std::string& getName() const { return name; } - const uint8_t* getHash() const; + const SchemaClassKey* getClassKey() const; int getPropertyCount() const { return properties.size(); } int getStatisticCount() const { return statistics.size(); } int getMethodCount() const { return methods.size(); } @@ -168,24 +197,24 @@ namespace qmf { }; struct SchemaEventClassImpl { + typedef boost::shared_ptr<SchemaEventClassImpl> Ptr; SchemaEventClass* envelope; std::string package; std::string name; mutable SchemaHash hash; mutable bool hasHash; + SchemaClassKeyImpl classKey; std::string description; std::vector<SchemaArgumentImpl*> arguments; SchemaEventClassImpl(SchemaEventClass* e, const char* p, const char* n) : - envelope(e), package(p), name(n), hasHash(false) {} + envelope(e), package(p), name(n), hasHash(false), classKey(package, name, hash) {} SchemaEventClassImpl(qpid::framing::Buffer& buffer); void encode(qpid::framing::Buffer& buffer) const; void addArgument(const SchemaArgument& argument); void setDesc(const char* desc) { description = desc; } - const std::string& getPackage() const { return package; } - const std::string& getName() const { return name; } - const uint8_t* getHash() const; + const SchemaClassKey* getClassKey() const; int getArgumentCount() const { return arguments.size(); } const SchemaArgument* getArgument(int idx) const; }; diff --git a/qpid/cpp/src/qmf/SequenceManager.cpp b/qpid/cpp/src/qmf/SequenceManager.cpp new file mode 100644 index 0000000000..3171e66fac --- /dev/null +++ b/qpid/cpp/src/qmf/SequenceManager.cpp @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qmf/SequenceManager.h" + +using namespace std; +using namespace qmf; +using namespace qpid::sys; + +SequenceManager::SequenceManager() : nextSequence(1) {} + +void SequenceManager::setUnsolicitedContext(SequenceContext::Ptr ctx) +{ + unsolicitedContext = ctx; +} + +uint32_t SequenceManager::reserve(SequenceContext::Ptr ctx) +{ + Mutex::ScopedLock _lock(lock); + if (ctx.get() == 0) + ctx = unsolicitedContext; + uint32_t seq = nextSequence; + while (contextMap.find(seq) != contextMap.end()) + seq = seq < 0xFFFFFFFF ? seq + 1 : 1; + nextSequence = seq < 0xFFFFFFFF ? seq + 1 : 1; + contextMap[seq] = ctx; + ctx->reserve(); + return seq; +} + +void SequenceManager::release(uint32_t sequence) +{ + Mutex::ScopedLock _lock(lock); + + if (sequence == 0) { + if (unsolicitedContext.get() != 0) + unsolicitedContext->release(); + return; + } + + map<uint32_t, SequenceContext::Ptr>::iterator iter = contextMap.find(sequence); + if (iter != contextMap.end()) { + if (iter->second != 0) + iter->second->release(); + contextMap.erase(iter); + } +} + +void SequenceManager::releaseAll() +{ + Mutex::ScopedLock _lock(lock); + contextMap.clear(); +} + +void SequenceManager::dispatch(uint8_t opcode, uint32_t sequence, qpid::framing::Buffer& buffer) +{ + Mutex::ScopedLock _lock(lock); + bool done; + + if (sequence == 0) { + if (unsolicitedContext.get() != 0) { + done = unsolicitedContext->handleMessage(opcode, sequence, buffer); + if (done) + unsolicitedContext->release(); + } + return; + } + + map<uint32_t, SequenceContext::Ptr>::iterator iter = contextMap.find(sequence); + if (iter != contextMap.end()) { + if (iter->second != 0) { + done = iter->second->handleMessage(opcode, sequence, buffer); + if (done) { + iter->second->release(); + contextMap.erase(iter); + } + } + } +} + diff --git a/qpid/cpp/src/qmf/SequenceManager.h b/qpid/cpp/src/qmf/SequenceManager.h new file mode 100644 index 0000000000..bbfd0728a7 --- /dev/null +++ b/qpid/cpp/src/qmf/SequenceManager.h @@ -0,0 +1,66 @@ +#ifndef _QmfSequenceManager_ +#define _QmfSequenceManager_ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "qpid/sys/Mutex.h" +#include <boost/shared_ptr.hpp> +#include <map> + +namespace qpid { + namespace framing { + class Buffer; + } +} + +namespace qmf { + + class SequenceContext { + public: + typedef boost::shared_ptr<SequenceContext> Ptr; + SequenceContext() {} + virtual ~SequenceContext() {} + + virtual void reserve() = 0; + virtual bool handleMessage(uint8_t opcode, uint32_t sequence, qpid::framing::Buffer& buffer) = 0; + virtual void release() = 0; + }; + + class SequenceManager { + public: + SequenceManager(); + + void setUnsolicitedContext(SequenceContext::Ptr ctx); + uint32_t reserve(SequenceContext::Ptr ctx = SequenceContext::Ptr()); + void release(uint32_t sequence); + void releaseAll(); + void dispatch(uint8_t opcode, uint32_t sequence, qpid::framing::Buffer& buffer); + + private: + mutable qpid::sys::Mutex lock; + uint32_t nextSequence; + SequenceContext::Ptr unsolicitedContext; + std::map<uint32_t, SequenceContext::Ptr> contextMap; + }; + +} + +#endif + diff --git a/qpid/cpp/src/qmf/Value.h b/qpid/cpp/src/qmf/Value.h index a45df14ea9..2ec2149110 100644 --- a/qpid/cpp/src/qmf/Value.h +++ b/qpid/cpp/src/qmf/Value.h @@ -30,7 +30,8 @@ namespace qmf { class Value { public: - Value(); + // Value(); + Value(const Value& from); Value(Typecode t, Typecode arrayType = TYPE_UINT8); Value(ValueImpl* impl); ~Value(); diff --git a/qpid/cpp/src/qmf/ValueImpl.cpp b/qpid/cpp/src/qmf/ValueImpl.cpp index f42c85eb33..b03d222835 100644 --- a/qpid/cpp/src/qmf/ValueImpl.cpp +++ b/qpid/cpp/src/qmf/ValueImpl.cpp @@ -67,6 +67,11 @@ ValueImpl::ValueImpl(Typecode t, Buffer& buf) : envelope(new Value(this)), typec } } +ValueImpl::ValueImpl(Value* e, Typecode t, Typecode at) : + envelope(e), typecode(t), valid(false), arrayTypecode(at) +{ +} + ValueImpl::ValueImpl(Typecode t) : envelope(new Value(this)), typecode(t) { ::memset(&value, 0, sizeof(value)); @@ -186,293 +191,64 @@ void ValueImpl::deleteArrayItem(uint32_t) // Wrappers //================================================================== -Value::Value(Typecode t, Typecode at) -{ - impl = new ValueImpl(this, t, at); -} - -Value::Value(ValueImpl* i) -{ - impl = i; -} - -Value::~Value() -{ - delete impl; -} - -Typecode Value::getType() const -{ - return impl->getType(); -} - -bool Value::isNull() const -{ - return impl->isNull(); -} - -void Value::setNull() -{ - impl->setNull(); -} - -bool Value::isObjectId() const -{ - return impl->isObjectId(); -} - -const ObjectId& Value::asObjectId() const -{ - return impl->asObjectId(); -} - -void Value::setObjectId(const ObjectId& oid) -{ - impl->setObjectId(oid); -} - -bool Value::isUint() const -{ - return impl->isUint(); -} - -uint32_t Value::asUint() const -{ - return impl->asUint(); -} - -void Value::setUint(uint32_t val) -{ - impl->setUint(val); -} - -bool Value::isInt() const -{ - return impl->isInt(); -} - -int32_t Value::asInt() const -{ - return impl->asInt(); -} - -void Value::setInt(int32_t val) -{ - impl->setInt(val); -} - -bool Value::isUint64() const -{ - return impl->isUint64(); -} - -uint64_t Value::asUint64() const -{ - return impl->asUint64(); -} - -void Value::setUint64(uint64_t val) -{ - impl->setUint64(val); -} - -bool Value::isInt64() const -{ - return impl->isInt64(); -} - -int64_t Value::asInt64() const -{ - return impl->asInt64(); -} - -void Value::setInt64(int64_t val) -{ - impl->setInt64(val); -} - -bool Value::isString() const -{ - return impl->isString(); -} - -const char* Value::asString() const -{ - return impl->asString(); -} - -void Value::setString(const char* val) -{ - impl->setString(val); -} - -bool Value::isBool() const -{ - return impl->isBool(); -} - -bool Value::asBool() const -{ - return impl->asBool(); -} - -void Value::setBool(bool val) -{ - impl->setBool(val); -} - -bool Value::isFloat() const -{ - return impl->isFloat(); -} - -float Value::asFloat() const -{ - return impl->asFloat(); -} - -void Value::setFloat(float val) -{ - impl->setFloat(val); -} - -bool Value::isDouble() const -{ - return impl->isDouble(); -} - -double Value::asDouble() const -{ - return impl->asDouble(); -} - -void Value::setDouble(double val) -{ - impl->setDouble(val); -} - -bool Value::isUuid() const -{ - return impl->isUuid(); -} - -const uint8_t* Value::asUuid() const -{ - return impl->asUuid(); -} - -void Value::setUuid(const uint8_t* val) -{ - impl->setUuid(val); -} - -bool Value::isObject() const -{ - return impl->isObject(); -} - -Object* Value::asObject() const -{ - return impl->asObject(); -} - -void Value::setObject(Object* val) -{ - impl->setObject(val); -} - -bool Value::isMap() const -{ - return impl->isMap(); -} - -bool Value::keyInMap(const char* key) const -{ - return impl->keyInMap(key); -} - -Value* Value::byKey(const char* key) -{ - return impl->byKey(key); -} - -const Value* Value::byKey(const char* key) const -{ - return impl->byKey(key); -} - -void Value::deleteKey(const char* key) -{ - impl->deleteKey(key); -} - -void Value::insert(const char* key, Value* val) -{ - impl->insert(key, val); -} - -uint32_t Value::keyCount() const -{ - return impl->keyCount(); -} - -const char* Value::key(uint32_t idx) const -{ - return impl->key(idx); -} - -bool Value::isList() const -{ - return impl->isList(); -} - -uint32_t Value::listItemCount() const -{ - return impl->listItemCount(); -} - -Value* Value::listItem(uint32_t idx) -{ - return impl->listItem(idx); -} - -void Value::appendToList(Value* val) -{ - impl->appendToList(val); -} - -void Value::deleteListItem(uint32_t idx) -{ - impl->deleteListItem(idx); -} - -bool Value::isArray() const -{ - return impl->isArray(); -} - -Typecode Value::arrayType() const -{ - return impl->arrayType(); -} - -uint32_t Value::arrayItemCount() const -{ - return impl->arrayItemCount(); -} - -Value* Value::arrayItem(uint32_t idx) -{ - return impl->arrayItem(idx); -} - -void Value::appendToArray(Value* val) -{ - impl->appendToArray(val); -} - -void Value::deleteArrayItem(uint32_t idx) -{ - impl->deleteArrayItem(idx); -} +Value::Value(const Value& from) : impl(new ValueImpl(*(from.impl))) {} +Value::Value(Typecode t, Typecode at) : impl(new ValueImpl(this, t, at)) {} +Value::Value(ValueImpl* i) : impl(i) {} +Value::~Value() { delete impl; } + +Typecode Value::getType() const { return impl->getType(); } +bool Value::isNull() const { return impl->isNull(); } +void Value::setNull() { impl->setNull(); } +bool Value::isObjectId() const { return impl->isObjectId(); } +const ObjectId& Value::asObjectId() const { return impl->asObjectId(); } +void Value::setObjectId(const ObjectId& oid) { impl->setObjectId(oid); } +bool Value::isUint() const { return impl->isUint(); } +uint32_t Value::asUint() const { return impl->asUint(); } +void Value::setUint(uint32_t val) { impl->setUint(val); } +bool Value::isInt() const { return impl->isInt(); } +int32_t Value::asInt() const { return impl->asInt(); } +void Value::setInt(int32_t val) { impl->setInt(val); } +bool Value::isUint64() const { return impl->isUint64(); } +uint64_t Value::asUint64() const { return impl->asUint64(); } +void Value::setUint64(uint64_t val) { impl->setUint64(val); } +bool Value::isInt64() const { return impl->isInt64(); } +int64_t Value::asInt64() const { return impl->asInt64(); } +void Value::setInt64(int64_t val) { impl->setInt64(val); } +bool Value::isString() const { return impl->isString(); } +const char* Value::asString() const { return impl->asString(); } +void Value::setString(const char* val) { impl->setString(val); } +bool Value::isBool() const { return impl->isBool(); } +bool Value::asBool() const { return impl->asBool(); } +void Value::setBool(bool val) { impl->setBool(val); } +bool Value::isFloat() const { return impl->isFloat(); } +float Value::asFloat() const { return impl->asFloat(); } +void Value::setFloat(float val) { impl->setFloat(val); } +bool Value::isDouble() const { return impl->isDouble(); } +double Value::asDouble() const { return impl->asDouble(); } +void Value::setDouble(double val) { impl->setDouble(val); } +bool Value::isUuid() const { return impl->isUuid(); } +const uint8_t* Value::asUuid() const { return impl->asUuid(); } +void Value::setUuid(const uint8_t* val) { impl->setUuid(val); } +bool Value::isObject() const { return impl->isObject(); } +Object* Value::asObject() const { return impl->asObject(); } +void Value::setObject(Object* val) { impl->setObject(val); } +bool Value::isMap() const { return impl->isMap(); } +bool Value::keyInMap(const char* key) const { return impl->keyInMap(key); } +Value* Value::byKey(const char* key) { return impl->byKey(key); } +const Value* Value::byKey(const char* key) const { return impl->byKey(key); } +void Value::deleteKey(const char* key) { impl->deleteKey(key); } +void Value::insert(const char* key, Value* val) { impl->insert(key, val); } +uint32_t Value::keyCount() const { return impl->keyCount(); } +const char* Value::key(uint32_t idx) const { return impl->key(idx); } +bool Value::isList() const { return impl->isList(); } +uint32_t Value::listItemCount() const { return impl->listItemCount(); } +Value* Value::listItem(uint32_t idx) { return impl->listItem(idx); } +void Value::appendToList(Value* val) { impl->appendToList(val); } +void Value::deleteListItem(uint32_t idx) { impl->deleteListItem(idx); } +bool Value::isArray() const { return impl->isArray(); } +Typecode Value::arrayType() const { return impl->arrayType(); } +uint32_t Value::arrayItemCount() const { return impl->arrayItemCount(); } +Value* Value::arrayItem(uint32_t idx) { return impl->arrayItem(idx); } +void Value::appendToArray(Value* val) { impl->appendToArray(val); } +void Value::deleteArrayItem(uint32_t idx) { impl->deleteArrayItem(idx); } diff --git a/qpid/cpp/src/qmf/ValueImpl.h b/qpid/cpp/src/qmf/ValueImpl.h index cf33035bf7..5be4cbc205 100644 --- a/qpid/cpp/src/qmf/ValueImpl.h +++ b/qpid/cpp/src/qmf/ValueImpl.h @@ -60,8 +60,7 @@ namespace qmf { uint8_t uuidVal[16]; } value; - ValueImpl(Value* e, Typecode t, Typecode at) : - envelope(e), typecode(t), valid(false), arrayTypecode(at) {} + ValueImpl(Value* e, Typecode t, Typecode at); ValueImpl(Typecode t, qpid::framing::Buffer& b); ValueImpl(Typecode t); ~ValueImpl(); diff --git a/qpid/cpp/src/qpid/acl/AclData.cpp b/qpid/cpp/src/qpid/acl/AclData.cpp index d2a55c0027..81519c3311 100644 --- a/qpid/cpp/src/qpid/acl/AclData.cpp +++ b/qpid/cpp/src/qpid/acl/AclData.cpp @@ -53,42 +53,65 @@ bool AclData::matchProp(const std::string & src, const std::string& src1) } } -AclResult AclData::lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& name, std::map<Property, std::string>* params) -{ - AclResult aclresult = decisionMode; - - if (actionList[action] && actionList[action][objType]){ - AclData::actObjItr itrRule = actionList[action][objType]->find(id); - if (itrRule == actionList[action][objType]->end()) - itrRule = actionList[action][objType]->find("*"); - if (itrRule != actionList[action][objType]->end() ) { - - //loop the vector - for (ruleSetItr i=itrRule->second.begin(); i<itrRule->second.end(); i++) { - - // loop the names looking for match - bool match =true; - for (propertyMapItr pMItr = i->props.begin(); (pMItr != i->props.end()) && match; pMItr++) - { - //match name is exists first - if (pMItr->first == acl::PROP_NAME){ - if (!matchProp(pMItr->second, name)){ - match= false; - } - }else if (params){ //match pMItr against params - propertyMapItr paramItr = params->find (pMItr->first); - if (paramItr == params->end()){ - match = false; - }else if (!matchProp(paramItr->second, pMItr->second)){ - match = false; - } +AclResult AclData::lookup(const std::string& id, const Action& action, const ObjectType& objType, + const std::string& name, std::map<Property, std::string>* params) { + + QPID_LOG(debug, "ACL: Lookup for id:" << id << " action:" << AclHelper::getActionStr((Action) action) + << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType) << " name:" << name + << " with params " << AclHelper::propertyMapToString(params)); + + AclResult aclresult = decisionMode; + if (actionList[action] && actionList[action][objType]) { + AclData::actObjItr itrRule = actionList[action][objType]->find(id); + if (itrRule == actionList[action][objType]->end()) + itrRule = actionList[action][objType]->find("*"); + if (itrRule != actionList[action][objType]->end()) { + + QPID_LOG(debug, "ACL: checking the following rules for : " << itrRule->first ); + + //loop the vector + for (ruleSetItr i = itrRule->second.begin(); i < itrRule->second.end(); i++) { + QPID_LOG(debug, "ACL: checking rule " << i->toString()); + // loop the names looking for match + bool match = true; + for (propertyMapItr pMItr = i->props.begin(); (pMItr != i->props.end()) && match; pMItr++) { + //match name is exists first + if (pMItr->first == acl::PROP_NAME) { + if (matchProp(pMItr->second, name)){ + QPID_LOG(debug, "ACL: name '" << name << "' matched with name '" + << pMItr->second << "' given in the rule"); + }else{ + match = false; + QPID_LOG(debug, "ACL: name '" << name << "' didn't match with name '" + << pMItr->second << "' given in the rule"); + } + } else if (params) { //match pMItr against params + propertyMapItr paramItr = params->find(pMItr->first); + if (paramItr == params->end()) { + match = false; + QPID_LOG(debug, "ACL: the given parameter map in lookup doesn't contain the property '" + << AclHelper::getPropertyStr(pMItr->first) << "'"); + } else if (!matchProp(pMItr->second, paramItr->second)) { + QPID_LOG(debug, "ACL: the pair(" + << AclHelper::getPropertyStr(paramItr->first) << "," << paramItr->second + << ") given in lookup doesn't match the pair(" + << AclHelper::getPropertyStr(pMItr->first) << "," << pMItr->second << ") given in the rule"); + match = false; } } - if (match) return getACLResult(i->logOnly, i->log); - } - } - } - return aclresult; + } + if (match) + { + aclresult = getACLResult(i->logOnly, i->log); + QPID_LOG(debug,"Successful match, the decision is:" << AclHelper::getAclResultStr(aclresult)); + return aclresult; + } + } + } + } + + QPID_LOG(debug,"No successful match, defaulting to the decision mode " << AclHelper::getAclResultStr(aclresult)); + return aclresult; } AclResult AclData::lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& /*Exchange*/ name, const std::string& RoutingKey) diff --git a/qpid/cpp/src/qpid/acl/AclData.h b/qpid/cpp/src/qpid/acl/AclData.h index 249c3523eb..a63afab12b 100644 --- a/qpid/cpp/src/qpid/acl/AclData.h +++ b/qpid/cpp/src/qpid/acl/AclData.h @@ -22,7 +22,7 @@ #include "qpid/broker/AclModule.h" #include <vector> - +#include <sstream> namespace qpid { namespace acl { @@ -45,6 +45,16 @@ public: rule (propertyMap& p):log(false),logOnly(false),props(p) {}; + + std::string toString () const { + std::ostringstream ruleStr; + ruleStr << "[log=" << log << ", logOnly=" << logOnly << " props{"; + for (propertyMapItr pMItr = props.begin(); pMItr != props.end(); pMItr++) { + ruleStr << " " << AclHelper::getPropertyStr((Property) pMItr-> first) << "=" << pMItr->second; + } + ruleStr << " }]"; + return ruleStr.str(); + } }; typedef std::vector<rule> ruleSet; typedef ruleSet::const_iterator ruleSetItr; diff --git a/qpid/cpp/src/qpid/acl/AclReader.cpp b/qpid/cpp/src/qpid/acl/AclReader.cpp index 8f5e4f5b57..8f419a6f50 100644 --- a/qpid/cpp/src/qpid/acl/AclReader.cpp +++ b/qpid/cpp/src/qpid/acl/AclReader.cpp @@ -83,115 +83,142 @@ std::string AclReader::aclRule::toString() { return oss.str(); } -void AclReader::loadDecisionData( boost::shared_ptr<AclData> d ) -{ - d->clear(); - QPID_LOG(debug, "ACL Load Rules"); - int cnt = rules.size(); +void AclReader::loadDecisionData(boost::shared_ptr<AclData> d) { + d->clear(); + QPID_LOG(debug, "ACL Load Rules"); + int cnt = rules.size(); bool foundmode = false; - for (rlCitr i=rules.end()-1; cnt; i--,cnt--) { - QPID_LOG(debug, "ACL Processing " << std::setfill(' ') << std::setw(2) << cnt << " " << (*i)->toString()); - - if (!foundmode && (*i)->actionAll && (*i)->names.size()==1 && (*((*i)->names.begin())).compare("*")==0 ){ - d->decisionMode = (*i)->res; - QPID_LOG(debug, "ACL FoundMode " << AclHelper::getAclResultStr(d->decisionMode)); - foundmode=true; - }else{ - AclData::rule rule((*i)->props); - bool addrule= true; - - switch ((*i)->res) - { - case qpid::acl::ALLOWLOG: - rule.log = true; - if (d->decisionMode == qpid::acl::ALLOW || d->decisionMode == qpid::acl::ALLOWLOG) - rule.logOnly = true; + + for (rlCitr i = rules.end() - 1; cnt; i--, cnt--) { + QPID_LOG(debug, "ACL Processing " << std::setfill(' ') << std::setw(2) + << cnt << " " << (*i)->toString()); + + if (!foundmode && (*i)->actionAll && (*i)->names.size() == 1 + && (*((*i)->names.begin())).compare("*") == 0) { + d->decisionMode = (*i)->res; + QPID_LOG(debug, "ACL FoundMode " << AclHelper::getAclResultStr( + d->decisionMode)); + foundmode = true; + } else { + AclData::rule rule((*i)->props); + bool addrule = true; + + switch ((*i)->res) { + case qpid::acl::ALLOWLOG: + rule.log = true; + if (d->decisionMode == qpid::acl::ALLOW || d->decisionMode + == qpid::acl::ALLOWLOG) + rule.logOnly = true; + break; + case qpid::acl::ALLOW: + if (d->decisionMode == qpid::acl::ALLOW || d->decisionMode + == qpid::acl::ALLOWLOG) + addrule = false; + break; + case qpid::acl::DENYLOG: + rule.log = true; + if (d->decisionMode == qpid::acl::DENY || d->decisionMode + == qpid::acl::DENYLOG) + rule.logOnly = true; + break; + case qpid::acl::DENY: + if (d->decisionMode == qpid::acl::DENY || d->decisionMode + == qpid::acl::DENYLOG) + addrule = false; break; - case qpid::acl::ALLOW: - if (d->decisionMode == qpid::acl::ALLOW || d->decisionMode == qpid::acl::ALLOWLOG) - addrule = false; - break; - case qpid::acl::DENYLOG: - rule.log = true; - if (d->decisionMode == qpid::acl::DENY || d->decisionMode == qpid::acl::DENYLOG) - rule.logOnly = true; - break; - case qpid::acl::DENY: - if (d->decisionMode == qpid::acl::DENY || d->decisionMode == qpid::acl::DENYLOG) - addrule = false; - break; - default: - throw Exception("Invalid ACL Result loading rules."); - } - - - // Action -> Object -> map<user -> set<Rule> > - if (addrule){ - for (int acnt= ((*i)->actionAll?0:(*i)->action); - acnt< acl::ACTIONSIZE; (*i)->actionAll?acnt++:acnt=acl::ACTIONSIZE ) { - - if (acnt == acl::ACT_PUBLISH) d->transferAcl = true; // we have transfer ACL - - QPID_LOG(debug, "ACL Adding action:" << AclHelper::getActionStr((Action)acnt) ); - - //find the Action, create if not exist - if (d->actionList[acnt]==NULL) { - d->actionList[acnt] = new AclData::aclAction[qpid::acl::OBJECTSIZE]; - for (int j=0;j<qpid::acl::OBJECTSIZE; j++) - d->actionList[acnt][j] = NULL; - } + default: + throw Exception("Invalid ACL Result loading rules."); + } + + // Action -> Object -> map<user -> set<Rule> > + if (addrule) { + std::ostringstream actionstr; + for (int acnt = ((*i)->actionAll ? 0 : (*i)->action); acnt + < acl::ACTIONSIZE; (*i)->actionAll ? acnt++ : acnt + = acl::ACTIONSIZE) { + + if (acnt == acl::ACT_PUBLISH) + d->transferAcl = true; // we have transfer ACL + + actionstr << AclHelper::getActionStr((Action) acnt) << ","; + + //find the Action, create if not exist + if (d->actionList[acnt] == NULL) { + d->actionList[acnt] + = new AclData::aclAction[qpid::acl::OBJECTSIZE]; + for (int j = 0; j < qpid::acl::OBJECTSIZE; j++) + d->actionList[acnt][j] = NULL; + } // optimize this loop to limit to valid options only!! - for (int ocnt= ((*i)->objStatus!=aclRule::VALUE ?0:(*i)->object); - ocnt< acl::OBJECTSIZE; - (*i)->objStatus!=aclRule::VALUE?ocnt++:ocnt=acl::OBJECTSIZE ) { - - QPID_LOG(debug, "ACL Adding object:" << AclHelper::getObjectTypeStr((ObjectType)ocnt) ); - - //find the Object, create if not exist - if (d->actionList[acnt][ocnt] == NULL) - d->actionList[acnt][ocnt] = new AclData::actionObject; - - // add users and Rule to object set - bool allNames=false; - // check to see if names.begin is '*' - if ( (*(*i)->names.begin()).compare("*")==0 ) allNames = true; - - for (nsCitr itr = (allNames?names.begin():(*i)->names.begin()); - itr != (allNames?names.end():(*i)->names.end()); itr++) { - AclData::actObjItr itrRule = d->actionList[acnt][ocnt]->find(*itr); - if (itrRule == d->actionList[acnt][ocnt]->end()) { - QPID_LOG(debug, "ACL Adding rule & user:" << *itr); - AclData::ruleSet rSet; - rSet.push_back(rule); - d->actionList[acnt][ocnt]->insert(make_pair( std::string(*itr) , rSet) ); - }else{ - - // TODO add code to check for dead rules - // allow peter create queue name=tmp <-- dead rule!! - // allow peter create queue - - itrRule->second.push_back(rule); - QPID_LOG(debug, "ACL Adding rule to user:" << *itr); - } - } - - } - - } - }else{ - QPID_LOG(debug, "ACL Skipping based on Mode:" << AclHelper::getAclResultStr(d->decisionMode) ); - } - } - - } + for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0 + : (*i)->object); ocnt < acl::OBJECTSIZE; (*i)->objStatus + != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) { + + //find the Object, create if not exist + if (d->actionList[acnt][ocnt] == NULL) + d->actionList[acnt][ocnt] + = new AclData::actionObject; + + // add users and Rule to object set + bool allNames = false; + // check to see if names.begin is '*' + if ((*(*i)->names.begin()).compare("*") == 0) + allNames = true; + + for (nsCitr itr = (allNames ? names.begin() + : (*i)->names.begin()); itr + != (allNames ? names.end() : (*i)->names.end()); itr++) { + + AclData::actObjItr itrRule = + d->actionList[acnt][ocnt]->find(*itr); + + if (itrRule == d->actionList[acnt][ocnt]->end()) { + AclData::ruleSet rSet; + rSet.push_back(rule); + d->actionList[acnt][ocnt]->insert(make_pair( + std::string(*itr), rSet)); + } else { + + // TODO add code to check for dead rules + // allow peter create queue name=tmp <-- dead rule!! + // allow peter create queue + + itrRule->second.push_back(rule); + } + } + + } + } + + std::ostringstream objstr; + for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0 : (*i)->object); ocnt < acl::OBJECTSIZE; + (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) { + objstr << AclHelper::getObjectTypeStr((ObjectType) ocnt) << ","; + } + + bool allNames = ((*(*i)->names.begin()).compare("*") == 0); + std::ostringstream userstr; + for (nsCitr itr = (allNames ? names.begin() : (*i)->names.begin()); + itr != (allNames ? names.end() : (*i)->names.end()); itr++) { + userstr << *itr << ","; + } + + QPID_LOG(debug,"ACL: Adding actions {" << actionstr.str().substr(0,actionstr.str().length()-1) + << "} to objects {" << objstr.str().substr(0,objstr.str().length()-1) + << "} with props " << AclHelper::propertyMapToString(&rule.props) + << " for users {" << userstr.str().substr(0,userstr.str().length()-1) << "}" ); + } else { + QPID_LOG(debug, "ACL Skipping based on Mode:" + << AclHelper::getAclResultStr(d->decisionMode)); + } + } + } } - - void AclReader::aclRule::processName(const std::string& name, const groupMap& groups) { if (name.compare("all") == 0) { names.insert("*"); diff --git a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp index 4a6590fb5f..093e9cea32 100644 --- a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp +++ b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp @@ -435,8 +435,8 @@ void ManagementAgentImpl::invokeMethodRequest(Buffer& inBuffer, uint32_t sequenc } else { if ((iter->second->getPackageName() != packageName) || (iter->second->getClassName() != className)) { - outBuffer.putLong (Manageable::STATUS_INVALID_PARAMETER); - outBuffer.putMediumString(Manageable::StatusText (Manageable::STATUS_INVALID_PARAMETER)); + outBuffer.putLong (Manageable::STATUS_PARAMETER_INVALID); + outBuffer.putMediumString(Manageable::StatusText (Manageable::STATUS_PARAMETER_INVALID)); } else try { diff --git a/qpid/cpp/src/qpid/broker/AclModule.h b/qpid/cpp/src/qpid/broker/AclModule.h index a78b2d5b4a..536fa21b2b 100644 --- a/qpid/cpp/src/qpid/broker/AclModule.h +++ b/qpid/cpp/src/qpid/broker/AclModule.h @@ -26,7 +26,7 @@ #include <map> #include <set> #include <string> - +#include <sstream> namespace qpid { @@ -179,6 +179,8 @@ class AclHelper { typedef std::map<ObjectType, actionMapPtr> objectMap; typedef objectMap::const_iterator omCitr; typedef boost::shared_ptr<objectMap> objectMapPtr; + typedef std::map<Property, std::string> propMap; + typedef propMap::const_iterator propMapItr; // This map contains the legal combinations of object/action/properties found in an ACL file static void loadValidationMap(objectMapPtr& map) { @@ -248,6 +250,19 @@ class AclHelper { map->insert(objectPair(OBJ_METHOD, a4)); } + + static std::string propertyMapToString(const std::map<Property, std::string>* params) { + std::ostringstream ss; + ss << "{"; + if (params) + { + for (propMapItr pMItr = params->begin(); pMItr != params->end(); pMItr++) { + ss << " " << getPropertyStr((Property) pMItr-> first) << "=" << pMItr->second; + } + } + ss << " }"; + return ss.str(); + } }; diff --git a/qpid/cpp/src/qpid/broker/Broker.cpp b/qpid/cpp/src/qpid/broker/Broker.cpp index 8b6cb5e049..13cf88fb11 100644 --- a/qpid/cpp/src/qpid/broker/Broker.cpp +++ b/qpid/cpp/src/qpid/broker/Broker.cpp @@ -386,7 +386,7 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId, if (queueMoveMessages(moveArgs.i_srcQueue, moveArgs.i_destQueue, moveArgs.i_qty)) status = Manageable::STATUS_OK; else - return Manageable::STATUS_INVALID_PARAMETER; + return Manageable::STATUS_PARAMETER_INVALID; break; } default: diff --git a/qpid/cpp/src/qpid/broker/Connection.cpp b/qpid/cpp/src/qpid/broker/Connection.cpp index a1a3c6ada7..3b8a6c71d4 100644 --- a/qpid/cpp/src/qpid/broker/Connection.cpp +++ b/qpid/cpp/src/qpid/broker/Connection.cpp @@ -269,11 +269,13 @@ bool Connection::doOutput() { cb(); // Lend the IO thread for management processing } } - if (mgmtClosing) + if (mgmtClosing) { + closed(); close(connection::CLOSE_CODE_CONNECTION_FORCED, "Closed by Management Request"); - else + } else { //then do other output as needed: return outputTasks.doOutput(); + } }catch(ConnectionException& e){ close(e.code, e.getMessage()); }catch(std::exception& e){ diff --git a/qpid/cpp/src/qpid/broker/Exchange.cpp b/qpid/cpp/src/qpid/broker/Exchange.cpp index b1fc1295f3..90d81b81c6 100644 --- a/qpid/cpp/src/qpid/broker/Exchange.cpp +++ b/qpid/cpp/src/qpid/broker/Exchange.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -38,7 +38,7 @@ using qpid::management::Manageable; using qpid::management::Args; namespace _qmf = qmf::org::apache::qpid::broker; -namespace +namespace { const std::string qpidMsgSequence("qpid.msg_sequence"); const std::string qpidSequenceCounter("qpid.sequence_counter"); @@ -51,17 +51,19 @@ const std::string fedOpBind("B"); const std::string fedOpUnbind("U"); const std::string fedOpReorigin("R"); const std::string fedOpHello("H"); + +const std::string QPID_MANAGEMENT("qpid.management"); } Exchange::PreRoute::PreRoute(Deliverable& msg, Exchange* _p):parent(_p) { if (parent){ if (parent->sequence || parent->ive) parent->sequenceLock.lock(); - + if (parent->sequence){ parent->sequenceNo++; - msg.getMessage().getProperties<MessageProperties>()->getApplicationHeaders().setInt64(qpidMsgSequence,parent->sequenceNo); - } + msg.getMessage().getProperties<MessageProperties>()->getApplicationHeaders().setInt64(qpidMsgSequence,parent->sequenceNo); + } if (parent->ive) { parent->lastMsg = &( msg.getMessage()); } @@ -99,11 +101,9 @@ Exchange::Exchange (const string& _name, Manageable* parent, Broker* b) : } } -static const std::string QPID_MANAGEMENT("qpid.management"); - Exchange::Exchange(const string& _name, bool _durable, const qpid::framing::FieldTable& _args, Manageable* parent, Broker* b) - : name(_name), durable(_durable), alternateUsers(0), persistenceId(0), + : name(_name), durable(_durable), alternateUsers(0), persistenceId(0), args(_args), sequence(false), sequenceNo(0), ive(false), mgmtExchange(0), broker(b) { if (parent != 0 && broker != 0) @@ -169,7 +169,7 @@ Exchange::shared_ptr Exchange::decode(ExchangeRegistry& exchanges, Buffer& buffe string name; string type; FieldTable args; - + buffer.getShortString(name); bool durable(buffer.getOctet()); buffer.getShortString(type); @@ -185,7 +185,7 @@ Exchange::shared_ptr Exchange::decode(ExchangeRegistry& exchanges, Buffer& buffe } } -void Exchange::encode(Buffer& buffer) const +void Exchange::encode(Buffer& buffer) const { buffer.putShortString(name); buffer.putOctet(durable); @@ -195,8 +195,8 @@ void Exchange::encode(Buffer& buffer) const buffer.put(args); } -uint32_t Exchange::encodedSize() const -{ +uint32_t Exchange::encodedSize() const +{ return name.size() + 1/*short string size*/ + 1 /*durable*/ + getType().size() + 1/*short string size*/ diff --git a/qpid/cpp/src/qpid/broker/Link.cpp b/qpid/cpp/src/qpid/broker/Link.cpp index 9ce0c710bd..cdba18ccf9 100644 --- a/qpid/cpp/src/qpid/broker/Link.cpp +++ b/qpid/cpp/src/qpid/broker/Link.cpp @@ -200,8 +200,10 @@ void Link::destroy () // Move the bridges to be deleted into a local vector so there is no // corruption of the iterator caused by bridge deletion. - for (Bridges::iterator i = active.begin(); i != active.end(); i++) + for (Bridges::iterator i = active.begin(); i != active.end(); i++) { + (*i)->closed(); toDelete.push_back(*i); + } active.clear(); for (Bridges::iterator i = created.begin(); i != created.end(); i++) @@ -264,7 +266,6 @@ void Link::ioThreadProcessing() } if (!cancellations.empty()) { for (Bridges::iterator i = cancellations.begin(); i != cancellations.end(); ++i) { - active.push_back(*i); (*i)->cancel(*connection); } cancellations.clear(); diff --git a/qpid/cpp/src/qpid/broker/Message.cpp b/qpid/cpp/src/qpid/broker/Message.cpp index 639f04faa2..7360010192 100644 --- a/qpid/cpp/src/qpid/broker/Message.cpp +++ b/qpid/cpp/src/qpid/broker/Message.cpp @@ -362,7 +362,7 @@ boost::intrusive_ptr<Message>& Message::getReplacementMessage(const Queue* qfor) Replacement::iterator i = replacement.find(qfor); if (i != replacement.end()){ return i->second; - } + } return empty; } diff --git a/qpid/cpp/src/qpid/broker/Message.h b/qpid/cpp/src/qpid/broker/Message.h index 0024509bc8..e4d09b1042 100644 --- a/qpid/cpp/src/qpid/broker/Message.h +++ b/qpid/cpp/src/qpid/broker/Message.h @@ -34,12 +34,12 @@ #include <vector> namespace qpid { - + namespace framing { class FieldTable; class SequenceNumber; } - + namespace broker { class ConnectionToken; class Exchange; @@ -145,9 +145,9 @@ public: bool isExcluded(const std::vector<std::string>& excludes) const; void addTraceId(const std::string& id); - - void forcePersistent(); - bool isForcedPersistent(); + + void forcePersistent(); + bool isForcedPersistent(); boost::intrusive_ptr<Message>& getReplacementMessage(const Queue* qfor) const; void setReplacementMessage(boost::intrusive_ptr<Message> msg, const Queue* qfor); diff --git a/qpid/cpp/src/qpid/broker/MessageBuilder.cpp b/qpid/cpp/src/qpid/broker/MessageBuilder.cpp index e01fd81074..14b233fd6c 100644 --- a/qpid/cpp/src/qpid/broker/MessageBuilder.cpp +++ b/qpid/cpp/src/qpid/broker/MessageBuilder.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -30,14 +30,14 @@ using boost::intrusive_ptr; using namespace qpid::broker; using namespace qpid::framing; -namespace +namespace { std::string type_str(uint8_t type); + const std::string QPID_MANAGEMENT("qpid.management"); } -MessageBuilder::MessageBuilder(MessageStore* const _store, uint64_t _stagingThreshold) : - state(DORMANT), store(_store), stagingThreshold(_stagingThreshold), staging(false) {} -static const std::string QPID_MANAGEMENT("qpid.management"); +MessageBuilder::MessageBuilder(MessageStore* const _store, uint64_t _stagingThreshold) : + state(DORMANT), store(_store), stagingThreshold(_stagingThreshold), staging(false) {} void MessageBuilder::handle(AMQFrame& frame) { @@ -54,10 +54,10 @@ void MessageBuilder::handle(AMQFrame& frame) AMQFrame header((AMQHeaderBody())); header.setBof(false); header.setEof(false); - message->getFrames().append(header); + message->getFrames().append(header); } else if (type != HEADER_BODY) { throw CommandInvalidException( - QPID_MSG("Invalid frame sequence for message, expected header or content got " + QPID_MSG("Invalid frame sequence for message, expected header or content got " << type_str(type) << ")")); } state = CONTENT; @@ -74,13 +74,13 @@ void MessageBuilder::handle(AMQFrame& frame) } else { message->getFrames().append(frame); //have we reached the staging limit? if so stage message and release content - if (state == CONTENT - && stagingThreshold + if (state == CONTENT + && stagingThreshold && message->getFrames().getContentSize() >= stagingThreshold && !NullMessageStore::isNullStore(store) - && message->getExchangeName() != QPID_MANAGEMENT /* don't stage mgnt messages */) + && message->getExchangeName() != QPID_MANAGEMENT /* don't stage mgnt messages */) { - message->releaseContent(store); + message->releaseContent(store); staging = true; } } @@ -108,7 +108,7 @@ const std::string CONTENT_BODY_S = "CONTENT"; const std::string HEARTBEAT_BODY_S = "HEARTBEAT"; const std::string UNKNOWN = "unknown"; -std::string type_str(uint8_t type) +std::string type_str(uint8_t type) { switch(type) { case METHOD_BODY: return METHOD_BODY_S; @@ -124,7 +124,7 @@ std::string type_str(uint8_t type) void MessageBuilder::checkType(uint8_t expected, uint8_t actual) { if (expected != actual) { - throw CommandInvalidException(QPID_MSG("Invalid frame sequence for message (expected " + throw CommandInvalidException(QPID_MSG("Invalid frame sequence for message (expected " << type_str(expected) << " got " << type_str(actual) << ")")); } } diff --git a/qpid/cpp/src/qpid/broker/PersistableMessage.cpp b/qpid/cpp/src/qpid/broker/PersistableMessage.cpp index c1f86d4ca4..2ef223aa81 100644 --- a/qpid/cpp/src/qpid/broker/PersistableMessage.cpp +++ b/qpid/cpp/src/qpid/broker/PersistableMessage.cpp @@ -62,7 +62,7 @@ void PersistableMessage::flush() void PersistableMessage::setContentReleased() {contentReleased = true; } bool PersistableMessage::isContentReleased()const { return contentReleased; } - + bool PersistableMessage::isEnqueueComplete() { sys::ScopedLock<sys::Mutex> l(asyncEnqueueLock); return asyncEnqueueCounter == 0; diff --git a/qpid/cpp/src/qpid/broker/PersistableMessage.h b/qpid/cpp/src/qpid/broker/PersistableMessage.h index 05de9ff4c3..0274b41375 100644 --- a/qpid/cpp/src/qpid/broker/PersistableMessage.h +++ b/qpid/cpp/src/qpid/broker/PersistableMessage.h @@ -46,7 +46,7 @@ class PersistableMessage : public Persistable sys::Mutex asyncEnqueueLock; sys::Mutex asyncDequeueLock; sys::Mutex storeLock; - + /** * Tracks the number of outstanding asynchronous enqueue * operations. When the message is enqueued asynchronously the @@ -97,7 +97,7 @@ class PersistableMessage : public Persistable void flush(); bool isContentReleased() const; - + QPID_BROKER_EXTERN bool isEnqueueComplete(); QPID_BROKER_EXTERN void enqueueComplete(); diff --git a/qpid/cpp/src/qpid/broker/Queue.cpp b/qpid/cpp/src/qpid/broker/Queue.cpp index 08ee133981..b2a8e223c5 100644 --- a/qpid/cpp/src/qpid/broker/Queue.cpp +++ b/qpid/cpp/src/qpid/broker/Queue.cpp @@ -597,7 +597,7 @@ void Queue::push(boost::intrusive_ptr<Message>& msg, bool isRecovery){ Mutex::ScopedUnlock u(messageLock); dequeue(0, QueuedMessage(qm.queue, old, qm.position)); } - } + } }else { messages.push_back(qm); listeners.populate(copy); @@ -702,7 +702,7 @@ bool Queue::enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message> msg) if (inLastNodeFailure && persistLastNode){ msg->forcePersistent(); } - + if (traceId.size()) { msg->addTraceId(traceId); } diff --git a/qpid/cpp/src/qpid/broker/QueueRegistry.h b/qpid/cpp/src/qpid/broker/QueueRegistry.h index c134f399c8..72a91dff24 100644 --- a/qpid/cpp/src/qpid/broker/QueueRegistry.h +++ b/qpid/cpp/src/qpid/broker/QueueRegistry.h @@ -111,7 +111,7 @@ class QueueRegistry { /** Call f for each queue in the registry. */ template <class F> void eachQueue(F f) const { - qpid::sys::RWlock::ScopedWlock l(lock); + qpid::sys::RWlock::ScopedRlock l(lock); for (QueueMap::const_iterator i = queues.begin(); i != queues.end(); ++i) f(i->second); } diff --git a/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp index 2a19115fd1..5bc4cdf960 100644 --- a/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp +++ b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp @@ -195,7 +195,7 @@ void RecoverableQueueImpl::setPersistenceId(uint64_t id) { queue->setPersistenceId(id); } - + uint64_t RecoverableQueueImpl::getPersistenceId() const { return queue->getPersistenceId(); diff --git a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp index b0c5e9ea00..a1ad5a0a30 100644 --- a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp +++ b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp @@ -72,7 +72,7 @@ void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const params.insert(make_pair(acl::PROP_PASSIVE, std::string(passive ? _TRUE : _FALSE) )); params.insert(make_pair(acl::PROP_DURABLE, std::string(durable ? _TRUE : _FALSE))); if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_EXCHANGE,exchange,¶ms) ) - throw NotAllowedException(QPID_MSG("ACL denied exhange declare request from " << getConnection().getUserId())); + throw NotAllowedException(QPID_MSG("ACL denied exchange declare request from " << getConnection().getUserId())); } //TODO: implement autoDelete @@ -121,9 +121,11 @@ void SessionAdapter::ExchangeHandlerImpl::checkType(Exchange::shared_ptr exchang void SessionAdapter::ExchangeHandlerImpl::checkAlternate(Exchange::shared_ptr exchange, Exchange::shared_ptr alternate) { - if (alternate && alternate != exchange->getAlternate()) + if (alternate && ((exchange->getAlternate() && alternate != exchange->getAlternate()) + || !exchange->getAlternate())) throw NotAllowedException(QPID_MSG("Exchange declared with alternate-exchange " - << exchange->getAlternate()->getName() << ", requested " + << (exchange->getAlternate() ? exchange->getAlternate()->getName() : "<nonexistent>") + << ", requested " << alternate->getName())); } @@ -132,7 +134,7 @@ void SessionAdapter::ExchangeHandlerImpl::delete_(const string& name, bool /*ifU AclModule* acl = getBroker().getAcl(); if (acl) { if (!acl->authorise(getConnection().getUserId(),acl::ACT_DELETE,acl::OBJ_EXCHANGE,name,NULL) ) - throw NotAllowedException(QPID_MSG("ACL denied exhange delete request from " << getConnection().getUserId())); + throw NotAllowedException(QPID_MSG("ACL denied exchange delete request from " << getConnection().getUserId())); } //TODO: implement unused @@ -152,7 +154,7 @@ ExchangeQueryResult SessionAdapter::ExchangeHandlerImpl::query(const string& nam AclModule* acl = getBroker().getAcl(); if (acl) { if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_EXCHANGE,name,NULL) ) - throw NotAllowedException(QPID_MSG("ACL denied exhange query request from " << getConnection().getUserId())); + throw NotAllowedException(QPID_MSG("ACL denied exchange query request from " << getConnection().getUserId())); } try { @@ -169,8 +171,12 @@ void SessionAdapter::ExchangeHandlerImpl::bind(const string& queueName, { AclModule* acl = getBroker().getAcl(); if (acl) { - if (!acl->authorise(getConnection().getUserId(),acl::ACT_BIND,acl::OBJ_EXCHANGE,exchangeName,routingKey) ) - throw NotAllowedException(QPID_MSG("ACL denied exhange bind request from " << getConnection().getUserId())); + std::map<acl::Property, std::string> params; + params.insert(make_pair(acl::PROP_QUEUENAME, queueName)); + params.insert(make_pair(acl::PROP_ROUTINGKEY, routingKey)); + + if (!acl->authorise(getConnection().getUserId(),acl::ACT_BIND,acl::OBJ_EXCHANGE,exchangeName,¶ms)) + throw NotAllowedException(QPID_MSG("ACL denied exchange bind request from " << getConnection().getUserId())); } Queue::shared_ptr queue = getQueue(queueName); @@ -232,8 +238,8 @@ ExchangeBoundResult SessionAdapter::ExchangeHandlerImpl::bound(const std::string std::map<acl::Property, std::string> params; params.insert(make_pair(acl::PROP_QUEUENAME, queueName)); params.insert(make_pair(acl::PROP_ROUTINGKEY, key)); - if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_EXCHANGE,exchangeName,¶ms) ) - throw NotAllowedException(QPID_MSG("ACL denied exhange bound request from " << getConnection().getUserId())); + if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_EXCHANGE,exchangeName,¶ms) ) + throw NotAllowedException(QPID_MSG("ACL denied exchange bound request from " << getConnection().getUserId())); } Exchange::shared_ptr exchange; diff --git a/qpid/cpp/src/qpid/client/Dispatcher.cpp b/qpid/cpp/src/qpid/client/Dispatcher.cpp index 43cbf3aa4d..a715c623bf 100644 --- a/qpid/cpp/src/qpid/client/Dispatcher.cpp +++ b/qpid/cpp/src/qpid/client/Dispatcher.cpp @@ -29,7 +29,14 @@ #include "qpid/client/Message.h" #include "qpid/client/MessageImpl.h" -#include <boost/state_saver.hpp> +#include <boost/version.hpp> +#if (BOOST_VERSION >= 104000) +# include <boost/serialization/state_saver.hpp> + using boost::serialization::state_saver; +#else +# include <boost/state_saver.hpp> + using boost::state_saver; +#endif /* BOOST_VERSION */ using qpid::framing::FrameSet; using qpid::framing::MessageTransferBody; @@ -65,7 +72,7 @@ void Dispatcher::run() Mutex::ScopedLock l(lock); if (running) throw Exception("Dispatcher is already running."); - boost::state_saver<bool> reset(running); // Reset to false on exit. + state_saver<bool> reset(running); // Reset to false on exit. running = true; try { while (!queue->isClosed()) { diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp b/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp new file mode 100644 index 0000000000..80be5c56f3 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp @@ -0,0 +1,111 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "AcceptTracker.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +void AcceptTracker::State::accept() +{ + unconfirmed.add(unaccepted); + unaccepted.clear(); +} + +void AcceptTracker::State::release() +{ + unaccepted.clear(); +} + +uint32_t AcceptTracker::State::acceptsPending() +{ + return unconfirmed.size(); +} + +void AcceptTracker::State::completed(qpid::framing::SequenceSet& set) +{ + unconfirmed.remove(set); +} + +void AcceptTracker::delivered(const std::string& destination, const qpid::framing::SequenceNumber& id) +{ + aggregateState.unaccepted.add(id); + destinationState[destination].unaccepted.add(id); +} + +void AcceptTracker::accept(qpid::client::AsyncSession& session) +{ + for (StateMap::iterator i = destinationState.begin(); i != destinationState.end(); ++i) { + i->second.accept(); + } + Record record; + record.status = session.messageAccept(aggregateState.unaccepted); + record.accepted = aggregateState.unaccepted; + pending.push_back(record); + aggregateState.accept(); +} + +void AcceptTracker::release(qpid::client::AsyncSession& session) +{ + session.messageRelease(aggregateState.unaccepted); + for (StateMap::iterator i = destinationState.begin(); i != destinationState.end(); ++i) { + i->second.release(); + } + aggregateState.release(); +} + +uint32_t AcceptTracker::acceptsPending() +{ + checkPending(); + return aggregateState.acceptsPending(); +} + +uint32_t AcceptTracker::acceptsPending(const std::string& destination) +{ + checkPending(); + return destinationState[destination].acceptsPending(); +} + +void AcceptTracker::reset() +{ + destinationState.clear(); + aggregateState.unaccepted.clear(); + aggregateState.unconfirmed.clear(); + pending.clear(); +} + +void AcceptTracker::checkPending() +{ + while (!pending.empty() && pending.front().status.isComplete()) { + completed(pending.front().accepted); + pending.pop_front(); + } +} + +void AcceptTracker::completed(qpid::framing::SequenceSet& set) +{ + for (StateMap::iterator i = destinationState.begin(); i != destinationState.end(); ++i) { + i->second.completed(set); + } + aggregateState.completed(set); +} + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.h b/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.h new file mode 100644 index 0000000000..fb58a3a8c8 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.h @@ -0,0 +1,85 @@ +#ifndef QPID_CLIENT_AMQP0_10_ACCEPTTRACKER_H +#define QPID_CLIENT_AMQP0_10_ACCEPTTRACKER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/AsyncSession.h" +#include "qpid/client/Completion.h" +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/SequenceSet.h" +#include <deque> +#include <map> + +namespace qpid { +namespace client { +namespace amqp0_10 { + +/** + * Tracks the set of messages requiring acceptance, and those for + * which an accept has been issued but is yet to be confirmed + * complete. + */ +class AcceptTracker +{ + public: + void delivered(const std::string& destination, const qpid::framing::SequenceNumber& id); + void accept(qpid::client::AsyncSession&); + void release(qpid::client::AsyncSession&); + uint32_t acceptsPending(); + uint32_t acceptsPending(const std::string& destination); + void reset(); + private: + struct State + { + /** + * ids of messages that have been delivered but not yet + * accepted + */ + qpid::framing::SequenceSet unaccepted; + /** + * ids of messages for which an accpet has been issued but not + * yet confirmed as completed + */ + qpid::framing::SequenceSet unconfirmed; + + void accept(); + void release(); + uint32_t acceptsPending(); + void completed(qpid::framing::SequenceSet&); + }; + typedef std::map<std::string, State> StateMap; + struct Record + { + qpid::client::Completion status; + qpid::framing::SequenceSet accepted; + }; + typedef std::deque<Record> Records; + + State aggregateState; + StateMap destinationState; + Records pending; + + void checkPending(); + void completed(qpid::framing::SequenceSet&); +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_ACCEPTTRACKER_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp index 6ff9c2397a..9b9f06ec57 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp @@ -22,6 +22,7 @@ #include "qpid/client/amqp0_10/Codecs.h" #include "qpid/client/amqp0_10/MessageSource.h" #include "qpid/client/amqp0_10/MessageSink.h" +#include "qpid/client/amqp0_10/OutgoingMessage.h" #include "qpid/messaging/Address.h" #include "qpid/messaging/Filter.h" #include "qpid/messaging/Message.h" @@ -122,7 +123,7 @@ class Exchange : public MessageSink 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 send(qpid::client::AsyncSession& session, const std::string& name, OutgoingMessage& message); void cancel(qpid::client::AsyncSession& session, const std::string& name); private: const std::string name; @@ -139,7 +140,7 @@ class QueueSink : public MessageSink 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 send(qpid::client::AsyncSession& session, const std::string& name, OutgoingMessage& message); void cancel(qpid::client::AsyncSession& session, const std::string& name); private: const std::string name; @@ -328,14 +329,12 @@ void Exchange::declare(qpid::client::AsyncSession& session, const std::string&) } } -void Exchange::send(qpid::client::AsyncSession& session, const std::string&, qpid::messaging::Message& m) +void Exchange::send(qpid::client::AsyncSession& session, const std::string&, OutgoingMessage& m) { - qpid::client::Message message; - convert(m, message); - if (message.getDeliveryProperties().getRoutingKey().empty() && !defaultSubject.empty()) { - message.getDeliveryProperties().setRoutingKey(defaultSubject); + if (m.message.getDeliveryProperties().getRoutingKey().empty() && !defaultSubject.empty()) { + m.message.getDeliveryProperties().setRoutingKey(defaultSubject); } - session.messageTransfer(arg::destination=name, arg::content=message); + m.status = session.messageTransfer(arg::destination=name, arg::content=m.message); } void Exchange::cancel(qpid::client::AsyncSession&, const std::string&) {} @@ -355,12 +354,10 @@ void QueueSink::declare(qpid::client::AsyncSession& session, const std::string&) arg::autoDelete=autoDelete, arg::arguments=options); } } -void QueueSink::send(qpid::client::AsyncSession& session, const std::string&, qpid::messaging::Message& m) +void QueueSink::send(qpid::client::AsyncSession& session, const std::string&, OutgoingMessage& m) { - qpid::client::Message message; - convert(m, message); - message.getDeliveryProperties().setRoutingKey(name); - session.messageTransfer(arg::content=message); + m.message.getDeliveryProperties().setRoutingKey(name); + m.status = session.messageTransfer(arg::content=m.message); } void QueueSink::cancel(qpid::client::AsyncSession&, const std::string&) {} diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h index 87758abe6d..9d5657450d 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h @@ -31,8 +31,8 @@ class ReplyTo; } namespace messaging { -class Address; -class Filter; +struct Address; +struct Filter; } namespace client { diff --git a/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp b/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp index 9aee3118fe..57184e3937 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp @@ -183,25 +183,25 @@ 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; + case VAR_VOID: out = boost::shared_ptr<FieldValue>(new VoidValue()); break; + case VAR_BOOL: out = boost::shared_ptr<FieldValue>(new BoolValue(in.asBool())); break; + case VAR_UINT8: out = boost::shared_ptr<FieldValue>(new Unsigned8Value(in.asUint8())); break; + case VAR_UINT16: out = boost::shared_ptr<FieldValue>(new Unsigned16Value(in.asUint16())); break; + case VAR_UINT32: out = boost::shared_ptr<FieldValue>(new Unsigned32Value(in.asUint32())); break; + case VAR_UINT64: out = boost::shared_ptr<FieldValue>(new Unsigned64Value(in.asUint64())); break; + case VAR_INT8: out = boost::shared_ptr<FieldValue>(new Integer8Value(in.asInt8())); break; + case VAR_INT16: out = boost::shared_ptr<FieldValue>(new Integer16Value(in.asInt16())); break; + case VAR_INT32: out = boost::shared_ptr<FieldValue>(new Integer32Value(in.asInt32())); break; + case VAR_INT64: out = boost::shared_ptr<FieldValue>(new Integer64Value(in.asInt64())); break; + case VAR_FLOAT: out = boost::shared_ptr<FieldValue>(new FloatValue(in.asFloat())); break; + case VAR_DOUBLE: out = boost::shared_ptr<FieldValue>(new DoubleValue(in.asDouble())); break; //TODO: check encoding (and length?) when deciding what AMQP type to treat string as - case STRING: out = boost::shared_ptr<FieldValue>(new Str16Value(in.asString())); break; - case MAP: + case VAR_STRING: out = boost::shared_ptr<FieldValue>(new Str16Value(in.asString())); break; + case VAR_MAP: //out = boost::shared_ptr<FieldValue>(toFieldValueCollection<FieldTableValue>(in.asMap(), &toFieldTableEntry)); out = boost::shared_ptr<FieldValue>(toFieldTableValue(in.asMap())); break; - case LIST: + case VAR_LIST: //out = boost::shared_ptr<FieldValue>(toFieldValueCollection<ListValue>(in.asList(), &toFieldValue)); out = boost::shared_ptr<FieldValue>(toListValue(in.asList())); break; diff --git a/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h b/qpid/cpp/src/qpid/client/amqp0_10/CodecsInternal.h index 6147c5682e..b5a561a9c3 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/CodecsInternal.h @@ -1,5 +1,5 @@ -#ifndef QPID_CLIENT_AMQP0_10_COMPLETIONTRACKER_H -#define QPID_CLIENT_AMQP0_10_COMPLETIONTRACKER_H +#ifndef QPID_CLIENT_AMQP0_10_CODECSINTERNAL_H +#define QPID_CLIENT_AMQP0_10_CODECSINTERNAL_H /* * @@ -21,30 +21,21 @@ * under the License. * */ - -#include "qpid/framing/SequenceNumber.h" -#include <map> +#include "qpid/messaging/Variant.h" +#include "qpid/framing/FieldTable.h" 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. + * Declarations of a couple of conversion functions implemented in + * Codecs.cpp but not exposed through API */ -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; -}; + +void translate(const qpid::messaging::Variant::Map& from, qpid::framing::FieldTable& to); +void translate(const qpid::framing::FieldTable& from, qpid::messaging::Variant::Map& to); + }}} // namespace qpid::client::amqp0_10 -#endif /*!QPID_CLIENT_AMQP0_10_COMPLETIONTRACKER_H*/ +#endif /*!QPID_CLIENT_AMQP0_10_CODECSINTERNAL_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp index 9f738731e2..3a735b5698 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp @@ -21,14 +21,16 @@ #include "ConnectionImpl.h" #include "SessionImpl.h" #include "qpid/messaging/Session.h" -#include "qpid/client/ConnectionSettings.h" +#include "qpid/client/PrivateImplRef.h" #include "qpid/log/Statement.h" +#include <boost/intrusive_ptr.hpp> namespace qpid { namespace client { namespace amqp0_10 { using qpid::messaging::Variant; +using namespace qpid::sys; template <class T> void setIfFound(const Variant::Map& map, const std::string& key, T& value) { @@ -56,24 +58,124 @@ void convert(const Variant::Map& from, ConnectionSettings& to) setIfFound(from, "bounds", to.bounds); } -ConnectionImpl::ConnectionImpl(const std::string& url, const Variant::Map& options) +ConnectionImpl::ConnectionImpl(const std::string& u, const Variant::Map& options) : + url(u), reconnectionEnabled(true), timeout(-1), + minRetryInterval(1), maxRetryInterval(30) { QPID_LOG(debug, "Opening connection to " << url << " with " << options); - Url u(url); - ConnectionSettings settings; convert(options, settings); - connection.open(u, settings); + setIfFound(options, "reconnection-enabled", reconnectionEnabled); + setIfFound(options, "reconnection-timeout", timeout); + setIfFound(options, "min-retry-interval", minRetryInterval); + setIfFound(options, "max-retry-interval", maxRetryInterval); + connection.open(url, settings); } void ConnectionImpl::close() { + qpid::sys::Mutex::ScopedLock l(lock); connection.close(); } +boost::intrusive_ptr<SessionImpl> getImplPtr(qpid::messaging::Session& session) +{ + return boost::dynamic_pointer_cast<SessionImpl>( + qpid::client::PrivateImplRef<qpid::messaging::Session>::get(session) + ); +} + +void ConnectionImpl::closed(SessionImpl& s) +{ + qpid::sys::Mutex::ScopedLock l(lock); + for (Sessions::iterator i = sessions.begin(); i != sessions.end(); ++i) { + if (getImplPtr(*i).get() == &s) { + sessions.erase(i); + break; + } + } +} + qpid::messaging::Session ConnectionImpl::newSession() { - qpid::messaging::Session impl(new SessionImpl(connection.newSession())); + qpid::messaging::Session impl(new SessionImpl(*this)); + { + qpid::sys::Mutex::ScopedLock l(lock); + sessions.push_back(impl); + } + try { + getImplPtr(impl)->setSession(connection.newSession()); + } catch (const TransportFailure&) { + reconnect(); + } return impl; } +void ConnectionImpl::reconnect() +{ + AbsTime start = now(); + ScopedLock<Semaphore> l(semaphore); + if (!connection.isOpen()) connect(start); +} + +bool expired(const AbsTime& start, int timeout) +{ + if (timeout == 0) return true; + if (timeout < 0) return false; + Duration used(start, now()); + Duration allowed = timeout * TIME_SEC; + return allowed > used; +} + +void ConnectionImpl::connect(const AbsTime& started) +{ + for (int i = minRetryInterval; !tryConnect(); i = std::min(i * 2, maxRetryInterval)) { + if (expired(started, timeout)) throw TransportFailure(); + else qpid::sys::sleep(i); + } +} + +bool ConnectionImpl::tryConnect() +{ + if (tryConnect(url) || tryConnect(connection.getKnownBrokers())) { + return resetSessions(); + } else { + return false; + } +} + +bool ConnectionImpl::tryConnect(const Url& u) +{ + try { + QPID_LOG(info, "Trying to connect to " << url << "..."); + connection.open(u, settings); + return true; + } catch (const Exception& e) { + //TODO: need to fix timeout on open so that it throws TransportFailure + QPID_LOG(info, "Failed to connect to " << u << ": " << e.what()); + } + return false; +} + +bool ConnectionImpl::tryConnect(const std::vector<Url>& urls) +{ + for (std::vector<Url>::const_iterator i = urls.begin(); i != urls.end(); ++i) { + if (tryConnect(*i)) return true; + } + return false; +} + +bool ConnectionImpl::resetSessions() +{ + try { + qpid::sys::Mutex::ScopedLock l(lock); + for (Sessions::iterator i = sessions.begin(); i != sessions.end(); ++i) { + getImplPtr(*i)->setSession(connection.newSession()); + } + return true; + } catch (const TransportFailure&) { + QPID_LOG(debug, "Connection failed while re-inialising sessions"); + return false; + } +} + }}} // 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 index 120a8ab9d8..565f2ec7ec 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h @@ -23,20 +23,46 @@ */ #include "qpid/messaging/ConnectionImpl.h" #include "qpid/messaging/Variant.h" +#include "qpid/Url.h" #include "qpid/client/Connection.h" +#include "qpid/client/ConnectionSettings.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Semaphore.h" +#include <vector> namespace qpid { namespace client { namespace amqp0_10 { +class SessionImpl; + class ConnectionImpl : public qpid::messaging::ConnectionImpl { public: ConnectionImpl(const std::string& url, const qpid::messaging::Variant::Map& options); void close(); qpid::messaging::Session newSession(); + void closed(SessionImpl&); + void reconnect(); private: + typedef std::vector<qpid::messaging::Session> Sessions; + + qpid::sys::Mutex lock;//used to protect data structures + qpid::sys::Semaphore semaphore;//used to coordinate reconnection qpid::client::Connection connection; + qpid::Url url; + qpid::client::ConnectionSettings settings; + Sessions sessions; + bool reconnectionEnabled; + int timeout; + int minRetryInterval; + int maxRetryInterval; + + void connect(const qpid::sys::AbsTime& started); + bool tryConnect(); + bool tryConnect(const std::vector<Url>& urls); + bool tryConnect(const Url&); + bool resetSessions(); }; }}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp index b69c1917e6..d22208368b 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp @@ -21,11 +21,13 @@ #include "qpid/client/amqp0_10/IncomingMessages.h" #include "qpid/client/amqp0_10/AddressResolution.h" #include "qpid/client/amqp0_10/Codecs.h" +#include "qpid/client/amqp0_10/CodecsInternal.h" #include "qpid/client/SessionImpl.h" #include "qpid/client/SessionBase_0_10Access.h" #include "qpid/log/Statement.h" #include "qpid/messaging/Address.h" #include "qpid/messaging/Message.h" +#include "qpid/messaging/MessageImpl.h" #include "qpid/messaging/Variant.h" #include "qpid/framing/DeliveryProperties.h" #include "qpid/framing/FrameSet.h" @@ -41,6 +43,7 @@ using namespace qpid::framing; using namespace qpid::framing::message; using qpid::sys::AbsTime; using qpid::sys::Duration; +using qpid::messaging::MessageImplAccess; using qpid::messaging::Variant; namespace { @@ -78,11 +81,32 @@ struct MatchAndTrack } } }; + +struct Match +{ + const std::string destination; + uint32_t matched; + + Match(const std::string& d) : destination(d), matched(0) {} + + bool operator()(boost::shared_ptr<qpid::framing::FrameSet> command) + { + if (command->as<MessageTransferBody>()->getDestination() == destination) { + ++matched; + return true; + } else { + return false; + } + } +}; } -IncomingMessages::IncomingMessages(qpid::client::AsyncSession s) : - session(s), - incoming(SessionBase_0_10Access(session).get()->getDemux().getDefault()) {} +void IncomingMessages::setSession(qpid::client::AsyncSession s) +{ + session = s; + incoming = SessionBase_0_10Access(session).get()->getDemux().getDefault(); + acceptTracker.reset(); +} bool IncomingMessages::get(Handler& handler, Duration timeout) { @@ -101,8 +125,7 @@ bool IncomingMessages::get(Handler& handler, Duration timeout) void IncomingMessages::accept() { - session.messageAccept(unaccepted); - unaccepted.clear(); + acceptTracker.accept(session); } void IncomingMessages::releaseAll() @@ -116,8 +139,7 @@ void IncomingMessages::releaseAll() GetAny handler; while (process(&handler, 0)) ; //now release all messages - session.messageRelease(unaccepted); - unaccepted.clear(); + acceptTracker.release(session); } void IncomingMessages::releasePending(const std::string& destination) @@ -161,6 +183,32 @@ bool IncomingMessages::process(Handler* handler, qpid::sys::Duration duration) return false; } +uint32_t IncomingMessages::pendingAccept() +{ + return acceptTracker.acceptsPending(); +} +uint32_t IncomingMessages::pendingAccept(const std::string& destination) +{ + return acceptTracker.acceptsPending(destination); +} + +uint32_t IncomingMessages::available() +{ + //first pump all available messages from incoming to received... + while (process(0, 0)) {} + //return the count of received messages + return received.size(); +} + +uint32_t IncomingMessages::available(const std::string& destination) +{ + //first pump all available messages from incoming to received... + while (process(0, 0)) {} + + //count all messages for this destination from received list + return std::for_each(received.begin(), received.end(), Match(destination)).matched; +} + void populate(qpid::messaging::Message& message, FrameSet& command); /** @@ -175,7 +223,7 @@ void IncomingMessages::retrieve(FrameSetPtr command, qpid::messaging::Message* m } const MessageTransferBody* transfer = command->as<MessageTransferBody>(); if (transfer->getAcquireMode() == ACQUIRE_MODE_PRE_ACQUIRED && transfer->getAcceptMode() == ACCEPT_MODE_EXPLICIT) { - unaccepted.add(command->getId()); + acceptTracker.delivered(transfer->getDestination(), command->getId()); } session.markCompleted(command->getId(), false, false); } @@ -191,8 +239,6 @@ void IncomingMessages::MessageTransfer::retrieve(qpid::messaging::Message* messa 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) @@ -206,6 +252,7 @@ void populateHeaders(qpid::messaging::Message& message, if (messageProperties->hasReplyTo()) { message.setReplyTo(AddressResolution::convert(messageProperties->getReplyTo())); } + message.getHeaders().clear(); translate(messageProperties->getApplicationHeaders(), message.getHeaders()); //TODO: convert other message properties } @@ -219,9 +266,8 @@ void populateHeaders(qpid::messaging::Message& message, const AMQHeaderBody* hea 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)); + //e.g. for rejecting. + MessageImplAccess::get(message).setInternalId(command.getId()); command.getContent(message.getBytes()); diff --git a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h index c4346fd7d7..e84cd18892 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h @@ -27,6 +27,7 @@ #include "qpid/framing/SequenceSet.h" #include "qpid/sys/BlockingQueue.h" #include "qpid/sys/Time.h" +#include "qpid/client/amqp0_10/AcceptTracker.h" namespace qpid { @@ -67,20 +68,26 @@ class IncomingMessages virtual bool accept(MessageTransfer& transfer) = 0; }; - IncomingMessages(qpid::client::AsyncSession session); + void setSession(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); + + uint32_t pendingAccept(); + uint32_t pendingAccept(const std::string& destination); + + uint32_t available(); + uint32_t available(const std::string& destination); private: typedef std::deque<FrameSetPtr> FrameSetQueue; qpid::client::AsyncSession session; - qpid::framing::SequenceSet unaccepted; boost::shared_ptr< sys::BlockingQueue<FrameSetPtr> > incoming; FrameSetQueue received; + AcceptTracker acceptTracker; bool process(Handler*, qpid::sys::Duration); void retrieve(FrameSetPtr, qpid::messaging::Message*); diff --git a/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h b/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h index 19d5e4ef82..d66d2ecb3c 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h @@ -33,6 +33,8 @@ class Message; namespace client { namespace amqp0_10 { +class OutgoingMessage; + /** * */ @@ -41,7 +43,7 @@ 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 send(qpid::client::AsyncSession& session, const std::string& name, OutgoingMessage& message) = 0; virtual void cancel(qpid::client::AsyncSession& session, const std::string& name) = 0; private: }; diff --git a/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp new file mode 100644 index 0000000000..716f955f98 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp @@ -0,0 +1,64 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/amqp0_10/OutgoingMessage.h" +#include "qpid/client/amqp0_10/AddressResolution.h" +#include "qpid/client/amqp0_10/Codecs.h" +#include "qpid/client/amqp0_10/CodecsInternal.h" +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/MessageImpl.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +using qpid::messaging::Address; +using qpid::messaging::MessageImplAccess; + +template <class T> void encode(const qpid::messaging::Message& from, qpid::client::Message& to) +{ + T codec; + MessageImplAccess::get(from).getEncodedContent(codec, to.getData()); + to.getMessageProperties().setContentType(T::contentType); +} + +void OutgoingMessage::convert(const qpid::messaging::Message& from) +{ + //TODO: need to avoid copying as much as possible + if (from.getContent().isList()) { + encode<ListCodec>(from, message); + } else if (from.getContent().isMap()) { + encode<MapCodec>(from, message); + } else { + message.setData(from.getBytes()); + message.getMessageProperties().setContentType(from.getContentType()); + } + const Address& address = from.getReplyTo(); + if (!address.value.empty()) { + message.getMessageProperties().setReplyTo(AddressResolution::convert(address)); + } + translate(from.getHeaders(), message.getMessageProperties().getApplicationHeaders()); + //TODO: set other message properties + message.getDeliveryProperties().setRoutingKey(from.getSubject()); + //TODO: set other delivery properties +} + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.cpp b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h index 52b623b65c..8801e4e769 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h @@ -1,3 +1,6 @@ +#ifndef QPID_CLIENT_AMQP0_10_OUTGOINGMESSAGE_H +#define QPID_CLIENT_AMQP0_10_OUTGOINGMESSAGE_H + /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -18,31 +21,26 @@ * under the License. * */ -#include "CompletionTracker.h" +#include "qpid/client/Completion.h" +#include "qpid/client/Message.h" namespace qpid { +namespace messaging { +class Message; +} namespace client { namespace amqp0_10 { -using qpid::framing::SequenceNumber; - -void CompletionTracker::track(SequenceNumber command, void* token) +struct OutgoingMessage { - tokens[command] = token; -} + qpid::client::Message message; + qpid::client::Completion status; + + void convert(const qpid::messaging::Message&); +}; -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 + +#endif /*!QPID_CLIENT_AMQP0_10_OUTGOINGMESSAGE_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp index e6ed4bfc4e..da91c4a160 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp @@ -19,6 +19,7 @@ * */ #include "ReceiverImpl.h" +#include "AddressResolution.h" #include "MessageSource.h" #include "SessionImpl.h" #include "qpid/messaging/MessageListener.h" @@ -38,11 +39,6 @@ void ReceiverImpl::received(qpid::messaging::Message&) 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) { @@ -50,24 +46,6 @@ qpid::messaging::Message ReceiverImpl::get(qpid::sys::Duration timeout) 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) { @@ -76,71 +54,152 @@ qpid::messaging::Message ReceiverImpl::fetch(qpid::sys::Duration timeout) return result; } +bool ReceiverImpl::get(qpid::messaging::Message& message, qpid::sys::Duration timeout) +{ + Get f(*this, message, timeout); + while (!parent.execute(f)) {} + return f.result; +} + +bool ReceiverImpl::fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout) +{ + Fetch f(*this, message, timeout); + while (!parent.execute(f)) {} + return f.result; +} + void ReceiverImpl::cancel() { - 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; - } + execute<Cancel>(); } void ReceiverImpl::start() { - if (!cancelled) { - started = true; - session.messageSetFlowMode(destination, capacity > 0); + execute<Start>(); +} + +void ReceiverImpl::stop() +{ + execute<Stop>(); +} + +void ReceiverImpl::setCapacity(uint32_t c) +{ + execute1<SetCapacity>(c); +} + +void ReceiverImpl::startFlow() +{ + if (capacity > 0) { + session.messageSetFlowMode(destination, FLOW_MODE_WINDOW); session.messageFlow(destination, CREDIT_UNIT_MESSAGE, capacity); session.messageFlow(destination, CREDIT_UNIT_BYTE, byteCredit); window = capacity; } } -void ReceiverImpl::stop() +void ReceiverImpl::init(qpid::client::AsyncSession s, AddressResolution& resolver) { - session.messageStop(destination); - started = false; + + session = s; + if (state == UNRESOLVED) { + source = resolver.resolveSource(session, address, filter, options); + state = STOPPED;//TODO: if session is started, go straight to started + } + if (state == CANCELLED) { + source->cancel(session, destination); + parent.receiverCancelled(destination); + } else { + source->subscribe(session, destination); + if (state == STARTED) start(); + } } -void ReceiverImpl::subscribe() +void ReceiverImpl::setListener(qpid::messaging::MessageListener* l) { listener = l; } +qpid::messaging::MessageListener* ReceiverImpl::getListener() { return listener; } + +const std::string& ReceiverImpl::getName() const { return destination; } + +uint32_t ReceiverImpl::getCapacity() +{ + return capacity; +} + +uint32_t ReceiverImpl::available() +{ + return parent.available(destination); +} + +uint32_t ReceiverImpl::pendingAck() { - source->subscribe(session, destination); + return parent.pendingAck(destination); } -void ReceiverImpl::setSession(qpid::client::AsyncSession s) +ReceiverImpl::ReceiverImpl(SessionImpl& p, const std::string& name, + const qpid::messaging::Address& a, + const qpid::messaging::Filter* f, + const qpid::messaging::Variant::Map& o) : + + parent(p), destination(name), address(a), filter(f), options(o), byteCredit(0xFFFFFFFF), + state(UNRESOLVED), capacity(0), listener(0), window(0) {} + +bool ReceiverImpl::getImpl(qpid::messaging::Message& message, qpid::sys::Duration timeout) +{ + return parent.get(*this, message, timeout); +} + +bool ReceiverImpl::fetchImpl(qpid::messaging::Message& message, qpid::sys::Duration timeout) +{ + if (state == CANCELLED) return false;//TODO: or should this be an error? + + if (capacity == 0 || state != STARTED) { + session.messageSetFlowMode(destination, FLOW_MODE_CREDIT); + session.messageFlow(destination, CREDIT_UNIT_MESSAGE, 1); + session.messageFlow(destination, CREDIT_UNIT_BYTE, 0xFFFFFFFF); + } + + if (getImpl(message, timeout)) { + return true; + } else { + sync(session).messageFlush(destination); + startFlow();//reallocate credit + return getImpl(message, 0); + } +} + +void ReceiverImpl::cancelImpl() { - 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(); + if (state != CANCELLED) { + state = CANCELLED; + source->cancel(session, destination); + parent.receiverCancelled(destination); } } -void ReceiverImpl::setCapacity(uint32_t c) +void ReceiverImpl::startImpl() +{ + if (state == STOPPED) { + state = STARTED; + startFlow(); + } +} + +void ReceiverImpl::stopImpl() +{ + state = STOPPED; + session.messageStop(destination); +} + +void ReceiverImpl::setCapacityImpl(uint32_t c) { if (c != capacity) { capacity = c; - if (!cancelled && started) { - stop(); - start(); + if (state == STARTED) { + session.messageStop(destination); + startFlow(); } } } -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 index b549242d35..b941348fc8 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h @@ -21,9 +21,13 @@ * under the License. * */ +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Filter.h" #include "qpid/messaging/Message.h" #include "qpid/messaging/ReceiverImpl.h" +#include "qpid/messaging/Variant.h" #include "qpid/client/AsyncSession.h" +#include "qpid/client/amqp0_10/SessionImpl.h" #include "qpid/sys/Time.h" #include <memory> @@ -31,8 +35,8 @@ namespace qpid { namespace client { namespace amqp0_10 { +class AddressResolution; class MessageSource; -class SessionImpl; /** * A receiver implementation based on an AMQP 0-10 subscription. @@ -41,8 +45,14 @@ class ReceiverImpl : public qpid::messaging::ReceiverImpl { public: - ReceiverImpl(SessionImpl& parent, const std::string& name, std::auto_ptr<MessageSource> source); + enum State {UNRESOLVED, STOPPED, STARTED, CANCELLED}; + ReceiverImpl(SessionImpl& parent, const std::string& name, + const qpid::messaging::Address& address, + const qpid::messaging::Filter* filter, + const qpid::messaging::Variant::Map& options); + + void init(qpid::client::AsyncSession session, AddressResolution& resolver); bool get(qpid::messaging::Message& message, qpid::sys::Duration timeout); qpid::messaging::Message get(qpid::sys::Duration timeout); bool fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout); @@ -50,25 +60,107 @@ class ReceiverImpl : public qpid::messaging::ReceiverImpl void cancel(); void start(); void stop(); - void subscribe(); - void setSession(qpid::client::AsyncSession s); const std::string& getName() const; void setCapacity(uint32_t); + uint32_t getCapacity(); + uint32_t available(); + uint32_t pendingAck(); 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 qpid::messaging::Address address; + const qpid::messaging::Filter* filter; + const qpid::messaging::Variant::Map options; const uint32_t byteCredit; - + State state; + + std::auto_ptr<MessageSource> source; uint32_t capacity; qpid::client::AsyncSession session; - bool started; - bool cancelled; qpid::messaging::MessageListener* listener; uint32_t window; + + void startFlow(); + //implementation of public facing methods + bool fetchImpl(qpid::messaging::Message& message, qpid::sys::Duration timeout); + bool getImpl(qpid::messaging::Message& message, qpid::sys::Duration timeout); + void startImpl(); + void stopImpl(); + void cancelImpl(); + void setCapacityImpl(uint32_t); + + //functors for public facing methods (allows locking and retry + //logic to be centralised) + struct Command + { + ReceiverImpl& impl; + + Command(ReceiverImpl& i) : impl(i) {} + }; + + struct Get : Command + { + qpid::messaging::Message& message; + qpid::sys::Duration timeout; + bool result; + + Get(ReceiverImpl& i, qpid::messaging::Message& m, qpid::sys::Duration t) : + Command(i), message(m), timeout(t), result(false) {} + void operator()() { result = impl.getImpl(message, timeout); } + }; + + struct Fetch : Command + { + qpid::messaging::Message& message; + qpid::sys::Duration timeout; + bool result; + + Fetch(ReceiverImpl& i, qpid::messaging::Message& m, qpid::sys::Duration t) : + Command(i), message(m), timeout(t), result(false) {} + void operator()() { result = impl.fetchImpl(message, timeout); } + }; + + struct Stop : Command + { + Stop(ReceiverImpl& i) : Command(i) {} + void operator()() { impl.stopImpl(); } + }; + + struct Start : Command + { + Start(ReceiverImpl& i) : Command(i) {} + void operator()() { impl.startImpl(); } + }; + + struct Cancel : Command + { + Cancel(ReceiverImpl& i) : Command(i) {} + void operator()() { impl.cancelImpl(); } + }; + + struct SetCapacity : Command + { + uint32_t capacity; + + SetCapacity(ReceiverImpl& i, uint32_t c) : Command(i), capacity(c) {} + void operator()() { impl.setCapacityImpl(capacity); } + }; + + //helper templates for some common patterns + template <class F> void execute() + { + F f(*this); + parent.execute(f); + } + + template <class F, class P> void execute1(P p) + { + F f(*this, p); + parent.execute(f); + } }; }}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp index ac36eb1537..4cd2dc0521 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp @@ -21,29 +21,113 @@ #include "SenderImpl.h" #include "MessageSink.h" #include "SessionImpl.h" +#include "AddressResolution.h" +#include "OutgoingMessage.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) {} +SenderImpl::SenderImpl(SessionImpl& _parent, const std::string& _name, + const qpid::messaging::Address& _address, + const qpid::messaging::Variant::Map& _options) : + parent(_parent), name(_name), address(_address), options(_options), state(UNRESOLVED), + capacity(50), window(0), flushed(false) {} -void SenderImpl::send(qpid::messaging::Message& m) +void SenderImpl::send(const qpid::messaging::Message& message) { - sink->send(session, name, m); + Send f(*this, &message); + while (f.repeat) parent.execute(f); } void SenderImpl::cancel() { - sink->cancel(session, name); - parent.senderCancelled(name); + execute<Cancel>(); +} + +void SenderImpl::setCapacity(uint32_t c) +{ + bool flush = c < capacity; + capacity = c; + execute1<CheckPendingSends>(flush); } +uint32_t SenderImpl::getCapacity() { return capacity; } +uint32_t SenderImpl::pending() +{ + CheckPendingSends f(*this, false); + parent.execute(f); + return f.pending; +} -void SenderImpl::setSession(qpid::client::AsyncSession s) +void SenderImpl::init(qpid::client::AsyncSession s, AddressResolution& resolver) { session = s; - sink->declare(session, name); + if (state == UNRESOLVED) { + sink = resolver.resolveSink(session, address, options); + state = ACTIVE; + } + if (state == CANCELLED) { + sink->cancel(session, name); + parent.senderCancelled(name); + } else { + sink->declare(session, name); + replay(); + } +} + +void SenderImpl::waitForCapacity() +{ + //TODO: add option to throw exception rather than blocking? + if (capacity <= (flushed ? checkPendingSends(false) : outgoing.size())) { + //Initial implementation is very basic. As outgoing is + //currently only reduced on receiving completions and we are + //blocking anyway we may as well sync(). If successful that + //should clear all outstanding sends. + session.sync(); + checkPendingSends(false); + } + //flush periodically and check for conmpleted sends + if (++window > (capacity / 4)) {//TODO: make this configurable? + checkPendingSends(true); + window = 0; + } +} + +void SenderImpl::sendImpl(const qpid::messaging::Message& m) +{ + //TODO: make recording for replay optional (would still want to track completion however) + std::auto_ptr<OutgoingMessage> msg(new OutgoingMessage()); + msg->convert(m); + outgoing.push_back(msg.release()); + sink->send(session, name, outgoing.back()); +} + +void SenderImpl::replay() +{ + for (OutgoingMessages::iterator i = outgoing.begin(); i != outgoing.end(); ++i) { + sink->send(session, name, *i); + } +} + +uint32_t SenderImpl::checkPendingSends(bool flush) +{ + if (flush) { + session.flush(); + flushed = true; + } else { + flushed = false; + } + while (!outgoing.empty() && outgoing.front().status.isComplete()) { + outgoing.pop_front(); + } + return outgoing.size(); +} + +void SenderImpl::cancelImpl() +{ + state = CANCELLED; + sink->cancel(session, name); + parent.senderCancelled(name); } }}} // 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 index e737450ba1..028d26bda7 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h @@ -21,17 +21,22 @@ * under the License. * */ +#include "qpid/messaging/Address.h" #include "qpid/messaging/Message.h" #include "qpid/messaging/SenderImpl.h" +#include "qpid/messaging/Variant.h" #include "qpid/client/AsyncSession.h" +#include "qpid/client/amqp0_10/SessionImpl.h" #include <memory> +#include <boost/ptr_container/ptr_deque.hpp> namespace qpid { namespace client { namespace amqp0_10 { +class AddressResolution; class MessageSink; -class SessionImpl; +class OutgoingMessage; /** * @@ -39,19 +44,96 @@ 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&); + enum State {UNRESOLVED, ACTIVE, CANCELLED}; + + SenderImpl(SessionImpl& parent, const std::string& name, + const qpid::messaging::Address& address, + const qpid::messaging::Variant::Map& options); + void send(const qpid::messaging::Message&); void cancel(); - void setSession(qpid::client::AsyncSession); + void setCapacity(uint32_t); + uint32_t getCapacity(); + uint32_t pending(); + void init(qpid::client::AsyncSession, AddressResolution&); private: SessionImpl& parent; const std::string name; + const qpid::messaging::Address address; + const qpid::messaging::Variant::Map options; + State state; std::auto_ptr<MessageSink> sink; qpid::client::AsyncSession session; std::string destination; std::string routingKey; + + typedef boost::ptr_deque<OutgoingMessage> OutgoingMessages; + OutgoingMessages outgoing; + uint32_t capacity; + uint32_t window; + bool flushed; + + uint32_t checkPendingSends(bool flush); + void replay(); + void waitForCapacity(); + + //logic for application visible methods: + void sendImpl(const qpid::messaging::Message&); + void cancelImpl(); + + + //functors for application visible methods (allowing locking and + //retry to be centralised): + struct Command + { + SenderImpl& impl; + + Command(SenderImpl& i) : impl(i) {} + }; + + struct Send : Command + { + const qpid::messaging::Message* message; + bool repeat; + + Send(SenderImpl& i, const qpid::messaging::Message* m) : Command(i), message(m), repeat(true) {} + void operator()() + { + impl.waitForCapacity(); + //from this point message will be recorded if there is any + //failure (and replayed) so need not repeat the call + repeat = false; + impl.sendImpl(*message); + } + }; + + struct Cancel : Command + { + Cancel(SenderImpl& i) : Command(i) {} + void operator()() { impl.cancelImpl(); } + }; + + struct CheckPendingSends : Command + { + bool flush; + uint32_t pending; + CheckPendingSends(SenderImpl& i, bool f) : Command(i), flush(f), pending(0) {} + void operator()() { pending = impl.checkPendingSends(flush); } + }; + + //helper templates for some common patterns + template <class F> void execute() + { + F f(*this); + parent.execute(f); + } + + template <class F, class P> bool execute1(P p) + { + F f(*this, p); + return parent.execute(f); + } }; }}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp index 9ea2a9f598..bc6289d84b 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp @@ -19,6 +19,7 @@ * */ #include "qpid/client/amqp0_10/SessionImpl.h" +#include "qpid/client/amqp0_10/ConnectionImpl.h" #include "qpid/client/amqp0_10/ReceiverImpl.h" #include "qpid/client/amqp0_10/SenderImpl.h" #include "qpid/client/amqp0_10/MessageSource.h" @@ -29,6 +30,7 @@ #include "qpid/messaging/Address.h" #include "qpid/messaging/Filter.h" #include "qpid/messaging/Message.h" +#include "qpid/messaging/MessageImpl.h" #include "qpid/messaging/MessageListener.h" #include "qpid/messaging/Sender.h" #include "qpid/messaging/Receiver.h" @@ -39,6 +41,7 @@ #include <boost/intrusive_ptr.hpp> using qpid::messaging::Filter; +using qpid::messaging::MessageImplAccess; using qpid::messaging::Sender; using qpid::messaging::Receiver; using qpid::messaging::VariantMap; @@ -47,64 +50,55 @@ namespace qpid { namespace client { namespace amqp0_10 { -SessionImpl::SessionImpl(qpid::client::Session s) : session(s), incoming(session) {} +SessionImpl::SessionImpl(ConnectionImpl& c) : connection(c) {} +void SessionImpl::sync() +{ + retry<Sync>(); +} + +void SessionImpl::flush() +{ + retry<Flush>(); +} + void SessionImpl::commit() { - qpid::sys::Mutex::ScopedLock l(lock); - incoming.accept(); - session.txCommit(); + if (!execute<Commit>()) { + throw Exception();//TODO: what type? + } } 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(); + //If the session fails during this operation, the transaction will + //be rolled back anyway. + execute<Rollback>(); } void SessionImpl::acknowledge() { - qpid::sys::Mutex::ScopedLock l(lock); - incoming.accept(); + //Should probably throw an exception on failure here, or indicate + //it through a return type at least. Failure means that the + //message may be redelivered; i.e. the application cannot delete + //any state necessary for preventing reprocessing of the message + execute<Acknowledge>(); } void SessionImpl::reject(qpid::messaging::Message& m) { - 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); + //Possibly want to somehow indicate failure here as well. Less + //clear need as compared to acknowledge however. + execute1<Reject>(m); } void SessionImpl::close() { + connection.closed(*this); 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)); @@ -120,38 +114,88 @@ template <class T> void getFreeKey(std::string& key, T& map) key = name; } -Sender SessionImpl::createSender(const qpid::messaging::Address& address, const VariantMap& options) -{ + +void SessionImpl::setSession(qpid::client::Session s) +{ 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; + session = s; + incoming.setSession(session); + for (Receivers::iterator i = receivers.begin(); i != receivers.end(); ++i) { + getImplPtr<Receiver, ReceiverImpl>(i->second)->init(session, resolver); + } + for (Senders::iterator i = senders.begin(); i != senders.end(); ++i) { + getImplPtr<Sender, SenderImpl>(i->second)->init(session, resolver); + } } + +struct SessionImpl::CreateReceiver : Command +{ + qpid::messaging::Receiver result; + const qpid::messaging::Address& address; + const Filter* filter; + const qpid::messaging::Variant::Map& options; + + CreateReceiver(SessionImpl& i, const qpid::messaging::Address& a, const Filter* f, + const qpid::messaging::Variant::Map& o) : + Command(i), address(a), filter(f), options(o) {} + void operator()() { result = impl.createReceiverImpl(address, filter, options); } +}; + Receiver SessionImpl::createReceiver(const qpid::messaging::Address& address, const VariantMap& options) { - return addReceiver(address, 0, options); + CreateReceiver f(*this, address, 0, options); + while (!execute(f)) {} + return f.result; } -Receiver SessionImpl::createReceiver(const qpid::messaging::Address& address, const Filter& filter, const VariantMap& options) + +Receiver SessionImpl::createReceiver(const qpid::messaging::Address& address, + const Filter& filter, const VariantMap& options) { - return addReceiver(address, &filter, options); + CreateReceiver f(*this, address, &filter, options); + while (!execute(f)) {} + return f.result; } -Receiver SessionImpl::addReceiver(const qpid::messaging::Address& address, const Filter* filter, const VariantMap& options) +Receiver SessionImpl::createReceiverImpl(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); + Receiver receiver(new ReceiverImpl(*this, name, address, filter, options)); + getImplPtr<Receiver, ReceiverImpl>(receiver)->init(session, resolver); receivers[name] = receiver; return receiver; } +struct SessionImpl::CreateSender : Command +{ + qpid::messaging::Sender result; + const qpid::messaging::Address& address; + const qpid::messaging::Variant::Map& options; + + CreateSender(SessionImpl& i, const qpid::messaging::Address& a, + const qpid::messaging::Variant::Map& o) : + Command(i), address(a), options(o) {} + void operator()() { result = impl.createSenderImpl(address, options); } +}; + +Sender SessionImpl::createSender(const qpid::messaging::Address& address, const VariantMap& options) +{ + CreateSender f(*this, address, options); + while (!execute(f)) {} + return f.result; +} + +Sender SessionImpl::createSenderImpl(const qpid::messaging::Address& address, const VariantMap& options) +{ + std::string name = address; + getFreeKey(name, senders); + Sender sender(new SenderImpl(*this, name, address, options)); + getImplPtr<Sender, SenderImpl>(sender)->init(session, resolver); + senders[name] = sender; + return sender; +} + qpid::messaging::Address SessionImpl::createTempQueue(const std::string& baseName) { std::string name = baseName + std::string("_") + session.getId().getName(); @@ -218,27 +262,135 @@ bool SessionImpl::acceptAny(qpid::messaging::Message* message, bool isDispatch, 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) +bool SessionImpl::get(ReceiverImpl& receiver, qpid::messaging::Message& message, qpid::sys::Duration timeout) { - qpid::messaging::Message message; - IncomingMessageHandler handler(boost::bind(&SessionImpl::acceptAny, this, &message, true, _1)); + IncomingMessageHandler handler(boost::bind(&SessionImpl::accept, this, &receiver, &message, false, _1)); return getIncoming(handler, timeout); } -bool SessionImpl::get(ReceiverImpl& receiver, qpid::messaging::Message& message, qpid::sys::Duration timeout) +bool SessionImpl::dispatch(qpid::sys::Duration timeout) { - IncomingMessageHandler handler(boost::bind(&SessionImpl::accept, this, &receiver, &message, false, _1)); - return getIncoming(handler, timeout); + qpid::sys::Mutex::ScopedLock l(lock); + while (true) { + try { + qpid::messaging::Message message; + IncomingMessageHandler handler(boost::bind(&SessionImpl::acceptAny, this, &message, true, _1)); + return getIncoming(handler, timeout); + } catch (TransportFailure&) { + reconnect(); + } + } } 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::sys::Mutex::ScopedLock l(lock); + while (true) { + try { + IncomingMessageHandler handler(boost::bind(&SessionImpl::acceptAny, this, &message, false, _1)); + return getIncoming(handler, timeout); + } catch (TransportFailure&) { + reconnect(); + } + } +} + +uint32_t SessionImpl::available() +{ + return get1<Available, uint32_t>((const std::string*) 0); +} +uint32_t SessionImpl::available(const std::string& destination) +{ + return get1<Available, uint32_t>(&destination); +} + +struct SessionImpl::Available : Command +{ + const std::string* destination; + uint32_t result; + + Available(SessionImpl& i, const std::string* d) : Command(i), destination(d), result(0) {} + void operator()() { result = impl.availableImpl(destination); } +}; + +uint32_t SessionImpl::availableImpl(const std::string* destination) +{ + if (destination) { + return incoming.available(*destination); + } else { + return incoming.available(); + } +} + +uint32_t SessionImpl::pendingAck() +{ + return get1<PendingAck, uint32_t>((const std::string*) 0); +} + +uint32_t SessionImpl::pendingAck(const std::string& destination) +{ + return get1<PendingAck, uint32_t>(&destination); +} + +struct SessionImpl::PendingAck : Command +{ + const std::string* destination; + uint32_t result; + + PendingAck(SessionImpl& i, const std::string* d) : Command(i), destination(d), result(0) {} + void operator()() { result = impl.pendingAckImpl(destination); } +}; + +uint32_t SessionImpl::pendingAckImpl(const std::string* destination) +{ + if (destination) { + return incoming.pendingAccept(*destination); + } else { + return incoming.pendingAccept(); + } +} + +void SessionImpl::syncImpl() +{ + session.sync(); +} + +void SessionImpl::flushImpl() +{ + session.flush(); +} + + +void SessionImpl::commitImpl() +{ + incoming.accept(); + session.txCommit(); +} + +void SessionImpl::rollbackImpl() +{ + for (Receivers::iterator i = receivers.begin(); i != receivers.end(); ++i) 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::acknowledgeImpl() +{ + incoming.accept(); +} + +void SessionImpl::rejectImpl(qpid::messaging::Message& m) +{ + SequenceSet set; + set.add(MessageImplAccess::get(m).getInternalId()); + session.messageReject(set); } qpid::messaging::Message SessionImpl::fetch(qpid::sys::Duration timeout) @@ -250,28 +402,19 @@ qpid::messaging::Message SessionImpl::fetch(qpid::sys::Duration timeout) void SessionImpl::receiverCancelled(const std::string& name) { - { - qpid::sys::Mutex::ScopedLock l(lock); - receivers.erase(name); - } + 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() +void SessionImpl::reconnect() { - session.sync(); -} - -void SessionImpl::flush() -{ - session.flush(); + connection.reconnect(); } void* SessionImpl::getLastConfirmedSent() diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h index 6926fb0235..b453f3f08f 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h @@ -32,8 +32,8 @@ namespace qpid { namespace messaging { -class Address; -class Filter; +struct Address; +struct Filter; class Message; class Receiver; class Sender; @@ -43,6 +43,7 @@ class Session; namespace client { namespace amqp0_10 { +class ConnectionImpl; class ReceiverImpl; class SenderImpl; @@ -53,7 +54,7 @@ class SenderImpl; class SessionImpl : public qpid::messaging::SessionImpl { public: - SessionImpl(qpid::client::Session); + SessionImpl(ConnectionImpl&); void commit(); void rollback(); void acknowledge(); @@ -81,26 +82,137 @@ class SessionImpl : public qpid::messaging::SessionImpl void receiverCancelled(const std::string& name); void senderCancelled(const std::string& name); - + + uint32_t available(); + uint32_t available(const std::string& destination); + + uint32_t pendingAck(); + uint32_t pendingAck(const std::string& destination); + + void setSession(qpid::client::Session); + + template <class T> bool execute(T& f) + { + try { + qpid::sys::Mutex::ScopedLock l(lock); + f(); + return true; + } catch (TransportFailure&) { + reconnect(); + return false; + } + } + static SessionImpl& convert(qpid::messaging::Session&); - 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; + ConnectionImpl& connection; + qpid::client::Session session; 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); + void reconnect(); + + void commitImpl(); + void rollbackImpl(); + void acknowledgeImpl(); + void rejectImpl(qpid::messaging::Message&); + void closeImpl(); + void syncImpl(); + void flushImpl(); + qpid::messaging::Sender createSenderImpl(const qpid::messaging::Address& address, + const qpid::messaging::VariantMap& options); + qpid::messaging::Receiver createReceiverImpl(const qpid::messaging::Address& address, + const qpid::messaging::Filter* filter, + const qpid::messaging::VariantMap& options); + uint32_t availableImpl(const std::string* destination); + uint32_t pendingAckImpl(const std::string* destination); + + //functors for public facing methods (allows locking and retry + //logic to be centralised) + struct Command + { + SessionImpl& impl; + + Command(SessionImpl& i) : impl(i) {} + }; + + struct Commit : Command + { + Commit(SessionImpl& i) : Command(i) {} + void operator()() { impl.commitImpl(); } + }; + + struct Rollback : Command + { + Rollback(SessionImpl& i) : Command(i) {} + void operator()() { impl.rollbackImpl(); } + }; + + struct Acknowledge : Command + { + Acknowledge(SessionImpl& i) : Command(i) {} + void operator()() { impl.acknowledgeImpl(); } + }; + + struct Sync : Command + { + Sync(SessionImpl& i) : Command(i) {} + void operator()() { impl.syncImpl(); } + }; + + struct Flush : Command + { + Flush(SessionImpl& i) : Command(i) {} + void operator()() { impl.flushImpl(); } + }; + + struct Reject : Command + { + qpid::messaging::Message& message; + + Reject(SessionImpl& i, qpid::messaging::Message& m) : Command(i), message(m) {} + void operator()() { impl.rejectImpl(message); } + }; + + struct CreateSender; + struct CreateReceiver; + struct PendingAck; + struct Available; + + //helper templates for some common patterns + template <class F> bool execute() + { + F f(*this); + return execute(f); + } + + template <class F> void retry() + { + while (!execute<F>()) {} + } + + template <class F, class P> bool execute1(P p) + { + F f(*this, p); + return execute(f); + } + + template <class F, class R, class P> R get1(P p) + { + F f(*this, p); + while (!execute(f)) {} + return f.result; + } }; }}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/cluster/Connection.cpp b/qpid/cpp/src/qpid/cluster/Connection.cpp index 4cc977d14a..6873827b81 100644 --- a/qpid/cpp/src/qpid/cluster/Connection.cpp +++ b/qpid/cpp/src/qpid/cluster/Connection.cpp @@ -156,8 +156,17 @@ bool Connection::checkUnsupported(const AMQBody& body) { return !message.empty(); } +struct GiveReadCreditOnExit { + Connection& connection; + int credit; + GiveReadCreditOnExit(Connection& connection_, int credit_) : + connection(connection_), credit(credit_) {} + ~GiveReadCreditOnExit() { connection.giveReadCredit(credit); } +}; + // Called in delivery thread, in cluster order. void Connection::deliveredFrame(const EventFrame& f) { + GiveReadCreditOnExit gc(*this, f.readCredit); assert(!catchUp); currentChannel = f.frame.getChannel(); if (f.frame.getBody() // frame can be emtpy with just readCredit @@ -171,7 +180,6 @@ void Connection::deliveredFrame(const EventFrame& f) { if (ss) ss->out(const_cast<AMQFrame&>(f.frame)); } } - giveReadCredit(f.readCredit); } // A local connection is closed by the network layer. diff --git a/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp b/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp index 277adaf7b1..507d9649b9 100644 --- a/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp +++ b/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -33,8 +33,8 @@ namespace { boost::function<void()> errorFn; -void cmanCallbackFn(cman_handle_t handle, void */*privdata*/, int reason, int arg) { - if (reason == CMAN_REASON_STATECHANGE && arg == 0) { +void cmanCallbackFn(cman_handle_t handle, void */*privdata*/, int reason, int /*arg*/) { + if (reason == CMAN_REASON_STATECHANGE && !cman_is_quorate(handle)) { QPID_LOG(critical, "Lost contact with cluster quorum."); if (errorFn) errorFn(); cman_stop_notification(handle); diff --git a/qpid/cpp/src/qpid/framing/Uuid.cpp b/qpid/cpp/src/qpid/framing/Uuid.cpp index c0b41c6906..71fa6a7329 100644 --- a/qpid/cpp/src/qpid/framing/Uuid.cpp +++ b/qpid/cpp/src/qpid/framing/Uuid.cpp @@ -17,6 +17,8 @@ */ #include "qpid/framing/Uuid.h" + +#include "qpid/sys/uuid.h" #include "qpid/Exception.h" #include "qpid/framing/Buffer.h" #include "qpid/framing/reply_exceptions.h" @@ -28,6 +30,35 @@ using namespace std; static const size_t UNPARSED_SIZE=36; +Uuid::Uuid(bool unique) { + if (unique) { + generate(); + } else { + clear(); + } +} + +Uuid::Uuid(const uint8_t* data) { + assign(data); +} + +void Uuid::assign(const uint8_t* data) { + uuid_copy(c_array(), data); +} + +void Uuid::generate() { + uuid_generate(c_array()); +} + +void Uuid::clear() { + uuid_clear(c_array()); +} + +// Force int 0/!0 to false/true; avoids compile warnings. +bool Uuid::isNull() { + return !!uuid_is_null(data()); +} + void Uuid::encode(Buffer& buf) const { buf.putRawData(data(), size()); } diff --git a/qpid/cpp/src/qpid/management/Manageable.cpp b/qpid/cpp/src/qpid/management/Manageable.cpp index e487dfc455..a3593e73e3 100644 --- a/qpid/cpp/src/qpid/management/Manageable.cpp +++ b/qpid/cpp/src/qpid/management/Manageable.cpp @@ -33,7 +33,7 @@ string Manageable::StatusText (status_t status, string text) case STATUS_UNKNOWN_OBJECT : return "UnknownObject"; case STATUS_UNKNOWN_METHOD : return "UnknownMethod"; case STATUS_NOT_IMPLEMENTED : return "NotImplemented"; - case STATUS_INVALID_PARAMETER : return "InvalidParameter"; + case STATUS_PARAMETER_INVALID : return "InvalidParameter"; case STATUS_FEATURE_NOT_IMPLEMENTED : return "FeatureNotImplemented"; case STATUS_FORBIDDEN : return "Forbidden"; } diff --git a/qpid/cpp/src/qpid/management/ManagementAgent.cpp b/qpid/cpp/src/qpid/management/ManagementAgent.cpp index 2df10b1e95..0e462342d4 100644 --- a/qpid/cpp/src/qpid/management/ManagementAgent.cpp +++ b/qpid/cpp/src/qpid/management/ManagementAgent.cpp @@ -535,8 +535,8 @@ void ManagementAgent::handleMethodRequestLH (Buffer& inBuffer, string replyToKey } else { if ((iter->second->getPackageName() != packageName) || (iter->second->getClassName() != className)) { - outBuffer.putLong (Manageable::STATUS_INVALID_PARAMETER); - outBuffer.putMediumString(Manageable::StatusText (Manageable::STATUS_INVALID_PARAMETER)); + outBuffer.putLong (Manageable::STATUS_PARAMETER_INVALID); + outBuffer.putMediumString(Manageable::StatusText (Manageable::STATUS_PARAMETER_INVALID)); } else try { diff --git a/qpid/cpp/src/qpid/messaging/Address.cpp b/qpid/cpp/src/qpid/messaging/Address.cpp index ed35054a00..813a8e1377 100644 --- a/qpid/cpp/src/qpid/messaging/Address.cpp +++ b/qpid/cpp/src/qpid/messaging/Address.cpp @@ -21,9 +21,6 @@ #include "qpid/messaging/Address.h" namespace qpid { -namespace client { -} - namespace messaging { Address::Address() {} diff --git a/qpid/cpp/src/qpid/messaging/Message.cpp b/qpid/cpp/src/qpid/messaging/Message.cpp index e95a05db17..1d844b3027 100644 --- a/qpid/cpp/src/qpid/messaging/Message.cpp +++ b/qpid/cpp/src/qpid/messaging/Message.cpp @@ -19,263 +19,11 @@ * */ #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" +#include "qpid/messaging/MessageImpl.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)) {} @@ -314,9 +62,6 @@ 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); diff --git a/qpid/cpp/src/qpid/messaging/MessageImpl.cpp b/qpid/cpp/src/qpid/messaging/MessageImpl.cpp new file mode 100644 index 0000000000..5df9218e03 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/MessageImpl.cpp @@ -0,0 +1,205 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "MessageImpl.h" +#include "qpid/messaging/Message.h" + +namespace qpid { +namespace messaging { + +namespace { +const std::string EMPTY_STRING = ""; +} + +MessageImpl::MessageImpl(const std::string& c) : bytes(c), type(VAR_VOID), internalId(0) {} +MessageImpl::MessageImpl(const char* chars, size_t count) : bytes(chars, count), type(VAR_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 == VAR_MAP) { + return out << content.asMap(); + } else if (type == VAR_LIST) { + return out << content.asList(); + } else { + return out << bytes; + } +} + +template <class T> MessageContent& MessageImpl::append(T& t) +{ + if (type == VAR_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 == VAR_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 = VAR_VOID; + bytes = s; + return *this; +} +MessageContent& MessageImpl::operator=(const char* c) +{ + type = VAR_VOID; + bytes = c; + return *this; +} +MessageContent& MessageImpl::operator=(const Variant::Map& m) +{ + type = VAR_MAP; + content = m; + return *this; +} + +MessageContent& MessageImpl::operator=(const Variant::List& l) +{ + type = VAR_LIST; + content = l; + return *this; +} + +void MessageImpl::encode(Codec& codec) +{ + if (content.getType() != VAR_VOID) { + bytes = EMPTY_STRING; + codec.encode(content, bytes); + } +} + +void MessageImpl::getEncodedContent(Codec& codec, std::string& out) const +{ + if (content.getType() != VAR_VOID) { + codec.encode(content, out); + } else { + out = bytes; + } +} + +void MessageImpl::decode(Codec& codec) +{ + codec.decode(bytes, content); + if (content.getType() == VAR_MAP) type = VAR_MAP; + else if (content.getType() == VAR_LIST) type = VAR_LIST; + else type = VAR_VOID;//TODO: what if codec set some type other than map or list?? +} + +void MessageImpl::setInternalId(qpid::framing::SequenceNumber i) { internalId = i; } +qpid::framing::SequenceNumber MessageImpl::getInternalId() { return internalId; } + +bool MessageImpl::isVoid() const { return type == VAR_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 = VAR_MAP; + } + return content.asMap(); +} +bool MessageImpl::isMap() const { return type == VAR_MAP; } + +const Variant::List& MessageImpl::asList() const { return content.asList(); } +Variant::List& MessageImpl::asList() +{ + if (isVoid()) { + content = Variant::List(); + type = VAR_LIST; + } + return content.asList(); +} +bool MessageImpl::isList() const { return type == VAR_LIST; } + +void MessageImpl::clear() { bytes = EMPTY_STRING; content.reset(); type = VAR_VOID; } + +MessageImpl& MessageImplAccess::get(Message& msg) +{ + return *msg.impl; +} +const MessageImpl& MessageImplAccess::get(const Message& msg) +{ + return *msg.impl; +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/MessageImpl.h b/qpid/cpp/src/qpid/messaging/MessageImpl.h new file mode 100644 index 0000000000..1173e7570a --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/MessageImpl.h @@ -0,0 +1,134 @@ +#ifndef QPID_MESSAGING_MESSAGEIMPL_H +#define QPID_MESSAGING_MESSAGEIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Codec.h" +#include "qpid/messaging/MessageContent.h" +#include "qpid/messaging/Variant.h" +#include "qpid/framing/SequenceNumber.h" + +namespace qpid { +namespace messaging { + +struct MessageImpl : MessageContent +{ + Address replyTo; + std::string subject; + std::string contentType; + Variant::Map 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 + + qpid::framing::SequenceNumber internalId; + + MessageImpl(const std::string& c); + MessageImpl(const char* chars, size_t count); + + void setReplyTo(const Address& d); + const Address& getReplyTo() const; + + void setSubject(const std::string& s); + const std::string& getSubject() const; + + void setContentType(const std::string& s); + const std::string& getContentType() const; + + const Variant::Map& getHeaders() const; + Variant::Map& getHeaders(); + + void setBytes(const std::string& bytes); + void setBytes(const char* chars, size_t count); + const std::string& getBytes() const; + std::string& getBytes(); + + void setInternalId(qpid::framing::SequenceNumber id); + qpid::framing::SequenceNumber getInternalId(); + + 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 getEncodedContent(Codec& codec, std::string&) const; + 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); +}; + +class Message; + +/** + * Provides access to the internal MessageImpl for a message which is + * useful when accessing any message state not exposed directly + * through the public API. + */ +struct MessageImplAccess +{ + static MessageImpl& get(Message&); + static const MessageImpl& get(const Message&); +}; + +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_MESSAGEIMPL_H*/ diff --git a/qpid/cpp/src/qpid/messaging/Receiver.cpp b/qpid/cpp/src/qpid/messaging/Receiver.cpp index 2e8b89d27f..3290ea98ac 100644 --- a/qpid/cpp/src/qpid/messaging/Receiver.cpp +++ b/qpid/cpp/src/qpid/messaging/Receiver.cpp @@ -45,6 +45,9 @@ Message Receiver::fetch(qpid::sys::Duration timeout) { return impl->fetch(timeou void Receiver::start() { impl->start(); } void Receiver::stop() { impl->stop(); } void Receiver::setCapacity(uint32_t c) { impl->setCapacity(c); } +uint32_t Receiver::getCapacity() { return impl->getCapacity(); } +uint32_t Receiver::available() { return impl->available(); } +uint32_t Receiver::pendingAck() { return impl->pendingAck(); } void Receiver::cancel() { impl->cancel(); } void Receiver::setListener(MessageListener* listener) { impl->setListener(listener); } diff --git a/qpid/cpp/src/qpid/messaging/ReceiverImpl.h b/qpid/cpp/src/qpid/messaging/ReceiverImpl.h index 77697b730c..7db20acc29 100644 --- a/qpid/cpp/src/qpid/messaging/ReceiverImpl.h +++ b/qpid/cpp/src/qpid/messaging/ReceiverImpl.h @@ -44,6 +44,9 @@ class ReceiverImpl : public virtual qpid::RefCounted virtual void start() = 0; virtual void stop() = 0; virtual void setCapacity(uint32_t) = 0; + virtual uint32_t getCapacity() = 0; + virtual uint32_t available() = 0; + virtual uint32_t pendingAck() = 0; virtual void cancel() = 0; virtual void setListener(MessageListener*) = 0; }; diff --git a/qpid/cpp/src/qpid/messaging/Sender.cpp b/qpid/cpp/src/qpid/messaging/Sender.cpp index 12a3a8eb0f..62b2944701 100644 --- a/qpid/cpp/src/qpid/messaging/Sender.cpp +++ b/qpid/cpp/src/qpid/messaging/Sender.cpp @@ -38,7 +38,10 @@ 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::send(const Message& message) { impl->send(message); } void Sender::cancel() { impl->cancel(); } +void Sender::setCapacity(uint32_t c) { impl->setCapacity(c); } +uint32_t Sender::getCapacity() { return impl->getCapacity(); } +uint32_t Sender::pending() { return impl->pending(); } }} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/SenderImpl.h b/qpid/cpp/src/qpid/messaging/SenderImpl.h index 3b61a37423..fa3794ca4e 100644 --- a/qpid/cpp/src/qpid/messaging/SenderImpl.h +++ b/qpid/cpp/src/qpid/messaging/SenderImpl.h @@ -35,8 +35,11 @@ class SenderImpl : public virtual qpid::RefCounted { public: virtual ~SenderImpl() {} - virtual void send(Message& message) = 0; + virtual void send(const Message& message) = 0; virtual void cancel() = 0; + virtual void setCapacity(uint32_t) = 0; + virtual uint32_t getCapacity() = 0; + virtual uint32_t pending() = 0; private: }; }} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/Session.cpp b/qpid/cpp/src/qpid/messaging/Session.cpp index 284b20dacc..62b1ca0dcf 100644 --- a/qpid/cpp/src/qpid/messaging/Session.cpp +++ b/qpid/cpp/src/qpid/messaging/Session.cpp @@ -103,15 +103,7 @@ bool Session::dispatch(qpid::sys::Duration timeout) { return impl->dispatch(timeout); } - -void* Session::getLastConfirmedSent() -{ - return impl->getLastConfirmedSent(); -} - -void* Session::getLastConfirmedAcknowledged() -{ - return impl->getLastConfirmedAcknowledged(); -} +uint32_t Session::available() { return impl->available(); } +uint32_t Session::pendingAck() { return impl->pendingAck(); } }} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/SessionImpl.h b/qpid/cpp/src/qpid/messaging/SessionImpl.h index 7a7ce731f8..0933cea9c8 100644 --- a/qpid/cpp/src/qpid/messaging/SessionImpl.h +++ b/qpid/cpp/src/qpid/messaging/SessionImpl.h @@ -32,8 +32,8 @@ namespace client { namespace messaging { -class Address; -class Filter; +struct Address; +struct Filter; class Message; class Sender; class Receiver; @@ -56,8 +56,8 @@ class SessionImpl : public virtual qpid::RefCounted 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; + virtual uint32_t available() = 0; + virtual uint32_t pendingAck() = 0; private: }; }} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/Variant.cpp b/qpid/cpp/src/qpid/messaging/Variant.cpp index 59770939e1..4e37134b39 100644 --- a/qpid/cpp/src/qpid/messaging/Variant.cpp +++ b/qpid/cpp/src/qpid/messaging/Variant.cpp @@ -113,31 +113,31 @@ class VariantImpl }; -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() : type(VAR_VOID) { value.i64 = 0; } +VariantImpl::VariantImpl(bool b) : type(VAR_BOOL) { value.b = b; } +VariantImpl::VariantImpl(uint8_t i) : type(VAR_UINT8) { value.ui8 = i; } +VariantImpl::VariantImpl(uint16_t i) : type(VAR_UINT16) { value.ui16 = i; } +VariantImpl::VariantImpl(uint32_t i) : type(VAR_UINT32) { value.ui32 = i; } +VariantImpl::VariantImpl(uint64_t i) : type(VAR_UINT64) { value.ui64 = i; } +VariantImpl::VariantImpl(int8_t i) : type(VAR_INT8) { value.i8 = i; } +VariantImpl::VariantImpl(int16_t i) : type(VAR_INT16) { value.i16 = i; } +VariantImpl::VariantImpl(int32_t i) : type(VAR_INT32) { value.i32 = i; } +VariantImpl::VariantImpl(int64_t i) : type(VAR_INT64) { value.i64 = i; } +VariantImpl::VariantImpl(float f) : type(VAR_FLOAT) { value.f = f; } +VariantImpl::VariantImpl(double d) : type(VAR_DOUBLE) { value.d = d; } +VariantImpl::VariantImpl(const std::string& s) : type(VAR_STRING) { value.v = new std::string(s); } +VariantImpl::VariantImpl(const Variant::Map& m) : type(VAR_MAP) { value.v = new Variant::Map(m); } +VariantImpl::VariantImpl(const Variant::List& l) : type(VAR_LIST) { value.v = new Variant::List(l); } VariantImpl::~VariantImpl() { switch (type) { - case STRING: + case VAR_STRING: delete reinterpret_cast<std::string*>(value.v); break; - case MAP: + case VAR_MAP: delete reinterpret_cast<Variant::Map*>(value.v); break; - case LIST: + case VAR_LIST: delete reinterpret_cast<Variant::List*>(value.v); break; default: @@ -175,169 +175,169 @@ bool toBool(const std::string& s) 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))); + case VAR_VOID: return false; + case VAR_BOOL: return value.b; + case VAR_UINT8: return value.ui8; + case VAR_UINT16: return value.ui16; + case VAR_UINT32: return value.ui32; + case VAR_UINT64: return value.ui64; + case VAR_INT8: return value.i8; + case VAR_INT16: return value.i16; + case VAR_INT32: return value.i32; + case VAR_INT64: return value.i64; + case VAR_STRING: return toBool(*reinterpret_cast<std::string*>(value.v)); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_BOOL))); } } uint8_t VariantImpl::asUint8() const { switch(type) { - case UINT8: return value.ui8; - case STRING: return convertFromString<uint8_t>(); - default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(UINT8))); + case VAR_UINT8: return value.ui8; + case VAR_STRING: return convertFromString<uint8_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_UINT8))); } } uint16_t VariantImpl::asUint16() const { switch(type) { - case 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))); + case VAR_UINT8: return value.ui8; + case VAR_UINT16: return value.ui16; + case VAR_STRING: return convertFromString<uint16_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_UINT16))); } } uint32_t VariantImpl::asUint32() const { switch(type) { - case 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))); + case VAR_UINT8: return value.ui8; + case VAR_UINT16: return value.ui16; + case VAR_UINT32: return value.ui32; + case VAR_STRING: return convertFromString<uint32_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_UINT32))); } } uint64_t VariantImpl::asUint64() const { switch(type) { - case 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))); + case VAR_UINT8: return value.ui8; + case VAR_UINT16: return value.ui16; + case VAR_UINT32: return value.ui32; + case VAR_UINT64: return value.ui64; + case VAR_STRING: return convertFromString<uint64_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_UINT64))); } } int8_t VariantImpl::asInt8() const { switch(type) { - case INT8: return value.i8; - case STRING: return convertFromString<int8_t>(); - default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(INT8))); + case VAR_INT8: return value.i8; + case VAR_STRING: return convertFromString<int8_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_INT8))); } } int16_t VariantImpl::asInt16() const { switch(type) { - case 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))); + case VAR_INT8: return value.i8; + case VAR_INT16: return value.i16; + case VAR_STRING: return convertFromString<int16_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_INT16))); } } int32_t VariantImpl::asInt32() const { switch(type) { - case 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))); + case VAR_INT8: return value.i8; + case VAR_INT16: return value.i16; + case VAR_INT32: return value.i32; + case VAR_STRING: return convertFromString<int32_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_INT32))); } } int64_t VariantImpl::asInt64() const { switch(type) { - case 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))); + case VAR_INT8: return value.i8; + case VAR_INT16: return value.i16; + case VAR_INT32: return value.i32; + case VAR_INT64: return value.i64; + case VAR_STRING: return convertFromString<int64_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_INT64))); } } float VariantImpl::asFloat() const { switch(type) { - case FLOAT: return value.f; - case STRING: return convertFromString<float>(); - default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(FLOAT))); + case VAR_FLOAT: return value.f; + case VAR_STRING: return convertFromString<float>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_FLOAT))); } } double VariantImpl::asDouble() const { switch(type) { - case 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))); + case VAR_FLOAT: return value.f; + case VAR_DOUBLE: return value.d; + case VAR_STRING: return convertFromString<double>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_DOUBLE))); } } std::string VariantImpl::asString() const { switch(type) { - case 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))); + case VAR_VOID: return EMPTY; + case VAR_BOOL: return value.b ? TRUE : FALSE; + case VAR_UINT8: return boost::lexical_cast<std::string>((int) value.ui8); + case VAR_UINT16: return boost::lexical_cast<std::string>(value.ui16); + case VAR_UINT32: return boost::lexical_cast<std::string>(value.ui32); + case VAR_UINT64: return boost::lexical_cast<std::string>(value.ui64); + case VAR_INT8: return boost::lexical_cast<std::string>((int) value.i8); + case VAR_INT16: return boost::lexical_cast<std::string>(value.i16); + case VAR_INT32: return boost::lexical_cast<std::string>(value.i32); + case VAR_INT64: return boost::lexical_cast<std::string>(value.i64); + case VAR_DOUBLE: return boost::lexical_cast<std::string>(value.d); + case VAR_FLOAT: return boost::lexical_cast<std::string>(value.f); + case VAR_STRING: return *reinterpret_cast<std::string*>(value.v); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_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))); + case VAR_MAP: return *reinterpret_cast<Variant::Map*>(value.v); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_MAP))); } } Variant::Map& VariantImpl::asMap() { switch(type) { - case MAP: return *reinterpret_cast<Variant::Map*>(value.v); - default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(MAP))); + case VAR_MAP: return *reinterpret_cast<Variant::Map*>(value.v); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_MAP))); } } const Variant::List& VariantImpl::asList() const { switch(type) { - case LIST: return *reinterpret_cast<Variant::List*>(value.v); - default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(LIST))); + case VAR_LIST: return *reinterpret_cast<Variant::List*>(value.v); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_LIST))); } } Variant::List& VariantImpl::asList() { switch(type) { - case LIST: return *reinterpret_cast<Variant::List*>(value.v); - default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(LIST))); + case VAR_LIST: return *reinterpret_cast<Variant::List*>(value.v); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_LIST))); } } std::string& VariantImpl::getString() { switch(type) { - case STRING: return *reinterpret_cast<std::string*>(value.v); + case VAR_STRING: return *reinterpret_cast<std::string*>(value.v); default: throw InvalidConversion(QPID_MSG("Variant is not a string; use asString() if conversion is required.")); } } @@ -345,7 +345,7 @@ std::string& VariantImpl::getString() const std::string& VariantImpl::getString() const { switch(type) { - case STRING: return *reinterpret_cast<std::string*>(value.v); + case VAR_STRING: return *reinterpret_cast<std::string*>(value.v); default: throw InvalidConversion(QPID_MSG("Variant is not a string; use asString() if conversion is required.")); } } @@ -356,21 +356,21 @@ 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"; + case VAR_VOID: return "void"; + case VAR_BOOL: return "bool"; + case VAR_UINT8: return "uint8"; + case VAR_UINT16: return "uint16"; + case VAR_UINT32: return "uint32"; + case VAR_UINT64: return "uint64"; + case VAR_INT8: return "int8"; + case VAR_INT16: return "int16"; + case VAR_INT32: return "int32"; + case VAR_INT64: return "int64"; + case VAR_FLOAT: return "float"; + case VAR_DOUBLE: return "double"; + case VAR_STRING: return "string"; + case VAR_MAP: return "map"; + case VAR_LIST: return "list"; } return "<unknown>";//should never happen } @@ -378,20 +378,20 @@ std::string VariantImpl::getTypeName(VariantType type) const 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()); + case VAR_BOOL: return new VariantImpl(v.asBool()); + case VAR_UINT8: return new VariantImpl(v.asUint8()); + case VAR_UINT16: return new VariantImpl(v.asUint16()); + case VAR_UINT32: return new VariantImpl(v.asUint32()); + case VAR_UINT64: return new VariantImpl(v.asUint64()); + case VAR_INT8: return new VariantImpl(v.asInt8()); + case VAR_INT16: return new VariantImpl(v.asInt16()); + case VAR_INT32: return new VariantImpl(v.asInt32()); + case VAR_INT64: return new VariantImpl(v.asInt64()); + case VAR_FLOAT: return new VariantImpl(v.asFloat()); + case VAR_DOUBLE: return new VariantImpl(v.asDouble()); + case VAR_STRING: return new VariantImpl(v.asString()); + case VAR_MAP: return new VariantImpl(v.asMap()); + case VAR_LIST: return new VariantImpl(v.asList()); default: return new VariantImpl(); } } @@ -584,13 +584,13 @@ std::ostream& operator<<(std::ostream& out, const Variant::List& list) std::ostream& operator<<(std::ostream& out, const Variant& value) { switch (value.getType()) { - case MAP: + case VAR_MAP: out << "{" << value.asMap() << "}"; break; - case LIST: + case VAR_LIST: out << "[" << value.asList() << "]"; break; - case VOID: + case VAR_VOID: out << "<void>"; break; default: diff --git a/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp b/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp index 5d1ac6d034..8545ebd9cb 100644 --- a/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp +++ b/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp @@ -305,6 +305,13 @@ private: * thread processing this handle. */ volatile bool writePending; + /** + * This records whether we've been reading is flow controlled: + * it's safe as a simple boolean as the only way to be stopped + * is in calls only allowed in the callback context, the only calls + * checking it are also in calls only allowed in callback context. + */ + volatile bool readingStopped; }; AsynchIO::AsynchIO(const Socket& s, @@ -323,7 +330,8 @@ AsynchIO::AsynchIO(const Socket& s, idleCallback(iCb), socket(s), queuedClose(false), - writePending(false) { + writePending(false), + readingStopped(false) { s.setNonblocking(); } @@ -351,8 +359,11 @@ void AsynchIO::queueReadBuffer(BufferBase* buff) { assert(buff); buff->dataStart = 0; buff->dataCount = 0; + + bool queueWasEmpty = bufferQueue.empty(); bufferQueue.push_back(buff); - DispatchHandle::rewatchRead(); + if (queueWasEmpty && !readingStopped) + DispatchHandle::rewatchRead(); } void AsynchIO::unread(BufferBase* buff) { @@ -361,15 +372,18 @@ void AsynchIO::unread(BufferBase* buff) { memmove(buff->bytes, buff->bytes+buff->dataStart, buff->dataCount); buff->dataStart = 0; } + + bool queueWasEmpty = bufferQueue.empty(); bufferQueue.push_front(buff); - DispatchHandle::rewatchRead(); + if (queueWasEmpty && !readingStopped) + DispatchHandle::rewatchRead(); } void AsynchIO::queueWrite(BufferBase* buff) { assert(buff); // If we've already closed the socket then throw the write away if (queuedClose) { - bufferQueue.push_front(buff); + queueReadBuffer(buff); return; } else { writeQueue.push_front(buff); @@ -378,6 +392,7 @@ void AsynchIO::queueWrite(BufferBase* buff) { DispatchHandle::rewatchWrite(); } +// This can happen outside the callback context void AsynchIO::notifyPendingWrite() { writePending = true; DispatchHandle::rewatchWrite(); @@ -392,11 +407,14 @@ bool AsynchIO::writeQueueEmpty() { return writeQueue.empty(); } +// This can happen outside the callback context void AsynchIO::startReading() { + readingStopped = false; DispatchHandle::rewatchRead(); } void AsynchIO::stopReading() { + readingStopped = true; DispatchHandle::unwatchRead(); } diff --git a/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp b/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp index 52208d0519..9da6c835ce 100644 --- a/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp +++ b/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -45,6 +45,9 @@ using qpid::sys::Duration; using qpid::sys::TIME_SEC; using qpid::sys::TIME_INFINITE; +namespace qpid { +namespace tests { + // count of messages int64_t smsgs = 0; int64_t sbytes = 0; @@ -144,6 +147,10 @@ void rejected(boost::shared_ptr<Poller> p, Rdma::Connection::intrusive_ptr&, con p->shutdown(); } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char* argv[]) { vector<string> args(&argv[0], &argv[argc]); diff --git a/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp b/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp index 1ab5268596..07d6379362 100644 --- a/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp +++ b/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -39,6 +39,9 @@ using qpid::sys::Poller; using qpid::sys::Dispatcher; // All the accepted connections +namespace qpid { +namespace tests { + struct ConRec { Rdma::Connection::intrusive_ptr connection; Rdma::AsynchIO* data; @@ -134,6 +137,10 @@ void connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci) { cr->data->start(poller); } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char* argv[]) { vector<string> args(&argv[0], &argv[argc]); diff --git a/qpid/cpp/include/qpid/sys/uuid.h b/qpid/cpp/src/qpid/sys/uuid.h index 804ab34463..804ab34463 100644 --- a/qpid/cpp/include/qpid/sys/uuid.h +++ b/qpid/cpp/src/qpid/sys/uuid.h diff --git a/qpid/cpp/include/qpid/sys/windows/uuid.h b/qpid/cpp/src/qpid/sys/windows/uuid.h index c79abe95c6..c79abe95c6 100644 --- a/qpid/cpp/include/qpid/sys/windows/uuid.h +++ b/qpid/cpp/src/qpid/sys/windows/uuid.h diff --git a/qpid/cpp/src/qpid/xml/XmlExchange.cpp b/qpid/cpp/src/qpid/xml/XmlExchange.cpp index a41c8840ff..8a1ef6149e 100644 --- a/qpid/cpp/src/qpid/xml/XmlExchange.cpp +++ b/qpid/cpp/src/qpid/xml/XmlExchange.cpp @@ -156,7 +156,7 @@ bool XmlExchange::matches(Query& query, Deliverable& msg, const qpid::framing::F // This will parse the document using either Xerces or FastXDM, depending // on your XQilla configuration. FastXDM can be as much as 10x faster. - + Sequence seq(context->parseDocument(xml)); if(!seq.isEmpty() && seq.first()->isNode()) { @@ -206,11 +206,11 @@ void XmlExchange::route(Deliverable& msg, const string& routingKey, const FieldT PreRoute pr(msg, this); try { XmlBinding::vector::ConstPtr p; - { + { RWlock::ScopedRlock l(lock); - p = bindingsMap[routingKey].snapshot(); - if (!p) return; - } + p = bindingsMap[routingKey].snapshot(); + if (!p) return; + } int count(0); for (std::vector<XmlBinding::shared_ptr>::const_iterator i = p->begin(); i != p->end(); i++) { @@ -222,24 +222,24 @@ void XmlExchange::route(Deliverable& msg, const string& routingKey, const FieldT if ((*i)->mgmtBinding != 0) (*i)->mgmtBinding->inc_msgMatched (); } - } - if (!count) { - QPID_LOG(warning, "XMLExchange " << getName() << ": could not route message with query " << routingKey); - if (mgmtExchange != 0) { - mgmtExchange->inc_msgDrops (); - mgmtExchange->inc_byteDrops (msg.contentSize ()); - } - } else { - if (mgmtExchange != 0) { - mgmtExchange->inc_msgRoutes (count); - mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); - } - } - - if (mgmtExchange != 0) { - mgmtExchange->inc_msgReceives (); - mgmtExchange->inc_byteReceives (msg.contentSize ()); - } + } + if (!count) { + QPID_LOG(warning, "XMLExchange " << getName() << ": could not route message with query " << routingKey); + if (mgmtExchange != 0) { + mgmtExchange->inc_msgDrops (); + mgmtExchange->inc_byteDrops (msg.contentSize ()); + } + } else { + if (mgmtExchange != 0) { + mgmtExchange->inc_msgRoutes (count); + mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + } + } + + if (mgmtExchange != 0) { + mgmtExchange->inc_msgReceives (); + mgmtExchange->inc_byteReceives (msg.contentSize ()); + } } catch (...) { QPID_LOG(warning, "XMLExchange " << getName() << ": exception routing message with query " << routingKey); } diff --git a/qpid/cpp/src/tests/AccumulatedAckTest.cpp b/qpid/cpp/src/tests/AccumulatedAckTest.cpp index 028ce71907..c736a519d2 100644 --- a/qpid/cpp/src/tests/AccumulatedAckTest.cpp +++ b/qpid/cpp/src/tests/AccumulatedAckTest.cpp @@ -8,9 +8,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -28,6 +28,9 @@ using std::list; using namespace qpid::framing; +namespace qpid { +namespace tests { + bool covers(const AccumulatedAck& ack, int i) { return ack.covers(SequenceNumber(i)); @@ -97,7 +100,7 @@ QPID_AUTO_TEST_CASE(testUpdateFromCompletionData) ack.update(mark, ranges); - for(int i = 0; i <= 15; i++) { + for(int i = 0; i <= 15; i++) { BOOST_CHECK(covers(ack, i)); } BOOST_CHECK(!covers(ack, 16)); @@ -221,7 +224,7 @@ QPID_AUTO_TEST_CASE(testConsolidation4) ack.update(SequenceNumber(9), SequenceNumber(9)); ack.update(SequenceNumber(3), SequenceNumber(4)); - for(int i = 0; i <= 15; i++) { + for(int i = 0; i <= 15; i++) { BOOST_CHECK(covers(ack, i)); } BOOST_CHECK(!covers(ack, 16)); @@ -230,3 +233,5 @@ QPID_AUTO_TEST_CASE(testConsolidation4) QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/Array.cpp b/qpid/cpp/src/tests/Array.cpp index c779cbe901..7622b89d15 100644 --- a/qpid/cpp/src/tests/Array.cpp +++ b/qpid/cpp/src/tests/Array.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -25,6 +25,9 @@ #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ArrayTestSuite) using namespace qpid::framing; @@ -69,7 +72,7 @@ QPID_AUTO_TEST_CASE(testArrayAssignment) Array a(data); b = a; BOOST_CHECK_EQUAL(a, b); - } + } std::vector<std::string> data2; b.collect(data2); //BOOST_CHECK_EQUAL(data, data2); @@ -77,3 +80,5 @@ QPID_AUTO_TEST_CASE(testArrayAssignment) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/AsyncCompletion.cpp b/qpid/cpp/src/tests/AsyncCompletion.cpp index 41423d8245..4492e6b6bc 100644 --- a/qpid/cpp/src/tests/AsyncCompletion.cpp +++ b/qpid/cpp/src/tests/AsyncCompletion.cpp @@ -49,10 +49,13 @@ using boost::intrusive_ptr; * message enqueues at the correct time. */ +namespace qpid { +namespace tests { + class AsyncCompletionMessageStore : public NullMessageStore { public: sys::BlockingQueue<boost::intrusive_ptr<PersistableMessage> > enqueued; - + AsyncCompletionMessageStore() : NullMessageStore() {} ~AsyncCompletionMessageStore(){} @@ -82,10 +85,10 @@ QPID_AUTO_TEST_CASE(testWaitTillComplete) { transfers[i] = s.messageTransfer(arg::content=msg); } - // Get hold of the broker-side messages. + // Get hold of the broker-side messages. typedef vector<intrusive_ptr<PersistableMessage> > BrokerMessages; BrokerMessages enqueued; - for (int j = 0; j < count; ++j) + for (int j = 0; j < count; ++j) enqueued.push_back(store->enqueued.pop(TIME_SEC)); // Send a sync, make sure it does not complete till all messages are complete. @@ -111,3 +114,5 @@ QPID_AUTO_TEST_CASE(testGetResult) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/AtomicValue.cpp b/qpid/cpp/src/tests/AtomicValue.cpp index 05083ad177..d855d993a7 100644 --- a/qpid/cpp/src/tests/AtomicValue.cpp +++ b/qpid/cpp/src/tests/AtomicValue.cpp @@ -21,6 +21,9 @@ #include "test_tools.h" #include "qpid/sys/AtomicValue.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(AtomicValueTestSuite) QPID_AUTO_TEST_CASE(test) { @@ -47,3 +50,5 @@ QPID_AUTO_TEST_CASE(test) { QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/BrokerFixture.h b/qpid/cpp/src/tests/BrokerFixture.h index 397045d00b..bb985cf7e1 100644 --- a/qpid/cpp/src/tests/BrokerFixture.h +++ b/qpid/cpp/src/tests/BrokerFixture.h @@ -10,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -35,6 +35,9 @@ #include "qpid/sys/Thread.h" #include <boost/noncopyable.hpp> +namespace qpid { +namespace tests { + /** * A fixture with an in-process broker. */ @@ -55,7 +58,7 @@ struct BrokerFixture : private boost::noncopyable { } opts.port=0; // Management doesn't play well with multiple in-process brokers. - opts.enableMgmt=false; + opts.enableMgmt=false; opts.workerThreads=1; opts.dataDir=""; opts.auth=false; @@ -144,5 +147,6 @@ struct SessionFixtureT : BrokerFixture, ClientT<ConnectionType,SessionType> { typedef SessionFixtureT<LocalConnection> SessionFixture; typedef SessionFixtureT<ProxyConnection> ProxySessionFixture; +}} // namespace qpid::tests #endif /*!TESTS_BROKERFIXTURE_H*/ diff --git a/qpid/cpp/src/tests/CMakeLists.txt b/qpid/cpp/src/tests/CMakeLists.txt index 56a4aaf2b0..b1219aad74 100644 --- a/qpid/cpp/src/tests/CMakeLists.txt +++ b/qpid/cpp/src/tests/CMakeLists.txt @@ -95,7 +95,7 @@ set(unit_tests_to_build InlineAllocator InlineVector ClientSessionTest - MessagingSessionTest + MessagingSessionTests SequenceSet StringUtils IncompleteMessageList diff --git a/qpid/cpp/src/tests/ClientMessageTest.cpp b/qpid/cpp/src/tests/ClientMessageTest.cpp index bc0945674f..f925f1c234 100644 --- a/qpid/cpp/src/tests/ClientMessageTest.cpp +++ b/qpid/cpp/src/tests/ClientMessageTest.cpp @@ -24,6 +24,9 @@ using namespace qpid::client; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ClientMessageTestSuite) QPID_AUTO_TEST_CASE(MessageCopyAssign) { @@ -33,7 +36,7 @@ QPID_AUTO_TEST_CASE(MessageCopyAssign) { Message c(m); BOOST_CHECK_EQUAL("foo", c.getData()); Message a; - BOOST_CHECK_EQUAL("", a.getData()); + BOOST_CHECK_EQUAL("", a.getData()); a = m; BOOST_CHECK_EQUAL("foo", a.getData()); a.setData("a"); @@ -44,3 +47,5 @@ QPID_AUTO_TEST_CASE(MessageCopyAssign) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ClientSessionTest.cpp b/qpid/cpp/src/tests/ClientSessionTest.cpp index 3ed7491f7d..6ca0aa6d44 100644 --- a/qpid/cpp/src/tests/ClientSessionTest.cpp +++ b/qpid/cpp/src/tests/ClientSessionTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -40,6 +40,9 @@ #include <vector> +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ClientSessionTest) using namespace qpid::client; @@ -122,12 +125,12 @@ QPID_AUTO_TEST_CASE(testDispatcher) ClientSessionFixture fix; fix.session =fix.connection.newSession(); size_t count = 100; - for (size_t i = 0; i < count; ++i) + for (size_t i = 0; i < count; ++i) fix.session.messageTransfer(arg::content=Message(boost::lexical_cast<string>(i), "my-queue")); DummyListener listener(fix.session, "my-queue", count); listener.run(); - BOOST_CHECK_EQUAL(count, listener.messages.size()); - for (size_t i = 0; i < count; ++i) + BOOST_CHECK_EQUAL(count, listener.messages.size()); + for (size_t i = 0; i < count; ++i) BOOST_CHECK_EQUAL(boost::lexical_cast<string>(i), listener.messages[i].getData()); } @@ -142,8 +145,8 @@ QPID_AUTO_TEST_CASE(testDispatcherThread) fix.session.messageTransfer(arg::content=Message(boost::lexical_cast<string>(i), "my-queue")); } t.join(); - BOOST_CHECK_EQUAL(count, listener.messages.size()); - for (size_t i = 0; i < count; ++i) + BOOST_CHECK_EQUAL(count, listener.messages.size()); + for (size_t i = 0; i < count; ++i) BOOST_CHECK_EQUAL(boost::lexical_cast<string>(i), listener.messages[i].getData()); } @@ -215,7 +218,7 @@ QPID_AUTO_TEST_CASE(testLocalQueue) { BOOST_CHECK_EQUAL("foo1", lq.pop().getData()); BOOST_CHECK(lq.empty()); // Credit exhausted. fix.subs.getSubscription("lq").setFlowControl(FlowControl::unlimited()); - BOOST_CHECK_EQUAL("foo2", lq.pop().getData()); + BOOST_CHECK_EQUAL("foo2", lq.pop().getData()); } struct DelayedTransfer : sys::Runnable @@ -246,7 +249,7 @@ QPID_AUTO_TEST_CASE(testGet) { Thread t(sender); //test timed get where message shows up after a short delay BOOST_CHECK(fix.subs.get(got, "getq", 5*TIME_SEC)); - BOOST_CHECK_EQUAL("foo2", got.getData()); + BOOST_CHECK_EQUAL("foo2", got.getData()); t.join(); } @@ -271,8 +274,8 @@ QPID_AUTO_TEST_CASE(testPeriodicExpiration) { ClientSessionFixture fix(opts); fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true); - for (uint i = 0; i < 10; i++) { - Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + for (uint i = 0; i < 10; i++) { + Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); if (i % 2) m.getDeliveryProperties().setTtl(500); fix.session.messageTransfer(arg::content=m); } @@ -286,15 +289,15 @@ QPID_AUTO_TEST_CASE(testExpirationOnPop) { ClientSessionFixture fix; fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true); - for (uint i = 0; i < 10; i++) { - Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + for (uint i = 0; i < 10; i++) { + Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); if (i % 2) m.getDeliveryProperties().setTtl(200); fix.session.messageTransfer(arg::content=m); } qpid::sys::usleep(300* 1000); - for (uint i = 0; i < 10; i++) { + for (uint i = 0; i < 10; i++) { if (i % 2) continue; Message m; BOOST_CHECK(fix.subs.get(m, "my-queue", TIME_SEC)); @@ -306,8 +309,8 @@ QPID_AUTO_TEST_CASE(testRelease) { ClientSessionFixture fix; const uint count=10; - for (uint i = 0; i < count; i++) { - Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + for (uint i = 0; i < count; i++) { + Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); fix.session.messageTransfer(arg::content=m); } @@ -334,7 +337,7 @@ QPID_AUTO_TEST_CASE(testRelease) { for (uint i = 0; i < count; i++) { BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), l2.messages[i].getData()); } - + fix.subs.stop(); fix.subs.wait(); fix.session.close(); @@ -344,8 +347,8 @@ QPID_AUTO_TEST_CASE(testCompleteOnAccept) { ClientSessionFixture fix; const uint count = 8; const uint chunk = 4; - for (uint i = 0; i < count; i++) { - Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + for (uint i = 0; i < count; i++) { + Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); fix.session.messageTransfer(arg::content=m); } @@ -358,25 +361,25 @@ QPID_AUTO_TEST_CASE(testCompleteOnAccept) { Subscription s = fix.subs.subscribe(q, "my-queue", settings); fix.session.messageFlush(arg::destination=s.getName()); SequenceSet accepted; - for (uint i = 0; i < chunk; i++) { + for (uint i = 0; i < chunk; i++) { Message m; BOOST_CHECK(q.get(m)); BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData()); accepted.add(m.getId()); - } + } Message m; BOOST_CHECK(!q.get(m)); - + s.accept(accepted); fix.session.messageFlush(arg::destination=s.getName()); accepted.clear(); - - for (uint i = chunk; i < count; i++) { + + for (uint i = chunk; i < count; i++) { Message m; BOOST_CHECK(q.get(m)); BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData()); accepted.add(m.getId()); - } + } fix.session.messageAccept(accepted); } @@ -424,7 +427,7 @@ QPID_AUTO_TEST_CASE(testConcurrentSenders) connection.open(settings); AsyncSession session = connection.newSession(); Message message(string(512, 'X')); - + boost::ptr_vector<Publisher> publishers; for (size_t i = 0; i < 5; i++) { publishers.push_back(new Publisher(connection, message, 100)); @@ -447,7 +450,7 @@ QPID_AUTO_TEST_CASE(testExclusiveSubscribe) ScopedSuppressLogging sl; BOOST_CHECK_THROW(fix.subs.subscribe(q, "myq", "second"), ResourceLockedException); ; - + } QPID_AUTO_TEST_CASE(testExclusiveBinding) { @@ -478,7 +481,7 @@ QPID_AUTO_TEST_CASE(testResubscribeWithLocalQueue) { fix.subs.subscribe(p, "some-queue"); fix.subs.cancel("some-queue"); fix.subs.subscribe(q, "some-queue"); - + fix.session.messageTransfer(arg::content=Message("some-data", "some-queue")); fix.session.messageFlush(arg::destination="some-queue"); @@ -542,10 +545,10 @@ QPID_AUTO_TEST_CASE(testLVQVariedSize) { std::ostringstream data; size_t size = 100 - ((i % 10) * 10); data << std::string(size, 'x'); - + Message m(data.str(), queue); m.getHeaders().setString(key, "abc"); - fix.session.messageTransfer(arg::content=m); + fix.session.messageTransfer(arg::content=m); } } @@ -594,7 +597,7 @@ QPID_AUTO_TEST_CASE(testExpirationNotAltered) { ClientSessionFixture fix; fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true); - Message m("my-message", "my-queue"); + Message m("my-message", "my-queue"); m.getDeliveryProperties().setTtl(60000); m.getDeliveryProperties().setExpiration(12345); fix.session.messageTransfer(arg::content=m); @@ -606,4 +609,4 @@ QPID_AUTO_TEST_CASE(testExpirationNotAltered) { QPID_AUTO_TEST_SUITE_END() - +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ClusterFailover.cpp b/qpid/cpp/src/tests/ClusterFailover.cpp index 9ce9c4a36b..c2fb1282b1 100644 --- a/qpid/cpp/src/tests/ClusterFailover.cpp +++ b/qpid/cpp/src/tests/ClusterFailover.cpp @@ -32,6 +32,9 @@ #include <boost/algorithm/string.hpp> #include <boost/bind.hpp> +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ClusterFailoverTestSuite) using namespace std; @@ -60,3 +63,5 @@ QPID_AUTO_TEST_CASE(testReconnectSameSessionName) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ClusterFixture.cpp b/qpid/cpp/src/tests/ClusterFixture.cpp index e12106c464..7c357c3cd1 100644 --- a/qpid/cpp/src/tests/ClusterFixture.cpp +++ b/qpid/cpp/src/tests/ClusterFixture.cpp @@ -61,6 +61,9 @@ using boost::assign::list_of; #include "ClusterFixture.h" +namespace qpid { +namespace tests { + ClusterFixture::ClusterFixture(size_t n, const Args& args_, int localIndex_) : name(Uuid(true).str()), localIndex(localIndex_), userArgs(args_) { @@ -152,3 +155,5 @@ std::set<int> knownBrokerPorts(qpid::client::Connection& source, int n) { s.insert((*i)[0].get<qpid::TcpAddress>()->port); return s; } + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ClusterFixture.h b/qpid/cpp/src/tests/ClusterFixture.h index 08b314499e..5952cc1736 100644 --- a/qpid/cpp/src/tests/ClusterFixture.h +++ b/qpid/cpp/src/tests/ClusterFixture.h @@ -60,6 +60,9 @@ using qpid::broker::Broker; using boost::shared_ptr; using qpid::cluster::Cluster; +namespace qpid { +namespace tests { + /** Cluster fixture is a vector of ports for the replicas. * * At most one replica (by default replica 0) is in the current @@ -107,4 +110,6 @@ class ClusterFixture : public vector<uint16_t> { */ std::set<int> knownBrokerPorts(qpid::client::Connection& source, int n=-1); +}} // namespace qpid::tests + #endif /*!CLUSTER_FIXTURE_H*/ diff --git a/qpid/cpp/src/tests/ConnectionOptions.h b/qpid/cpp/src/tests/ConnectionOptions.h index cf86894235..6fd6c2c63f 100644 --- a/qpid/cpp/src/tests/ConnectionOptions.h +++ b/qpid/cpp/src/tests/ConnectionOptions.h @@ -10,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -25,6 +25,8 @@ #include "qpid/client/ConnectionSettings.h" #include "qpid/Options.h" +namespace qpid { + /** * Options parser for ConnectionOptions. */ @@ -35,9 +37,9 @@ struct ConnectionOptions : public qpid::Options, { using namespace qpid; addOptions() - ("broker,b", optValue(host, "HOST"), "Broker host to connect to") + ("broker,b", optValue(host, "HOST"), "Broker host to connect to") ("port,p", optValue(port, "PORT"), "Broker port to connect to") - ("protocol,P", optValue(protocol, "tcp|rdma"), "Protocol to use for broker connection") + ("protocol,P", optValue(protocol, "tcp|rdma"), "Protocol to use for broker connection") ("virtualhost,v", optValue(virtualhost, "VHOST"), "virtual host") ("username", optValue(username, "USER"), "user name for broker log in.") ("password", optValue(password, "PASSWORD"), "password for broker log in.") @@ -46,7 +48,7 @@ struct ConnectionOptions : public qpid::Options, ("max-channels", optValue(maxChannels, "N"), "the maximum number of channels the client requires.") ("heartbeat", optValue(heartbeat, "N"), "Desired heartbeat interval in seconds.") ("max-frame-size", optValue(maxFrameSize, "N"), "the maximum frame size to request.") - ("bounds-multiplier", optValue(bounds, "N"), + ("bounds-multiplier", optValue(bounds, "N"), "bound size of write queue (as a multiple of the max frame size).") ("tcp-nodelay", optValue(tcpNoDelay), "Turn on tcp-nodelay") ("service", optValue(service, "SERVICE-NAME"), "SASL service name.") @@ -55,4 +57,6 @@ struct ConnectionOptions : public qpid::Options, } }; +} // namespace qpid + #endif /*!QPID_CLIENT_CONNECTIONOPTIONS_H*/ diff --git a/qpid/cpp/src/tests/ConsoleTest.cpp b/qpid/cpp/src/tests/ConsoleTest.cpp index 1d55b13f3c..107472ed9e 100644 --- a/qpid/cpp/src/tests/ConsoleTest.cpp +++ b/qpid/cpp/src/tests/ConsoleTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -23,6 +23,9 @@ #include "qpid/console/ClassKey.h" #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ConsoleTestSuite) using namespace qpid::framing; @@ -40,4 +43,4 @@ QPID_AUTO_TEST_CASE(testClassKey) { QPID_AUTO_TEST_SUITE_END() - +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/DeliveryRecordTest.cpp b/qpid/cpp/src/tests/DeliveryRecordTest.cpp index 8ff7ad3584..17f9a0d148 100644 --- a/qpid/cpp/src/tests/DeliveryRecordTest.cpp +++ b/qpid/cpp/src/tests/DeliveryRecordTest.cpp @@ -8,9 +8,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -31,6 +31,9 @@ using namespace qpid::framing; using boost::dynamic_pointer_cast; using std::list; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(DeliveryRecordTestSuite) QPID_AUTO_TEST_CASE(testSort) @@ -60,3 +63,4 @@ QPID_AUTO_TEST_CASE(testSort) QPID_AUTO_TEST_SUITE_END() +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/DispatcherTest.cpp b/qpid/cpp/src/tests/DispatcherTest.cpp index c619a36438..17b3b4e3e6 100644 --- a/qpid/cpp/src/tests/DispatcherTest.cpp +++ b/qpid/cpp/src/tests/DispatcherTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -39,6 +39,9 @@ using namespace std; using namespace qpid::sys; +namespace qpid { +namespace tests { + int writeALot(int fd, const string& s) { int bytesWritten = 0; do { @@ -46,7 +49,7 @@ int writeALot(int fd, const string& s) { int lastWrite = ::write(fd, s.c_str(), s.size()); if ( lastWrite >= 0) { bytesWritten += lastWrite; - } + } } while (errno != EAGAIN); return bytesWritten; } @@ -54,13 +57,13 @@ int writeALot(int fd, const string& s) { int readALot(int fd) { int bytesRead = 0; char buf[10240]; - + do { errno = 0; int lastRead = ::read(fd, buf, sizeof(buf)); if ( lastRead >= 0) { bytesRead += lastRead; - } + } } while (errno != EAGAIN); return bytesRead; } @@ -83,7 +86,7 @@ void rInterrupt(DispatchHandle&) { } void wInterrupt(DispatchHandle&) { - cerr << "W"; + cerr << "W"; } DispatchHandle::Callback rcb = rInterrupt; @@ -108,15 +111,19 @@ void timer_handler(int /*signo*/, siginfo_t* /*info*/, void* /*context*/) { wh->call(wcb); } else { phase1finished = true; - assert(::timer_delete(timer) == 0); + assert(::timer_delete(timer) == 0); } } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int /*argc*/, char** /*argv*/) { // Create poller Poller::shared_ptr poller(new Poller); - + // Create dispatcher thread Dispatcher d(poller); Dispatcher d1(poller); @@ -131,14 +138,14 @@ int main(int /*argc*/, char** /*argv*/) int sv[2]; int rc = ::socketpair(AF_UNIX, SOCK_STREAM, 0, sv); assert(rc >= 0); - + // Set non-blocking rc = ::fcntl(sv[0], F_SETFL, O_NONBLOCK); assert(rc >= 0); rc = ::fcntl(sv[1], F_SETFL, O_NONBLOCK); assert(rc >= 0); - + // Make up a large string string testString = "This is only a test ... 1,2,3,4,5,6,7,8,9,10;"; for (int i = 0; i < 8; i++) @@ -148,19 +155,19 @@ int main(int /*argc*/, char** /*argv*/) PosixIOHandle f1(sv[1]); rh = new DispatchHandleRef(f0, boost::bind(reader, _1, sv[0]), 0, 0); - wh = new DispatchHandleRef(f1, 0, boost::bind(writer, _1, sv[1], testString), 0); + wh = new DispatchHandleRef(f1, 0, boost::bind(writer, _1, sv[1], testString), 0); rh->startWatch(poller); wh->startWatch(poller); // Set up a regular itimer interupt - + // Ignore signal in this thread ::sigset_t sm; ::sigemptyset(&sm); ::sigaddset(&sm, SIGRTMIN); ::pthread_sigmask(SIG_BLOCK, &sm, 0); - + // Signal handling struct ::sigaction sa; sa.sa_sigaction = timer_handler; @@ -168,18 +175,18 @@ int main(int /*argc*/, char** /*argv*/) ::sigemptyset(&sa.sa_mask); rc = ::sigaction(SIGRTMIN, &sa,0); assert(rc == 0); - + ::sigevent se; ::memset(&se, 0, sizeof(se)); // Clear to make valgrind happy (this *is* the neatest way to do this portably - sigh) se.sigev_notify = SIGEV_SIGNAL; se.sigev_signo = SIGRTMIN; rc = ::timer_create(CLOCK_REALTIME, &se, &timer); assert(rc == 0); - + itimerspec ts = { /*.it_value = */ {2, 0}, // s, ns /*.it_interval = */ {2, 0}}; // s, ns - + rc = ::timer_settime(timer, 0, &ts, 0); assert(rc == 0); @@ -200,11 +207,11 @@ int main(int /*argc*/, char** /*argv*/) sa.sa_sigaction = stop_handler; rc = ::sigaction(SIGRTMIN, &sa,0); assert(rc == 0); - + itimerspec nts = { /*.it_value = */ {30, 0}, // s, ns /*.it_interval = */ {30, 0}}; // s, ns - + rc = ::timer_create(CLOCK_REALTIME, &se, &timer); assert(rc == 0); rc = ::timer_settime(timer, 0, &nts, 0); @@ -228,7 +235,7 @@ int main(int /*argc*/, char** /*argv*/) rc = ::timer_delete(timer); assert(rc == 0); - + poller->shutdown(); dt.join(); dt1.join(); @@ -237,6 +244,6 @@ int main(int /*argc*/, char** /*argv*/) cout << "\nWrote: " << writtenBytes << "\n"; cout << "Read: " << readBytes << "\n"; - + return 0; } diff --git a/qpid/cpp/src/tests/DtxWorkRecordTest.cpp b/qpid/cpp/src/tests/DtxWorkRecordTest.cpp index c7c1b460ff..9d7666dca4 100644 --- a/qpid/cpp/src/tests/DtxWorkRecordTest.cpp +++ b/qpid/cpp/src/tests/DtxWorkRecordTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -27,6 +27,9 @@ using namespace qpid::broker; using boost::static_pointer_cast; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(DtxWorkRecordTestSuite) QPID_AUTO_TEST_CASE(testOnePhaseCommit){ @@ -44,7 +47,7 @@ QPID_AUTO_TEST_CASE(testOnePhaseCommit){ DtxBuffer::shared_ptr bufferB(new DtxBuffer()); bufferB->enlist(static_pointer_cast<TxOp>(opB)); bufferB->markEnded(); - + DtxWorkRecord work("my-xid", &store); work.add(bufferA); work.add(bufferB); @@ -77,7 +80,7 @@ QPID_AUTO_TEST_CASE(testFailOnOnePhaseCommit){ DtxBuffer::shared_ptr bufferC(new DtxBuffer()); bufferC->enlist(static_pointer_cast<TxOp>(opC)); bufferC->markEnded(); - + DtxWorkRecord work("my-xid", &store); work.add(bufferA); work.add(bufferB); @@ -108,7 +111,7 @@ QPID_AUTO_TEST_CASE(testTwoPhaseCommit){ DtxBuffer::shared_ptr bufferB(new DtxBuffer()); bufferB->enlist(static_pointer_cast<TxOp>(opB)); bufferB->markEnded(); - + DtxWorkRecord work("my-xid", &store); work.add(bufferA); work.add(bufferB); @@ -142,7 +145,7 @@ QPID_AUTO_TEST_CASE(testFailOnTwoPhaseCommit){ DtxBuffer::shared_ptr bufferC(new DtxBuffer()); bufferC->enlist(static_pointer_cast<TxOp>(opC)); bufferC->markEnded(); - + DtxWorkRecord work("my-xid", &store); work.add(bufferA); work.add(bufferB); @@ -171,7 +174,7 @@ QPID_AUTO_TEST_CASE(testRollback){ DtxBuffer::shared_ptr bufferB(new DtxBuffer()); bufferB->enlist(static_pointer_cast<TxOp>(opB)); bufferB->markEnded(); - + DtxWorkRecord work("my-xid", &store); work.add(bufferA); work.add(bufferB); @@ -187,3 +190,4 @@ QPID_AUTO_TEST_CASE(testRollback){ QPID_AUTO_TEST_SUITE_END() +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ExchangeTest.cpp b/qpid/cpp/src/tests/ExchangeTest.cpp index 2100fb5ed7..44835c6184 100644 --- a/qpid/cpp/src/tests/ExchangeTest.cpp +++ b/qpid/cpp/src/tests/ExchangeTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -39,9 +39,12 @@ using namespace qpid::framing; using namespace qpid::sys; using namespace qpid; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ExchangeTestSuite) -QPID_AUTO_TEST_CASE(testMe) +QPID_AUTO_TEST_CASE(testMe) { Queue::shared_ptr queue(new Queue("queue", true)); Queue::shared_ptr queue2(new Queue("queue2", true)); @@ -70,7 +73,7 @@ QPID_AUTO_TEST_CASE(testIsBound) Queue::shared_ptr b(new Queue("b", true)); Queue::shared_ptr c(new Queue("c", true)); Queue::shared_ptr d(new Queue("d", true)); - + string k1("abc"); string k2("def"); string k3("xyz"); @@ -139,7 +142,7 @@ QPID_AUTO_TEST_CASE(testIsBound) headers.bind(a, "", &args3); headers.bind(b, "", &args2); headers.bind(c, "", &args1); - + BOOST_CHECK(headers.isBound(a, 0, 0)); BOOST_CHECK(headers.isBound(a, 0, &args1)); BOOST_CHECK(headers.isBound(a, 0, &args3)); @@ -153,7 +156,7 @@ QPID_AUTO_TEST_CASE(testIsBound) BOOST_CHECK(!headers.isBound(d, 0, &args3)); } -QPID_AUTO_TEST_CASE(testDeleteGetAndRedeclare) +QPID_AUTO_TEST_CASE(testDeleteGetAndRedeclare) { ExchangeRegistry exchanges; exchanges.declare("my-exchange", "direct", false, FieldTable()); @@ -162,7 +165,7 @@ QPID_AUTO_TEST_CASE(testDeleteGetAndRedeclare) exchanges.get("my-exchange"); } catch (const NotFoundException&) {} std::pair<Exchange::shared_ptr, bool> response = exchanges.declare("my-exchange", "direct", false, FieldTable()); - BOOST_CHECK_EQUAL(string("direct"), response.first->getType()); + BOOST_CHECK_EQUAL(string("direct"), response.first->getType()); } intrusive_ptr<Message> cmessage(std::string exchange, std::string routingKey) { @@ -175,7 +178,7 @@ intrusive_ptr<Message> cmessage(std::string exchange, std::string routingKey) { return msg; } -QPID_AUTO_TEST_CASE(testSequenceOptions) +QPID_AUTO_TEST_CASE(testSequenceOptions) { FieldTable args; args.setInt("qpid.msg_sequence",1); @@ -225,22 +228,22 @@ QPID_AUTO_TEST_CASE(testSequenceOptions) direct.encode(buffer); } { - + ExchangeRegistry exchanges; buffer.reset(); DirectExchange::shared_ptr exch_dec = Exchange::decode(exchanges, buffer); - + intrusive_ptr<Message> msg1 = cmessage("e", "A"); DeliverableMessage dmsg1(msg1); exch_dec->route(dmsg1, "abc", 0); BOOST_CHECK_EQUAL(4, msg1->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); - + } delete [] buff; } -QPID_AUTO_TEST_CASE(testIVEOption) +QPID_AUTO_TEST_CASE(testIVEOption) { FieldTable args; args.setInt("qpid.ive",1); @@ -248,7 +251,7 @@ QPID_AUTO_TEST_CASE(testIVEOption) FanOutExchange fanout("fanout1", false, args); HeadersExchange header("headers1", false, args); TopicExchange topic ("topic1", false, args); - + intrusive_ptr<Message> msg1 = cmessage("direct1", "abc"); msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString("a", "abc"); DeliverableMessage dmsg1(msg1); @@ -256,7 +259,7 @@ QPID_AUTO_TEST_CASE(testIVEOption) FieldTable args2; args2.setString("x-match", "any"); args2.setString("a", "abc"); - + direct.route(dmsg1, "abc", 0); fanout.route(dmsg1, "abc", 0); header.route(dmsg1, "abc", &args2); @@ -265,20 +268,22 @@ QPID_AUTO_TEST_CASE(testIVEOption) Queue::shared_ptr queue1(new Queue("queue1", true)); Queue::shared_ptr queue2(new Queue("queue2", true)); Queue::shared_ptr queue3(new Queue("queue3", true)); - + BOOST_CHECK(HeadersExchange::match(args2, msg1->getProperties<MessageProperties>()->getApplicationHeaders())); - + BOOST_CHECK(direct.bind(queue, "abc", 0)); BOOST_CHECK(fanout.bind(queue1, "abc", 0)); BOOST_CHECK(header.bind(queue2, "", &args2)); BOOST_CHECK(topic.bind(queue3, "abc", 0)); - + BOOST_CHECK_EQUAL(1u,queue->getMessageCount()); BOOST_CHECK_EQUAL(1u,queue1->getMessageCount()); BOOST_CHECK_EQUAL(1u,queue2->getMessageCount()); BOOST_CHECK_EQUAL(1u,queue3->getMessageCount()); - + } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/FieldTable.cpp b/qpid/cpp/src/tests/FieldTable.cpp index 5b43871f6d..fe2a14ec03 100644 --- a/qpid/cpp/src/tests/FieldTable.cpp +++ b/qpid/cpp/src/tests/FieldTable.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -29,6 +29,9 @@ using namespace qpid::framing; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(FieldTableTestSuite) QPID_AUTO_TEST_CASE(testMe) @@ -57,7 +60,7 @@ QPID_AUTO_TEST_CASE(testAssignment) a.setInt("B", 1234); b = a; a.setString("A", "CCCC"); - + BOOST_CHECK(string("CCCC") == a.getAsString("A")); BOOST_CHECK(string("BBBB") == b.getAsString("A")); BOOST_CHECK_EQUAL(1234, a.getAsInt("B")); @@ -69,7 +72,7 @@ QPID_AUTO_TEST_CASE(testAssignment) { FieldTable c; c = a; - + char* buff = static_cast<char*>(::alloca(c.encodedSize())); Buffer wbuffer(buff, c.encodedSize()); wbuffer.put(c); @@ -102,7 +105,7 @@ QPID_AUTO_TEST_CASE(testNestedValues) 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); @@ -192,7 +195,7 @@ QPID_AUTO_TEST_CASE(test64GetAndSetConverts) FieldTable args; args.setInt64("a",100); args.setInt64("b",-(int64_t) ((int64_t) 1<<34)); - + args.setUInt64("c",1u); args.setUInt64("d",(uint64_t) ((uint64_t) 1<<34)); BOOST_CHECK_EQUAL(1u, args.getAsUInt64("c")); @@ -204,5 +207,7 @@ QPID_AUTO_TEST_CASE(test64GetAndSetConverts) BOOST_CHECK_EQUAL((int64_t) ((int64_t) 1<<34), args.getAsInt64("d")); } - + QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/FieldValue.cpp b/qpid/cpp/src/tests/FieldValue.cpp index 448f068107..0ebd0d7d44 100644 --- a/qpid/cpp/src/tests/FieldValue.cpp +++ b/qpid/cpp/src/tests/FieldValue.cpp @@ -6,9 +6,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -20,6 +20,9 @@ #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(FieldValueTestSuite) using namespace qpid::framing; @@ -32,7 +35,7 @@ IntegerValue i(42); QPID_AUTO_TEST_CASE(testStr16ValueEquals) { - + BOOST_CHECK(Str16Value("abc") == s); BOOST_CHECK(Str16Value("foo") != s); BOOST_CHECK(s != i); @@ -73,7 +76,7 @@ QPID_AUTO_TEST_CASE(testFieldTableValueEquals) BOOST_CHECK_EQUAL(std::string("FOO"), ft.getValue().getString("foo")); BOOST_CHECK_EQUAL(7, ft.getValue().getInt("magic")); - + FieldTableValue f2; BOOST_CHECK(ft != f2); f2.getValue().setString("foo", "FOO"); @@ -88,3 +91,5 @@ QPID_AUTO_TEST_CASE(testFieldTableValueEquals) #endif QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ForkedBroker.cpp b/qpid/cpp/src/tests/ForkedBroker.cpp index 30c346850c..e1a96c8e90 100644 --- a/qpid/cpp/src/tests/ForkedBroker.cpp +++ b/qpid/cpp/src/tests/ForkedBroker.cpp @@ -30,6 +30,16 @@ using namespace std; using qpid::ErrnoException; +namespace std { +static ostream& operator<<(ostream& o, const qpid::tests::ForkedBroker::Args& a) { +copy(a.begin(), a.end(), ostream_iterator<string>(o, " ")); +return o; +} +} + +namespace qpid { +namespace tests { + ForkedBroker::ForkedBroker(const Args& constArgs) { Args args(constArgs); Args::iterator i = find(args.begin(), args.end(), string("TMP_DATA_DIR")); @@ -71,12 +81,6 @@ void ForkedBroker::kill(int sig) { throw qpid::Exception(QPID_MSG("Forked broker exited with: " << WEXITSTATUS(status))); } -namespace std { -static ostream& operator<<(ostream& o, const ForkedBroker::Args& a) { - copy(a.begin(), a.end(), ostream_iterator<string>(o, " ")); - return o; -} - bool isLogOption(const std::string& s) { const char * log_enable = "--log-enable", * trace = "--trace"; @@ -85,8 +89,6 @@ bool isLogOption(const std::string& s) { ); } -} - void ForkedBroker::init(const Args& userArgs) { using qpid::ErrnoException; port = 0; @@ -125,3 +127,5 @@ void ForkedBroker::init(const Args& userArgs) { ::exit(1); } } + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ForkedBroker.h b/qpid/cpp/src/tests/ForkedBroker.h index 45b522068c..ddbad185d8 100644 --- a/qpid/cpp/src/tests/ForkedBroker.h +++ b/qpid/cpp/src/tests/ForkedBroker.h @@ -11,9 +11,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -31,9 +31,12 @@ #include <stdio.h> #include <sys/wait.h> +namespace qpid { +namespace tests { + /** * Class to fork a broker child process. - * + * * For most tests a BrokerFixture may be more convenient as it starts * a broker in the same process which allows you to easily debug into * the broker. @@ -42,17 +45,17 @@ * those brokers can't coexist in the same process (e.g. for cluster * tests where CPG doesn't allow multiple group members in a single * process.) - * + * */ class ForkedBroker { public: typedef std::vector<std::string> Args; // argv args are passed to broker. - // + // // Special value "TMP_DATA_DIR" is substituted with a temporary // data directory for the broker. - // + // ForkedBroker(const Args& argv); ~ForkedBroker(); @@ -70,4 +73,6 @@ class ForkedBroker { std::string dataDir; }; +}} // namespace qpid::tests + #endif /*!TESTS_FORKEDBROKER_H*/ diff --git a/qpid/cpp/src/tests/Frame.cpp b/qpid/cpp/src/tests/Frame.cpp index 11905911fa..1270eabba3 100644 --- a/qpid/cpp/src/tests/Frame.cpp +++ b/qpid/cpp/src/tests/Frame.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -23,6 +23,9 @@ #include <boost/lexical_cast.hpp> #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(FrameTestSuite) using namespace std; @@ -78,3 +81,5 @@ QPID_AUTO_TEST_CASE(testLoop) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/FrameDecoder.cpp b/qpid/cpp/src/tests/FrameDecoder.cpp index f5db66d5fe..9eeff2a41e 100644 --- a/qpid/cpp/src/tests/FrameDecoder.cpp +++ b/qpid/cpp/src/tests/FrameDecoder.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -27,6 +27,9 @@ #include <string> +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(FrameDecoderTest) using namespace std; @@ -69,5 +72,7 @@ QPID_AUTO_TEST_CASE(testByteFragments) { } - + QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/FramingTest.cpp b/qpid/cpp/src/tests/FramingTest.cpp index e09ee19bc2..3d0fa0c0de 100644 --- a/qpid/cpp/src/tests/FramingTest.cpp +++ b/qpid/cpp/src/tests/FramingTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -39,8 +39,11 @@ using namespace qpid; using namespace qpid::framing; using namespace std; +namespace qpid { +namespace tests { + template <class T> -std::string tostring(const T& x) +std::string tostring(const T& x) { std::ostringstream out; out << x; @@ -49,7 +52,7 @@ std::string tostring(const T& x) QPID_AUTO_TEST_SUITE(FramingTestSuite) -QPID_AUTO_TEST_CASE(testMessageTransferBody) +QPID_AUTO_TEST_CASE(testMessageTransferBody) { char buffer[1024]; ProtocolVersion version(highestProtocolVersion); @@ -62,8 +65,8 @@ QPID_AUTO_TEST_CASE(testMessageTransferBody) out.decode(rbuff); BOOST_CHECK_EQUAL(tostring(in), tostring(out)); } - -QPID_AUTO_TEST_CASE(testConnectionSecureBody) + +QPID_AUTO_TEST_CASE(testConnectionSecureBody) { char buffer[1024]; ProtocolVersion version(highestProtocolVersion); @@ -88,10 +91,10 @@ QPID_AUTO_TEST_CASE(testConnectionRedirectBody) Array hosts(0x95); hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(a))); hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(b))); - + ConnectionRedirectBody in(version, a, hosts); in.encode(wbuff); - + Buffer rbuff(buffer, sizeof(buffer)); ConnectionRedirectBody out(version); out.decode(rbuff); @@ -111,7 +114,7 @@ QPID_AUTO_TEST_CASE(testQueueDeclareBody) out.decode(rbuff); BOOST_CHECK_EQUAL(tostring(in), tostring(out)); } - + QPID_AUTO_TEST_CASE(testConnectionRedirectBodyFrame) { char buffer[1024]; @@ -122,7 +125,7 @@ QPID_AUTO_TEST_CASE(testConnectionRedirectBodyFrame) Array hosts(0x95); hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(a))); hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(b))); - + AMQFrame in((ConnectionRedirectBody(version, a, hosts))); in.setChannel(999); in.encode(wbuff); @@ -149,3 +152,5 @@ QPID_AUTO_TEST_CASE(testMessageCancelBodyFrame) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/HeaderTest.cpp b/qpid/cpp/src/tests/HeaderTest.cpp index 01e7c22ee6..4b16f3c793 100644 --- a/qpid/cpp/src/tests/HeaderTest.cpp +++ b/qpid/cpp/src/tests/HeaderTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -26,9 +26,12 @@ using namespace qpid::framing; using namespace std; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(HeaderTestSuite) -QPID_AUTO_TEST_CASE(testGenericProperties) +QPID_AUTO_TEST_CASE(testGenericProperties) { AMQHeaderBody body; body.get<MessageProperties>(true)->getApplicationHeaders().setString( @@ -47,10 +50,10 @@ QPID_AUTO_TEST_CASE(testGenericProperties) props->getApplicationHeaders().get("A")->get<string>()); } -QPID_AUTO_TEST_CASE(testMessageProperties) +QPID_AUTO_TEST_CASE(testMessageProperties) { AMQFrame out((AMQHeaderBody())); - MessageProperties* props1 = + MessageProperties* props1 = out.castBody<AMQHeaderBody>()->get<MessageProperties>(true); props1->setContentLength(42); @@ -82,10 +85,10 @@ QPID_AUTO_TEST_CASE(testMessageProperties) } -QPID_AUTO_TEST_CASE(testDeliveryProperies) +QPID_AUTO_TEST_CASE(testDeliveryProperies) { AMQFrame out((AMQHeaderBody())); - DeliveryProperties* props1 = + DeliveryProperties* props1 = out.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true); props1->setDiscardUnroutable(true); @@ -108,3 +111,4 @@ QPID_AUTO_TEST_CASE(testDeliveryProperies) QPID_AUTO_TEST_SUITE_END() +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/HeadersExchangeTest.cpp b/qpid/cpp/src/tests/HeadersExchangeTest.cpp index 46933f955a..40deb59c86 100644 --- a/qpid/cpp/src/tests/HeadersExchangeTest.cpp +++ b/qpid/cpp/src/tests/HeadersExchangeTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -28,9 +28,12 @@ using namespace qpid::broker; using namespace qpid::framing; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(HeadersExchangeTestSuite) - -QPID_AUTO_TEST_CASE(testMatchAll) + +QPID_AUTO_TEST_CASE(testMatchAll) { FieldTable b, m, n; b.setString("x-match", "all"); @@ -43,7 +46,7 @@ QPID_AUTO_TEST_CASE(testMatchAll) // Ignore extras. m.setString("extra", "x"); BOOST_CHECK(HeadersExchange::match(b, m)); - + // Fail mismatch, wrong value. m.setString("foo", "NotFoo"); BOOST_CHECK(!HeadersExchange::match(b, m)); @@ -54,7 +57,7 @@ QPID_AUTO_TEST_CASE(testMatchAll) BOOST_CHECK(!HeadersExchange::match(b, n)); } -QPID_AUTO_TEST_CASE(testMatchAny) +QPID_AUTO_TEST_CASE(testMatchAny) { FieldTable b, m, n; b.setString("x-match", "any"); @@ -67,7 +70,7 @@ QPID_AUTO_TEST_CASE(testMatchAny) BOOST_CHECK(HeadersExchange::match(b, m)); } -QPID_AUTO_TEST_CASE(testMatchEmptyValue) +QPID_AUTO_TEST_CASE(testMatchEmptyValue) { FieldTable b, m; b.setString("x-match", "all"); @@ -82,23 +85,23 @@ QPID_AUTO_TEST_CASE(testMatchEmptyArgs) { FieldTable b, m; m.setString("foo", "FOO"); - + b.setString("x-match", "all"); BOOST_CHECK(HeadersExchange::match(b, m)); b.setString("x-match", "any"); BOOST_CHECK(!HeadersExchange::match(b, m)); } - -QPID_AUTO_TEST_CASE(testMatchNoXMatch) + +QPID_AUTO_TEST_CASE(testMatchNoXMatch) { FieldTable b, m; b.setString("foo", "FOO"); m.setString("foo", "FOO"); BOOST_CHECK(!HeadersExchange::match(b, m)); } - -QPID_AUTO_TEST_CASE(testBindNoXMatch) + +QPID_AUTO_TEST_CASE(testBindNoXMatch) { HeadersExchange exchange("test"); Queue::shared_ptr queue; @@ -112,4 +115,6 @@ QPID_AUTO_TEST_CASE(testBindNoXMatch) } } -QPID_AUTO_TEST_SUITE_END() +QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/IncompleteMessageList.cpp b/qpid/cpp/src/tests/IncompleteMessageList.cpp index e830df0e8b..303d83cd66 100644 --- a/qpid/cpp/src/tests/IncompleteMessageList.cpp +++ b/qpid/cpp/src/tests/IncompleteMessageList.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -27,6 +27,9 @@ #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(IncompleteMessageListTestSuite) using namespace qpid::broker; @@ -41,7 +44,7 @@ struct Checker Checker(uint start, uint end) { for (uint i = start; i <= end; i++) { ids.push_back(i); - } + } } Checker& expect(const SequenceNumber& id) { @@ -49,11 +52,11 @@ struct Checker return *this; } - void operator()(boost::intrusive_ptr<Message> msg) { + void operator()(boost::intrusive_ptr<Message> msg) { BOOST_CHECK(!ids.empty()); BOOST_CHECK_EQUAL(msg->getCommandId(), ids.front()); ids.pop_front(); - } + } }; QPID_AUTO_TEST_CASE(testProcessSimple) @@ -91,7 +94,7 @@ QPID_AUTO_TEST_CASE(testProcessWithIncomplete) list.process(Checker(1, 2), false); //mark message complete and re-process to get remaining messages sent to listener middle->enqueueComplete(); - list.process(Checker(3, 5), false); + list.process(Checker(3, 5), false); } @@ -127,3 +130,5 @@ QPID_AUTO_TEST_CASE(testSyncProcessWithIncomplete) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/InlineAllocator.cpp b/qpid/cpp/src/tests/InlineAllocator.cpp index fe6eaefaf4..a4c4d64cea 100644 --- a/qpid/cpp/src/tests/InlineAllocator.cpp +++ b/qpid/cpp/src/tests/InlineAllocator.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -22,6 +22,9 @@ #include "qpid/InlineAllocator.h" #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(InlineAllocatorTestSuite) using namespace qpid; @@ -48,16 +51,18 @@ QPID_AUTO_TEST_CASE(testAllocateFull) { char* p = alloc.allocate(1); BOOST_CHECK(p == (char*)&alloc); - + char* q = alloc.allocate(1); BOOST_CHECK(q != (char*)&alloc); alloc.deallocate(p,1); p = alloc.allocate(1); BOOST_CHECK(p == (char*)&alloc); - + alloc.deallocate(p,1); alloc.deallocate(q,1); } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/InlineVector.cpp b/qpid/cpp/src/tests/InlineVector.cpp index 009f10af9d..ba5165886d 100644 --- a/qpid/cpp/src/tests/InlineVector.cpp +++ b/qpid/cpp/src/tests/InlineVector.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -22,6 +22,9 @@ #include "qpid/InlineVector.h" #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(InlineVectorTestSuite) using namespace qpid; @@ -117,7 +120,9 @@ QPID_AUTO_TEST_CASE(testAssign) { QPID_AUTO_TEST_CASE(testResize) { Vec v; v.resize(5); - BOOST_CHECK(!isInline(v)); + BOOST_CHECK(!isInline(v)); } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/Makefile.am b/qpid/cpp/src/tests/Makefile.am index 2e04c85b93..a15ba3578c 100644 --- a/qpid/cpp/src/tests/Makefile.am +++ b/qpid/cpp/src/tests/Makefile.am @@ -270,6 +270,11 @@ check_PROGRAMS+=qrsh qrsh_SOURCES=qrsh.cpp qrsh_LDADD=$(lib_client) +check_PROGRAMS+=qpid_stream +qpid_stream_INCLUDES=$(PUBLIC_INCLUDES) +qpid_stream_SOURCES=qpid_stream.cpp +qpid_stream_LDADD=$(lib_client) + TESTS_ENVIRONMENT = \ VALGRIND=$(VALGRIND) \ diff --git a/qpid/cpp/src/tests/ManagementTest.cpp b/qpid/cpp/src/tests/ManagementTest.cpp index e6f2e2e3fd..d05b4676ba 100644 --- a/qpid/cpp/src/tests/ManagementTest.cpp +++ b/qpid/cpp/src/tests/ManagementTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -24,6 +24,9 @@ #include "qpid/console/ObjectId.h" #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ManagementTestSuite) using namespace qpid::framing; @@ -109,4 +112,4 @@ QPID_AUTO_TEST_CASE(testConsoleObjectId) { QPID_AUTO_TEST_SUITE_END() - +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/MessageBuilderTest.cpp b/qpid/cpp/src/tests/MessageBuilderTest.cpp index 1f3f830633..c2fb8ad32e 100644 --- a/qpid/cpp/src/tests/MessageBuilderTest.cpp +++ b/qpid/cpp/src/tests/MessageBuilderTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -31,15 +31,18 @@ using namespace qpid::broker; using namespace qpid::framing; using namespace qpid::sys; +namespace qpid { +namespace tests { + class MockMessageStore : public NullMessageStore { enum Op {STAGE=1, APPEND=2}; uint64_t id; - boost::intrusive_ptr<PersistableMessage> expectedMsg; + boost::intrusive_ptr<PersistableMessage> expectedMsg; string expectedData; std::list<Op> ops; - + void checkExpectation(Op actual) { BOOST_CHECK_EQUAL(ops.front(), actual); @@ -49,17 +52,17 @@ class MockMessageStore : public NullMessageStore public: MockMessageStore() : id(0), expectedMsg(0) {} - void expectStage(PersistableMessage& msg) - { + void expectStage(PersistableMessage& msg) + { expectedMsg = &msg; - ops.push_back(STAGE); + ops.push_back(STAGE); } - void expectAppendContent(PersistableMessage& msg, const string& data) - { + void expectAppendContent(PersistableMessage& msg, const string& data) + { expectedMsg = &msg; expectedData = data; - ops.push_back(APPEND); + ops.push_back(APPEND); } void stage(const boost::intrusive_ptr<PersistableMessage>& msg) @@ -74,7 +77,7 @@ class MockMessageStore : public NullMessageStore { checkExpectation(APPEND); BOOST_CHECK_EQUAL(boost::static_pointer_cast<const PersistableMessage>(expectedMsg), msg); - BOOST_CHECK_EQUAL(expectedData, data); + BOOST_CHECK_EQUAL(expectedData, data); } bool expectationsMet() @@ -89,7 +92,7 @@ class MockMessageStore : public NullMessageStore } }; - + QPID_AUTO_TEST_SUITE(MessageBuilderTestSuite) QPID_AUTO_TEST_CASE(testHeaderOnly) @@ -103,7 +106,7 @@ QPID_AUTO_TEST_CASE(testHeaderOnly) AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0))); AMQFrame header((AMQHeaderBody())); - header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(0); + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(0); header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); builder.handle(method); @@ -132,7 +135,7 @@ QPID_AUTO_TEST_CASE(test1ContentFrame) header.setEof(false); content.setBof(false); - header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data.size()); + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data.size()); header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); builder.handle(method); @@ -143,7 +146,7 @@ QPID_AUTO_TEST_CASE(test1ContentFrame) BOOST_CHECK(builder.getMessage()); BOOST_CHECK(!builder.getMessage()->getFrames().isComplete()); - builder.handle(content); + builder.handle(content); BOOST_CHECK(builder.getMessage()); BOOST_CHECK(builder.getMessage()->getFrames().isComplete()); } @@ -169,7 +172,7 @@ QPID_AUTO_TEST_CASE(test2ContentFrames) content1.setEof(false); content2.setBof(false); - header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size()); + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size()); header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); builder.handle(method); @@ -188,7 +191,7 @@ QPID_AUTO_TEST_CASE(testStaging) MockMessageStore store; MessageBuilder builder(&store, 5); builder.start(SequenceNumber()); - + std::string data1("abcdefg"); std::string data2("hijklmn"); std::string exchange("builder-exchange"); @@ -199,7 +202,7 @@ QPID_AUTO_TEST_CASE(testStaging) AMQFrame content1((AMQContentBody(data1))); AMQFrame content2((AMQContentBody(data2))); - header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size()); + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size()); header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); builder.handle(method); @@ -223,7 +226,7 @@ QPID_AUTO_TEST_CASE(testNoManagementStaging) MockMessageStore store; MessageBuilder builder(&store, 5); builder.start(SequenceNumber()); - + std::string data1("abcdefg"); std::string exchange("qpid.management"); std::string key("builder-exchange"); @@ -232,7 +235,7 @@ QPID_AUTO_TEST_CASE(testNoManagementStaging) AMQFrame header((AMQHeaderBody())); AMQFrame content1((AMQContentBody(data1))); - header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size()); + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size()); header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); builder.handle(method); @@ -242,4 +245,7 @@ QPID_AUTO_TEST_CASE(testNoManagementStaging) BOOST_CHECK(store.expectationsMet()); BOOST_CHECK_EQUAL((uint64_t) 0, builder.getMessage()->getPersistenceId()); } + QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/MessageReplayTracker.cpp b/qpid/cpp/src/tests/MessageReplayTracker.cpp index a5121cdeb7..3d79ee53c2 100644 --- a/qpid/cpp/src/tests/MessageReplayTracker.cpp +++ b/qpid/cpp/src/tests/MessageReplayTracker.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -23,6 +23,9 @@ #include "qpid/client/MessageReplayTracker.h" #include "qpid/sys/Time.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(MessageReplayTrackerTests) using namespace qpid::client; @@ -53,8 +56,8 @@ QPID_AUTO_TEST_CASE(testReplay) MessageReplayTracker tracker(10); tracker.init(fix.session); - for (uint i = 0; i < 5; i++) { - Message message((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + for (uint i = 0; i < 5; i++) { + Message message((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); tracker.send(message); } ReplayBufferChecker checker(1, 10); @@ -62,7 +65,7 @@ QPID_AUTO_TEST_CASE(testReplay) tracker.replay(fix.session); for (uint j = 0; j < 2; j++) {//each message should have been sent twice - for (uint i = 0; i < 5; i++) { + for (uint i = 0; i < 5; i++) { Message m; BOOST_CHECK(fix.subs.get(m, "my-queue", TIME_SEC)); BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData()); @@ -79,15 +82,15 @@ QPID_AUTO_TEST_CASE(testCheckCompletion) MessageReplayTracker tracker(10); tracker.init(fix.session); - for (uint i = 0; i < 5; i++) { - Message message((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); + for (uint i = 0; i < 5; i++) { + Message message((boost::format("Message_%1%") % (i+1)).str(), "my-queue"); tracker.send(message); } fix.session.sync();//ensures all messages are complete tracker.checkCompletion(); tracker.replay(fix.session); Message received; - for (uint i = 0; i < 5; i++) { + for (uint i = 0; i < 5; i++) { BOOST_CHECK(fix.subs.get(received, "my-queue")); BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), received.getData()); } @@ -96,4 +99,4 @@ QPID_AUTO_TEST_CASE(testCheckCompletion) QPID_AUTO_TEST_SUITE_END() - +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/MessageTest.cpp b/qpid/cpp/src/tests/MessageTest.cpp index cd63f64a37..7d67c92b37 100644 --- a/qpid/cpp/src/tests/MessageTest.cpp +++ b/qpid/cpp/src/tests/MessageTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -33,6 +33,9 @@ using namespace qpid::broker; using namespace qpid::framing; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(MessageTestSuite) QPID_AUTO_TEST_CASE(testEncodeDecode) @@ -56,7 +59,7 @@ QPID_AUTO_TEST_CASE(testEncodeDecode) msg->getFrames().append(content2); MessageProperties* mProps = msg->getFrames().getHeaders()->get<MessageProperties>(true); - mProps->setContentLength(data1.size() + data2.size()); + mProps->setContentLength(data1.size() + data2.size()); mProps->setMessageId(messageId); FieldTable applicationHeaders; applicationHeaders.setString("abc", "xyz"); @@ -69,7 +72,7 @@ QPID_AUTO_TEST_CASE(testEncodeDecode) char* buff = static_cast<char*>(::alloca(msg->encodedSize())); Buffer wbuffer(buff, msg->encodedSize()); msg->encode(wbuffer); - + Buffer rbuffer(buff, msg->encodedSize()); msg = new Message(); msg->decodeHeader(rbuffer); @@ -86,3 +89,4 @@ QPID_AUTO_TEST_CASE(testEncodeDecode) QPID_AUTO_TEST_SUITE_END() +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/MessageUtils.h b/qpid/cpp/src/tests/MessageUtils.h index 6a12c72007..dae74cce7d 100644 --- a/qpid/cpp/src/tests/MessageUtils.h +++ b/qpid/cpp/src/tests/MessageUtils.h @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -28,9 +28,12 @@ using namespace qpid; using namespace broker; using namespace framing; +namespace qpid { +namespace tests { + struct MessageUtils { - static boost::intrusive_ptr<Message> createMessage(const string& exchange="", const string& routingKey="", + static boost::intrusive_ptr<Message> createMessage(const string& exchange="", const string& routingKey="", const Uuid& messageId=Uuid(true), uint64_t contentSize = 0) { boost::intrusive_ptr<broker::Message> msg(new broker::Message()); @@ -41,7 +44,7 @@ struct MessageUtils msg->getFrames().append(method); msg->getFrames().append(header); MessageProperties* props = msg->getFrames().getHeaders()->get<MessageProperties>(true); - props->setContentLength(contentSize); + props->setContentLength(contentSize); props->setMessageId(messageId); msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey); return msg; @@ -53,3 +56,5 @@ struct MessageUtils msg->getFrames().append(content); } }; + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/MessagingSessionTests.cpp b/qpid/cpp/src/tests/MessagingSessionTests.cpp index 6eebf717bf..f5a5420d3a 100644 --- a/qpid/cpp/src/tests/MessagingSessionTests.cpp +++ b/qpid/cpp/src/tests/MessagingSessionTests.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -36,6 +36,9 @@ #include <string> #include <vector> +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(MessagingSessionTests) using namespace qpid::messaging; @@ -86,7 +89,7 @@ struct MessagingFixture : public BrokerFixture Session session; BrokerAdmin admin; - MessagingFixture(Broker::Options opts = Broker::Options()) : + 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()), @@ -133,7 +136,7 @@ struct TopicFixture : MessagingFixture struct MultiQueueFixture : MessagingFixture { - typedef std::vector<std::string>::const_iterator const_iterator; + 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) @@ -161,7 +164,7 @@ struct MessageDataCollector : MessageListener } }; -std::vector<std::string> fetch(Receiver& receiver, int count, qpid::sys::Duration timeout=qpid::sys::TIME_SEC*5) +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; @@ -183,6 +186,25 @@ QPID_AUTO_TEST_CASE(testSimpleSendReceive) BOOST_CHECK_EQUAL(in.getBytes(), out.getBytes()); } +QPID_AUTO_TEST_CASE(testSendReceiveHeaders) +{ + QueueFixture fix; + Sender sender = fix.session.createSender(fix.queue); + Message out("test-message"); + for (uint i = 0; i < 10; ++i) { + out.getHeaders()["a"] = i; + sender.send(out); + } + Receiver receiver = fix.session.createReceiver(fix.queue); + Message in; + for (uint i = 0; i < 10; ++i) { + BOOST_CHECK(receiver.fetch(in, 5 * qpid::sys::TIME_SEC)); + BOOST_CHECK_EQUAL(in.getBytes(), out.getBytes()); + BOOST_CHECK_EQUAL(in.getHeaders()["a"].asUint32(), i); + fix.session.acknowledge(); + } +} + QPID_AUTO_TEST_CASE(testSenderError) { MessagingFixture fix; @@ -229,14 +251,14 @@ QPID_AUTO_TEST_CASE(testSimpleTopic) 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); @@ -247,8 +269,8 @@ QPID_AUTO_TEST_CASE(testSessionFetch) 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)); @@ -259,7 +281,7 @@ QPID_AUTO_TEST_CASE(testSessionFetch) 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]); @@ -272,10 +294,10 @@ QPID_AUTO_TEST_CASE(testSessionDispatch) 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")); } @@ -289,8 +311,7 @@ QPID_AUTO_TEST_CASE(testMapMessage) 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()); + Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); BOOST_CHECK_EQUAL(in.getContent().asMap()["abc"].asString(), "def"); BOOST_CHECK_EQUAL(in.getContent().asMap()["pi"].asFloat(), 3.14f); fix.session.acknowledge(); @@ -305,12 +326,11 @@ QPID_AUTO_TEST_CASE(testListMessage) out.getContent() << "abc"; out.getContent() << 1234; out.getContent() << "def"; - out.getContent() << 56.789; + 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(); + Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); + 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(); @@ -322,4 +342,100 @@ QPID_AUTO_TEST_CASE(testListMessage) fix.session.acknowledge(); } +QPID_AUTO_TEST_CASE(testReject) +{ + QueueFixture fix; + Sender sender = fix.session.createSender(fix.queue); + Message m1("reject-me"); + sender.send(m1); + Message m2("accept-me"); + sender.send(m2); + Receiver receiver = fix.session.createReceiver(fix.queue); + Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); + BOOST_CHECK_EQUAL(in.getBytes(), m1.getBytes()); + fix.session.reject(in); + in = receiver.fetch(5 * qpid::sys::TIME_SEC); + BOOST_CHECK_EQUAL(in.getBytes(), m2.getBytes()); + fix.session.acknowledge(); +} + +QPID_AUTO_TEST_CASE(testAvailable) +{ + MultiQueueFixture fix; + + Receiver r1 = fix.session.createReceiver(fix.queues[0]); + r1.setCapacity(100); + r1.start(); + + Receiver r2 = fix.session.createReceiver(fix.queues[1]); + r2.setCapacity(100); + r2.start(); + + Sender s1 = fix.session.createSender(fix.queues[0]); + Sender s2 = fix.session.createSender(fix.queues[1]); + + for (uint i = 0; i < 10; ++i) { + s1.send(Message((boost::format("A_%1%") % (i+1)).str())); + } + for (uint i = 0; i < 5; ++i) { + s2.send(Message((boost::format("B_%1%") % (i+1)).str())); + } + qpid::sys::sleep(1);//is there any avoid an arbitrary sleep while waiting for messages to be dispatched? + for (uint i = 0; i < 5; ++i) { + BOOST_CHECK_EQUAL(fix.session.available(), 15u - 2*i); + BOOST_CHECK_EQUAL(r1.available(), 10u - i); + BOOST_CHECK_EQUAL(r1.fetch().getBytes(), (boost::format("A_%1%") % (i+1)).str()); + BOOST_CHECK_EQUAL(r2.available(), 5u - i); + BOOST_CHECK_EQUAL(r2.fetch().getBytes(), (boost::format("B_%1%") % (i+1)).str()); + fix.session.acknowledge(); + } + for (uint i = 5; i < 10; ++i) { + BOOST_CHECK_EQUAL(fix.session.available(), 10u - i); + BOOST_CHECK_EQUAL(r1.available(), 10u - i); + BOOST_CHECK_EQUAL(r1.fetch().getBytes(), (boost::format("A_%1%") % (i+1)).str()); + } +} + +QPID_AUTO_TEST_CASE(testPendingAck) +{ + QueueFixture fix; + Sender sender = fix.session.createSender(fix.queue); + for (uint i = 0; i < 10; ++i) { + sender.send(Message((boost::format("Message_%1%") % (i+1)).str())); + } + Receiver receiver = fix.session.createReceiver(fix.queue); + for (uint i = 0; i < 10; ++i) { + BOOST_CHECK_EQUAL(receiver.fetch().getBytes(), (boost::format("Message_%1%") % (i+1)).str()); + } + BOOST_CHECK_EQUAL(fix.session.pendingAck(), 0u); + fix.session.acknowledge(); + BOOST_CHECK_EQUAL(fix.session.pendingAck(), 10u); + fix.session.sync(); + BOOST_CHECK_EQUAL(fix.session.pendingAck(), 0u); +} + +QPID_AUTO_TEST_CASE(testPendingSend) +{ + QueueFixture fix; + Sender sender = fix.session.createSender(fix.queue); + for (uint i = 0; i < 10; ++i) { + sender.send(Message((boost::format("Message_%1%") % (i+1)).str())); + } + //Note: this test relies on 'inside knowledge' of the sender + //implementation and the fact that the simple test case makes it + //possible to predict when completion information will be sent to + //the client. TODO: is there a better way of testing this? + BOOST_CHECK_EQUAL(sender.pending(), 10u); + fix.session.sync(); + BOOST_CHECK_EQUAL(sender.pending(), 0u); + + Receiver receiver = fix.session.createReceiver(fix.queue); + for (uint i = 0; i < 10; ++i) { + BOOST_CHECK_EQUAL(receiver.fetch().getBytes(), (boost::format("Message_%1%") % (i+1)).str()); + } + fix.session.acknowledge(); +} + QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/PartialFailure.cpp b/qpid/cpp/src/tests/PartialFailure.cpp index f77a1401f8..8d9970f909 100644 --- a/qpid/cpp/src/tests/PartialFailure.cpp +++ b/qpid/cpp/src/tests/PartialFailure.cpp @@ -31,6 +31,9 @@ #include <boost/algorithm/string.hpp> #include <boost/bind.hpp> +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(PartialFailureTestSuite) using namespace std; @@ -257,3 +260,5 @@ QPID_AUTO_TEST_CASE(testPartialFailureMemberLeaves) { #endif #endif // FIXME aconway 2009-07-30: QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/PollableCondition.cpp b/qpid/cpp/src/tests/PollableCondition.cpp index b5cf1b4cd2..f9b3c25c93 100644 --- a/qpid/cpp/src/tests/PollableCondition.cpp +++ b/qpid/cpp/src/tests/PollableCondition.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -28,6 +28,8 @@ #include "qpid/sys/Thread.h" #include <boost/bind.hpp> +namespace qpid { +namespace tests { QPID_AUTO_TEST_SUITE(PollableConditionTest) @@ -37,7 +39,7 @@ const Duration SHORT = TIME_SEC/100; const Duration LONG = TIME_SEC/10; class Callback { - public: + public: enum Action { NONE, CLEAR }; Callback() : count(), action(NONE) {} @@ -46,7 +48,7 @@ class Callback { Mutex::ScopedLock l(lock); ++count; switch(action) { - case NONE: break; + case NONE: break; case CLEAR: pc.clear(); break; } action = NONE; @@ -62,9 +64,9 @@ class Callback { action = a; return wait(LONG); } - + private: - bool wait(Duration timeout) { + bool wait(Duration timeout) { int n = count; AbsTime deadline(now(), timeout); while (n == count && lock.wait(deadline)) @@ -83,7 +85,7 @@ QPID_AUTO_TEST_CASE(testPollableCondition) { PollableCondition pc(boost::bind(&Callback::call, &callback, _1), poller); Thread runner = Thread(*poller); - + BOOST_CHECK(callback.isNotCalling()); // condition is not set. pc.set(); @@ -104,4 +106,4 @@ QPID_AUTO_TEST_CASE(testPollableCondition) { QPID_AUTO_TEST_SUITE_END() - +}} //namespace qpid::tests diff --git a/qpid/cpp/src/tests/ProxyTest.cpp b/qpid/cpp/src/tests/ProxyTest.cpp index 4ea10f7be9..a926b28395 100644 --- a/qpid/cpp/src/tests/ProxyTest.cpp +++ b/qpid/cpp/src/tests/ProxyTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -28,6 +28,9 @@ using namespace qpid::framing; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ProxyTestSuite) @@ -47,5 +50,7 @@ QPID_AUTO_TEST_CASE(testScopedSync) Proxy::ScopedSync s(p); p.send(ExecutionSyncBody(p.getVersion())); } - + QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/QueueEvents.cpp b/qpid/cpp/src/tests/QueueEvents.cpp index cd9439355e..bd18fa45fb 100644 --- a/qpid/cpp/src/tests/QueueEvents.cpp +++ b/qpid/cpp/src/tests/QueueEvents.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -31,6 +31,9 @@ #include <boost/bind.hpp> #include <boost/format.hpp> +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(QueueEventsSuite) using namespace qpid::client; @@ -156,7 +159,7 @@ QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing) fixture.session.queueDeclare(arg::queue=q, arg::arguments=options); //send and consume some messages LocalQueue incoming; - Subscription sub = fixture.subs.subscribe(incoming, q); + Subscription sub = fixture.subs.subscribe(incoming, q); for (int i = 0; i < 5; i++) { fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); } @@ -177,7 +180,7 @@ QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing) SequenceNumber dequeueId(1); for (int i = 0; i < 5; i++) { listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++); - } + } for (int i = 0; i < 3; i++) { listener.checkDequeue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), dequeueId++); } @@ -203,7 +206,7 @@ QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing_enqueuesOnly) fixture.session.queueDeclare(arg::queue=q, arg::arguments=options); //send and consume some messages LocalQueue incoming; - Subscription sub = fixture.subs.subscribe(incoming, q); + Subscription sub = fixture.subs.subscribe(incoming, q); for (int i = 0; i < 5; i++) { fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); } @@ -224,7 +227,7 @@ QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing_enqueuesOnly) SequenceNumber dequeueId(1); for (int i = 0; i < 5; i++) { listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++); - } + } for (int i = 5; i < 10; i++) { listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++); } @@ -232,4 +235,4 @@ QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing_enqueuesOnly) QPID_AUTO_TEST_SUITE_END() - +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/QueueOptionsTest.cpp b/qpid/cpp/src/tests/QueueOptionsTest.cpp index 93d1961caa..f2fbaba2c1 100644 --- a/qpid/cpp/src/tests/QueueOptionsTest.cpp +++ b/qpid/cpp/src/tests/QueueOptionsTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -27,14 +27,17 @@ using namespace qpid::client; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(QueueOptionsTestSuite) QPID_AUTO_TEST_CASE(testSizePolicy) { QueueOptions ft; - + ft.setSizePolicy(REJECT,1,2); - + BOOST_CHECK(QueueOptions::strREJECT == ft.getAsString(QueueOptions::strTypeKey)); BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strMaxSizeKey)); BOOST_CHECK(2 == ft.getAsInt(QueueOptions::strMaxCountKey)); @@ -49,7 +52,7 @@ QPID_AUTO_TEST_CASE(testSizePolicy) ft.setSizePolicy(RING_STRICT,1,0); BOOST_CHECK(QueueOptions::strRING_STRICT == ft.getAsString(QueueOptions::strTypeKey)); - + ft.clearSizePolicy(); BOOST_CHECK(!ft.isSet(QueueOptions::strTypeKey)); BOOST_CHECK(!ft.isSet(QueueOptions::strMaxSizeKey)); @@ -59,13 +62,13 @@ QPID_AUTO_TEST_CASE(testSizePolicy) QPID_AUTO_TEST_CASE(testFlags) { QueueOptions ft; - + ft.setPersistLastNode(); ft.setOrdering(LVQ); - + BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strPersistLastNode)); BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strLastValueQueue)); - + ft.clearPersistLastNode(); ft.setOrdering(FIFO); @@ -78,8 +81,8 @@ QPID_AUTO_TEST_CASE(testSetOrdering) { //ensure setOrdering(FIFO) works even if not preceded by a call to //setOrdering(LVQ) - QueueOptions ft; - ft.setOrdering(FIFO); + QueueOptions ft; + ft.setOrdering(FIFO); BOOST_CHECK(!ft.isSet(QueueOptions::strLastValueQueue)); } @@ -88,10 +91,12 @@ QPID_AUTO_TEST_CASE(testClearPersistLastNode) { //ensure clear works even if not preceded by the setting on the //option - QueueOptions ft; + QueueOptions ft; ft.clearPersistLastNode(); BOOST_CHECK(!ft.isSet(QueueOptions::strPersistLastNode)); } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/QueuePolicyTest.cpp b/qpid/cpp/src/tests/QueuePolicyTest.cpp index 7c7f8b7a10..f40d30b588 100644 --- a/qpid/cpp/src/tests/QueuePolicyTest.cpp +++ b/qpid/cpp/src/tests/QueuePolicyTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -32,6 +32,9 @@ using namespace qpid::broker; using namespace qpid::client; using namespace qpid::framing; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(QueuePolicyTestSuite) QueuedMessage createMessage(uint32_t size) @@ -50,11 +53,11 @@ QPID_AUTO_TEST_CASE(testCount) BOOST_CHECK_EQUAL((uint32_t) 5, policy->getMaxCount()); QueuedMessage msg = createMessage(10); - for (size_t i = 0; i < 5; i++) { + for (size_t i = 0; i < 5; i++) { policy->tryEnqueue(msg); } try { - policy->tryEnqueue(msg); + policy->tryEnqueue(msg); BOOST_FAIL("Policy did not fail on enqueuing sixth message"); } catch (const ResourceLimitExceededException&) {} @@ -62,7 +65,7 @@ QPID_AUTO_TEST_CASE(testCount) policy->tryEnqueue(msg); try { - policy->tryEnqueue(msg); + policy->tryEnqueue(msg); BOOST_FAIL("Policy did not fail on enqueuing sixth message (after dequeue)"); } catch (const ResourceLimitExceededException&) {} } @@ -71,12 +74,12 @@ QPID_AUTO_TEST_CASE(testSize) { std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy(0, 50)); QueuedMessage msg = createMessage(10); - - for (size_t i = 0; i < 5; i++) { + + for (size_t i = 0; i < 5; i++) { policy->tryEnqueue(msg); } try { - policy->tryEnqueue(msg); + policy->tryEnqueue(msg); BOOST_FAIL("Policy did not fail on aggregate size exceeding 50. " << *policy); } catch (const ResourceLimitExceededException&) {} @@ -84,7 +87,7 @@ QPID_AUTO_TEST_CASE(testSize) policy->tryEnqueue(msg); try { - policy->tryEnqueue(msg); + policy->tryEnqueue(msg); BOOST_FAIL("Policy did not fail on aggregate size exceeding 50 (after dequeue). " << *policy); } catch (const ResourceLimitExceededException&) {} } @@ -104,7 +107,7 @@ QPID_AUTO_TEST_CASE(testBoth) messages.push_back(createMessage(11)); messages.push_back(createMessage(2)); messages.push_back(createMessage(7)); - for (size_t i = 0; i < messages.size(); i++) { + for (size_t i = 0; i < messages.size(); i++) { policy->tryEnqueue(messages[i]); } //size = 45 at this point, count = 5 @@ -140,7 +143,7 @@ QPID_AUTO_TEST_CASE(testSettings) BOOST_CHECK_EQUAL(a->getMaxSize(), b->getMaxSize()); } -QPID_AUTO_TEST_CASE(testRingPolicy) +QPID_AUTO_TEST_CASE(testRingPolicy) { FieldTable args; std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy(5, 0, QueuePolicy::RING); @@ -169,7 +172,7 @@ QPID_AUTO_TEST_CASE(testRingPolicy) BOOST_CHECK(!f.subs.get(msg, q)); } -QPID_AUTO_TEST_CASE(testStrictRingPolicy) +QPID_AUTO_TEST_CASE(testStrictRingPolicy) { FieldTable args; std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy(5, 0, QueuePolicy::RING_STRICT); @@ -181,7 +184,7 @@ QPID_AUTO_TEST_CASE(testStrictRingPolicy) LocalQueue incoming; SubscriptionSettings settings(FlowControl::unlimited()); settings.autoAck = 0; // no auto ack. - Subscription sub = f.subs.subscribe(incoming, q, settings); + Subscription sub = f.subs.subscribe(incoming, q, settings); for (int i = 0; i < 5; i++) { f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); } @@ -192,10 +195,10 @@ QPID_AUTO_TEST_CASE(testStrictRingPolicy) ScopedSuppressLogging sl; // Suppress messages for expected errors. f.session.messageTransfer(arg::content=client::Message("Message_6", q)); BOOST_FAIL("expecting ResourceLimitExceededException."); - } catch (const ResourceLimitExceededException&) {} + } catch (const ResourceLimitExceededException&) {} } -QPID_AUTO_TEST_CASE(testPolicyWithDtx) +QPID_AUTO_TEST_CASE(testPolicyWithDtx) { FieldTable args; std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy(5, 0, QueuePolicy::REJECT); @@ -207,7 +210,7 @@ QPID_AUTO_TEST_CASE(testPolicyWithDtx) LocalQueue incoming; SubscriptionSettings settings(FlowControl::unlimited()); settings.autoAck = 0; // no auto ack. - Subscription sub = f.subs.subscribe(incoming, q, settings); + Subscription sub = f.subs.subscribe(incoming, q, settings); f.session.dtxSelect(); Xid tx1(1, "test-dtx-mgr", "tx1"); f.session.dtxStart(arg::xid=tx1); @@ -244,7 +247,7 @@ QPID_AUTO_TEST_CASE(testPolicyWithDtx) ScopedSuppressLogging sl; // Suppress messages for expected errors. other.messageTransfer(arg::content=client::Message("Message_6", q)); BOOST_FAIL("expecting ResourceLimitExceededException."); - } catch (const ResourceLimitExceededException&) {} + } catch (const ResourceLimitExceededException&) {} f.session.dtxCommit(arg::xid=tx3); //now retry and this time should succeed @@ -252,7 +255,7 @@ QPID_AUTO_TEST_CASE(testPolicyWithDtx) other.messageTransfer(arg::content=client::Message("Message_6", q)); } -QPID_AUTO_TEST_CASE(testFlowToDiskWithNoStore) +QPID_AUTO_TEST_CASE(testFlowToDiskWithNoStore) { //Ensure that with no store loaded, we don't flow to disk but //fallback to rejecting messages @@ -265,7 +268,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskWithNoStore) LocalQueue incoming; SubscriptionSettings settings(FlowControl::unlimited()); settings.autoAck = 0; // no auto ack. - Subscription sub = f.subs.subscribe(incoming, q, settings); + Subscription sub = f.subs.subscribe(incoming, q, settings); for (int i = 0; i < 5; i++) { f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); } @@ -276,8 +279,10 @@ QPID_AUTO_TEST_CASE(testFlowToDiskWithNoStore) ScopedSuppressLogging sl; // Suppress messages for expected errors. f.session.messageTransfer(arg::content=client::Message("Message_6", q)); BOOST_FAIL("expecting ResourceLimitExceededException."); - } catch (const ResourceLimitExceededException&) {} + } catch (const ResourceLimitExceededException&) {} } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/QueueRegistryTest.cpp b/qpid/cpp/src/tests/QueueRegistryTest.cpp index 7ad4e0b89d..712cb568c3 100644 --- a/qpid/cpp/src/tests/QueueRegistryTest.cpp +++ b/qpid/cpp/src/tests/QueueRegistryTest.cpp @@ -6,9 +6,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -23,6 +23,9 @@ using namespace qpid::broker; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(QueueRegistryTest) QPID_AUTO_TEST_CASE(testDeclare) @@ -49,7 +52,7 @@ QPID_AUTO_TEST_CASE(testDeclare) BOOST_CHECK_EQUAL(bar, q->getName()); } -QPID_AUTO_TEST_CASE(testDeclareTmp) +QPID_AUTO_TEST_CASE(testDeclareTmp) { QueueRegistry reg; std::pair<Queue::shared_ptr, bool> qc; @@ -58,8 +61,8 @@ QPID_AUTO_TEST_CASE(testDeclareTmp) BOOST_CHECK(qc.second); BOOST_CHECK_EQUAL(std::string("tmp_1"), qc.first->getName()); } - -QPID_AUTO_TEST_CASE(testFind) + +QPID_AUTO_TEST_CASE(testFind) { std::string foo("foo"); std::string bar("bar"); @@ -75,7 +78,7 @@ QPID_AUTO_TEST_CASE(testFind) BOOST_CHECK_EQUAL(bar, q->getName()); } -QPID_AUTO_TEST_CASE(testDestroy) +QPID_AUTO_TEST_CASE(testDestroy) { std::string foo("foo"); QueueRegistry reg; @@ -92,3 +95,5 @@ QPID_AUTO_TEST_CASE(testDestroy) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/QueueTest.cpp b/qpid/cpp/src/tests/QueueTest.cpp index b70afa52a7..841a19f7c1 100644 --- a/qpid/cpp/src/tests/QueueTest.cpp +++ b/qpid/cpp/src/tests/QueueTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -39,9 +39,12 @@ using namespace qpid::broker; using namespace qpid::framing; using namespace qpid::sys; +namespace qpid { +namespace tests { + class TestConsumer : public virtual Consumer{ public: - typedef boost::shared_ptr<TestConsumer> shared_ptr; + typedef boost::shared_ptr<TestConsumer> shared_ptr; intrusive_ptr<Message> last; bool received; @@ -82,68 +85,68 @@ QPID_AUTO_TEST_SUITE(QueueTestSuite) QPID_AUTO_TEST_CASE(testAsyncMessage) { Queue::shared_ptr queue(new Queue("my_test_queue", true)); intrusive_ptr<Message> received; - + TestConsumer::shared_ptr c1(new TestConsumer()); queue->consume(c1); - - + + //Test basic delivery: intrusive_ptr<Message> msg1 = create_message("e", "A"); msg1->enqueueAsync(queue, 0);//this is done on enqueue which is not called from process queue->process(msg1); sleep(2); - + BOOST_CHECK(!c1->received); msg1->enqueueComplete(); - + received = queue->get().payload; - BOOST_CHECK_EQUAL(msg1.get(), received.get()); + BOOST_CHECK_EQUAL(msg1.get(), received.get()); } - - + + QPID_AUTO_TEST_CASE(testAsyncMessageCount){ Queue::shared_ptr queue(new Queue("my_test_queue", true)); intrusive_ptr<Message> msg1 = create_message("e", "A"); msg1->enqueueAsync(queue, 0);//this is done on enqueue which is not called from process - + queue->process(msg1); sleep(2); uint32_t compval=0; BOOST_CHECK_EQUAL(compval, queue->getMessageCount()); msg1->enqueueComplete(); compval=1; - BOOST_CHECK_EQUAL(compval, queue->getMessageCount()); + BOOST_CHECK_EQUAL(compval, queue->getMessageCount()); } QPID_AUTO_TEST_CASE(testConsumers){ Queue::shared_ptr queue(new Queue("my_queue", true)); - + //Test adding consumers: TestConsumer::shared_ptr c1(new TestConsumer()); TestConsumer::shared_ptr c2(new TestConsumer()); queue->consume(c1); queue->consume(c2); - + BOOST_CHECK_EQUAL(uint32_t(2), queue->getConsumerCount()); - + //Test basic delivery: intrusive_ptr<Message> msg1 = create_message("e", "A"); intrusive_ptr<Message> msg2 = create_message("e", "B"); intrusive_ptr<Message> msg3 = create_message("e", "C"); - + queue->deliver(msg1); BOOST_CHECK(queue->dispatch(c1)); BOOST_CHECK_EQUAL(msg1.get(), c1->last.get()); - + queue->deliver(msg2); BOOST_CHECK(queue->dispatch(c2)); BOOST_CHECK_EQUAL(msg2.get(), c2->last.get()); - + c1->received = false; queue->deliver(msg3); BOOST_CHECK(queue->dispatch(c1)); - BOOST_CHECK_EQUAL(msg3.get(), c1->last.get()); - + BOOST_CHECK_EQUAL(msg3.get(), c1->last.get()); + //Test cancellation: queue->cancel(c1); BOOST_CHECK_EQUAL(uint32_t(1), queue->getConsumerCount()); @@ -157,15 +160,15 @@ QPID_AUTO_TEST_CASE(testRegistry){ registry.declare("queue1", true, true); registry.declare("queue2", true, true); registry.declare("queue3", true, true); - + BOOST_CHECK(registry.find("queue1")); BOOST_CHECK(registry.find("queue2")); BOOST_CHECK(registry.find("queue3")); - + registry.destroy("queue1"); registry.destroy("queue2"); registry.destroy("queue3"); - + BOOST_CHECK(!registry.find("queue1")); BOOST_CHECK(!registry.find("queue2")); BOOST_CHECK(!registry.find("queue3")); @@ -177,13 +180,13 @@ QPID_AUTO_TEST_CASE(testDequeue){ intrusive_ptr<Message> msg2 = create_message("e", "B"); intrusive_ptr<Message> msg3 = create_message("e", "C"); intrusive_ptr<Message> received; - + queue->deliver(msg1); queue->deliver(msg2); queue->deliver(msg3); - + BOOST_CHECK_EQUAL(uint32_t(3), queue->getMessageCount()); - + received = queue->get().payload; BOOST_CHECK_EQUAL(msg1.get(), received.get()); BOOST_CHECK_EQUAL(uint32_t(2), queue->getMessageCount()); @@ -204,7 +207,7 @@ QPID_AUTO_TEST_CASE(testDequeue){ received = queue->get().payload; BOOST_CHECK(!received); BOOST_CHECK_EQUAL(uint32_t(0), queue->getMessageCount()); - + } QPID_AUTO_TEST_CASE(testBound) @@ -236,7 +239,7 @@ QPID_AUTO_TEST_CASE(testBound) queue->unbind(exchanges, queue); //ensure the remaining exchanges don't still have the queue bound to them: - FailOnDeliver deliverable; + FailOnDeliver deliverable; exchange1->route(deliverable, key, &args); exchange3->route(deliverable, key, &args); } @@ -245,10 +248,10 @@ QPID_AUTO_TEST_CASE(testPersistLastNodeStanding){ client::QueueOptions args; args.setPersistLastNode(); - + Queue::shared_ptr queue(new Queue("my-queue", true)); queue->configure(args); - + intrusive_ptr<Message> msg1 = create_message("e", "A"); intrusive_ptr<Message> msg2 = create_message("e", "B"); intrusive_ptr<Message> msg3 = create_message("e", "C"); @@ -256,13 +259,13 @@ QPID_AUTO_TEST_CASE(testPersistLastNodeStanding){ //enqueue 2 messages queue->deliver(msg1); queue->deliver(msg2); - + //change mode queue->setLastNodeFailure(); - + //enqueue 1 message queue->deliver(msg3); - + //check all have persistent ids. BOOST_CHECK(msg1->isPersistent()); BOOST_CHECK(msg2->isPersistent()); @@ -277,7 +280,7 @@ class TestMessageStoreOC : public NullMessageStore uint enqCnt; uint deqCnt; bool error; - + virtual void dequeue(TransactionContext*, const boost::intrusive_ptr<PersistableMessage>& /*msg*/, const PersistableQueue& /*queue*/) @@ -298,7 +301,7 @@ class TestMessageStoreOC : public NullMessageStore { error=true; } - + TestMessageStoreOC() : NullMessageStore(),enqCnt(0),deqCnt(0),error(false) {} ~TestMessageStoreOC(){} }; @@ -312,7 +315,7 @@ QPID_AUTO_TEST_CASE(testLVQOrdering){ Queue::shared_ptr queue(new Queue("my-queue", true )); queue->configure(args); - + intrusive_ptr<Message> msg1 = create_message("e", "A"); intrusive_ptr<Message> msg2 = create_message("e", "B"); intrusive_ptr<Message> msg3 = create_message("e", "C"); @@ -324,27 +327,27 @@ QPID_AUTO_TEST_CASE(testLVQOrdering){ string key; args.getLVQKey(key); BOOST_CHECK_EQUAL(key, "qpid.LVQ_key"); - + msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a"); msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b"); msg3->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c"); msg4->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a"); - + //enqueue 4 message queue->deliver(msg1); queue->deliver(msg2); queue->deliver(msg3); queue->deliver(msg4); - + BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); - + received = queue->get().payload; BOOST_CHECK_EQUAL(msg4.get(), received.get()); received = queue->get().payload; BOOST_CHECK_EQUAL(msg2.get(), received.get()); - + received = queue->get().payload; BOOST_CHECK_EQUAL(msg3.get(), received.get()); @@ -357,18 +360,18 @@ QPID_AUTO_TEST_CASE(testLVQOrdering){ queue->deliver(msg5); queue->deliver(msg6); queue->deliver(msg7); - + BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); - + received = queue->get().payload; BOOST_CHECK_EQUAL(msg5.get(), received.get()); received = queue->get().payload; BOOST_CHECK_EQUAL(msg6.get(), received.get()); - + received = queue->get().payload; BOOST_CHECK_EQUAL(msg7.get(), received.get()); - + } QPID_AUTO_TEST_CASE(testLVQEmptyKey){ @@ -379,20 +382,20 @@ QPID_AUTO_TEST_CASE(testLVQEmptyKey){ Queue::shared_ptr queue(new Queue("my-queue", true )); queue->configure(args); - + intrusive_ptr<Message> msg1 = create_message("e", "A"); intrusive_ptr<Message> msg2 = create_message("e", "B"); string key; args.getLVQKey(key); BOOST_CHECK_EQUAL(key, "qpid.LVQ_key"); - + msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a"); queue->deliver(msg1); queue->deliver(msg2); BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u); - + } QPID_AUTO_TEST_CASE(testLVQAcquire){ @@ -403,7 +406,7 @@ QPID_AUTO_TEST_CASE(testLVQAcquire){ Queue::shared_ptr queue(new Queue("my-queue", true )); queue->configure(args); - + intrusive_ptr<Message> msg1 = create_message("e", "A"); intrusive_ptr<Message> msg2 = create_message("e", "B"); intrusive_ptr<Message> msg3 = create_message("e", "C"); @@ -416,7 +419,7 @@ QPID_AUTO_TEST_CASE(testLVQAcquire){ string key; args.getLVQKey(key); BOOST_CHECK_EQUAL(key, "qpid.LVQ_key"); - + msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a"); msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b"); @@ -424,13 +427,13 @@ QPID_AUTO_TEST_CASE(testLVQAcquire){ msg4->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a"); msg5->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b"); msg6->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c"); - + //enqueue 4 message queue->deliver(msg1); queue->deliver(msg2); queue->deliver(msg3); queue->deliver(msg4); - + BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); framing::SequenceNumber sequence(1); @@ -439,9 +442,9 @@ QPID_AUTO_TEST_CASE(testLVQAcquire){ BOOST_CHECK(!queue->acquire(qmsg)); BOOST_CHECK(queue->acquire(qmsg2)); - + BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u); - + queue->deliver(msg5); BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); @@ -449,11 +452,11 @@ QPID_AUTO_TEST_CASE(testLVQAcquire){ args.setOrdering(client::LVQ_NO_BROWSE); queue->configure(args); TestConsumer::shared_ptr c1(new TestConsumer(false)); - + queue->dispatch(c1); queue->dispatch(c1); queue->dispatch(c1); - + queue->deliver(msg6); BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); @@ -474,7 +477,7 @@ QPID_AUTO_TEST_CASE(testLVQMultiQueue){ intrusive_ptr<Message> received; queue1->configure(args); queue2->configure(args); - + intrusive_ptr<Message> msg1 = create_message("e", "A"); intrusive_ptr<Message> msg2 = create_message("e", "A"); @@ -484,17 +487,17 @@ QPID_AUTO_TEST_CASE(testLVQMultiQueue){ msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a"); msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a"); - + queue1->deliver(msg1); queue2->deliver(msg1); queue1->deliver(msg2); - + received = queue1->get().payload; BOOST_CHECK_EQUAL(msg2.get(), received.get()); received = queue2->get().payload; BOOST_CHECK_EQUAL(msg1.get(), received.get()); - + } QPID_AUTO_TEST_CASE(testLVQRecover){ @@ -518,7 +521,7 @@ QPID_AUTO_TEST_CASE(testLVQRecover){ Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore)); intrusive_ptr<Message> received; queue1->configure(args); - + intrusive_ptr<Message> msg1 = create_message("e", "A"); intrusive_ptr<Message> msg2 = create_message("e", "A"); // 2 @@ -544,7 +547,7 @@ QPID_AUTO_TEST_CASE(testLVQRecover){ BOOST_CHECK_EQUAL(testStore.deqCnt, 1u); } -void addMessagesToQueue(uint count, Queue& queue, uint oddTtl = 200, uint evenTtl = 0) +void addMessagesToQueue(uint count, Queue& queue, uint oddTtl = 200, uint evenTtl = 0) { for (uint i = 0; i < count; i++) { intrusive_ptr<Message> m = create_message("exchange", "key"); @@ -592,7 +595,7 @@ QPID_AUTO_TEST_CASE(testMultiQueueLastNode){ queue1->configure(args); Queue::shared_ptr queue2(new Queue("queue2", true, &testStore )); queue2->configure(args); - + intrusive_ptr<Message> msg1 = create_message("e", "A"); queue1->deliver(msg1); @@ -623,7 +626,7 @@ QPID_AUTO_TEST_CASE(testMultiQueueLastNode){ // check no failure messages are stored queue1->clearLastNodeFailure(); queue2->clearLastNodeFailure(); - + intrusive_ptr<Message> msg3 = create_message("e", "B"); queue1->deliver(msg3); queue2->deliver(msg3); @@ -631,7 +634,7 @@ QPID_AUTO_TEST_CASE(testMultiQueueLastNode){ queue1->setLastNodeFailure(); queue2->setLastNodeFailure(); BOOST_CHECK_EQUAL(testStore.enqCnt, 6u); - + // check requeue 1 intrusive_ptr<Message> msg4 = create_message("e", "C"); intrusive_ptr<Message> msg5 = create_message("e", "D"); @@ -639,17 +642,17 @@ QPID_AUTO_TEST_CASE(testMultiQueueLastNode){ framing::SequenceNumber sequence(1); QueuedMessage qmsg1(queue1.get(), msg4, sequence); QueuedMessage qmsg2(queue2.get(), msg5, ++sequence); - + queue1->requeue(qmsg1); BOOST_CHECK_EQUAL(testStore.enqCnt, 7u); - + // check requeue 2 queue2->clearLastNodeFailure(); queue2->requeue(qmsg2); BOOST_CHECK_EQUAL(testStore.enqCnt, 7u); queue2->setLastNodeFailure(); BOOST_CHECK_EQUAL(testStore.enqCnt, 8u); - + queue2->clearLastNodeFailure(); queue2->setLastNodeFailure(); BOOST_CHECK_EQUAL(testStore.enqCnt, 8u); @@ -664,8 +667,8 @@ simulate this: 4. stop and recover remaining node 5. add another node 6. kill that new node again -make sure that an attempt to re-enqueue a message does not happen which will -result in the last man standing exiting with an error. +make sure that an attempt to re-enqueue a message does not happen which will +result in the last man standing exiting with an error. we need to make sure that recover is safe, i.e. messages are not requeued to the store. @@ -678,7 +681,7 @@ not requeued to the store. Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore)); intrusive_ptr<Message> received; queue1->configure(args); - + // check requeue 1 intrusive_ptr<Message> msg1 = create_message("e", "C"); intrusive_ptr<Message> msg2 = create_message("e", "D"); @@ -711,17 +714,29 @@ simulate store excption going into last node standing Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore)); intrusive_ptr<Message> received; queue1->configure(args); - + // check requeue 1 intrusive_ptr<Message> msg1 = create_message("e", "C"); queue1->deliver(msg1); testStore.createError(); - + ScopedSuppressLogging sl; // Suppress messages for expected errors. queue1->setLastNodeFailure(); BOOST_CHECK_EQUAL(testStore.enqCnt, 0u); -}QPID_AUTO_TEST_SUITE_END() +} + +intrusive_ptr<Message> mkMsg(std::string exchange, std::string routingKey) { + intrusive_ptr<Message> msg(new Message()); + AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0))); + AMQFrame header((AMQHeaderBody())); + msg->getFrames().append(method); + msg->getFrames().append(header); + msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey); + return msg; +} +QPID_AUTO_TEST_SUITE_END() +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/RangeSet.cpp b/qpid/cpp/src/tests/RangeSet.cpp index 9c602de78d..db3a964086 100644 --- a/qpid/cpp/src/tests/RangeSet.cpp +++ b/qpid/cpp/src/tests/RangeSet.cpp @@ -24,6 +24,9 @@ using namespace std; using namespace qpid; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(RangeSetTestSuite) typedef qpid::Range<int> TestRange; @@ -44,8 +47,8 @@ QPID_AUTO_TEST_CASE(testRangeSetAddPoint) { BOOST_CHECK_MESSAGE(r.contains(TestRange(3,4)), r); BOOST_CHECK(!r.empty()); r += 5; - BOOST_CHECK_MESSAGE(r.contains(5), r); - BOOST_CHECK_MESSAGE(r.contains(TestRange(5,6)), r); + BOOST_CHECK_MESSAGE(r.contains(5), r); + BOOST_CHECK_MESSAGE(r.contains(TestRange(5,6)), r); BOOST_CHECK_MESSAGE(!r.contains(TestRange(3,6)), r); r += 4; BOOST_CHECK_MESSAGE(r.contains(TestRange(3,6)), r); @@ -139,3 +142,5 @@ QPID_AUTO_TEST_CASE(testRangeContaining) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/RateFlowcontrolTest.cpp b/qpid/cpp/src/tests/RateFlowcontrolTest.cpp index b8fda09f61..80ad06af8c 100644 --- a/qpid/cpp/src/tests/RateFlowcontrolTest.cpp +++ b/qpid/cpp/src/tests/RateFlowcontrolTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -27,25 +27,28 @@ using namespace qpid::broker; using namespace qpid::sys; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(RateFlowcontrolTestSuite) QPID_AUTO_TEST_CASE(RateFlowcontrolTest) { // BOOST_CHECK(predicate); // BOOST_CHECK_EQUAL(a, b); - + RateFlowcontrol fc(100); AbsTime n=AbsTime::now(); - + BOOST_CHECK_EQUAL( fc.receivedMessage(n, 0), 0U ); - + fc.sentCredit(n, 0); - + BOOST_CHECK_EQUAL( fc.receivedMessage(n, 0), 0U ); fc.sentCredit(n, 50); Duration d=250*TIME_MSEC; - + n = AbsTime(n,d); BOOST_CHECK_EQUAL( fc.receivedMessage(n, 25), 0U ); BOOST_CHECK_EQUAL( fc.availableCredit(n), 25U ); @@ -64,3 +67,5 @@ QPID_AUTO_TEST_CASE(RateFlowcontrolTest) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/RefCounted.cpp b/qpid/cpp/src/tests/RefCounted.cpp index 8c679a3d2e..e4c1da5696 100644 --- a/qpid/cpp/src/tests/RefCounted.cpp +++ b/qpid/cpp/src/tests/RefCounted.cpp @@ -27,6 +27,9 @@ using boost::intrusive_ptr; using namespace std; using namespace qpid; +namespace qpid { +namespace tests { + struct CountMe : public RefCounted { static int instances; CountMe() { ++instances; } @@ -48,3 +51,5 @@ QPID_AUTO_TEST_CASE(testRefCounted) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/ReplicationTest.cpp b/qpid/cpp/src/tests/ReplicationTest.cpp index 38dc1a9e52..ed768f1306 100644 --- a/qpid/cpp/src/tests/ReplicationTest.cpp +++ b/qpid/cpp/src/tests/ReplicationTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -42,6 +42,9 @@ using namespace qpid::framing; using namespace qpid::replication::constants; using boost::assign::list_of; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ReplicationTestSuite) // The CMake-based build passes in the module suffix; if it's not there, this @@ -63,7 +66,7 @@ qpid::broker::Broker::Options getBrokerOpts(const std::vector<std::string>& args return opts; } -QPID_AUTO_TEST_CASE(testReplicationExchange) +QPID_AUTO_TEST_CASE(testReplicationExchange) { qpid::broker::Broker::Options brokerOpts(getBrokerOpts(list_of<string>("qpidd") ("--replication-exchange-name=qpid.replication"))); @@ -79,7 +82,7 @@ QPID_AUTO_TEST_CASE(testReplicationExchange) f.session.queueDeclare(arg::queue=eventQ, arg::exclusive=true, arg::autoDelete=true, arg::arguments=eventQopts); f.session.exchangeBind(arg::exchange="qpid.replication", arg::queue=eventQ, arg::bindingKey=dataQ); - + f.session.queueDeclare(arg::queue=eventQ2, arg::exclusive=true, arg::autoDelete=true, arg::arguments=eventQopts); f.session.exchangeBind(arg::exchange="qpid.replication", arg::queue=eventQ2, arg::bindingKey=dataQ2); @@ -133,3 +136,5 @@ QPID_AUTO_TEST_CASE(testReplicationExchange) QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/RetryList.cpp b/qpid/cpp/src/tests/RetryList.cpp index 80f59bf15f..d1d22348a3 100644 --- a/qpid/cpp/src/tests/RetryList.cpp +++ b/qpid/cpp/src/tests/RetryList.cpp @@ -24,6 +24,9 @@ using namespace qpid; using namespace qpid::broker; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(RetryListTestSuite) struct RetryListFixture @@ -36,7 +39,7 @@ struct RetryListFixture { urls.push_back(Url(s)); } - + void addExpectation(const std::string& host, uint16_t port) { expected.push_back(TcpAddress(host, port)); @@ -57,7 +60,7 @@ struct RetryListFixture } }; -QPID_AUTO_TEST_CASE(testWithSingleAddress) +QPID_AUTO_TEST_CASE(testWithSingleAddress) { RetryListFixture test; test.addUrl("amqp:host:5673"); @@ -65,7 +68,7 @@ QPID_AUTO_TEST_CASE(testWithSingleAddress) test.check(); } -QPID_AUTO_TEST_CASE(testWithSingleUrlOfMultipleAddresses) +QPID_AUTO_TEST_CASE(testWithSingleUrlOfMultipleAddresses) { RetryListFixture test; test.addUrl("amqp:host1,host2:2222,tcp:host3:5673,host4:1"); @@ -78,7 +81,7 @@ QPID_AUTO_TEST_CASE(testWithSingleUrlOfMultipleAddresses) test.check(); } -QPID_AUTO_TEST_CASE(testWithMultipleUrlsOfMultipleAddresses) +QPID_AUTO_TEST_CASE(testWithMultipleUrlsOfMultipleAddresses) { RetryListFixture test; test.addUrl("amqp:my-host"); @@ -97,10 +100,12 @@ QPID_AUTO_TEST_CASE(testWithMultipleUrlsOfMultipleAddresses) test.check(); } -QPID_AUTO_TEST_CASE(testEmptyList) +QPID_AUTO_TEST_CASE(testEmptyList) { RetryListFixture test; test.check(); } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/SequenceNumberTest.cpp b/qpid/cpp/src/tests/SequenceNumberTest.cpp index e4c6d066ef..f3c934e3ca 100644 --- a/qpid/cpp/src/tests/SequenceNumberTest.cpp +++ b/qpid/cpp/src/tests/SequenceNumberTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -26,6 +26,8 @@ using namespace qpid::framing; +namespace qpid { +namespace tests { void checkDifference(SequenceNumber& a, SequenceNumber& b, int gap) { @@ -54,7 +56,7 @@ void checkComparison(SequenceNumber& a, SequenceNumber& b, int gap) BOOST_CHECK(++a < ++b);//test prefix } //keep incrementing until a also wraps around - for (int i = 0; i < (gap + 2); i++) { + for (int i = 0; i < (gap + 2); i++) { BOOST_CHECK(a++ < b++);//test postfix } //let a 'catch up' @@ -91,7 +93,7 @@ QPID_AUTO_TEST_CASE(testIncrementPostfix) BOOST_CHECK(b != c); } -QPID_AUTO_TEST_CASE(testIncrementPrefix) +QPID_AUTO_TEST_CASE(testIncrementPrefix) { SequenceNumber a; SequenceNumber b; @@ -203,3 +205,5 @@ QPID_AUTO_TEST_CASE(testDifferenceWithWrapAround2) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/SequenceSet.cpp b/qpid/cpp/src/tests/SequenceSet.cpp index ba2f1391a1..aaeb68e3c5 100644 --- a/qpid/cpp/src/tests/SequenceSet.cpp +++ b/qpid/cpp/src/tests/SequenceSet.cpp @@ -20,6 +20,9 @@ #include "unit_test.h" #include <list> +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(SequenceSetTestSuite) using namespace qpid::framing; @@ -72,7 +75,7 @@ QPID_AUTO_TEST_CASE(testAdd) { BOOST_CHECK(!s.contains(i)); RangeExpectations().expect(2, 5).expect(8, 8).check(s); - + SequenceSet t; t.add(6, 10); t.add(s); @@ -90,7 +93,7 @@ QPID_AUTO_TEST_CASE(testAdd2) { SequenceSet s; s.add(7,6); s.add(4,4); - s.add(3,10); + s.add(3,10); s.add(2); RangeExpectations().expect(2, 10).check(s); } @@ -137,4 +140,4 @@ QPID_AUTO_TEST_CASE(testRemove) { QPID_AUTO_TEST_SUITE_END() - +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/SessionState.cpp b/qpid/cpp/src/tests/SessionState.cpp index 5e21ff2b70..157cabfb63 100644 --- a/qpid/cpp/src/tests/SessionState.cpp +++ b/qpid/cpp/src/tests/SessionState.cpp @@ -28,6 +28,9 @@ #include <functional> #include <numeric> +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(SessionStateTestSuite) using namespace std; @@ -94,7 +97,7 @@ size_t transfer1(qpid::SessionState& s, string content) { size_t transfer1Char(qpid::SessionState& s, char content) { return transfer1(s, string(1,content)); } - + // Send transfer frame with multiple single-byte content frames. size_t transferN(qpid::SessionState& s, string content) { size_t size=send(s, transferFrame(!content.empty())); @@ -134,7 +137,7 @@ QPID_AUTO_TEST_CASE(testSendGetReplyList) { BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(0,0))),"CabcCdCeCfCxyz"); // Ignore controls. s.senderRecord(AMQFrame(new SessionFlushBody())); - BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(2,0))),"CeCfCxyz"); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(2,0))),"CeCfCxyz"); } QPID_AUTO_TEST_CASE(testNeedFlush) { @@ -185,7 +188,7 @@ QPID_AUTO_TEST_CASE(testPeerConfirmed) { s.senderConfirmed(SessionPoint(5)); BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(5,0))), "CxCy"); BOOST_CHECK(s.senderNeedFlush()); - + s.senderConfirmed(SessionPoint(6)); BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(6,0))), "Cy"); BOOST_CHECK(!s.senderNeedFlush()); @@ -195,7 +198,7 @@ QPID_AUTO_TEST_CASE(testPeerCompleted) { qpid::SessionState s; s.setTimeout(1); s.senderGetCommandPoint(); - // Completion implies confirmation + // Completion implies confirmation transfers(s, "abc"); BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(0,0))), "CaCbCc"); SequenceSet set(SequenceSet() + 0 + 1); @@ -205,7 +208,7 @@ QPID_AUTO_TEST_CASE(testPeerCompleted) { transfers(s, "def"); // We dont do out-of-order confirmation, so this will only confirm up to 3: set = SequenceSet(SequenceSet() + 2 + 3 + 5); - s.senderCompleted(set); + s.senderCompleted(set); BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(4,0))), "CeCf"); } @@ -215,11 +218,11 @@ QPID_AUTO_TEST_CASE(testReceive) { s.receiverSetCommandPoint(SessionPoint()); BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(0)); BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(0)); - + BOOST_CHECK(s.receiverRecord(transferFrame(false))); BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(1)); BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(1)); - + BOOST_CHECK(s.receiverRecord(transferFrame(true))); SessionPoint point = SessionPoint(1, transferFrameSize()); BOOST_CHECK_EQUAL(s.receiverGetExpected(), point); @@ -297,3 +300,5 @@ QPID_AUTO_TEST_CASE(testNeedKnownCompleted) { QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/Shlib.cpp b/qpid/cpp/src/tests/Shlib.cpp index 7d2f2456c7..692cfcdff9 100644 --- a/qpid/cpp/src/tests/Shlib.cpp +++ b/qpid/cpp/src/tests/Shlib.cpp @@ -24,6 +24,9 @@ #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(ShlibTestSuite) using namespace qpid::sys; @@ -51,7 +54,7 @@ QPID_AUTO_TEST_CASE(testShlib) { } catch (const qpid::Exception&) {} } - + QPID_AUTO_TEST_CASE(testAutoShlib) { int unloaded = 0; { @@ -66,6 +69,8 @@ QPID_AUTO_TEST_CASE(testAutoShlib) { } BOOST_CHECK_EQUAL(42, unloaded); } - + QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/SocketProxy.h b/qpid/cpp/src/tests/SocketProxy.h index ccce3c8842..9df32a1336 100644 --- a/qpid/cpp/src/tests/SocketProxy.h +++ b/qpid/cpp/src/tests/SocketProxy.h @@ -35,8 +35,11 @@ #include "qpid/sys/Mutex.h" #include "qpid/log/Statement.h" +namespace qpid { +namespace tests { + /** - * A simple socket proxy that forwards to another socket. + * A simple socket proxy that forwards to another socket. * Used between client & local broker to simulate network failures. */ class SocketProxy : private qpid::sys::Runnable @@ -59,7 +62,7 @@ class SocketProxy : private qpid::sys::Runnable joined = false; thread = qpid::sys::Thread(static_cast<qpid::sys::Runnable*>(this)); } - + ~SocketProxy() { close(); if (!joined) thread.join(); } /** Simulate a network disconnect. */ @@ -88,7 +91,7 @@ class SocketProxy : private qpid::sys::Runnable } uint16_t getPort() const { return port; } - + private: static void throwErrno(const std::string& msg) { throw qpid::Exception(msg+":"+qpid::sys::strError(errno)); @@ -153,7 +156,7 @@ class SocketProxy : private qpid::sys::Runnable } try { if (server.get()) server->close(); - close(); + close(); } catch (const std::exception& e) { QPID_LOG(debug, "SocketProxy::run exception in client/server close()" << e.what()); @@ -169,4 +172,6 @@ class SocketProxy : private qpid::sys::Runnable bool dropClient, dropServer; }; +}} // namespace qpid::tests + #endif diff --git a/qpid/cpp/src/tests/TestMessageStore.h b/qpid/cpp/src/tests/TestMessageStore.h index be1ed57349..20e0b755b2 100644 --- a/qpid/cpp/src/tests/TestMessageStore.h +++ b/qpid/cpp/src/tests/TestMessageStore.h @@ -10,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -28,6 +28,9 @@ using namespace qpid; using namespace qpid::broker; using namespace qpid::framing; +namespace qpid { +namespace tests { + typedef std::pair<string, boost::intrusive_ptr<PersistableMessage> > msg_queue_pair; class TestMessageStore : public NullMessageStore @@ -35,7 +38,7 @@ class TestMessageStore : public NullMessageStore public: std::vector<boost::intrusive_ptr<PersistableMessage> > dequeued; std::vector<msg_queue_pair> enqueued; - + void dequeue(TransactionContext*, const boost::intrusive_ptr<PersistableMessage>& msg, const PersistableQueue& /*queue*/) @@ -47,7 +50,7 @@ class TestMessageStore : public NullMessageStore const boost::intrusive_ptr<PersistableMessage>& msg, const PersistableQueue& queue) { - msg->enqueueComplete(); + msg->enqueueComplete(); enqueued.push_back(msg_queue_pair(queue.getName(), msg)); } @@ -55,4 +58,6 @@ class TestMessageStore : public NullMessageStore ~TestMessageStore(){} }; +}} // namespace qpid::tests + #endif diff --git a/qpid/cpp/src/tests/TestOptions.h b/qpid/cpp/src/tests/TestOptions.h index a400fe5ecb..f8da0f59cf 100644 --- a/qpid/cpp/src/tests/TestOptions.h +++ b/qpid/cpp/src/tests/TestOptions.h @@ -9,9 +9,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -67,7 +67,7 @@ struct TestOptions : public qpid::Options connection.open(con); } - + bool help; ConnectionOptions con; qpid::log::Options log; diff --git a/qpid/cpp/src/tests/TimerTest.cpp b/qpid/cpp/src/tests/TimerTest.cpp index 2642c980ba..1552421ba0 100644 --- a/qpid/cpp/src/tests/TimerTest.cpp +++ b/qpid/cpp/src/tests/TimerTest.cpp @@ -8,9 +8,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -32,6 +32,9 @@ using namespace qpid::sys; using boost::intrusive_ptr; using boost::dynamic_pointer_cast; +namespace qpid { +namespace tests { + class Counter { Mutex lock; @@ -44,7 +47,7 @@ class Counter return ++counter; } }; - + class TestTask : public TimerTask { const AbsTime start; @@ -56,7 +59,7 @@ class TestTask : public TimerTask Counter& counter; public: - TestTask(Duration timeout, Counter& _counter) + TestTask(Duration timeout, Counter& _counter) : TimerTask(timeout), start(now()), expected(timeout), end(start), fired(false), counter(_counter) {} void fire() @@ -106,14 +109,14 @@ QPID_AUTO_TEST_CASE(testGeneral) intrusive_ptr<TestTask> task2(new TestTask(Duration(1 * TIME_SEC), counter)); intrusive_ptr<TestTask> task3(new TestTask(Duration(4 * TIME_SEC), counter)); intrusive_ptr<TestTask> task4(new TestTask(Duration(2 * TIME_SEC), counter)); - + timer.add(task1); timer.add(task2); timer.add(task3); timer.add(task4); - + dynamic_pointer_cast<TestTask>(task3)->wait(Duration(6 * TIME_SEC)); - + dynamic_pointer_cast<TestTask>(task1)->check(3); dynamic_pointer_cast<TestTask>(task2)->check(1); dynamic_pointer_cast<TestTask>(task3)->check(4); @@ -121,3 +124,5 @@ QPID_AUTO_TEST_CASE(testGeneral) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/TopicExchangeTest.cpp b/qpid/cpp/src/tests/TopicExchangeTest.cpp index d707066534..c103620dbf 100644 --- a/qpid/cpp/src/tests/TopicExchangeTest.cpp +++ b/qpid/cpp/src/tests/TopicExchangeTest.cpp @@ -6,9 +6,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -22,11 +22,15 @@ using namespace qpid::broker; using namespace std; + +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(TopicExchangeTestSuite) #define CHECK_NORMALIZED(expect, pattern) BOOST_CHECK_EQUAL(expect, TopicExchange::normalize(pattern)); -QPID_AUTO_TEST_CASE(testNormalize) +QPID_AUTO_TEST_CASE(testNormalize) { CHECK_NORMALIZED("", ""); CHECK_NORMALIZED("a.b.c", "a.b.c"); @@ -38,8 +42,8 @@ QPID_AUTO_TEST_CASE(testNormalize) CHECK_NORMALIZED("a.*.*.*.#", "a.*.#.*.#.*"); CHECK_NORMALIZED("*.*.*.#", "*.#.#.*.*.#"); } - -QPID_AUTO_TEST_CASE(testPlain) + +QPID_AUTO_TEST_CASE(testPlain) { string pattern("ab.cd.e"); BOOST_CHECK(TopicExchange::match(pattern, "ab.cd.e")); @@ -57,7 +61,7 @@ QPID_AUTO_TEST_CASE(testPlain) } -QPID_AUTO_TEST_CASE(testStar) +QPID_AUTO_TEST_CASE(testStar) { string pattern("a.*.b"); BOOST_CHECK(TopicExchange::match(pattern, "a.xx.b")); @@ -75,7 +79,7 @@ QPID_AUTO_TEST_CASE(testStar) BOOST_CHECK(!TopicExchange::match(pattern, "q.x.y")); } -QPID_AUTO_TEST_CASE(testHash) +QPID_AUTO_TEST_CASE(testHash) { string pattern("a.#.b"); BOOST_CHECK(TopicExchange::match(pattern, "a.b")); @@ -99,7 +103,7 @@ QPID_AUTO_TEST_CASE(testHash) BOOST_CHECK(TopicExchange::match(pattern, "a.x.x.b.y.y.c")); } -QPID_AUTO_TEST_CASE(testMixed) +QPID_AUTO_TEST_CASE(testMixed) { string pattern("*.x.#.y"); BOOST_CHECK(TopicExchange::match(pattern, "a.x.y")); @@ -119,3 +123,5 @@ QPID_AUTO_TEST_CASE(testMixed) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/TxBufferTest.cpp b/qpid/cpp/src/tests/TxBufferTest.cpp index 3d6a12cacc..4807026ab7 100644 --- a/qpid/cpp/src/tests/TxBufferTest.cpp +++ b/qpid/cpp/src/tests/TxBufferTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -27,6 +27,9 @@ using namespace qpid::broker; using boost::static_pointer_cast; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(TxBufferTestSuite) QPID_AUTO_TEST_CASE(testCommitLocal) @@ -174,3 +177,5 @@ QPID_AUTO_TEST_CASE(testBufferIsClearedAfterCommit) } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/TxMocks.h b/qpid/cpp/src/tests/TxMocks.h index fe103c5fe5..a34d864bae 100644 --- a/qpid/cpp/src/tests/TxMocks.h +++ b/qpid/cpp/src/tests/TxMocks.h @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -32,6 +32,9 @@ using namespace qpid::broker; using boost::static_pointer_cast; using std::string; +namespace qpid { +namespace tests { + template <class T> void assertEqualVector(std::vector<T>& expected, std::vector<T>& actual){ unsigned int i = 0; while(i < expected.size() && i < actual.size()){ @@ -62,15 +65,15 @@ class MockTxOp : public TxOp, public TxOpConstants{ string debugName; public: typedef boost::shared_ptr<MockTxOp> shared_ptr; - + MockTxOp() : failOnPrepare(false) {} MockTxOp(bool _failOnPrepare) : failOnPrepare(_failOnPrepare) {} - + void setDebugName(string name){ debugName = name; } - void printExpected(){ + void printExpected(){ std::cout << std::endl << "MockTxOp[" << debugName << "] expects: "; for (std::vector<string>::iterator i = expected.begin(); i < expected.end(); i++) { if(i != expected.begin()) std::cout << ", "; @@ -79,7 +82,7 @@ public: std::cout << std::endl; } - void printActual(){ + void printActual(){ std::cout << std::endl << "MockTxOp[" << debugName << "] actual: "; for (std::vector<string>::iterator i = actual.begin(); i < actual.end(); i++) { if(i != actual.begin()) std::cout << ", "; @@ -87,7 +90,7 @@ public: } std::cout << std::endl; } - + bool prepare(TransactionContext*) throw(){ actual.push_back(PREPARE); return !failOnPrepare; @@ -116,8 +119,8 @@ public: } void accept(TxOpConstVisitor&) const {} - - ~MockTxOp(){} + + ~MockTxOp(){} }; class MockTransactionalStore : public TransactionalStore{ @@ -128,10 +131,10 @@ class MockTransactionalStore : public TransactionalStore{ const string ABORT; std::vector<string> expected; std::vector<string> actual; - + enum states {OPEN = 1, PREPARED = 2, COMMITTED = 3, ABORTED = 4}; int state; - + class TestTransactionContext : public TPCTransactionContext{ MockTransactionalStore* store; public: @@ -145,29 +148,29 @@ class MockTransactionalStore : public TransactionalStore{ if(!store->isOpen() && !store->isPrepared()) throw "txn already completed"; store->state = COMMITTED; } - + void abort(){ if(!store->isOpen() && !store->isPrepared()) throw "txn already completed"; store->state = ABORTED; } ~TestTransactionContext(){} }; - + public: MockTransactionalStore() : BEGIN("BEGIN"), BEGIN2PC("BEGIN2PC"), PREPARE("PREPARE"), COMMIT("COMMIT"), ABORT("ABORT"), state(OPEN){} void collectPreparedXids(std::set<std::string>&) { - throw "Operation not supported"; + throw "Operation not supported"; } - - std::auto_ptr<TPCTransactionContext> begin(const std::string&){ + + std::auto_ptr<TPCTransactionContext> begin(const std::string&){ actual.push_back(BEGIN2PC); std::auto_ptr<TPCTransactionContext> txn(new TestTransactionContext(this)); return txn; } - std::auto_ptr<TransactionContext> begin(){ + std::auto_ptr<TransactionContext> begin(){ actual.push_back(BEGIN); std::auto_ptr<TransactionContext> txn(new TestTransactionContext(this)); return txn; @@ -183,7 +186,7 @@ public: void abort(TransactionContext& ctxt){ actual.push_back(ABORT); dynamic_cast<TestTransactionContext&>(ctxt).abort(); - } + } MockTransactionalStore& expectBegin(){ expected.push_back(BEGIN); return *this; @@ -207,23 +210,25 @@ public: void check(){ assertEqualVector(expected, actual); } - + bool isPrepared(){ return state == PREPARED; } - + bool isCommitted(){ return state == COMMITTED; } - + bool isAborted(){ return state == ABORTED; } - + bool isOpen() const{ return state == OPEN; } ~MockTransactionalStore(){} }; +}} // namespace qpid::tests + #endif diff --git a/qpid/cpp/src/tests/TxPublishTest.cpp b/qpid/cpp/src/tests/TxPublishTest.cpp index 63dbf99266..fabb01b864 100644 --- a/qpid/cpp/src/tests/TxPublishTest.cpp +++ b/qpid/cpp/src/tests/TxPublishTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -35,30 +35,33 @@ using boost::intrusive_ptr; using namespace qpid::broker; using namespace qpid::framing; +namespace qpid { +namespace tests { + struct TxPublishTest { - + TestMessageStore store; Queue::shared_ptr queue1; Queue::shared_ptr queue2; intrusive_ptr<Message> msg; TxPublish op; - + TxPublishTest() : - queue1(new Queue("queue1", false, &store, 0)), - queue2(new Queue("queue2", false, &store, 0)), + queue1(new Queue("queue1", false, &store, 0)), + queue2(new Queue("queue2", false, &store, 0)), msg(MessageUtils::createMessage("exchange", "routing_key", "id")), op(msg) { msg->getProperties<DeliveryProperties>()->setDeliveryMode(PERSISTENT); op.deliverTo(queue1); op.deliverTo(queue2); - } + } }; QPID_AUTO_TEST_SUITE(TxPublishTestSuite) - + QPID_AUTO_TEST_CASE(testPrepare) { TxPublishTest t; @@ -88,7 +91,9 @@ QPID_AUTO_TEST_CASE(testCommit) BOOST_CHECK_EQUAL(t.msg, msg_dequeue); BOOST_CHECK_EQUAL((uint32_t) 1, t.queue2->getMessageCount()); - BOOST_CHECK_EQUAL(t.msg, t.queue2->get().payload); + BOOST_CHECK_EQUAL(t.msg, t.queue2->get().payload); } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/Url.cpp b/qpid/cpp/src/tests/Url.cpp index f3b42a7208..343186eb1f 100644 --- a/qpid/cpp/src/tests/Url.cpp +++ b/qpid/cpp/src/tests/Url.cpp @@ -26,6 +26,9 @@ using namespace std; using namespace qpid; using namespace boost::assign; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(UrlTestSuite) #define URL_CHECK_STR(STR) BOOST_CHECK_EQUAL(Url(STR).str(), STR) @@ -65,3 +68,5 @@ QPID_AUTO_TEST_CASE(TestInvalidAddress) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/Uuid.cpp b/qpid/cpp/src/tests/Uuid.cpp index ea2e80b63b..a6ddb9b5a5 100644 --- a/qpid/cpp/src/tests/Uuid.cpp +++ b/qpid/cpp/src/tests/Uuid.cpp @@ -24,6 +24,9 @@ #include <set> +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(UuidTestSuite) using namespace std; @@ -77,3 +80,5 @@ QPID_AUTO_TEST_CASE(testUuidEncodeDecode) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/Variant.cpp b/qpid/cpp/src/tests/Variant.cpp index 1bf2ed98ce..2d68bb842c 100644 --- a/qpid/cpp/src/tests/Variant.cpp +++ b/qpid/cpp/src/tests/Variant.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -25,6 +25,9 @@ using namespace qpid::messaging; +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(VariantSuite) QPID_AUTO_TEST_CASE(testConversions) @@ -87,20 +90,20 @@ QPID_AUTO_TEST_CASE(testAssignment) { Variant value("abc"); Variant other = value; - BOOST_CHECK_EQUAL(STRING, value.getType()); + BOOST_CHECK_EQUAL(VAR_STRING, value.getType()); BOOST_CHECK_EQUAL(other.getType(), value.getType()); BOOST_CHECK_EQUAL(other.asString(), value.asString()); const uint32_t i(1000); value = i; - BOOST_CHECK_EQUAL(UINT32, value.getType()); - BOOST_CHECK_EQUAL(STRING, other.getType()); + BOOST_CHECK_EQUAL(VAR_UINT32, value.getType()); + BOOST_CHECK_EQUAL(VAR_STRING, other.getType()); } QPID_AUTO_TEST_CASE(testList) -{ +{ const std::string s("abc"); - const float f(9.876); + const float f(9.876f); const int16_t x(1000); Variant value = Variant::List(); @@ -108,20 +111,20 @@ QPID_AUTO_TEST_CASE(testList) 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(); + Variant::List::const_iterator i = value.asList().begin(); BOOST_CHECK(i != value.asList().end()); - BOOST_CHECK_EQUAL(STRING, i->getType()); + BOOST_CHECK_EQUAL(VAR_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(VAR_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(VAR_INT16, i->getType()); BOOST_CHECK_EQUAL(x, i->asInt16()); i++; @@ -129,9 +132,9 @@ QPID_AUTO_TEST_CASE(testList) } QPID_AUTO_TEST_CASE(testMap) -{ +{ const std::string red("red"); - const float pi(3.14); + const float pi(3.14f); const int16_t x(1000); Variant value = Variant::Map(); @@ -140,18 +143,20 @@ QPID_AUTO_TEST_CASE(testMap) value.asMap()["my-key"] = x; BOOST_CHECK_EQUAL(3u, value.asMap().size()); - BOOST_CHECK_EQUAL(STRING, value.asMap()["colour"].getType()); + BOOST_CHECK_EQUAL(VAR_STRING, value.asMap()["colour"].getType()); BOOST_CHECK_EQUAL(red, value.asMap()["colour"].asString()); - BOOST_CHECK_EQUAL(FLOAT, value.asMap()["pi"].getType()); + BOOST_CHECK_EQUAL(VAR_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(VAR_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(VAR_STRING, value.asMap()["my-key"].getType()); BOOST_CHECK_EQUAL(std::string("now it's a string"), value.asMap()["my-key"].asString()); } - + QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/XmlClientSessionTest.cpp b/qpid/cpp/src/tests/XmlClientSessionTest.cpp index b6b8520bd8..46a4c826a3 100644 --- a/qpid/cpp/src/tests/XmlClientSessionTest.cpp +++ b/qpid/cpp/src/tests/XmlClientSessionTest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -39,6 +39,9 @@ #include <vector> +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(XmlClientSessionTest) using namespace qpid::client; @@ -118,10 +121,10 @@ QPID_AUTO_TEST_CASE(testXmlBinding) { FieldTable binding; binding.setString("xquery", "declare variable $color external;" "(./message/id mod 2 = 1) and ($color = 'blue')"); - f.session.exchangeBind(qpid::client::arg::exchange="xml", qpid::client::arg::queue="odd_blue", qpid::client::arg::bindingKey="query_name", qpid::client::arg::arguments=binding); + f.session.exchangeBind(qpid::client::arg::exchange="xml", qpid::client::arg::queue="odd_blue", qpid::client::arg::bindingKey="query_name", qpid::client::arg::arguments=binding); Message message; - message.getDeliveryProperties().setRoutingKey("query_name"); + message.getDeliveryProperties().setRoutingKey("query_name"); message.getHeaders().setString("color", "blue"); string m = "<message><id>1</id></message>"; @@ -130,7 +133,7 @@ QPID_AUTO_TEST_CASE(testXmlBinding) { f.session.messageTransfer(qpid::client::arg::content=message, qpid::client::arg::destination="xml"); Message m2 = localQueue.get(); - BOOST_CHECK_EQUAL(m, m2.getData()); + BOOST_CHECK_EQUAL(m, m2.getData()); } /** @@ -146,10 +149,10 @@ QPID_AUTO_TEST_CASE(testXMLBindMultipleQueues) { FieldTable blue; blue.setString("xquery", "./colour = 'blue'"); - f.session.exchangeBind(arg::exchange="xml", arg::queue="blue", arg::bindingKey="by-colour", arg::arguments=blue); + f.session.exchangeBind(arg::exchange="xml", arg::queue="blue", arg::bindingKey="by-colour", arg::arguments=blue); FieldTable red; red.setString("xquery", "./colour = 'red'"); - f.session.exchangeBind(arg::exchange="xml", arg::queue="red", arg::bindingKey="by-colour", arg::arguments=red); + f.session.exchangeBind(arg::exchange="xml", arg::queue="red", arg::bindingKey="by-colour", arg::arguments=red); Message sent1("<colour>blue</colour>", "by-colour"); f.session.messageTransfer(arg::content=sent1, arg::destination="xml"); @@ -223,3 +226,4 @@ olour", arg::arguments=blue); QPID_AUTO_TEST_SUITE_END() +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/acl.py b/qpid/cpp/src/tests/acl.py index b62288a769..fc53d2ce8b 100755 --- a/qpid/cpp/src/tests/acl.py +++ b/qpid/cpp/src/tests/acl.py @@ -61,7 +61,7 @@ class ACLTests(TestBase010): # ACL general tests #===================================== - def test_deny_all(self): + def test_deny_mode(self): """ Test the deny all mode """ @@ -71,7 +71,9 @@ class ACLTests(TestBase010): aclf.write('acl deny all all') aclf.close() - self.reload_acl() + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) session = self.get_session('bob','bob') try: @@ -87,7 +89,7 @@ class ACLTests(TestBase010): except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) - def test_allow_all(self): + def test_allow_mode(self): """ Test the allow all mode """ @@ -96,7 +98,9 @@ class ACLTests(TestBase010): aclf.write('acl allow all all') aclf.close() - self.reload_acl() + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) session = self.get_session('bob','bob') try: @@ -124,7 +128,9 @@ class ACLTests(TestBase010): aclf.write('acl allow all all') aclf.close() - self.reload_acl() + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) session = self.get_session('bob','bob') try: @@ -208,9 +214,9 @@ class ACLTests(TestBase010): # ACL queue tests #===================================== - def test_queue_acl(self): + def test_queue_allow_mode(self): """ - Test various modes for queue acl + Test cases for queue acl in allow mode """ aclf = ACLFile() aclf.write('acl deny bob@QPID create queue name=q1 durable=true passive=true\n') @@ -221,27 +227,35 @@ class ACLTests(TestBase010): aclf.write('acl allow all all') aclf.close() - self.reload_acl() + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) session = self.get_session('bob','bob') try: - session.queue_declare(queue="q1", durable='true', passive='true') + session.queue_declare(queue="q1", durable=True, passive=True) self.fail("ACL should deny queue create request with name=q1 durable=true passive=true"); except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) session = self.get_session('bob','bob') try: - session.queue_declare(queue="q2", exclusive='true') + session.queue_declare(queue="q2", exclusive=True) self.fail("ACL should deny queue create request with name=q2 exclusive=true"); except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) session = self.get_session('bob','bob') try: - session.queue_declare(queue="q3", exclusive='true') - session.queue_declare(queue="q4", durable='true') + session.queue_declare(queue="q2", durable=True) + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue create request for q2 with any parameter other than exclusive=true"); + + try: + session.queue_declare(queue="q3", exclusive=True) + session.queue_declare(queue="q4", durable=True) except qpid.session.SessionException, e: if (530 == e.args[0].error_code): self.fail("ACL should allow queue create request for q3 and q4 with any parameter"); @@ -279,57 +293,185 @@ class ACLTests(TestBase010): if (530 == e.args[0].error_code): self.fail("ACL should allow queue delete request for q3"); + + def test_queue_deny_mode(self): + """ + Test cases for queue acl in deny mode + """ + aclf = ACLFile() + aclf.write('acl allow bob@QPID create queue name=q1 durable=true passive=true\n') + aclf.write('acl allow bob@QPID create queue name=q2 exclusive=true\n') + aclf.write('acl allow bob@QPID access queue name=q3\n') + aclf.write('acl allow bob@QPID purge queue name=q3\n') + aclf.write('acl allow bob@QPID create queue name=q3\n') + aclf.write('acl allow bob@QPID create queue name=q4\n') + aclf.write('acl allow bob@QPID delete queue name=q4\n') + aclf.write('acl allow guest@QPID all all\n') + aclf.write('acl deny all all') + aclf.close() + + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) + + session = self.get_session('bob','bob') + + try: + session.queue_declare(queue="q1", durable=True, passive=True) + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue create request with name=q1 durable=true passive=true"); + + try: + session.queue_declare(queue="q1", durable=False, passive=False) + self.fail("ACL should deny queue create request with name=q1 durable=true passive=false"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.queue_declare(queue="q2", exclusive=False) + self.fail("ACL should deny queue create request with name=q2 exclusive=false"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.queue_declare(queue="q2", exclusive=True) + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue create request for q2 with exclusive=true"); + + try: + session.queue_declare(queue="q3") + session.queue_declare(queue="q4") + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue create request for q3 and q4"); + + try: + session.queue_query(queue="q4") + self.fail("ACL should deny queue query request for q4"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.queue_purge(queue="q4") + self.fail("ACL should deny queue purge request for q4"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.queue_purge(queue="q3") + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue purge request for q3"); + + try: + session.queue_query(queue="q3") + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue query request for q3"); + + try: + session.queue_delete(queue="q3") + self.fail("ACL should deny queue delete request for q3"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.queue_delete(queue="q4") + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow queue delete request for q4"); + #===================================== # ACL exchange tests #===================================== - def test_exchange_acl(self): + def test_exchange_acl_allow_mode(self): + session = self.get_session('bob','bob') + session.queue_declare(queue="baz") + """ - Test various modes for exchange acl + Test cases for exchange acl in allow mode """ aclf = ACLFile() aclf.write('acl deny bob@QPID create exchange name=testEx durable=true passive=true\n') aclf.write('acl deny bob@QPID create exchange name=ex1 type=direct\n') - aclf.write('acl deny bob@QPID access exchange name=myEx\n') + aclf.write('acl deny bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n') aclf.write('acl deny bob@QPID bind exchange name=myEx queuename=q1 routingkey=rk1\n') aclf.write('acl deny bob@QPID unbind exchange name=myEx queuename=q1 routingkey=rk1\n') - aclf.write('acl deny bob@QPID delete exchange name=myEx\n') + aclf.write('acl deny bob@QPID delete exchange name=myEx\n') aclf.write('acl allow all all') aclf.close() - self.reload_acl() + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) session = self.get_session('bob','bob') - + session.queue_declare(queue='q1') + session.queue_declare(queue='q2') + session.exchange_declare(exchange='myEx', type='direct') + try: - session.exchange_declare(exchange='testEx', durable='true', passive='true') + session.exchange_declare(exchange='testEx', durable=True, passive=True) self.fail("ACL should deny exchange create request with name=testEx durable=true passive=true"); except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) session = self.get_session('bob','bob') try: + session.exchange_declare(exchange='testEx', type='direct', durable=True, passive=False) + except qpid.session.SessionException, e: + print e + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange create request for testEx with any parameter other than durable=true and passive=true"); + + try: session.exchange_declare(exchange='ex1', type='direct') self.fail("ACL should deny exchange create request with name=ex1 type=direct"); - except qpid.session.SessionException, e: + except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) session = self.get_session('bob','bob') try: session.exchange_declare(exchange='myXml', type='direct') - session.queue_declare(queue='q1') except qpid.session.SessionException, e: if (530 == e.args[0].error_code): self.fail("ACL should allow exchange create request for myXml with any parameter"); try: session.exchange_query(name='myEx') - self.fail("ACL should deny queue query request for q3"); + self.fail("ACL should deny exchange query request for myEx"); except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) session = self.get_session('bob','bob') + + try: + session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk1.*') + self.fail("ACL should deny exchange bound request for myEx with queuename=q1 and routing_key='rk1.*' "); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_query(name='amq.topic') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange query request for exchange='amq.topic'"); try: + session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk2.*') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange bound request for myEx with queuename=q1 and binding_key='rk2.*'"); + + try: session.exchange_bind(exchange='myEx', queue='q1', binding_key='rk1') self.fail("ACL should deny exchange bind request with exchange='myEx' queuename='q1' bindingkey='rk1'"); except qpid.session.SessionException, e: @@ -337,10 +479,17 @@ class ACLTests(TestBase010): session = self.get_session('bob','bob') try: - session.exchange_bind(exchange='myXml', queue='q1', binding_key='x') + session.exchange_bind(exchange='myEx', queue='q1', binding_key='x') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange bind request for exchange='myEx', queue='q1', binding_key='x'"); + + try: + session.exchange_bind(exchange='myEx', queue='q2', binding_key='rk1') except qpid.session.SessionException, e: if (530 == e.args[0].error_code): - self.fail("ACL should allow exchange bind request for exchange='myXml', queue='q1', binding_key='x'"); + self.fail("ACL should allow exchange bind request for exchange='myEx', queue='q2', binding_key='rk1'"); + try: session.exchange_unbind(exchange='myEx', queue='q1', binding_key='rk1') self.fail("ACL should deny exchange unbind request with exchange='myEx' queuename='q1' bindingkey='rk1'"); @@ -349,10 +498,16 @@ class ACLTests(TestBase010): session = self.get_session('bob','bob') try: - session.exchange_unbind(exchange='myXml', queue='q1', binding_key='x') + session.exchange_unbind(exchange='myEx', queue='q1', binding_key='x') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange unbind request for exchange='myEx', queue='q1', binding_key='x'"); + + try: + session.exchange_unbind(exchange='myEx', queue='q2', binding_key='rk1') except qpid.session.SessionException, e: if (530 == e.args[0].error_code): - self.fail("ACL should allow exchange unbind request for exchange='myXml', queue='q1', binding_key='x'"); + self.fail("ACL should allow exchange unbind request for exchange='myEx', queue='q2', binding_key='rk1'"); try: session.exchange_delete(exchange='myEx') @@ -366,45 +521,161 @@ class ACLTests(TestBase010): except qpid.session.SessionException, e: if (530 == e.args[0].error_code): self.fail("ACL should allow exchange delete request for myXml"); - + + + def test_exchange_acl_deny_mode(self): + session = self.get_session('bob','bob') + session.queue_declare(queue='bar') + + """ + Test cases for exchange acl in deny mode + """ + aclf = ACLFile() + aclf.write('acl allow bob@QPID create exchange name=myEx durable=true passive=false\n') + aclf.write('acl allow bob@QPID bind exchange name=amq.topic queuename=bar routingkey=foo.*\n') + aclf.write('acl allow bob@QPID unbind exchange name=amq.topic queuename=bar routingkey=foo.*\n') + aclf.write('acl allow bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n') + aclf.write('acl allow bob@QPID delete exchange name=myEx\n') + aclf.write('acl allow guest@QPID all all\n') + aclf.write('acl deny all all') + aclf.close() + + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) + + session = self.get_session('bob','bob') + + try: + session.exchange_declare(exchange='myEx', type='direct', durable=True, passive=False) + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange create request for myEx with durable=true and passive=false"); + try: + session.exchange_declare(exchange='myEx', type='direct', durable=False) + self.fail("ACL should deny exchange create request with name=myEx durable=false"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_bind(exchange='amq.topic', queue='bar', binding_key='foo.bar') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange bind request for exchange='amq.topic', queue='bar', binding_key='foor.bar'"); + + try: + session.exchange_bind(exchange='amq.topic', queue='baz', binding_key='foo.bar') + self.fail("ACL should deny exchange bind request for exchange='amq.topic', queue='baz', binding_key='foo.bar'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_bind(exchange='amq.topic', queue='bar', binding_key='fooz.bar') + self.fail("ACL should deny exchange bind request for exchange='amq.topic', queue='bar', binding_key='fooz.bar'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_unbind(exchange='amq.topic', queue='bar', binding_key='foo.bar') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange unbind request for exchange='amq.topic', queue='bar', binding_key='foor.bar'"); + try: + session.exchange_unbind(exchange='amq.topic', queue='baz', binding_key='foo.bar') + self.fail("ACL should deny exchange unbind request for exchange='amq.topic', queue='baz', binding_key='foo.bar'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_unbind(exchange='amq.topic', queue='bar', binding_key='fooz.bar') + self.fail("ACL should deny exchange unbind request for exchange='amq.topic', queue='bar', binding_key='fooz.bar'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_query(name='amq.topic') + self.fail("ACL should deny exchange query request for amq.topic"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk2.*') + self.fail("ACL should deny exchange bound request for amq.topic with queuename=q1 and routing_key='rk2.*' "); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_query(name='myEx') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange query request for exchange='myEx'"); + + try: + session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk1.*') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange bound request for myEx with queuename=q1 and binding_key='rk1.*'"); + + try: + session.exchange_delete(exchange='myXml') + self.fail("ACL should deny exchange delete request for myXml"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + session.exchange_delete(exchange='myEx') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange delete request for myEx"); + #===================================== # ACL consume tests #===================================== - def test_consume_acl(self): + def test_consume_allow_mode(self): """ - Test various consume acl + Test cases for consume in allow mode """ aclf = ACLFile() - aclf.write('acl deny bob@QPID consume queue name=q1 durable=true\n') - aclf.write('acl deny bob@QPID consume queue name=q2 exclusive=true\n') + aclf.write('acl deny bob@QPID consume queue name=q1\n') + aclf.write('acl deny bob@QPID consume queue name=q2\n') aclf.write('acl allow all all') aclf.close() - self.reload_acl() + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) session = self.get_session('bob','bob') try: - session.queue_declare(queue='q1', durable='true') - session.queue_declare(queue='q2', exclusive='true') - session.queue_declare(queue='q3', durable='true') + session.queue_declare(queue='q1') + session.queue_declare(queue='q2') + session.queue_declare(queue='q3') except qpid.session.SessionException, e: if (530 == e.args[0].error_code): self.fail("ACL should allow create queue request"); try: session.message_subscribe(queue='q1', destination='myq1') - self.fail("ACL should deny message subscriber request for queue='q1'"); + self.fail("ACL should deny subscription for queue='q1'"); except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) session = self.get_session('bob','bob') try: session.message_subscribe(queue='q2', destination='myq1') - self.fail("ACL should deny message subscriber request for queue='q2'"); + self.fail("ACL should deny subscription for queue='q2'"); except qpid.session.SessionException, e: self.assertEqual(530,e.args[0].error_code) session = self.get_session('bob','bob') @@ -413,9 +684,51 @@ class ACLTests(TestBase010): session.message_subscribe(queue='q3', destination='myq1') except qpid.session.SessionException, e: if (530 == e.args[0].error_code): - self.fail("ACL should allow create message subscribe"); + self.fail("ACL should allow subscription for q3"); + def test_consume_deny_mode(self): + """ + Test cases for consume in allow mode + """ + aclf = ACLFile() + aclf.write('acl allow bob@QPID consume queue name=q1\n') + aclf.write('acl allow bob@QPID consume queue name=q2\n') + aclf.write('acl allow bob@QPID create queue\n') + aclf.write('acl allow guest@QPID all\n') + aclf.write('acl deny all all') + aclf.close() + + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) + + session = self.get_session('bob','bob') + + + try: + session.queue_declare(queue='q1') + session.queue_declare(queue='q2') + session.queue_declare(queue='q3') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow create queue request"); + + try: + session.message_subscribe(queue='q1', destination='myq1') + session.message_subscribe(queue='q2', destination='myq2') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow subscription for q1 and q2"); + + try: + session.message_subscribe(queue='q3', destination='myq3') + self.fail("ACL should deny subscription for queue='q3'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = self.get_session('bob','bob') + + #===================================== # ACL publish tests #===================================== @@ -431,15 +744,11 @@ class ACLTests(TestBase010): aclf.write('acl allow all all') aclf.close() - self.reload_acl() + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) session = self.get_session('bob','bob') - - try: - session.exchange_declare(exchange='myEx', type='topic') - except qpid.session.SessionException, e: - if (530 == e.args[0].error_code): - self.fail("ACL should allow exchange create request for myEx with any parameter"); props = session.delivery_properties(routing_key="rk1") @@ -458,6 +767,7 @@ class ACLTests(TestBase010): session = self.get_session('bob','bob') try: + session.exchange_declare(exchange='myEx', type='direct', durable=False) session.message_transfer(destination="myEx", message=Message(props,"Test")) except qpid.session.SessionException, e: if (530 == e.args[0].error_code): diff --git a/qpid/cpp/src/tests/client_test.cpp b/qpid/cpp/src/tests/client_test.cpp index 05b42f620c..2f5e8e5afe 100644 --- a/qpid/cpp/src/tests/client_test.cpp +++ b/qpid/cpp/src/tests/client_test.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -40,13 +40,16 @@ using namespace qpid::client; using namespace qpid::framing; using std::string; +namespace qpid { +namespace tests { + struct Args : public TestOptions { uint msgSize; bool verbose; Args() : TestOptions("Simple test of Qpid c++ client; sends and receives a single message."), msgSize(26) { - addOptions() + addOptions() ("size", optValue(msgSize, "N"), "message size") ("verbose", optValue(verbose), "print out some status messages"); } @@ -58,7 +61,7 @@ std::string generateData(uint size) { if (size < chars.length()) { return chars.substr(0, size); - } + } std::string data; for (uint i = 0; i < (size / chars.length()); i++) { data += chars; @@ -78,6 +81,10 @@ void print(const std::string& text, const Message& msg) std::cout << std::endl; } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { try { @@ -92,7 +99,7 @@ int main(int argc, char** argv) //Create and open a session on the connection through which //most functionality is exposed: Session session = connection.newSession(); - if (opts.verbose) std::cout << "Opened session." << std::endl; + if (opts.verbose) std::cout << "Opened session." << std::endl; //'declare' the exchange and the queue, which will create them @@ -116,13 +123,13 @@ int main(int argc, char** argv) // Using the SubscriptionManager, get the message from the queue. SubscriptionManager subs(session); Message msgIn = subs.get("MyQueue"); - if (msgIn.getData() == msgOut.getData()) + if (msgIn.getData() == msgOut.getData()) if (opts.verbose) std::cout << "Received the exepected message." << std::endl; //close the session & connection session.close(); if (opts.verbose) std::cout << "Closed session." << std::endl; - connection.close(); + connection.close(); if (opts.verbose) std::cout << "Closed connection." << std::endl; return 0; } catch(const std::exception& e) { diff --git a/qpid/cpp/src/tests/cluster_python_tests_failing.txt b/qpid/cpp/src/tests/cluster_python_tests_failing.txt index 337fb4a0f2..53b609942d 100644 --- a/qpid/cpp/src/tests/cluster_python_tests_failing.txt +++ b/qpid/cpp/src/tests/cluster_python_tests_failing.txt @@ -1,4 +1,5 @@ tests_0-10.management.ManagementTest.test_purge_queue +tests_0-10.management.ManagementTest.test_connection_close tests_0-10.dtx.DtxTests.test_bad_resume tests_0-10.dtx.DtxTests.test_commit_unknown tests_0-10.dtx.DtxTests.test_end diff --git a/qpid/cpp/src/tests/cluster_test.cpp b/qpid/cpp/src/tests/cluster_test.cpp index 50ca241b5d..28fcdd13ad 100644 --- a/qpid/cpp/src/tests/cluster_test.cpp +++ b/qpid/cpp/src/tests/cluster_test.cpp @@ -59,8 +59,6 @@ template <class T> ostream& operator<<(ostream& o, const std::set<T>& s) { return seqPrint(o, s); } } -QPID_AUTO_TEST_SUITE(cluster_test) - using namespace std; using namespace qpid; using namespace qpid::cluster; @@ -70,6 +68,11 @@ using namespace boost::assign; using broker::Broker; using boost::shared_ptr; +namespace qpid { +namespace tests { + +QPID_AUTO_TEST_SUITE(cluster_test) + bool durableFlag = std::getenv("STORE_LIB") != 0; void prepareArgs(ClusterFixture::Args& args, const bool durableFlag = false) { @@ -1098,3 +1101,5 @@ QPID_AUTO_TEST_CASE(testRelease) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/consume.cpp b/qpid/cpp/src/tests/consume.cpp index 3aacf8b3da..69110d151f 100644 --- a/qpid/cpp/src/tests/consume.cpp +++ b/qpid/cpp/src/tests/consume.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -36,6 +36,9 @@ using namespace qpid::client; using namespace qpid::sys; using namespace std; +namespace qpid { +namespace tests { + typedef vector<string> StringSet; struct Args : public qpid::TestOptions { @@ -46,7 +49,7 @@ struct Args : public qpid::TestOptions { bool summary; bool print; bool durable; - + Args() : count(1000), ack(0), queue("publish-consume"), declare(false), summary(false), print(false) { @@ -63,12 +66,12 @@ struct Args : public qpid::TestOptions { Args opts; -struct Client +struct Client { Connection connection; Session session; - Client() + Client() { opts.open(connection); session = connection.newSession(); @@ -85,7 +88,7 @@ struct Client settings.flowControl = FlowControl(opts.count, SubscriptionManager::UNLIMITED,false); Subscription sub = subs.subscribe(lq, opts.queue, settings); Message msg; - AbsTime begin=now(); + AbsTime begin=now(); for (size_t i = 0; i < opts.count; ++i) { msg=lq.pop(); QPID_LOG(info, "Received: " << msg.getMessageProperties().getCorrelationId()); @@ -99,7 +102,7 @@ struct Client else cout << "Time: " << secs << "s Rate: " << opts.count/secs << endl; } - ~Client() + ~Client() { try{ session.close(); @@ -110,6 +113,10 @@ struct Client } }; +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { try { diff --git a/qpid/cpp/src/tests/datagen.cpp b/qpid/cpp/src/tests/datagen.cpp index 175f14cc57..acbc07d63c 100644 --- a/qpid/cpp/src/tests/datagen.cpp +++ b/qpid/cpp/src/tests/datagen.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -25,7 +25,10 @@ #include <time.h> #include "qpid/Options.h" -struct Args : public qpid::Options +namespace qpid { +namespace tests { + +struct Args : public qpid::Options { uint count; uint minSize; @@ -34,12 +37,12 @@ struct Args : public qpid::Options uint maxChar; bool help; - Args() : qpid::Options("Random data generator"), - count(1), minSize(8), maxSize(4096), + Args() : qpid::Options("Random data generator"), + count(1), minSize(8), maxSize(4096), minChar(32), maxChar(126),//safely printable ascii chars help(false) { - addOptions() + addOptions() ("count", qpid::optValue(count, "N"), "number of data strings to generate") ("min-size", qpid::optValue(minSize, "N"), "minimum size of data string") ("max-size", qpid::optValue(maxSize, "N"), "maximum size of data string") @@ -81,6 +84,10 @@ std::string generateData(uint size, uint min, uint max) return data; } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { Args opts; diff --git a/qpid/cpp/src/tests/echotest.cpp b/qpid/cpp/src/tests/echotest.cpp index 98590e35ff..ab26dcf3fd 100644 --- a/qpid/cpp/src/tests/echotest.cpp +++ b/qpid/cpp/src/tests/echotest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -33,6 +33,9 @@ using namespace qpid::framing; using namespace qpid::sys; using namespace std; +namespace qpid { +namespace tests { + struct Args : public qpid::Options, public qpid::client::ConnectionSettings { @@ -48,7 +51,7 @@ struct Args : public qpid::Options, ("help", optValue(help), "Print this usage statement") ("count", optValue(count, "N"), "Number of messages to send") ("size", optValue(count, "N"), "Size of messages") - ("broker,b", optValue(host, "HOST"), "Broker host to connect to") + ("broker,b", optValue(host, "HOST"), "Broker host to connect to") ("port,p", optValue(port, "PORT"), "Broker port to connect to") ("username", optValue(username, "USER"), "user name for broker log in.") ("password", optValue(password, "PASSWORD"), "password for broker log in.") @@ -75,7 +78,7 @@ class Listener : public MessageListener Message request; double total, min, max; bool summary; - + public: Listener(Session& session, uint limit, bool summary); void start(uint size); @@ -92,7 +95,7 @@ void Listener::start(uint size) { session.queueDeclare(arg::queue=queue, arg::exclusive=true, arg::autoDelete=true); request.getDeliveryProperties().setRoutingKey(queue); - subscriptions.subscribe(*this, queue, SubscriptionSettings(FlowControl::unlimited(), ACCEPT_MODE_NONE)); + subscriptions.subscribe(*this, queue, SubscriptionSettings(FlowControl::unlimited(), ACCEPT_MODE_NONE)); request.getDeliveryProperties().setTimestamp(current_time()); if (size) request.setData(std::string(size, 'X')); @@ -100,7 +103,7 @@ void Listener::start(uint size) subscriptions.run(); } -void Listener::received(Message& response) +void Listener::received(Message& response) { //extract timestamp and compute latency: uint64_t sentAt = response.getDeliveryProperties().getTimestamp(); @@ -122,7 +125,11 @@ void Listener::received(Message& response) } } -int main(int argc, char** argv) +}} // namespace qpid::tests + +using namespace qpid::tests; + +int main(int argc, char** argv) { Args opts; opts.parse(argc, argv); diff --git a/qpid/cpp/src/tests/exception_test.cpp b/qpid/cpp/src/tests/exception_test.cpp index 379e957ef1..0e9a948f00 100644 --- a/qpid/cpp/src/tests/exception_test.cpp +++ b/qpid/cpp/src/tests/exception_test.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -28,6 +28,9 @@ #include "qpid/sys/Thread.h" #include "qpid/framing/reply_exceptions.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(exception_test) // FIXME aconway 2008-06-12: need to update our exception handling to @@ -49,12 +52,12 @@ struct Catcher : public Runnable { function<void ()> f; bool caught; Thread thread; - + Catcher(function<void ()> f_) : f(f_), caught(false), thread(this) {} ~Catcher() { join(); } - + void run() { - try { + try { ScopedSuppressLogging sl; // Suppress messages for expected errors. f(); } @@ -110,7 +113,7 @@ QPID_AUTO_TEST_CASE(DisconnectedListen) { Catcher<TransportFailure> runner(bind(&SubscriptionManager::run, boost::ref(fix.subs))); fix.connection.proxy.close(); runner.join(); - BOOST_CHECK_THROW(fix.session.close(), TransportFailure); + BOOST_CHECK_THROW(fix.session.close(), TransportFailure); } QPID_AUTO_TEST_CASE(NoSuchQueueTest) { @@ -120,3 +123,5 @@ QPID_AUTO_TEST_CASE(NoSuchQueueTest) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/failover_soak.cpp b/qpid/cpp/src/tests/failover_soak.cpp index 4a59e7b160..16291fe60f 100644 --- a/qpid/cpp/src/tests/failover_soak.cpp +++ b/qpid/cpp/src/tests/failover_soak.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -52,7 +52,8 @@ using namespace qpid::framing; using namespace qpid::client; - +namespace qpid { +namespace tests { typedef vector<ForkedBroker *> brokerVector; @@ -91,9 +92,9 @@ ostream& operator<< ( ostream& os, const childType& ct ) { struct child { - child ( string & name, pid_t pid, childType type ) + child ( string & name, pid_t pid, childType type ) : name(name), pid(pid), retval(-999), status(RUNNING), type(type) - { + { gettimeofday ( & startTime, 0 ); } @@ -108,7 +109,7 @@ struct child void - setType ( childType t ) + setType ( childType t ) { type = t; } @@ -127,7 +128,7 @@ struct child struct children : public vector<child *> -{ +{ void add ( string & name, pid_t pid, childType type ) @@ -136,7 +137,7 @@ struct children : public vector<child *> } - child * + child * get ( pid_t pid ) { vector<child *>::iterator i; @@ -156,7 +157,7 @@ struct children : public vector<child *> { if ( verbosity > 1 ) { - cerr << "children::exited warning: Can't find child with pid " + cerr << "children::exited warning: Can't find child with pid " << pid << endl; } @@ -193,7 +194,7 @@ struct children : public vector<child *> << endl; return (*i)->retval; } - + return 0; } @@ -227,11 +228,11 @@ struct children : public vector<child *> children allMyChildren; -void -childExit ( int ) +void +childExit ( int ) { - int childReturnCode; - pid_t pid = waitpid ( 0, & childReturnCode, WNOHANG); + int childReturnCode; + pid_t pid = waitpid ( 0, & childReturnCode, WNOHANG); if ( pid > 0 ) allMyChildren.exited ( pid, childReturnCode ); @@ -271,10 +272,10 @@ printBrokers ( brokerVector & brokers ) { cout << "Broker List ------------ size: " << brokers.size() << "\n"; for ( brokerVector::iterator i = brokers.begin(); i != brokers.end(); ++ i) { - cout << "pid: " - << (*i)->getPID() - << " port: " - << (*i)->getPort() + cout << "pid: " + << (*i)->getPID() + << " port: " + << (*i)->getPort() << endl; } cout << "end Broker List ------------\n"; @@ -294,7 +295,7 @@ wait_for_newbie ( ) if ( ! newbie ) return true; - try + try { Connection connection; connection.open ( "127.0.0.1", newbie_port ); @@ -304,8 +305,8 @@ wait_for_newbie ( ) } catch ( const std::exception& error ) { - std::cerr << "wait_for_newbie error: " - << error.what() + std::cerr << "wait_for_newbie error: " + << error.what() << endl; return false; } @@ -321,7 +322,7 @@ startNewBroker ( brokerVector & brokers, char const * moduleOrDir, string const clusterName, int verbosity, - int durable ) + int durable ) { static int brokerId = 0; stringstream path, prefix; @@ -354,8 +355,8 @@ startNewBroker ( brokerVector & brokers, ForkedBroker * broker = newbie; if ( verbosity > 0 ) - std::cerr << "new broker created: pid == " - << broker->getPID() + std::cerr << "new broker created: pid == " + << broker->getPID() << " log-prefix == " << "soak-" << brokerId << endl; @@ -382,8 +383,8 @@ killFrontBroker ( brokerVector & brokers, int verbosity ) catch ( const exception& error ) { if ( verbosity > 0 ) { - cout << "error killing broker: " - << error.what() + cout << "error killing broker: " + << error.what() << endl; } @@ -399,14 +400,14 @@ killFrontBroker ( brokerVector & brokers, int verbosity ) /* - * The optional delay is to avoid killing newbie brokers that have just + * The optional delay is to avoid killing newbie brokers that have just * been added and are still in the process of updating. This causes * spurious, test-generated errors that scare everybody. */ void killAllBrokers ( brokerVector & brokers, int delay ) { - if ( delay > 0 ) + if ( delay > 0 ) { std::cerr << "Killing all brokers after delay of " << delay << endl; sleep ( delay ); @@ -414,8 +415,8 @@ killAllBrokers ( brokerVector & brokers, int delay ) for ( uint i = 0; i < brokers.size(); ++ i ) try { brokers[i]->kill(9); } - catch ( const exception& error ) - { + catch ( const exception& error ) + { std::cerr << "killAllBrokers Warning: exception during kill on broker " << i << " " @@ -429,21 +430,21 @@ killAllBrokers ( brokerVector & brokers, int delay ) pid_t -runDeclareQueuesClient ( brokerVector brokers, +runDeclareQueuesClient ( brokerVector brokers, char const * host, char const * path, int verbosity, int durable - ) + ) { string name("declareQueues"); int port = brokers[0]->getPort ( ); if ( verbosity > 1 ) - cout << "startDeclareQueuesClient: host: " - << host - << " port: " - << port + cout << "startDeclareQueuesClient: host: " + << host + << " port: " + << port << endl; stringstream portSs; portSs << port; @@ -474,12 +475,12 @@ runDeclareQueuesClient ( brokerVector brokers, pid_t -startReceivingClient ( brokerVector brokers, +startReceivingClient ( brokerVector brokers, char const * host, char const * receiverPath, char const * reportFrequency, int verbosity - ) + ) { string name("receiver"); int port = brokers[0]->getPort ( ); @@ -521,14 +522,14 @@ startReceivingClient ( brokerVector brokers, pid_t -startSendingClient ( brokerVector brokers, +startSendingClient ( brokerVector brokers, char const * host, char const * senderPath, char const * nMessages, char const * reportFrequency, int verbosity, int durability - ) + ) { string name("sender"); int port = brokers[0]->getPort ( ); @@ -581,13 +582,14 @@ startSendingClient ( brokerVector brokers, #define HANGING 7 #define ERROR_KILLING_BROKER 8 +}} // namespace qpid::tests -// If you want durability, use the "dir" option of "moduleOrDir" . - +using namespace qpid::tests; +// If you want durability, use the "dir" option of "moduleOrDir" . int -main ( int argc, char const ** argv ) -{ +main ( int argc, char const ** argv ) +{ if ( argc != 9 ) { cerr << "Usage: " << argv[0] @@ -627,10 +629,10 @@ main ( int argc, char const ** argv ) int nBrokers = 3; for ( int i = 0; i < nBrokers; ++ i ) { startNewBroker ( brokers, - moduleOrDir, + moduleOrDir, clusterName, verbosity, - durable ); + durable ); } @@ -639,7 +641,7 @@ main ( int argc, char const ** argv ) // Run the declareQueues child. int childStatus; - pid_t dqClientPid = + pid_t dqClientPid = runDeclareQueuesClient ( brokers, host, declareQueuesPath, verbosity, durable ); if ( -1 == dqClientPid ) { cerr << "END_OF_TEST ERROR_START_DECLARE_1\n"; @@ -658,8 +660,8 @@ main ( int argc, char const ** argv ) // Start the receiving client. pid_t receivingClientPid = - startReceivingClient ( brokers, - host, + startReceivingClient ( brokers, + host, receiverPath, reportFrequency, verbosity ); @@ -670,10 +672,10 @@ main ( int argc, char const ** argv ) // Start the sending client. - pid_t sendingClientPid = - startSendingClient ( brokers, - host, - senderPath, + pid_t sendingClientPid = + startSendingClient ( brokers, + host, + senderPath, nMessages, reportFrequency, verbosity, @@ -688,10 +690,10 @@ main ( int argc, char const ** argv ) maxSleep = 4; - for ( int totalBrokers = 3; - totalBrokers < maxBrokers; - ++ totalBrokers - ) + for ( int totalBrokers = 3; + totalBrokers < maxBrokers; + ++ totalBrokers + ) { if ( verbosity > 0 ) cout << totalBrokers << " brokers have been added to the cluster.\n\n\n"; @@ -722,14 +724,14 @@ main ( int argc, char const ** argv ) cout << "Starting new broker.\n\n"; startNewBroker ( brokers, - moduleOrDir, + moduleOrDir, clusterName, verbosity, - durable ); - + durable ); + if ( verbosity > 1 ) printBrokers ( brokers ); - + // If all children have exited, quit. int unfinished = allMyChildren.unfinished(); if ( ! unfinished ) { diff --git a/qpid/cpp/src/tests/federation.py b/qpid/cpp/src/tests/federation.py index 174932adf8..aa68e8198b 100755 --- a/qpid/cpp/src/tests/federation.py +++ b/qpid/cpp/src/tests/federation.py @@ -503,6 +503,20 @@ class FederationTests(TestBase010): self.assertEqual(result.status, 0) result = bridge2.close() self.assertEqual(result.status, 0) + + # extra check: verify we don't leak bridge objects - keep the link + # around and verify the bridge count has gone to zero + + attempts = 0 + bridgeCount = len(qmf.getObjects(_class="bridge")) + while bridgeCount > 0: + attempts += 1 + if attempts >= 5: + self.fail("Bridges didn't clean up") + return + sleep(1) + bridgeCount = len(qmf.getObjects(_class="bridge")) + result = link.close() self.assertEqual(result.status, 0) @@ -559,8 +573,13 @@ class FederationTests(TestBase010): result = bridge.close() self.assertEqual(result.status, 0) - result = bridge2.close() - self.assertEqual(result.status, 0) + + # Extra test: don't explicitly close() bridge2. When the link is closed, + # it should clean up bridge2 automagically. verify_cleanup() will detect + # if bridge2 isn't cleaned up and will fail the test. + # + #result = bridge2.close() + #self.assertEqual(result.status, 0) result = link.close() self.assertEqual(result.status, 0) diff --git a/qpid/cpp/src/tests/latencytest.cpp b/qpid/cpp/src/tests/latencytest.cpp index e1c47eab05..a205ef6c7c 100644 --- a/qpid/cpp/src/tests/latencytest.cpp +++ b/qpid/cpp/src/tests/latencytest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -40,6 +40,9 @@ using namespace qpid::client; using namespace qpid::sys; using std::string; +namespace qpid { +namespace tests { + typedef std::vector<std::string> StringSet; struct Args : public qpid::TestOptions { @@ -64,7 +67,7 @@ struct Args : public qpid::TestOptions { durable(false), base("latency-test"), singleConnect(false) { - addOptions() + addOptions() ("size", optValue(size, "N"), "message size") ("concurrentTests", optValue(concurrentConnections, "N"), "number of concurrent test setups, will create another publisher,\ @@ -73,9 +76,9 @@ struct Args : public qpid::TestOptions { ("count", optValue(count, "N"), "number of messages to send") ("rate", optValue(rate, "N"), "target message rate (causes count to be ignored)") ("sync", optValue(sync), "send messages synchronously") - ("report-frequency", optValue(reportFrequency, "N"), + ("report-frequency", optValue(reportFrequency, "N"), "number of milliseconds to wait between reports (ignored unless rate specified)") - ("time-limit", optValue(timeLimit, "N"), + ("time-limit", optValue(timeLimit, "N"), "test duration, in seconds") ("prefetch", optValue(prefetch, "N"), "prefetch count (0 implies no flow control, and no acking)") ("ack", optValue(ack, "N"), "Ack frequency in messages (defaults to half the prefetch value)") @@ -98,7 +101,7 @@ uint64_t current_time() return t; } -struct Stats +struct Stats { Mutex lock; uint count; @@ -132,7 +135,7 @@ public: }; class Receiver : public Client, public MessageListener -{ +{ SubscriptionManager mgr; uint count; Stats& stats; @@ -168,7 +171,7 @@ class Test Receiver receiver; Sender sender; AbsTime begin; - + public: Test(const string& q) : queue(q), receiver(queue, stats), sender(queue, receiver), begin(now()) {} void start(); @@ -186,7 +189,7 @@ Client::Client(const string& q) : queue(q) connection = &localConnection; opts.open(localConnection); } - session = connection->newSession(); + session = connection->newSession(); } void Client::start() @@ -235,7 +238,7 @@ Receiver::Receiver(const string& q, Stats& s) : Client(q), mgr(session), count(0 settings.acceptMode = ACCEPT_MODE_NONE; settings.flowControl = FlowControl::unlimited(); } - mgr.subscribe(*this, queue, settings); + mgr.subscribe(*this, queue, settings); } void Receiver::test() @@ -283,7 +286,7 @@ void Stats::print() if (!opts.csv) { if (count) { std::cout << "Latency(ms): min=" << minLatency << ", max=" << - maxLatency << ", avg=" << aux_avg; + maxLatency << ", avg=" << aux_avg; } else { std::cout << "Stalled: no samples for interval"; } @@ -368,7 +371,7 @@ void Sender::sendByRate() Duration delay(sentAt, waitTill); if (delay < 0) ++missedRate; - else + else sys::usleep(delay / TIME_USEC); if (timeLimit != 0 && Duration(start, now()) > timeLimit) { session.sync(); @@ -382,7 +385,7 @@ string Sender::generateData(uint size) { if (size < chars.length()) { return chars.substr(0, size); - } + } std::string data; for (uint i = 0; i < (size / chars.length()); i++) { data += chars; @@ -392,35 +395,39 @@ string Sender::generateData(uint size) } -void Test::start() -{ - receiver.start(); +void Test::start() +{ + receiver.start(); begin = AbsTime(now()); - sender.start(); + sender.start(); } -void Test::join() -{ - sender.join(); - receiver.join(); +void Test::join() +{ + sender.join(); + receiver.join(); AbsTime end = now(); Duration time(begin, end); double msecs(time / TIME_MSEC); if (!opts.csv) { - std::cout << "Sent " << receiver.getCount() << " msgs through " << queue + std::cout << "Sent " << receiver.getCount() << " msgs through " << queue << " in " << msecs << "ms (" << (receiver.getCount() * 1000 / msecs) << " msgs/s) "; } stats.print(); std::cout << std::endl; } -void Test::report() -{ +void Test::report() +{ stats.print(); std::cout << std::endl; stats.reset(); } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { try { diff --git a/qpid/cpp/src/tests/logging.cpp b/qpid/cpp/src/tests/logging.cpp index 00e1d7de85..5cb563c7d3 100644 --- a/qpid/cpp/src/tests/logging.cpp +++ b/qpid/cpp/src/tests/logging.cpp @@ -37,6 +37,9 @@ #include <time.h> +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(loggingTestSuite) using namespace std; @@ -106,7 +109,7 @@ struct TestOutput : public Logger::Output { TestOutput(Logger& l) { l.output(std::auto_ptr<Logger::Output>(this)); } - + void log(const Statement& s, const string& m) { msg.push_back(m); stmt.push_back(s); @@ -117,7 +120,7 @@ struct TestOutput : public Logger::Output { using boost::assign::list_of; QPID_AUTO_TEST_CASE(testLoggerOutput) { - Logger l; + Logger l; l.clear(); l.select(Selector(debug)); Statement s=QPID_LOG_STATEMENT_INIT(debug); @@ -174,7 +177,7 @@ QPID_AUTO_TEST_CASE(testLoggerFormat) { l.format(Logger::FUNCTION); QPID_LOG(critical, "foo"); BOOST_CHECK_EQUAL(string(BOOST_CURRENT_FUNCTION) + ": foo\n", out->last()); - + l.format(Logger::LEVEL); QPID_LOG(critical, "foo"); BOOST_CHECK_EQUAL("critical foo\n", out->last()); @@ -228,12 +231,12 @@ clock_t timeLoop(int times, int (*fp)()) { // Overhead test disabled because it consumes a ton of CPU and takes // forever under valgrind. Not friendly for regular test runs. -// +// #if 0 QPID_AUTO_TEST_CASE(testOverhead) { // Ensure that the ratio of CPU time for an incrementing loop // with and without disabled log statements is in acceptable limits. - // + // int times=100000000; clock_t noLog=timeLoop(times, count); clock_t withLog=timeLoop(times, loggedCount); @@ -242,9 +245,9 @@ QPID_AUTO_TEST_CASE(testOverhead) { // NB: in initial tests the ratio was consistently below 1.5, // 2.5 is reasonable and should avoid spurios failures // due to machine load. - // - BOOST_CHECK_SMALL(ratio, 2.5); -} + // + BOOST_CHECK_SMALL(ratio, 2.5); +} #endif // 0 Statement statement( @@ -290,7 +293,7 @@ QPID_AUTO_TEST_CASE(testOptionsParse) { } QPID_AUTO_TEST_CASE(testOptionsDefault) { - Options opts(""); + qpid::log::Options opts(""); #ifdef _WIN32 qpid::log::windows::SinkOptions sinks("test"); #else @@ -328,10 +331,10 @@ QPID_AUTO_TEST_CASE(testSelectorFromOptions) { QPID_AUTO_TEST_CASE(testLoggerStateure) { Logger& l=Logger::instance(); ScopedSuppressLogging ls(l); - Options opts("test"); + qpid::log::Options opts("test"); const char* argv[]={ 0, - "--log-time", "no", + "--log-time", "no", "--log-source", "yes", "--log-to-stderr", "no", "--log-to-file", "logging.tmp", @@ -352,7 +355,7 @@ QPID_AUTO_TEST_CASE(testLoggerStateure) { QPID_AUTO_TEST_CASE(testQuoteNonPrintable) { Logger& l=Logger::instance(); ScopedSuppressLogging ls(l); - Options opts("test"); + qpid::log::Options opts("test"); opts.time=false; #ifdef _WIN32 qpid::log::windows::SinkOptions *sinks = @@ -367,7 +370,7 @@ QPID_AUTO_TEST_CASE(testQuoteNonPrintable) { char s[] = "null\0tab\tspace newline\nret\r\x80\x99\xff"; string str(s, sizeof(s)); - QPID_LOG(critical, str); + QPID_LOG(critical, str); ifstream log("logging.tmp"); string line; getline(log, line, '\0'); @@ -378,3 +381,5 @@ QPID_AUTO_TEST_CASE(testQuoteNonPrintable) { } QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/perftest.cpp b/qpid/cpp/src/tests/perftest.cpp index d383e0eb80..88d9fd15cb 100644 --- a/qpid/cpp/src/tests/perftest.cpp +++ b/qpid/cpp/src/tests/perftest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -49,6 +49,9 @@ using namespace sys; using boost::lexical_cast; using boost::bind; +namespace qpid { +namespace tests { + enum Mode { SHARED, FANOUT, TOPIC }; const char* modeNames[] = { "shared", "fanout", "topic" }; @@ -105,9 +108,9 @@ struct Opts : public TestOptions { bool commitAsync; static const std::string helpText; - + Opts() : - TestOptions(helpText), + 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), @@ -136,16 +139,16 @@ struct Opts : public TestOptions { ("nsubs", optValue(subs, "N"), "Create N subscribers.") ("sub-ack", optValue(ack, "N"), "N>0: Subscriber acks batches of N.\n" "N==0: Subscriber uses unconfirmed mode") - + ("qt", optValue(qt, "N"), "Create N queues or topics.") ("single-connection", optValue(singleConnect, "yes|no"), "Use one connection for multiple sessions.") - + ("iterations", optValue(iterations, "N"), "Desired number of iterations of the test.") ("summary,s", optValue(summary), "Summary output: pubs/sec subs/sec transfers/sec Mbytes/sec") ("queue-max-count", optValue(queueMaxCount, "N"), "queue policy: count to trigger 'flow to disk'") ("queue-max-size", optValue(queueMaxSize, "N"), "queue policy: accumulated size to trigger 'flow to disk'") - ("base-name", optValue(baseName, "NAME"), "base name used for queues or topics") + ("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") @@ -171,7 +174,7 @@ struct Opts : public TestOptions { count += subs - (count % subs); cout << "WARNING: Adjusted --count to " << count << " the nearest multiple of --nsubs" << endl; - } + } totalPubs = pubs*qt; totalSubs = subs*qt; subQuota = (pubs*count)/subs; @@ -258,7 +261,7 @@ struct Client : public Runnable { }; struct Setup : public Client { - + void queueInit(string name, bool durable=false, const framing::FieldTable& settings=framing::FieldTable()) { session.queueDeclare(arg::queue=name, arg::durable=durable, arg::arguments=settings); session.queuePurge(arg::queue=name); @@ -278,7 +281,7 @@ struct Setup : public Client { for (size_t i = 0; i < opts.qt; ++i) { ostringstream qname; qname << opts.baseName << i; - queueInit(qname.str(), opts.durable || opts.queueDurable, settings); + queueInit(qname.str(), opts.durable || opts.queueDurable, settings); } } } @@ -303,7 +306,7 @@ class Stats { public: Stats() : sum(0) {} - + // Functor to collect rates. void operator()(const string& data) { try { @@ -314,7 +317,7 @@ class Stats { throw Exception("Bad report: "+data); } } - + double mean() const { return sum/values.size(); } @@ -331,7 +334,7 @@ class Stats { } return sqrt(ssq/(values.size()-1)); } - + ostream& print(ostream& out) { ostream_iterator<double> o(out, "\n"); copy(values.begin(), values.end(), o); @@ -341,11 +344,11 @@ class Stats { return out << endl; } }; - + // Manage control queues, collect and print reports. struct Controller : public Client { - + SubscriptionManager subs; Controller() : subs(session) {} @@ -354,7 +357,7 @@ struct Controller : public Client { void process(size_t n, string queue, boost::function<void (const string&)> msgFn) { - if (!opts.summary) + if (!opts.summary) cout << "Processing " << n << " messages from " << queue << " " << flush; LocalQueue lq; @@ -370,8 +373,8 @@ struct Controller : public Client { void process(size_t n, LocalQueue lq, string queue, boost::function<void (const string&)> msgFn) { - session.messageFlow(queue, 0, n); - if (!opts.summary) + session.messageFlow(queue, 0, n); + if (!opts.summary) cout << "Processing " << n << " messages from " << queue << " " << flush; for (size_t i = 0; i < n; ++i) { @@ -386,7 +389,7 @@ struct Controller : public Client { cout << "Sending " << data << " " << n << " times to " << queue << endl; Message msg(data, queue); - for (size_t i = 0; i < n; ++i) + for (size_t i = 0; i < n; ++i) session.messageTransfer(arg::content=msg, arg::acceptMode=1); } @@ -419,7 +422,7 @@ struct Controller : public Client { process(opts.totalPubs, pubDone, fqn("pub_done"), boost::ref(pubRates)); process(opts.totalSubs, subDone, fqn("sub_done"), boost::ref(subRates)); - AbsTime end=now(); + AbsTime end=now(); double time=secs(start, end); double txrate=opts.transfers/time; @@ -469,12 +472,12 @@ struct PublishThread : public Client { string routingKey; PublishThread() {}; - + PublishThread(string key, string dest=string()) { destination=dest; routingKey=key; } - + void run() { // Publisher try { string data; @@ -492,7 +495,7 @@ struct PublishThread : public Client { } } else { size_t msgSize=max(opts.size, sizeof(size_t)); - data = string(msgSize, 'X'); + data = string(msgSize, 'X'); } Message msg(data, routingKey); @@ -500,21 +503,21 @@ struct PublishThread : public Client { msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); - if (opts.txPub){ + if (opts.txPub){ session.txSelect(); } SubscriptionManager subs(session); LocalQueue lq; - subs.setFlowControl(1, SubscriptionManager::UNLIMITED, true); - subs.subscribe(lq, fqn("pub_start")); - + subs.setFlowControl(1, SubscriptionManager::UNLIMITED, true); + subs.subscribe(lq, fqn("pub_start")); + for (size_t j = 0; j < opts.iterations; ++j) { expect(lq.pop().getData(), "start"); AbsTime start=now(); for (size_t i=0; i<opts.count; i++) { // Stamp the iteration into the message data, avoid // any heap allocation. - const_cast<std::string&>(msg.getData()).replace(offset, sizeof(size_t), + const_cast<std::string&>(msg.getData()).replace(offset, sizeof(size_t), reinterpret_cast<const char*>(&i), sizeof(size_t)); if (opts.syncPub) { sync(session).messageTransfer( @@ -540,7 +543,7 @@ struct PublishThread : public Client { if (opts.confirm) session.sync(); AbsTime end=now(); double time=secs(start,end); - + // Send result to controller. Message report(lexical_cast<string>(opts.count/time), fqn("pub_done")); session.messageTransfer(arg::content=report, arg::acceptMode=1); @@ -561,7 +564,7 @@ struct SubscribeThread : public Client { string queue; SubscribeThread() {} - + SubscribeThread(string q) { queue = q; } SubscribeThread(string key, string ex) { @@ -586,7 +589,7 @@ struct SubscribeThread : public Client { } void run() { // Subscribe - try { + try { if (opts.txSub) sync(session).txSelect(); SubscriptionManager subs(session); SubscriptionSettings settings; @@ -606,15 +609,15 @@ struct SubscribeThread : public Client { if (opts.iterations > 1) { subs.subscribe(iterationControl, fqn("sub_iteration"), SubscriptionSettings(FlowControl::messageCredit(0))); } - + for (size_t j = 0; j < opts.iterations; ++j) { if (j > 0) { //need to wait here until all subs are done - session.messageFlow(fqn("sub_iteration"), 0, 1); + session.messageFlow(fqn("sub_iteration"), 0, 1); iterationControl.pop(); //need to allocate some more credit for subscription - session.messageFlow(queue, 0, opts.subQuota); + session.messageFlow(queue, 0, opts.subQuota); } Message msg; AbsTime start=now(); @@ -627,7 +630,7 @@ struct SubscribeThread : public Client { } if (opts.intervalSub) qpid::sys::usleep(opts.intervalSub*1000); - // TODO aconway 2007-11-23: check message order for. + // TODO aconway 2007-11-23: check message order for. // multiple publishers. Need an array of counters, // one per publisher and a publisher ID in the // message. Careful not to introduce a lot of overhead @@ -664,6 +667,10 @@ struct SubscribeThread : public Client { } }; +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { int exitCode = 0; boost::ptr_vector<Client> subs(opts.subs); diff --git a/qpid/cpp/src/tests/publish.cpp b/qpid/cpp/src/tests/publish.cpp index 34c2b8fefc..3f456e7588 100644 --- a/qpid/cpp/src/tests/publish.cpp +++ b/qpid/cpp/src/tests/publish.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -36,6 +36,9 @@ using namespace qpid::client; using namespace qpid::sys; using namespace std; +namespace qpid { +namespace tests { + typedef vector<string> StringSet; struct Args : public qpid::TestOptions { @@ -61,12 +64,12 @@ struct Args : public qpid::TestOptions { Args opts; -struct Client +struct Client { Connection connection; AsyncSession session; - Client() + Client() { opts.open(connection); session = connection.newSession(); @@ -75,7 +78,7 @@ struct Client // Cheap hex calculation, avoid expensive ostrstream and string // creation to generate correlation ids in message loop. char hex(char i) { return i<10 ? '0'+i : 'A'+i-10; } - void hex(char i, string& s) { + void hex(char i, string& s) { s[0]=hex(i>>24); s[1]=hex(i>>16); s[2]=hex(i>>8); s[3]=i; } @@ -86,7 +89,7 @@ struct Client string correlationId = "0000"; if (opts.durable) msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); - + for (uint i = 0; i < opts.count; i++) { if (opts.id) { hex(i+1, correlationId); @@ -103,7 +106,7 @@ struct Client else cout << "Time: " << secs << "s Rate: " << opts.count/secs << endl; } - ~Client() + ~Client() { try{ session.close(); @@ -114,6 +117,10 @@ struct Client } }; +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { try { diff --git a/qpid/cpp/src/tests/qpid_ping.cpp b/qpid/cpp/src/tests/qpid_ping.cpp index cc07ade7bb..b046fdf54b 100644 --- a/qpid/cpp/src/tests/qpid_ping.cpp +++ b/qpid/cpp/src/tests/qpid_ping.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -38,6 +38,9 @@ using namespace qpid::framing; using namespace qpid::client; using namespace qpid; +namespace qpid { +namespace tests { + struct PingOptions : public qpid::TestOptions { int timeout; // Timeout in seconds. bool quiet; // No output @@ -58,7 +61,7 @@ class Ping : public Runnable { public: Ping() : status(WAITING) {} - + void run() { try { opts.open(connection); @@ -100,6 +103,9 @@ class Ping : public Runnable { } }; +}} // namespace qpid::tests + +using namespace qpid::tests; int main(int argc, char** argv) { try { diff --git a/qpid/cpp/src/tests/qpid_stream.cpp b/qpid/cpp/src/tests/qpid_stream.cpp new file mode 100644 index 0000000000..8e02baa8a0 --- /dev/null +++ b/qpid/cpp/src/tests/qpid_stream.cpp @@ -0,0 +1,163 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <qpid/messaging/Connection.h> +#include <qpid/messaging/Message.h> +#include <qpid/messaging/Receiver.h> +#include <qpid/messaging/Sender.h> +#include <qpid/messaging/Session.h> +#include <qpid/sys/Runnable.h> +#include <qpid/sys/Thread.h> +#include <qpid/sys/Time.h> +#include <qpid/Options.h> +#include <iostream> +#include <string> + +using namespace qpid::messaging; +using namespace qpid::sys; + +struct Args : public qpid::Options +{ + std::string url; + std::string address; + uint rate; + bool durable; + + Args() : url("amqp:tcp:127.0.0.1:5672"), address("test-queue"), rate(1000), durable(false) + { + addOptions() + ("url", qpid::optValue(url, "URL"), "Url to connect to.") + ("address", qpid::optValue(address, "ADDRESS"), "Address to stream messages through.") + ("rate", qpid::optValue(rate, "msgs/sec"), "Rate at which to stream messages.") + ("durable", qpid::optValue(durable, "true|false"), "Mark messages as durable."); + } +}; + +Args opts; + +const std::string TIMESTAMP = "ts"; + +uint64_t timestamp(const AbsTime& time) +{ + Duration t(time); + return t; +} + +struct Client : Runnable +{ + virtual ~Client() {} + virtual void doWork(Session&) = 0; + + void run() + { + try { + Connection connection = Connection::open(opts.url); + Session session = connection.newSession(); + doWork(session); + session.close(); + connection.close(); + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + } + + Thread thread; + + void start() { thread = Thread(this); } + void join() { thread.join(); } +}; + +struct Publish : Client +{ + void doWork(Session& session) + { + Sender sender = session.createSender(opts.address); + Message msg; + uint64_t interval = TIME_SEC / opts.rate; + uint64_t sent = 0, missedRate = 0; + AbsTime start = now(); + while (true) { + AbsTime sentAt = now(); + msg.getHeaders()[TIMESTAMP] = timestamp(sentAt); + sender.send(msg); + ++sent; + AbsTime waitTill(start, sent*interval); + Duration delay(sentAt, waitTill); + if (delay < 0) { + ++missedRate; + } else { + qpid::sys::usleep(delay / TIME_USEC); + } + } + } +}; + +struct Consume : Client +{ + void doWork(Session& session) + { + Message msg; + uint64_t received = 0; + double minLatency = std::numeric_limits<double>::max(); + double maxLatency = 0; + double totalLatency = 0; + Receiver receiver = session.createReceiver(opts.address); + while (receiver.fetch(msg)) { + session.acknowledge();//TODO: add batching option + ++received; + //calculate latency + uint64_t receivedAt = timestamp(now()); + uint64_t sentAt = msg.getHeaders()[TIMESTAMP].asUint64(); + double latency = ((double) (receivedAt - sentAt)) / TIME_MSEC; + + //update avg, min & max + minLatency = std::min(minLatency, latency); + maxLatency = std::max(maxLatency, latency); + totalLatency += latency; + + if (received % opts.rate == 0) { + std::cout << "count=" << received + << ", avg=" << (totalLatency/received) + << ", min=" << minLatency + << ", max=" << maxLatency << std::endl; + } + } + } +}; + +int main(int argc, char** argv) +{ + try { + opts.parse(argc, argv); + Publish publish; + Consume consume; + publish.start(); + consume.start(); + consume.join(); + publish.join(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/src/tests/qrsh.cpp b/qpid/cpp/src/tests/qrsh.cpp index 2d71b600d5..0cb52b6b05 100644 --- a/qpid/cpp/src/tests/qrsh.cpp +++ b/qpid/cpp/src/tests/qrsh.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -37,11 +37,13 @@ using namespace qpid::framing; using namespace std; +namespace qpid { +namespace tests { class ResponseListener : public MessageListener { public : - + int exitCode; ResponseListener ( SubscriptionManager & subscriptions ) @@ -50,7 +52,7 @@ class ResponseListener : public MessageListener { } - virtual void + virtual void received ( Message & message ) { char first_word[1000]; @@ -66,9 +68,9 @@ class ResponseListener : public MessageListener if ( ! strcmp ( first_word, "get_response" ) ) { // The remainder of the message is the file we requested. - fprintf ( stdout, - "%s", - message.getData().c_str() + strlen("get_response" ) + fprintf ( stdout, + "%s", + message.getData().c_str() + strlen("get_response" ) ); subscriptions.cancel(message.getDestination()); } @@ -76,12 +78,13 @@ class ResponseListener : public MessageListener private : - + SubscriptionManager & subscriptions; }; +}} // namespace qpid::tests - +using namespace qpid::tests; /* * argv[1] host @@ -90,8 +93,8 @@ class ResponseListener : public MessageListener * argv[4] command name * argv[5..N] args to the command */ -int -main ( int argc, char ** argv ) +int +main ( int argc, char ** argv ) { const char* host = argv[1]; int port = atoi(argv[2]); @@ -99,14 +102,14 @@ main ( int argc, char ** argv ) Connection connection; - try + try { connection.open ( host, port ); Session session = connection.newSession ( ); // Make a queue and bind it to fanout. string myQueue = session.getId().getName(); - + session.queueDeclare ( arg::queue=myQueue, arg::exclusive=true, arg::autoDelete=true @@ -136,7 +139,7 @@ main ( int argc, char ** argv ) response_command = true; // Send the payload message. - // Skip "qrsh host_name port" + // Skip "qrsh host_name port" Message message; stringstream ss; for ( int i = 3; i < argc; ++ i ) @@ -144,7 +147,7 @@ main ( int argc, char ** argv ) message.setData ( ss.str() ); - session.messageTransfer(arg::content=message, + session.messageTransfer(arg::content=message, arg::destination="amq.fanout"); if ( response_command ) @@ -153,8 +156,8 @@ main ( int argc, char ** argv ) session.close(); connection.close(); return responseListener.exitCode; - } - catch ( exception const & e) + } + catch ( exception const & e) { cerr << e.what() << endl; } diff --git a/qpid/cpp/src/tests/qrsh_server.cpp b/qpid/cpp/src/tests/qrsh_server.cpp index 4b80212eae..f1163ba479 100644 --- a/qpid/cpp/src/tests/qrsh_server.cpp +++ b/qpid/cpp/src/tests/qrsh_server.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -43,7 +43,8 @@ using namespace qpid::framing; using namespace std; - +namespace qpid { +namespace tests { int mrand ( int max_desired_val ) @@ -54,7 +55,7 @@ mrand ( int max_desired_val ) -char * +char * file2str ( char const * file_name ) { FILE * fp = fopen ( file_name, "r" ); @@ -71,9 +72,9 @@ file2str ( char const * file_name ) if ( ! content ) { - fprintf ( stderr, - "file2str error: can't malloc %d bytes.\n", - (int)file_len + fprintf ( stderr, + "file2str error: can't malloc %d bytes.\n", + (int)file_len ); return 0; } @@ -123,9 +124,9 @@ class QrshServer : public MessageListener bool myMessage ( Message const & message ); /* ---------------------------------------------- - * Special Commands + * Special Commands * These are commands that the qrsh_server executes - * directly, rather than through a child process + * directly, rather than through a child process * instance of qrsh_run. */ void runCommand ( Message const & message ); @@ -157,9 +158,9 @@ class QrshServer : public MessageListener char const * skipWord ( char const * s ); - void string_replaceAll ( string & str, - string & target, - string & replacement + void string_replaceAll ( string & str, + string & target, + string & replacement ); @@ -186,12 +187,12 @@ class QrshServer : public MessageListener -QrshServer::QrshServer ( SubscriptionManager & subs, +QrshServer::QrshServer ( SubscriptionManager & subs, char const * name, char const * qrsh_run_path, char const * host, int port - ) + ) : subscriptions ( subs ), name ( name ), qrsh_run_path ( qrsh_run_path ), @@ -202,11 +203,11 @@ QrshServer::QrshServer ( SubscriptionManager & subs, { data_dir << "/tmp/qrsh_" << getpid(); - + if(mkdir ( data_dir.str().c_str(), 0777 ) ) { - fprintf ( stderr, - "QrshServer::QrshServer error: can't mkdir |%s|\n", + fprintf ( stderr, + "QrshServer::QrshServer error: can't mkdir |%s|\n", data_dir.str().c_str() ); exit ( 1 ); @@ -239,21 +240,21 @@ QrshServer::start ( ) << name; send ( announcement_data.str() ); - + saidHello = true; } -void +void QrshServer::send ( string const & content ) { try { Message message; message.setData ( content ); - + Connection connection; connection.open ( host, port ); Session session = connection.newSession ( ); @@ -289,7 +290,7 @@ QrshServer::sayHello ( ) -void +void QrshServer::sayName ( ) { fprintf ( stderr, "My name is: |%s|\n", name.c_str() ); @@ -343,7 +344,7 @@ QrshServer::getStraw ( Message const & message ) break; } } - + if ( i_win && (ties <= 0) ) { myStraw = 0; @@ -364,10 +365,10 @@ QrshServer::getStraw ( Message const & message ) /* * "APB" command (all-points-bullitens (commands that are not addressed * specifically to any server)) are handled directly, here. - * Because if I return simply "true", the normal command processing code + * Because if I return simply "true", the normal command processing code * will misinterpret the command. */ -bool +bool QrshServer::myMessage ( Message const & message ) { int const maxlen = 100; @@ -414,7 +415,7 @@ QrshServer::myMessage ( Message const & message ) { return true; } - else + else if ( ! strcmp ( first_word, "any" ) ) { straws.clear(); @@ -443,7 +444,7 @@ QrshServer::rememberIntroduction ( Message const & message ) -void +void QrshServer::addAlias ( Message const & message ) { char alias[1000]; @@ -463,8 +464,8 @@ QrshServer::getNames ( ) if ( ! dir ) { - fprintf ( stderr, - "QrshServer::getNames error: could not open dir |%s|.\n", + fprintf ( stderr, + "QrshServer::getNames error: could not open dir |%s|.\n", data_dir.str().c_str() ); return; @@ -491,8 +492,8 @@ QrshServer::getNames ( ) } else { - /* - * Fail silently. The non-existence of this file + /* + * Fail silently. The non-existence of this file * is not necessarily an error. */ } @@ -504,9 +505,9 @@ QrshServer::getNames ( ) void -QrshServer::string_replaceAll ( string & str, - string & target, - string & replacement +QrshServer::string_replaceAll ( string & str, + string & target, + string & replacement ) { int target_size = target.size(); @@ -519,7 +520,7 @@ QrshServer::string_replaceAll ( string & str, -bool +bool QrshServer::isProcessName ( char const * str ) { getNames(); @@ -537,12 +538,12 @@ QrshServer::isProcessName ( char const * str ) -int +int QrshServer::string_countWords ( char const * s1 ) { int count = 0; char const * s2 = s1 + 1; - + if ( ! isspace(* s1) ) { ++ count; @@ -603,7 +604,7 @@ QrshServer::get ( Message const & request_message ) */ char file_or_process_name[1000]; sscanf ( request_message.getData().c_str(), "%*s%*s%s", file_or_process_name ); - + if ( isProcessName ( file_or_process_name ) ) { stringstream desired_file_name; @@ -612,13 +613,13 @@ QrshServer::get ( Message const & request_message ) << file_or_process_name << '/'; char requested_output_stream[1000]; - if(1 != sscanf ( request_message.getData().c_str(), - "%*s%*s%*s%s", - requested_output_stream + if(1 != sscanf ( request_message.getData().c_str(), + "%*s%*s%*s%s", + requested_output_stream ) ) { - fprintf ( stderr, + fprintf ( stderr, "QrshServer::get error: Can't read requested data file name from this message: |%s|\n", request_message.getData().c_str() ); @@ -674,7 +675,7 @@ QrshServer::exited ( Message const & message ) if ( truncated_command ) { stringstream ss; - ss << qrsh_run_path + ss << qrsh_run_path << ' ' << data_dir.str() << ' ' @@ -706,9 +707,9 @@ QrshServer::exited ( Message const & message ) fprintf ( stderr, "qrsh_server error awaiting child!\n" ); exit ( 1 ); } - + exit_code >>= 8; - + stringstream data; data << "wait_response " << exit_code; @@ -731,7 +732,7 @@ QrshServer::wait ( Message const & message ) // The second word is "exec_wait". // The third word is the symbolic name of the command to wait for. // The fact that there are exactly three words means that this - // must be a command that has already been named and started -- + // must be a command that has already been named and started -- // we just need to find its pid and wait on it. pre_existing = true; } @@ -762,7 +763,7 @@ QrshServer::wait ( Message const & message ) if ( truncated_command ) { stringstream ss; - ss << qrsh_run_path + ss << qrsh_run_path << ' ' << data_dir.str() << ' ' @@ -795,7 +796,7 @@ QrshServer::wait ( Message const & message ) exit ( 1 ); } } - + exit_code >>= 8; stringstream data; @@ -810,7 +811,7 @@ QrshServer::wait ( Message const & message ) -char const * +char const * QrshServer::skipWord ( char const * s ) { if(! (s && *s) ) @@ -884,7 +885,7 @@ QrshServer::getArgs ( char const * str ) arg_len = 0; } - done: + done: if ( arg_len > 0 ) lengths.push_back ( arg_len ); @@ -896,8 +897,8 @@ QrshServer::getArgs ( char const * str ) for ( int i = 0; i < n_args; ++ i ) { argv[i] = ( char *) malloc ( lengths[i] + 1 ); - strncpy ( argv[i], - str + start_positions[i], + strncpy ( argv[i], + str + start_positions[i], lengths[i] ); argv[i][lengths[i]] = 0; @@ -971,12 +972,12 @@ QrshServer::runCommand ( Message const & message ) * qrsh_run, which will save all its data in the qrsh dir. */ stringstream ss; - ss << qrsh_run_path + ss << qrsh_run_path << ' ' << data_dir.str() << ' ' << s; - + if ( ! fork() ) { char ** argv = getArgs ( ss.str().c_str() ); @@ -988,8 +989,8 @@ QrshServer::runCommand ( Message const & message ) -void -QrshServer::received ( Message & message ) +void +QrshServer::received ( Message & message ) { if ( myMessage ( message ) ) runCommand ( message ); @@ -997,7 +998,9 @@ QrshServer::received ( Message & message ) +}} // namespace qpid::tests +using namespace qpid::tests; /* * fixme mick Mon Aug 3 10:29:26 EDT 2009 @@ -1024,23 +1027,23 @@ main ( int /*argc*/, char** argv ) // Declare queues. string myQueue = session.getId().getName(); - session.queueDeclare ( arg::queue=myQueue, + session.queueDeclare ( arg::queue=myQueue, arg::exclusive=true, arg::autoDelete=true); - session.exchangeBind ( arg::exchange="amq.fanout", - arg::queue=myQueue, + session.exchangeBind ( arg::exchange="amq.fanout", + arg::queue=myQueue, arg::bindingKey="my-key"); - + // Create a server and subscribe it to my queue. SubscriptionManager subscriptions ( session ); - QrshServer server ( subscriptions, + QrshServer server ( subscriptions, argv[1], // server name argv[2], // qrsh exe path host, port ); - subscriptions.subscribe ( server, myQueue ); + subscriptions.subscribe ( server, myQueue ); // Receive messages until the subscription is cancelled // by QrshServer::received() @@ -1048,7 +1051,7 @@ main ( int /*argc*/, char** argv ) connection.close(); } - catch(const exception& error) + catch(const exception& error) { cout << error.what() << endl; return 1; diff --git a/qpid/cpp/src/tests/receiver.cpp b/qpid/cpp/src/tests/receiver.cpp index 49f7ff0338..e01954e31a 100644 --- a/qpid/cpp/src/tests/receiver.cpp +++ b/qpid/cpp/src/tests/receiver.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -36,7 +36,10 @@ using namespace qpid::framing; using namespace std; -struct Args : public qpid::TestOptions +namespace qpid { +namespace tests { + +struct Args : public qpid::TestOptions { string queue; uint messages; @@ -47,7 +50,7 @@ struct Args : public qpid::TestOptions Args() : queue("test-queue"), messages(0), ignoreDuplicates(false), creditWindow(0), ackFrequency(1), browse(false) { - addOptions() + addOptions() ("queue", qpid::optValue(queue, "QUEUE NAME"), "Queue from which to request messages") ("messages", qpid::optValue(messages, "N"), "Number of messages to receive; 0 means receive indefinitely") ("ignore-duplicates", qpid::optValue(ignoreDuplicates), "Detect and ignore duplicates (by checking 'sn' header)") @@ -77,15 +80,15 @@ class Receiver : public MessageListener, public FailoverManager::Command bool isDuplicate(Message& message); }; -Receiver::Receiver(const string& q, uint messages, bool ignoreDuplicates, uint creditWindow, uint ackFrequency, bool browse) : - queue(q), count(messages), skipDups(ignoreDuplicates), processed(0), lastSn(0) +Receiver::Receiver(const string& q, uint messages, bool ignoreDuplicates, uint creditWindow, uint ackFrequency, bool browse) : + queue(q), count(messages), skipDups(ignoreDuplicates), processed(0), lastSn(0) { if (browse) settings.acquireMode = ACQUIRE_MODE_NOT_ACQUIRED; if (creditWindow) settings.flowControl = FlowControl::messageWindow(creditWindow); settings.autoAck = ackFrequency; } -void Receiver::received(Message& message) +void Receiver::received(Message& message) { if (!(skipDups && isDuplicate(message))) { bool eos = message.getData() == EOS; @@ -94,7 +97,7 @@ void Receiver::received(Message& message) } } -bool Receiver::isDuplicate(Message& message) +bool Receiver::isDuplicate(Message& message) { uint sn = message.getHeaders().getAsInt("sn"); if (lastSn < sn) { @@ -115,6 +118,10 @@ void Receiver::execute(AsyncSession& session, bool /*isRetry*/) } } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char ** argv) { Args opts; @@ -130,6 +137,3 @@ int main(int argc, char ** argv) } return 1; } - - - diff --git a/qpid/cpp/src/tests/replaying_sender.cpp b/qpid/cpp/src/tests/replaying_sender.cpp index 3ee69eec14..bfb4b042b6 100644 --- a/qpid/cpp/src/tests/replaying_sender.cpp +++ b/qpid/cpp/src/tests/replaying_sender.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -35,12 +35,15 @@ using namespace qpid::framing; using namespace std; +namespace qpid { +namespace tests { + class Sender : public FailoverManager::Command { public: Sender(const std::string& queue, uint count, uint reportFreq); void execute(AsyncSession& session, bool isRetry); - uint getSent(); + uint getSent(); void setVerbosity ( int v ) { verbosity = v; } void setPersistence ( int p ) { persistence = p; } @@ -51,7 +54,7 @@ class Sender : public FailoverManager::Command uint sent; const uint reportFrequency; Message message; - + int verbosity; int persistence; }; @@ -93,7 +96,11 @@ uint Sender::getSent() return sent; } -int main(int argc, char ** argv) +}} // namespace qpid::tests + +using namespace qpid::tests; + +int main(int argc, char ** argv) { ConnectionSettings settings; @@ -118,23 +125,23 @@ int main(int argc, char ** argv) connection.execute ( sender ); if ( verbosity > 0 ) { - std::cout << "Sender finished. Sent " - << sender.getSent() - << " messages." + std::cout << "Sender finished. Sent " + << sender.getSent() + << " messages." << endl; } connection.close(); - return 0; - } - catch(const std::exception& error) + return 0; + } + catch(const std::exception& error) { - cerr << "Sender (host: " - << settings.host - << " port: " + cerr << "Sender (host: " + << settings.host + << " port: " << settings.port << " ) " - << " Failed: " - << error.what() + << " Failed: " + << error.what() << std::endl; } return 1; diff --git a/qpid/cpp/src/tests/resuming_receiver.cpp b/qpid/cpp/src/tests/resuming_receiver.cpp index ef559a009d..807bd83bee 100644 --- a/qpid/cpp/src/tests/resuming_receiver.cpp +++ b/qpid/cpp/src/tests/resuming_receiver.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -35,8 +35,11 @@ using namespace qpid::framing; using namespace std; -class Listener : public MessageListener, - public FailoverManager::Command, +namespace qpid { +namespace tests { + +class Listener : public MessageListener, + public FailoverManager::Command, public FailoverManager::ReconnectionStrategy { public: @@ -57,32 +60,32 @@ class Listener : public MessageListener, }; -Listener::Listener(int freq, int verbosity) - : count(0), - received_twice(0), - lastSn(0), - gaps(false), +Listener::Listener(int freq, int verbosity) + : count(0), + received_twice(0), + lastSn(0), + gaps(false), reportFrequency(freq), verbosity(verbosity), done(false) {} -void Listener::received(Message & message) +void Listener::received(Message & message) { - if (message.getData() == "That's all, folks!") + if (message.getData() == "That's all, folks!") { done = true; if(verbosity > 0 ) { - std::cout << "Shutting down listener for " + std::cout << "Shutting down listener for " << message.getDestination() << std::endl; - std::cout << "Listener received " - << count - << " messages (" - << received_twice - << " received_twice)" + std::cout << "Listener received " + << count + << " messages (" + << received_twice + << " received_twice)" << endl; } subscription.cancel(); @@ -99,8 +102,8 @@ void Listener::received(Message & message) ++count; if ( ! ( count % reportFrequency ) ) { if ( verbosity > 0 ) - std::cout << "Listener has received " - << count + std::cout << "Listener has received " + << count << " messages.\n"; } } else { @@ -133,6 +136,10 @@ void Listener::editUrlList(std::vector<Url>& urls) if (urls.size() > 1) std::rotate(urls.begin(), urls.begin() + 1, urls.end()); } +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char ** argv) { ConnectionSettings settings; diff --git a/qpid/cpp/src/tests/sender.cpp b/qpid/cpp/src/tests/sender.cpp index 311de2e5f8..4e845c42b4 100644 --- a/qpid/cpp/src/tests/sender.cpp +++ b/qpid/cpp/src/tests/sender.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -37,22 +37,27 @@ using namespace qpid::framing; using namespace std; -struct Args : public qpid::TestOptions +namespace qpid { +namespace tests { + +struct Args : public qpid::TestOptions { string destination; string key; uint sendEos; bool durable; + uint ttl; string lvqMatchValue; string lvqMatchFile; - Args() : key("test-queue"), sendEos(0), durable(false) + Args() : key("test-queue"), sendEos(0), durable(false), ttl(0) { addOptions() ("exchange", qpid::optValue(destination, "EXCHANGE"), "Exchange to send messages to") ("routing-key", qpid::optValue(key, "KEY"), "Routing key to add to messages") ("send-eos", qpid::optValue(sendEos, "N"), "Send N EOS messages to mark end of input") ("durable", qpid::optValue(durable, "true|false"), "Mark messages as durable.") + ("ttl", qpid::optValue(ttl, "msecs"), "Time-to-live for messages, in milliseconds") ("lvq-match-value", qpid::optValue(lvqMatchValue, "KEY"), "The value to set for the LVQ match key property") ("lvq-match-file", qpid::optValue(lvqMatchFile, "FILE"), "A file containing values to set for the LVQ match key property"); } @@ -63,26 +68,29 @@ const string EOS("eos"); class Sender : public FailoverManager::Command { public: - Sender(const std::string& destination, const std::string& key, uint sendEos, bool durable, + Sender(const std::string& destination, const std::string& key, uint sendEos, bool durable, uint ttl, const std::string& lvqMatchValue, const std::string& lvqMatchFile); void execute(AsyncSession& session, bool isRetry); private: const std::string destination; MessageReplayTracker sender; - Message message; + Message message; const uint sendEos; uint sent; std::ifstream lvqMatchValues; }; -Sender::Sender(const std::string& dest, const std::string& key, uint eos, bool durable, - const std::string& lvqMatchValue, const std::string& lvqMatchFile) : +Sender::Sender(const std::string& dest, const std::string& key, uint eos, bool durable, uint ttl, const std::string& lvqMatchValue, const std::string& lvqMatchFile) : destination(dest), sender(10), message("", key), sendEos(eos), sent(0) , lvqMatchValues(lvqMatchFile.c_str()) { if (durable){ message.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); } + if (ttl) { + message.getDeliveryProperties().setTtl(ttl); + } + if (!lvqMatchValue.empty()) { message.getHeaders().setString(QueueOptions::strLVQMatchProperty, lvqMatchValue); } @@ -108,16 +116,20 @@ void Sender::execute(AsyncSession& session, bool isRetry) } } -int main(int argc, char ** argv) +}} // namespace qpid::tests + +using namespace qpid::tests; + +int main(int argc, char ** argv) { Args opts; try { opts.parse(argc, argv); FailoverManager connection(opts.con); - Sender sender(opts.destination, opts.key, opts.sendEos, opts.durable, opts.lvqMatchValue, opts.lvqMatchFile); + Sender sender(opts.destination, opts.key, opts.sendEos, opts.durable, opts.ttl, opts.lvqMatchValue, opts.lvqMatchFile); connection.execute(sender); connection.close(); - return 0; + return 0; } catch(const std::exception& error) { std::cout << "Failed: " << error.what() << std::endl; } diff --git a/qpid/cpp/src/tests/shlibtest.cpp b/qpid/cpp/src/tests/shlibtest.cpp index e485cc9397..5655eb7e64 100644 --- a/qpid/cpp/src/tests/shlibtest.cpp +++ b/qpid/cpp/src/tests/shlibtest.cpp @@ -18,6 +18,9 @@ * */ +namespace qpid { +namespace tests { + int* loaderData = 0; extern "C" #ifdef WIN32 @@ -28,5 +31,4 @@ void callMe(int *i) { loaderData=i; } struct OnUnload { ~OnUnload() { *loaderData=42; } }; OnUnload unloader; // For destructor. - - +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/test_store.cpp b/qpid/cpp/src/tests/test_store.cpp index 90503818ed..64a96bf71a 100644 --- a/qpid/cpp/src/tests/test_store.cpp +++ b/qpid/cpp/src/tests/test_store.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -22,7 +22,7 @@ /**@file * Plug-in message store for tests. - * + * * Add functionality as required, build up a comprehensive set of * features to support persistent behavior tests. * @@ -46,6 +46,9 @@ using namespace std; using namespace boost; using namespace qpid::sys; +namespace qpid { +namespace tests { + struct TestStoreOptions : public Options { string name; @@ -66,7 +69,7 @@ struct Completer : public Runnable { delete this; } }; - + class TestStore : public NullMessageStore { public: TestStore(const string& name_, Broker& broker_) : name(name_), broker(broker_) {} @@ -83,7 +86,7 @@ class TestStore : public NullMessageStore { // Check the message for special instructions. size_t i = string::npos; - size_t j = string::npos; + size_t j = string::npos; if (strncmp(data.c_str(), TEST_STORE_DO.c_str(), strlen(TEST_STORE_DO.c_str())) == 0 && (i = data.find(name+"[")) != string::npos && (j = data.find("]", i)) != string::npos) @@ -144,3 +147,5 @@ struct TestStorePlugin : public Plugin { }; static TestStorePlugin pluginInstance; + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/test_tools.h b/qpid/cpp/src/tests/test_tools.h index 54837d3e5b..832c04af04 100644 --- a/qpid/cpp/src/tests/test_tools.h +++ b/qpid/cpp/src/tests/test_tools.h @@ -34,7 +34,7 @@ template <class T> std::ostream& seqPrint(std::ostream& o, const T& seq) { return o; } -// Compare sequences +// Compare sequences template <class T, class U> bool seqEqual(const T& a, const U& b) { typename T::const_iterator i = a.begin(); @@ -60,6 +60,9 @@ template <class T> bool operator == (const boost::assign_detail::generic_list<T>& b, const vector<T>& a) { return seqEqual(a, b); } } +namespace qpid { +namespace tests { + /** NB: order of parameters is regex first, in line with * CHECK(expected, actual) convention. */ @@ -98,6 +101,7 @@ inline std::string getLibPath(const char* envName, const char* defaultPath = 0) return defaultPath; } +}} // namespace qpid::tests #endif /*!TEST_TOOLS_H*/ diff --git a/qpid/cpp/src/tests/topic_listener.cpp b/qpid/cpp/src/tests/topic_listener.cpp index 44070cd4c9..aa8c19df99 100644 --- a/qpid/cpp/src/tests/topic_listener.cpp +++ b/qpid/cpp/src/tests/topic_listener.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -23,7 +23,7 @@ * This file provides one half of a test and example of a pub-sub * style of interaction. See topic_publisher.cpp for the other half, * in which the logic for publishing is defined. - * + * * This file contains the listener logic. A listener will subscribe to * a logical 'topic'. It will count the number of messages it receives * and the time elapsed between the first one and the last one. It @@ -50,11 +50,14 @@ using namespace qpid::sys; using namespace qpid::framing; using namespace std; +namespace qpid { +namespace tests { + /** * A message listener implementation in which the runtime logic is * defined. */ -class Listener : public MessageListener{ +class Listener : public MessageListener{ Session session; SubscriptionManager& mgr; const string responseQueue; @@ -62,7 +65,7 @@ class Listener : public MessageListener{ bool init; int count; AbsTime start; - + void shutdown(); void report(); public: @@ -91,6 +94,52 @@ struct Args : public qpid::TestOptions { } }; +Listener::Listener(const Session& s, SubscriptionManager& m, const string& _responseq, bool tx) : + session(s), mgr(m), responseQueue(_responseq), transactional(tx), init(false), count(0){} + +void Listener::received(Message& message){ + if(!init){ + start = now(); + count = 0; + init = true; + cout << "Batch started." << endl; + } + string type = message.getHeaders().getAsString("TYPE"); + + if(string("TERMINATION_REQUEST") == type){ + shutdown(); + }else if(string("REPORT_REQUEST") == type){ + subscription.accept(subscription.getUnaccepted()); // Accept everything upto this point + cout <<"Batch ended, sending report." << endl; + //send a report: + report(); + init = false; + }else if (++count % 1000 == 0){ + cout <<"Received " << count << " messages." << endl; + } +} + +void Listener::shutdown(){ + mgr.stop(); +} + +void Listener::report(){ + AbsTime finish = now(); + Duration time(start, finish); + stringstream reportstr; + reportstr << "Received " << count << " messages in " + << time/TIME_MSEC << " ms."; + Message msg(reportstr.str(), responseQueue); + msg.getHeaders().setString("TYPE", "REPORT"); + session.messageTransfer(arg::destination="amq.direct", arg::content=msg, arg::acceptMode=1); + if(transactional){ + sync(session).txCommit(); + } +} + +}} // namespace qpid::tests + +using namespace qpid::tests; /** * The main routine creates a Listener instance and sets it up to @@ -142,7 +191,7 @@ int main(int argc, char** argv){ if (args.transactional) { session.txSelect(); } - + cout << "topic_listener: listening..." << endl; mgr.run(); if (args.durable) { @@ -158,47 +207,3 @@ int main(int argc, char** argv){ } return 1; } - -Listener::Listener(const Session& s, SubscriptionManager& m, const string& _responseq, bool tx) : - session(s), mgr(m), responseQueue(_responseq), transactional(tx), init(false), count(0){} - -void Listener::received(Message& message){ - if(!init){ - start = now(); - count = 0; - init = true; - cout << "Batch started." << endl; - } - string type = message.getHeaders().getAsString("TYPE"); - - if(string("TERMINATION_REQUEST") == type){ - shutdown(); - }else if(string("REPORT_REQUEST") == type){ - subscription.accept(subscription.getUnaccepted()); // Accept everything upto this point - cout <<"Batch ended, sending report." << endl; - //send a report: - report(); - init = false; - }else if (++count % 1000 == 0){ - cout <<"Received " << count << " messages." << endl; - } -} - -void Listener::shutdown(){ - mgr.stop(); -} - -void Listener::report(){ - AbsTime finish = now(); - Duration time(start, finish); - stringstream reportstr; - reportstr << "Received " << count << " messages in " - << time/TIME_MSEC << " ms."; - Message msg(reportstr.str(), responseQueue); - msg.getHeaders().setString("TYPE", "REPORT"); - session.messageTransfer(arg::destination="amq.direct", arg::content=msg, arg::acceptMode=1); - if(transactional){ - sync(session).txCommit(); - } -} - diff --git a/qpid/cpp/src/tests/topic_publisher.cpp b/qpid/cpp/src/tests/topic_publisher.cpp index 40055bbfa0..3381132b1a 100644 --- a/qpid/cpp/src/tests/topic_publisher.cpp +++ b/qpid/cpp/src/tests/topic_publisher.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -23,7 +23,7 @@ * This file provides one half of a test and example of a pub-sub * style of interaction. See topic_listener.cpp for the other half, in * which the logic for subscribers is defined. - * + * * This file contains the publisher logic. The publisher will send a * number of messages to the exchange with the appropriate routing key * for the logical 'topic'. Once it has done this it will then send a @@ -49,19 +49,22 @@ using namespace qpid::client; using namespace qpid::sys; using namespace std; +namespace qpid { +namespace tests { + /** * The publishing logic is defined in this class. It implements * message listener and can therfore be used to receive messages sent * back by the subscribers. */ -class Publisher { +class Publisher { AsyncSession session; SubscriptionManager mgr; LocalQueue queue; const string controlTopic; const bool transactional; const bool durable; - + string generateData(int size); public: @@ -99,6 +102,64 @@ struct Args : public TestOptions { } }; +Publisher::Publisher(const AsyncSession& _session, const string& _controlTopic, bool tx, bool d) : + session(_session), mgr(session), controlTopic(_controlTopic), transactional(tx), durable(d) +{ + mgr.subscribe(queue, "response"); +} + +int64_t Publisher::publish(int msgs, int listeners, int size){ + Message msg(generateData(size), controlTopic); + if (durable) { + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + } + AbsTime start = now(); + + for(int i = 0; i < msgs; i++){ + session.messageTransfer(arg::content=msg, arg::destination="amq.topic", arg::acceptMode=1); + } + //send report request + Message reportRequest("", controlTopic); + reportRequest.getHeaders().setString("TYPE", "REPORT_REQUEST"); + session.messageTransfer(arg::content=reportRequest, arg::destination="amq.topic", arg::acceptMode=1); + if(transactional){ + sync(session).txCommit(); + } + //wait for a response from each listener (TODO, could log these) + for (int i = 0; i < listeners; i++) { + Message report = queue.pop(); + } + + if(transactional){ + sync(session).txCommit(); + } + + AbsTime finish = now(); + return Duration(start, finish); +} + +string Publisher::generateData(int size){ + string data; + for(int i = 0; i < size; i++){ + data += ('A' + (i / 26)); + } + return data; +} + +void Publisher::terminate(){ + //send termination request + Message terminationRequest("", controlTopic); + terminationRequest.getHeaders().setString("TYPE", "TERMINATION_REQUEST"); + session.messageTransfer(arg::content=terminationRequest, arg::destination="amq.topic", arg::acceptMode=1); + if(transactional){ + session.txCommit(); + } +} + +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { try{ Args args; @@ -120,11 +181,11 @@ int main(int argc, char** argv) { Message m = statusQ.get(); if( m.getData().find("topic_listener: ", 0) == 0 ) { cout << "Listener " << (i+1) << " of " << args.subscribers - << " is ready (pid " << m.getData().substr(16, m.getData().length() - 16) - << ")" << endl; + << " is ready (pid " << m.getData().substr(16, m.getData().length() - 16) + << ")" << endl; } else { throw Exception(QPID_MSG("Unexpected message received on status queue: " << m.getData())); - } + } } } @@ -150,12 +211,12 @@ int main(int argc, char** argv) { if(!min || msecs < min) min = msecs; sum += msecs; cout << "Completed " << (i+1) << " of " << batchSize - << " in " << msecs << "ms" << endl; + << " in " << msecs << "ms" << endl; } publisher.terminate(); int64_t avg = sum / batchSize; if(batchSize > 1){ - cout << batchSize << " batches completed. avg=" << avg << + cout << batchSize << " batches completed. avg=" << avg << ", max=" << max << ", min=" << min << endl; } session.close(); @@ -167,57 +228,3 @@ int main(int argc, char** argv) { } return 1; } - -Publisher::Publisher(const AsyncSession& _session, const string& _controlTopic, bool tx, bool d) : - session(_session), mgr(session), controlTopic(_controlTopic), transactional(tx), durable(d) -{ - mgr.subscribe(queue, "response"); -} - -int64_t Publisher::publish(int msgs, int listeners, int size){ - Message msg(generateData(size), controlTopic); - if (durable) { - msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); - } - AbsTime start = now(); - - for(int i = 0; i < msgs; i++){ - session.messageTransfer(arg::content=msg, arg::destination="amq.topic", arg::acceptMode=1); - } - //send report request - Message reportRequest("", controlTopic); - reportRequest.getHeaders().setString("TYPE", "REPORT_REQUEST"); - session.messageTransfer(arg::content=reportRequest, arg::destination="amq.topic", arg::acceptMode=1); - if(transactional){ - sync(session).txCommit(); - } - //wait for a response from each listener (TODO, could log these) - for (int i = 0; i < listeners; i++) { - Message report = queue.pop(); - } - - if(transactional){ - sync(session).txCommit(); - } - - AbsTime finish = now(); - return Duration(start, finish); -} - -string Publisher::generateData(int size){ - string data; - for(int i = 0; i < size; i++){ - data += ('A' + (i / 26)); - } - return data; -} - -void Publisher::terminate(){ - //send termination request - Message terminationRequest("", controlTopic); - terminationRequest.getHeaders().setString("TYPE", "TERMINATION_REQUEST"); - session.messageTransfer(arg::content=terminationRequest, arg::destination="amq.topic", arg::acceptMode=1); - if(transactional){ - session.txCommit(); - } -} diff --git a/qpid/cpp/src/tests/txjob.cpp b/qpid/cpp/src/tests/txjob.cpp index 94db96a666..a7a905c1b7 100644 --- a/qpid/cpp/src/tests/txjob.cpp +++ b/qpid/cpp/src/tests/txjob.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -33,7 +33,10 @@ using namespace qpid::client; using namespace qpid::sys; -struct Args : public qpid::TestOptions +namespace qpid { +namespace tests { + +struct Args : public qpid::TestOptions { string workQueue; string source; @@ -43,10 +46,10 @@ struct Args : public qpid::TestOptions bool quit; bool declareQueues; - Args() : workQueue("txshift-control"), source("txshift-1"), dest("txshift-2"), messages(0), jobs(0), + Args() : workQueue("txshift-control"), source("txshift-1"), dest("txshift-2"), messages(0), jobs(0), quit(false), declareQueues(false) { - addOptions() + addOptions() ("messages", qpid::optValue(messages, "N"), "Number of messages to shift") ("jobs", qpid::optValue(jobs, "N"), "Number of shift jobs to request") ("source", qpid::optValue(source, "QUEUE NAME"), "source queue from which messages will be shifted") @@ -57,6 +60,10 @@ struct Args : public qpid::TestOptions } }; +}} // namespace qpid::tests + +using namespace qpid::tests; + //TODO: might be nice to make this capable of failover as well at some //point; for now its just for the setup phase. int main(int argc, char** argv) diff --git a/qpid/cpp/src/tests/txshift.cpp b/qpid/cpp/src/tests/txshift.cpp index 97135c9829..882d3716d8 100644 --- a/qpid/cpp/src/tests/txshift.cpp +++ b/qpid/cpp/src/tests/txshift.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -34,14 +34,17 @@ using namespace qpid::client; using namespace qpid::sys; -struct Args : public qpid::TestOptions +namespace qpid { +namespace tests { + +struct Args : public qpid::TestOptions { string workQueue; size_t workers; Args() : workQueue("txshift-control"), workers(1) { - addOptions() + addOptions() ("workers", qpid::optValue(workers, "N"), "Number of separate worker sessions to start") ("work-queue", qpid::optValue(workQueue, "NAME"), "work queue from which to take instructions"); } @@ -61,7 +64,7 @@ struct Transfer : MessageListener Transfer(const std::string control_) : control(control_), expected(0), transfered(0) {} - void subscribeToSource(SubscriptionManager manager) + void subscribeToSource(SubscriptionManager manager) { sourceSettings.autoAck = 0;//will accept once at the end of the batch sourceSettings.flowControl = FlowControl::messageCredit(expected); @@ -69,7 +72,7 @@ struct Transfer : MessageListener QPID_LOG(info, "Subscribed to source: " << source << " expecting: " << expected); } - void subscribeToControl(SubscriptionManager manager) + void subscribeToControl(SubscriptionManager manager) { controlSettings.flowControl = FlowControl::messageCredit(1); controlSubscription = manager.subscribe(*this, control, controlSettings); @@ -94,7 +97,7 @@ struct Transfer : MessageListener message.getDeliveryProperties().setRoutingKey(destination); async(sourceSubscription.getSession()).messageTransfer(arg::content=message); if (++transfered == expected) { - QPID_LOG(info, "completed job: " << transfered << " messages shifted from " << + QPID_LOG(info, "completed job: " << transfered << " messages shifted from " << source << " to " << destination); sourceSubscription.accept(sourceSubscription.getUnaccepted()); sourceSubscription.getSession().txCommit(); @@ -111,7 +114,7 @@ struct Transfer : MessageListener destination = message.getHeaders().getAsString("dest"); expected = message.getHeaders().getAsInt("count"); transfered = 0; - QPID_LOG(info, "received transfer request: " << expected << " messages to be shifted from " << + QPID_LOG(info, "received transfer request: " << expected << " messages to be shifted from " << source << " to " << destination); subscribeToSource(controlSubscription.getSubscriptionManager()); } else if (message.getData() == "quit") { @@ -133,7 +136,7 @@ struct Worker : FailoverManager::Command, Runnable Worker(FailoverManager& c, const std::string& controlQueue) : connection(c), transfer(controlQueue) {} - void run() + void run() { connection.execute(*this); } @@ -148,7 +151,7 @@ struct Worker : FailoverManager::Command, Runnable runner.join(); } - void execute(AsyncSession& session, bool isRetry) + void execute(AsyncSession& session, bool isRetry) { if (isRetry) QPID_LOG(info, "Retrying..."); session.txSelect(); @@ -159,6 +162,10 @@ struct Worker : FailoverManager::Command, Runnable } }; +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { Args opts; diff --git a/qpid/cpp/src/tests/txtest.cpp b/qpid/cpp/src/tests/txtest.cpp index c1ee246e2c..d0ba2f1245 100644 --- a/qpid/cpp/src/tests/txtest.cpp +++ b/qpid/cpp/src/tests/txtest.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -33,7 +33,7 @@ #include "qpid/client/SubscriptionManager.h" #include "qpid/framing/Array.h" #include "qpid/framing/Buffer.h" -#include "qpid/sys/uuid.h" +#include "qpid/framing/Uuid.h" #include "qpid/sys/Thread.h" using namespace qpid; @@ -41,6 +41,9 @@ using namespace qpid::client; using namespace qpid::sys; using std::string; +namespace qpid { +namespace tests { + typedef std::vector<std::string> StringSet; struct Args : public qpid::TestOptions { @@ -55,12 +58,12 @@ struct Args : public qpid::TestOptions { bool dtx; bool quiet; - Args() : init(true), transfer(true), check(true), - size(256), durable(true), queues(2), + Args() : init(true), transfer(true), check(true), + size(256), durable(true), queues(2), base("tx-test"), msgsPerTx(1), txCount(1), totalMsgCount(10), dtx(false), quiet(false) { - addOptions() + addOptions() ("init", optValue(init, "yes|no"), "Declare queues and populate one with the initial set of messages.") ("transfer", optValue(transfer, "yes|no"), "'Move' messages from one queue to another using transactions to ensure no message loss.") @@ -83,7 +86,7 @@ std::string generateData(uint size) { if (size < chars.length()) { return chars.substr(0, size); - } + } std::string data; for (uint i = 0; i < (size / chars.length()); i++) { data += chars; @@ -103,18 +106,18 @@ void generateSet(const std::string& base, uint count, StringSet& collection) Args opts; -struct Client +struct Client { Connection connection; AsyncSession session; - Client() + Client() { opts.open(connection); session = connection.newSession(); } - ~Client() + ~Client() { try{ session.close(); @@ -130,25 +133,23 @@ struct Transfer : public Client, public Runnable std::string src; std::string dest; Thread thread; - uuid_t uuid; - char uuidStr[37]; // Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + trailing \0 framing::Xid xid; Transfer(const std::string& to, const std::string& from) : src(to), dest(from), xid(0x4c414e47, "", from) {} - void run() + void run() { try { - + if (opts.dtx) session.dtxSelect(); else session.txSelect(); SubscriptionManager subs(session); - + LocalQueue lq; SubscriptionSettings settings(FlowControl::messageWindow(opts.msgsPerTx)); settings.autoAck = 0; // Disabled Subscription sub = subs.subscribe(lq, src, settings); - + for (uint t = 0; t < opts.txCount; t++) { Message in; Message out("", dest); @@ -184,13 +185,12 @@ struct Transfer : public Client, public Runnable } void setNewXid(framing::Xid& xid) { - ::uuid_generate(uuid); - ::uuid_unparse(uuid, uuidStr); - xid.setGlobalId(uuidStr); + framing::Uuid uuid(true); + xid.setGlobalId(uuid.str()); } }; -struct Controller : public Client +struct Controller : public Client { StringSet ids; StringSet queues; @@ -201,7 +201,7 @@ struct Controller : public Client generateSet("msg", opts.totalMsgCount, ids); } - void init() + void init() { //declare queues for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) { @@ -239,7 +239,7 @@ struct Controller : public Client } } - int check() + int check() { SubscriptionManager subs(session); @@ -294,10 +294,10 @@ struct Controller : public Client //check that drained == ids StringSet missing; - set_difference(ids.begin(), ids.end(), drained.begin(), drained.end(), back_inserter(missing)); + set_difference(ids.begin(), ids.end(), drained.begin(), drained.end(), back_inserter(missing)); StringSet extra; - set_difference(drained.begin(), drained.end(), ids.begin(), ids.end(), back_inserter(extra)); + set_difference(drained.begin(), drained.end(), ids.begin(), ids.end(), back_inserter(extra)); if (missing.empty() && extra.empty()) { std::cout << "All expected messages were retrieved." << std::endl; @@ -306,26 +306,30 @@ struct Controller : public Client if (!missing.empty()) { std::cout << "The following ids were missing:" << std::endl; for (StringSet::iterator i = missing.begin(); i != missing.end(); i++) { - std::cout << " '" << *i << "'" << std::endl; - } + std::cout << " '" << *i << "'" << std::endl; + } } if (!extra.empty()) { std::cout << "The following extra ids were encountered:" << std::endl; for (StringSet::iterator i = extra.begin(); i != extra.end(); i++) { - std::cout << " '" << *i << "'" << std::endl; - } + std::cout << " '" << *i << "'" << std::endl; + } } return 1; } } }; +}} // namespace qpid::tests + +using namespace qpid::tests; + int main(int argc, char** argv) { try { opts.parse(argc, argv); Controller controller; - if (opts.init) controller.init(); + if (opts.init) controller.init(); if (opts.transfer) controller.transfer(); if (opts.check) return controller.check(); return 0; diff --git a/qpid/cpp/src/windows/QpiddBroker.cpp b/qpid/cpp/src/windows/QpiddBroker.cpp index 5c6eef48f8..5bf9477e6a 100644 --- a/qpid/cpp/src/windows/QpiddBroker.cpp +++ b/qpid/cpp/src/windows/QpiddBroker.cpp @@ -133,6 +133,14 @@ void ShutdownHandler::run() { } } +// Console control handler to properly handle ctl-c. +BOOL CtrlHandler(DWORD ctl) +{ + ShutdownEvent shutter; // no pid specified == shut me down + shutter.signal(); + return ((ctl == CTRL_C_EVENT || ctl == CTRL_CLOSE_EVENT) ? TRUE : FALSE); +} + } struct ProcessControlOptions : public qpid::Options { @@ -245,6 +253,7 @@ int QpiddBroker::execute (QpiddOptions *options) { ShutdownHandler waitShut(brokerPtr); qpid::sys::Thread waitThr(waitShut); // Wait for shutdown event + SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); brokerPtr->run(); waitShut.signal(); // In case we shut down some other way waitThr.join(); diff --git a/qpid/java/broker/bin/qpid-passwd b/qpid/java/broker/bin/qpid-passwd index 63b30b5e71..b84580da60 100755 --- a/qpid/java/broker/bin/qpid-passwd +++ b/qpid/java/broker/bin/qpid-passwd @@ -1,35 +1,35 @@ -#!/bin/bash
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-if [ -z "$QPID_HOME" ]; then
- export QPID_HOME=$(dirname $(dirname $(readlink -f $0)))
- export PATH=${PATH}:${QPID_HOME}/bin
-fi
-
-# Set classpath to include Qpid jar with all required jars in manifest
-QPID_LIBS=$QPID_HOME/lib/qpid-all.jar
-
-# Set other variables used by the qpid-run script before calling
-export JAVA=java \
- JAVA_VM=-server \
- JAVA_MEM=-Xmx1024m \
- QPID_CLASSPATH=$QPID_LIBS
-
-. qpid-run org.apache.qpid.tools.security.Passwd "$@"
+#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +if [ -z "$QPID_HOME" ]; then + export QPID_HOME=$(dirname $(dirname $(readlink -f $0))) + export PATH=${PATH}:${QPID_HOME}/bin +fi + +# Set classpath to include Qpid jar with all required jars in manifest +QPID_LIBS=$QPID_HOME/lib/qpid-all.jar + +# Set other variables used by the qpid-run script before calling +export JAVA=java \ + JAVA_VM=-server \ + JAVA_MEM=-Xmx1024m \ + QPID_CLASSPATH=$QPID_LIBS + +. qpid-run org.apache.qpid.tools.security.Passwd "$@" diff --git a/qpid/java/broker/bin/qpid-server b/qpid/java/broker/bin/qpid-server index e5a9e998e2..738fc6e084 100755 --- a/qpid/java/broker/bin/qpid-server +++ b/qpid/java/broker/bin/qpid-server @@ -26,14 +26,19 @@ fi # Set classpath to include Qpid jar with all required jars in manifest QPID_LIBS=$QPID_HOME/lib/qpid-all.jar:$QPID_HOME/lib/bdbstore-launch.jar +# Default Log4j to append to its log file +if [ -z "$QPID_LOG_APPEND" ]; then + export QPID_LOG_APPEND="true" +fi + # Set other variables used by the qpid-run script before calling export JAVA=java \ JAVA_VM=-server \ JAVA_MEM=-Xmx1024m \ JAVA_GC="-XX:+UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError" \ QPID_CLASSPATH=$QPID_LIBS \ - QPID_RUN_LOG=2 + QPID_RUN_LOG=2 -QPID_OPTS="$QPID_OPTS -Damqj.read_write_pool_size=32" +QPID_OPTS="$QPID_OPTS -Damqj.read_write_pool_size=32 -DQPID_LOG_APPEND=$QPID_LOG_APPEND" . qpid-run org.apache.qpid.server.Main "$@" diff --git a/qpid/java/broker/etc/config-systests-derby.xml b/qpid/java/broker/etc/config-systests-derby.xml deleted file mode 100644 index e9cfa04ab5..0000000000 --- a/qpid/java/broker/etc/config-systests-derby.xml +++ /dev/null @@ -1,141 +0,0 @@ -<?xml version="1.0" encoding="ISO-8859-1"?> -<!-- - - - - Licensed to the Apache Software Foundation (ASF) under one - - or more contributor license agreements. See the NOTICE file - - distributed with this work for additional information - - regarding copyright ownership. The ASF licenses this file - - to you under the Apache License, Version 2.0 (the - - "License"); you may not use this file except in compliance - - with the License. You may obtain a copy of the License at - - - - http://www.apache.org/licenses/LICENSE-2.0 - - - - Unless required by applicable law or agreed to in writing, - - software distributed under the License is distributed on an - - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - - KIND, either express or implied. See the License for the - - specific language governing permissions and limitations - - under the License. - - - --> -<broker> - <prefix>${QPID_HOME}</prefix> - <work>${QPID_WORK}</work> - <conf>${prefix}/etc</conf> - <connector> - <!-- Uncomment out this block and edit the keystorePath and keystorePassword - to enable SSL support - <ssl> - <enabled>true</enabled> - <sslOnly>true</sslOnly> - <keystorePath>/path/to/keystore.ks</keystorePath> - <keystorePassword>keystorepass</keystorePassword> - </ssl>--> - <qpidnio>false</qpidnio> - <protectio> - <enabled>false</enabled> - </protectio> - <transport>nio</transport> - <port>5672</port> - <sslport>8672</sslport> - <socketReceiveBuffer>32768</socketReceiveBuffer> - <socketSendBuffer>32768</socketSendBuffer> - </connector> - <management> - <enabled>false</enabled> - <jmxport>8999</jmxport> - <ssl> - <enabled>false</enabled> - <!-- Update below path to your keystore location, eg ${conf}/qpid.keystore --> - <keyStorePath>${prefix}/../test-profiles/test_resources/ssl/keystore.jks</keyStorePath> - <keyStorePassword>password</keyStorePassword> - </ssl> - </management> - <advanced> - <filterchain enableExecutorPool="true"/> - <enablePooledAllocator>false</enablePooledAllocator> - <enableDirectBuffers>false</enableDirectBuffers> - <framesize>65535</framesize> - <compressBufferOnQueue>false</compressBufferOnQueue> - <enableJMSXUserID>false</enableJMSXUserID> - </advanced> - - <security> - <principal-databases> - <!-- Example use of Base64 encoded MD5 hashes for authentication via CRAM-MD5-Hashed --> - <principal-database> - <name>passwordfile</name> - <class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class> - <attributes> - <attribute> - <name>passwordFile</name> - <value>${conf}/passwd</value> - </attribute> - </attributes> - </principal-database> - </principal-databases> - - <access> - <class>org.apache.qpid.server.security.access.plugins.AllowAll</class> - </access> - - <msg-auth>false</msg-auth> - - <jmx> - <access>${conf}/jmxremote.access</access> - <principal-database>passwordfile</principal-database> - </jmx> - </security> - - <virtualhosts> - <directory>${conf}/virtualhosts</directory> - - <virtualhost> - <name>localhost</name> - <localhost> - <store> - <class>org.apache.qpid.server.store.DerbyMessageStore</class> - <environment-path>${work}/derbyDB/localhost-store</environment-path> - </store> - - <housekeeping> - <expiredMessageCheckPeriod>20000</expiredMessageCheckPeriod> - </housekeeping> - - </localhost> - </virtualhost> - - <virtualhost> - <name>development</name> - <development> - <store> - <class>org.apache.qpid.server.store.DerbyMessageStore</class> - <environment-path>${work}/derbyDB/development-store</environment-path> - </store> - </development> - </virtualhost> - - <virtualhost> - <name>test</name> - <test> - <store> - <class>org.apache.qpid.server.store.DerbyMessageStore</class> - <environment-path>${work}/derbyDB/test-store</environment-path> - </store> - </test> - </virtualhost> - - </virtualhosts> - <heartbeat> - <delay>0</delay> - <timeoutFactor>2.0</timeoutFactor> - </heartbeat> - <queue> - <auto_register>true</auto_register> - </queue> - - <virtualhosts>${conf}/virtualhosts-systests.xml</virtualhosts> -</broker> - - diff --git a/qpid/java/broker/etc/config-systests.xml b/qpid/java/broker/etc/config-systests.xml deleted file mode 100644 index 42e8c9dbba..0000000000 --- a/qpid/java/broker/etc/config-systests.xml +++ /dev/null @@ -1,143 +0,0 @@ -<?xml version="1.0" encoding="ISO-8859-1"?> -<!-- - - - - Licensed to the Apache Software Foundation (ASF) under one - - or more contributor license agreements. See the NOTICE file - - distributed with this work for additional information - - regarding copyright ownership. The ASF licenses this file - - to you under the Apache License, Version 2.0 (the - - "License"); you may not use this file except in compliance - - with the License. You may obtain a copy of the License at - - - - http://www.apache.org/licenses/LICENSE-2.0 - - - - Unless required by applicable law or agreed to in writing, - - software distributed under the License is distributed on an - - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - - KIND, either express or implied. See the License for the - - specific language governing permissions and limitations - - under the License. - - - --> -<broker> - <prefix>${QPID_HOME}</prefix> - <work>${QPID_WORK}</work> - <conf>${prefix}/etc</conf> - <connector> - <!-- Uncomment out this block and edit the keystorePath and keystorePassword - to enable SSL support - <ssl> - <enabled>true</enabled> - <sslOnly>true</sslOnly> - <keystorePath>/path/to/keystore.ks</keystorePath> - <keystorePassword>keystorepass</keystorePassword> - </ssl>--> - <qpidnio>false</qpidnio> - <protectio> - <enabled>false</enabled> - <readBufferLimitSize>262144</readBufferLimitSize> - <writeBufferLimitSize>262144</writeBufferLimitSize> - </protectio> - <transport>nio</transport> - <port>5672</port> - <sslport>8672</sslport> - <socketReceiveBuffer>32768</socketReceiveBuffer> - <socketSendBuffer>32768</socketSendBuffer> - </connector> - <management> - <enabled>false</enabled> - <jmxport>8999</jmxport> - <ssl> - <enabled>false</enabled> - <!-- Update below path to your keystore location, eg ${conf}/qpid.keystore --> - <keyStorePath>${prefix}/../test-profiles/test_resources/ssl/keystore.jks</keyStorePath> - <keyStorePassword>password</keyStorePassword> - </ssl> - </management> - <advanced> - <filterchain enableExecutorPool="true"/> - <enablePooledAllocator>false</enablePooledAllocator> - <enableDirectBuffers>false</enableDirectBuffers> - <framesize>65535</framesize> - <compressBufferOnQueue>false</compressBufferOnQueue> - <enableJMSXUserID>false</enableJMSXUserID> - <locale>en_US</locale> - </advanced> - - <security> - <principal-databases> - <!-- Example use of Base64 encoded MD5 hashes for authentication via CRAM-MD5-Hashed --> - <principal-database> - <name>passwordfile</name> - <class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class> - <attributes> - <attribute> - <name>passwordFile</name> - <value>${conf}/passwd</value> - </attribute> - </attributes> - </principal-database> - </principal-databases> - - <access> - <class>org.apache.qpid.server.security.access.plugins.AllowAll</class> - </access> - - <msg-auth>false</msg-auth> - - <jmx> - <access>${conf}/jmxremote.access</access> - <principal-database>passwordfile</principal-database> - </jmx> - </security> - - <virtualhosts> - <directory>${conf}/virtualhosts</directory> - - <virtualhost> - <name>localhost</name> - <localhost> - <store> - <class>org.apache.qpid.server.store.MemoryMessageStore</class> - </store> - - <housekeeping> - <expiredMessageCheckPeriod>20000</expiredMessageCheckPeriod> - </housekeeping> - - </localhost> - </virtualhost> - - <virtualhost> - <name>development</name> - <development> - <store> - <class>org.apache.qpid.server.store.MemoryMessageStore</class> - </store> - </development> - </virtualhost> - - <virtualhost> - <name>test</name> - <test> - <store> - <class>org.apache.qpid.server.store.MemoryMessageStore</class> - </store> - </test> - </virtualhost> - - </virtualhosts> - <heartbeat> - <delay>0</delay> - <timeoutFactor>2.0</timeoutFactor> - </heartbeat> - <queue> - <auto_register>true</auto_register> - </queue> - - <status-updates>ON</status-updates> - - <virtualhosts>${conf}/virtualhosts-systests.xml</virtualhosts> -</broker> - - diff --git a/qpid/java/broker/etc/config.xml b/qpid/java/broker/etc/config.xml index 928b773606..8fb3a8cf5a 100644 --- a/qpid/java/broker/etc/config.xml +++ b/qpid/java/broker/etc/config.xml @@ -24,17 +24,20 @@ <work>${QPID_WORK}</work> <conf>${prefix}/etc</conf> <connector> - <!-- Uncomment out this block and edit the keystorePath and keystorePassword - to enable SSL support + <!-- To enable SSL edit the keystorePath and keystorePassword + and set enabled to true. + To disasble Non-SSL port set sslOnly to true --> <ssl> - <enabled>true</enabled> - <sslOnly>true</sslOnly> + <enabled>false</enabled> + <sslOnly>false</sslOnly> <keystorePath>/path/to/keystore.ks</keystorePath> <keystorePassword>keystorepass</keystorePassword> - </ssl>--> + </ssl> <qpidnio>false</qpidnio> <protectio> <enabled>false</enabled> + <readBufferLimitSize>262144</readBufferLimitSize> + <writeBufferLimitSize>262144</writeBufferLimitSize> </protectio> <transport>nio</transport> <port>5672</port> @@ -59,6 +62,7 @@ <framesize>65535</framesize> <compressBufferOnQueue>false</compressBufferOnQueue> <enableJMSXUserID>false</enableJMSXUserID> + <locale>en_US</locale> </advanced> <security> @@ -89,40 +93,143 @@ </security> <virtualhosts> - <directory>${conf}/virtualhosts</directory> + <default>test</default> <virtualhost> <name>localhost</name> <localhost> <store> - <class>org.apache.qpid.server.store.MemoryMessageStore</class> + <class>org.apache.qpid.server.store.MemoryMessageStore + </class> </store> <housekeeping> <expiredMessageCheckPeriod>20000</expiredMessageCheckPeriod> </housekeeping> + <exchanges> + <exchange> + <type>direct</type> + <name>test.direct</name> + <durable>true</durable> + </exchange> + <exchange> + <type>topic</type> + <name>test.topic</name> + </exchange> + </exchanges> + <queues> + <exchange>amq.direct</exchange> + <maximumQueueDepth>4235264</maximumQueueDepth> + <!-- 4Mb --> + <maximumMessageSize>2117632</maximumMessageSize> + <!-- 2Mb --> + <maximumMessageAge>600000</maximumMessageAge> + <!-- 10 mins --> + <maximumMessageCount>50</maximumMessageCount> + <!-- 50 messages --> + + <queue> + <name>queue</name> + </queue> + <queue> + <name>ping</name> + </queue> + <queue> + <name>test-queue</name> + <test-queue> + <exchange>test.direct</exchange> + <durable>true</durable> + </test-queue> + </queue> + <queue> + <name>test-ping</name> + <test-ping> + <exchange>test.direct</exchange> + </test-ping> + </queue> + + </queues> </localhost> </virtualhost> + <virtualhost> <name>development</name> <development> <store> - <class>org.apache.qpid.server.store.MemoryMessageStore</class> + <class>org.apache.qpid.server.store.MemoryMessageStore + </class> </store> + + <queues> + <minimumAlertRepeatGap>30000</minimumAlertRepeatGap> + <maximumMessageCount>50</maximumMessageCount> + <queue> + <name>queue</name> + <queue> + <exchange>amq.direct</exchange> + <maximumQueueDepth>4235264</maximumQueueDepth> + <!-- 4Mb --> + <maximumMessageSize>2117632</maximumMessageSize> + <!-- 2Mb --> + <maximumMessageAge>600000</maximumMessageAge> + <!-- 10 mins --> + </queue> + </queue> + <queue> + <name>ping</name> + <ping> + <exchange>amq.direct</exchange> + <maximumQueueDepth>4235264</maximumQueueDepth> + <!-- 4Mb --> + <maximumMessageSize>2117632</maximumMessageSize> + <!-- 2Mb --> + <maximumMessageAge>600000</maximumMessageAge> + <!-- 10 mins --> + </ping> + </queue> + </queues> </development> </virtualhost> - <virtualhost> <name>test</name> <test> <store> - <class>org.apache.qpid.server.store.MemoryMessageStore</class> + <class>org.apache.qpid.server.store.MemoryMessageStore + </class> </store> + + <queues> + <minimumAlertRepeatGap>30000</minimumAlertRepeatGap> + <maximumMessageCount>50</maximumMessageCount> + <queue> + <name>queue</name> + <queue> + <exchange>amq.direct</exchange> + <maximumQueueDepth>4235264</maximumQueueDepth> + <!-- 4Mb --> + <maximumMessageSize>2117632</maximumMessageSize> + <!-- 2Mb --> + <maximumMessageAge>600000</maximumMessageAge> + <!-- 10 mins --> + </queue> + </queue> + <queue> + <name>ping</name> + <ping> + <exchange>amq.direct</exchange> + <maximumQueueDepth>4235264</maximumQueueDepth> + <!-- 4Mb --> + <maximumMessageSize>2117632</maximumMessageSize> + <!-- 2Mb --> + <maximumMessageAge>600000</maximumMessageAge> + <!-- 10 mins --> + </ping> + </queue> + </queues> </test> </virtualhost> - </virtualhosts> <heartbeat> <delay>0</delay> @@ -132,7 +239,8 @@ <auto_register>true</auto_register> </queue> - <virtualhosts>${conf}/virtualhosts.xml</virtualhosts> + <status-updates>ON</status-updates> + </broker> diff --git a/qpid/java/broker/etc/log4j.xml b/qpid/java/broker/etc/log4j.xml index a395d0fd56..8ca43ededd 100644 --- a/qpid/java/broker/etc/log4j.xml +++ b/qpid/java/broker/etc/log4j.xml @@ -50,7 +50,7 @@ <appender class="org.apache.log4j.FileAppender" name="FileAppender"> <param name="File" value="${QPID_WORK}/log/${logprefix}qpid${logsuffix}.log"/> - <param name="Append" value="false"/> + <param name="Append" value="${QPID_LOG_APPEND}"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/> diff --git a/qpid/java/broker/etc/virtualhosts-systests.xml b/qpid/java/broker/etc/virtualhosts-systests.xml deleted file mode 100644 index 4f23f55579..0000000000 --- a/qpid/java/broker/etc/virtualhosts-systests.xml +++ /dev/null @@ -1,124 +0,0 @@ -<?xml version="1.0" encoding="ISO-8859-1"?> -<!-- - - - - Licensed to the Apache Software Foundation (ASF) under one - - or more contributor license agreements. See the NOTICE file - - distributed with this work for additional information - - regarding copyright ownership. The ASF licenses this file - - to you under the Apache License, Version 2.0 (the - - "License"); you may not use this file except in compliance - - with the License. You may obtain a copy of the License at - - - - http://www.apache.org/licenses/LICENSE-2.0 - - - - Unless required by applicable law or agreed to in writing, - - software distributed under the License is distributed on an - - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - - KIND, either express or implied. See the License for the - - specific language governing permissions and limitations - - under the License. - - - --> -<virtualhosts> - <default>test</default> - <virtualhost> - <name>localhost</name> - <localhost> - <exchanges> - <exchange> - <type>direct</type> - <name>test.direct</name> - <durable>true</durable> - </exchange> - <exchange> - <type>topic</type> - <name>test.topic</name> - </exchange> - </exchanges> - <queues> - <exchange>amq.direct</exchange> - <maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb --> - <maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb --> - <maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins --> - <maximumMessageCount>50</maximumMessageCount> <!-- 50 messages --> - - <queue> - <name>queue</name> - </queue> - <queue> - <name>ping</name> - </queue> - <queue> - <name>test-queue</name> - <test-queue> - <exchange>test.direct</exchange> - <durable>true</durable> - </test-queue> - </queue> - <queue> - <name>test-ping</name> - <test-ping> - <exchange>test.direct</exchange> - </test-ping> - </queue> - - </queues> - </localhost> - </virtualhost> - - - <virtualhost> - <name>development</name> - <development> - <queues> - <minimumAlertRepeatGap>30000</minimumAlertRepeatGap> - <maximumMessageCount>50</maximumMessageCount> - <queue> - <name>queue</name> - <queue> - <exchange>amq.direct</exchange> - <maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb --> - <maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb --> - <maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins --> - </queue> - </queue> - <queue> - <name>ping</name> - <ping> - <exchange>amq.direct</exchange> - <maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb --> - <maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb --> - <maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins --> - </ping> - </queue> - </queues> - </development> - </virtualhost> - <virtualhost> - <name>test</name> - <test> - <queues> - <minimumAlertRepeatGap>30000</minimumAlertRepeatGap> - <maximumMessageCount>50</maximumMessageCount> - <queue> - <name>queue</name> - <queue> - <exchange>amq.direct</exchange> - <maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb --> - <maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb --> - <maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins --> - </queue> - </queue> - <queue> - <name>ping</name> - <ping> - <exchange>amq.direct</exchange> - <maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb --> - <maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb --> - <maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins --> - </ping> - </queue> - </queues> - </test> - </virtualhost> -</virtualhosts> diff --git a/qpid/java/broker/etc/virtualhosts.xml b/qpid/java/broker/etc/virtualhosts.xml deleted file mode 100644 index f62ec3f5d7..0000000000 --- a/qpid/java/broker/etc/virtualhosts.xml +++ /dev/null @@ -1,123 +0,0 @@ -<?xml version="1.0" encoding="ISO-8859-1"?> -<!-- - - - - Licensed to the Apache Software Foundation (ASF) under one - - or more contributor license agreements. See the NOTICE file - - distributed with this work for additional information - - regarding copyright ownership. The ASF licenses this file - - to you under the Apache License, Version 2.0 (the - - "License"); you may not use this file except in compliance - - with the License. You may obtain a copy of the License at - - - - http://www.apache.org/licenses/LICENSE-2.0 - - - - Unless required by applicable law or agreed to in writing, - - software distributed under the License is distributed on an - - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - - KIND, either express or implied. See the License for the - - specific language governing permissions and limitations - - under the License. - - - --> -<virtualhosts> - <default>test</default> - <virtualhost> - <name>localhost</name> - <localhost> - <exchanges> - <exchange> - <type>direct</type> - <name>test.direct</name> - <durable>true</durable> - </exchange> - <exchange> - <type>topic</type> - <name>test.topic</name> - </exchange> - </exchanges> - <queues> - <exchange>amq.direct</exchange> - <maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb --> - <maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb --> - <maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins --> - - <queue> - <name>queue</name> - </queue> - <queue> - <name>ping</name> - </queue> - <queue> - <name>test-queue</name> - <test-queue> - <exchange>test.direct</exchange> - <durable>true</durable> - </test-queue> - </queue> - <queue> - <name>test-ping</name> - <test-ping> - <exchange>test.direct</exchange> - </test-ping> - </queue> - - </queues> - </localhost> - </virtualhost> - - - <virtualhost> - <name>development</name> - <development> - <queues> - <minimumAlertRepeatGap>30000</minimumAlertRepeatGap> - <maximumMessageCount>5000</maximumMessageCount> - <queue> - <name>queue</name> - <queue> - <exchange>amq.direct</exchange> - <maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb --> - <maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb --> - <maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins --> - </queue> - </queue> - <queue> - <name>ping</name> - <ping> - <exchange>amq.direct</exchange> - <maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb --> - <maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb --> - <maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins --> - </ping> - </queue> - </queues> - </development> - </virtualhost> - <virtualhost> - <name>test</name> - <test> - <queues> - <minimumAlertRepeatGap>30000</minimumAlertRepeatGap> - <maximumMessageCount>5000</maximumMessageCount> - <queue> - <name>queue</name> - <queue> - <exchange>amq.direct</exchange> - <maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb --> - <maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb --> - <maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins --> - </queue> - </queue> - <queue> - <name>ping</name> - <ping> - <exchange>amq.direct</exchange> - <maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb --> - <maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb --> - <maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins --> - </ping> - </queue> - </queues> - </test> - </virtualhost> -</virtualhosts> diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java index 2afd3c1dc3..5cfa8066e5 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java @@ -317,8 +317,10 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr try { queue.delete(); - _messageStore.removeQueue(queue); - + if (queue.isDurable()) + { + _messageStore.removeQueue(queue); + } } catch (AMQException ex) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java index 7ea7738189..ed9d8acc08 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java @@ -147,32 +147,27 @@ public class ServerConfiguration implements SignalHandler Object thing = i.next(); if (thing instanceof String) { + //Open the Virtualhost.xml file and copy values in to main config XMLConfiguration vhostConfiguration = new XMLConfiguration((String) thing); - List hosts = vhostConfiguration.getList("virtualhost.name"); - for (int j = 0; j < hosts.size(); j++) - { - String name = (String) hosts.get(j); - // Add the keys of the virtual host to the main config then bail out - - Configuration myConf = vhostConfiguration.subset("virtualhost." + name); - Iterator k = myConf.getKeys(); - while (k.hasNext()) - { - String key = (String) k.next(); - conf.setProperty("virtualhosts.virtualhost."+name+"."+key, myConf.getProperty(key)); - } - VirtualHostConfiguration vhostConfig = new VirtualHostConfiguration(name, conf.subset("virtualhosts.virtualhost."+name)); - _virtualHosts.put(vhostConfig.getName(), vhostConfig); - } - // Grab things other than the virtualhosts themselves Iterator keys = vhostConfiguration.getKeys(); while (keys.hasNext()) { String key = (String) keys.next(); - conf.setProperty("virtualhosts."+key, vhostConfiguration.getProperty(key)); + conf.setProperty("virtualhosts." + key, vhostConfiguration.getProperty(key)); } } } + + List hosts = conf.getList("virtualhosts.virtualhost.name"); + for (int j = 0; j < hosts.size(); j++) + { + String name = (String) hosts.get(j); + // Add the keys of the virtual host to the main config then bail out + + VirtualHostConfiguration vhostConfig = new VirtualHostConfiguration(name, conf.subset("virtualhosts.virtualhost." + name)); + _virtualHosts.put(vhostConfig.getName(), vhostConfig); + } + } private void substituteEnvironmentVariables() @@ -202,7 +197,7 @@ public class ServerConfiguration implements SignalHandler } /** - * Check the configuration file to see if status updates are enabled. + * Check the configuration file to see if status updates are enabled. * @return true if status updates are enabled */ public boolean getStatusUpdatesEnabled() @@ -466,7 +461,7 @@ public class ServerConfiguration implements SignalHandler { return getConfig().getBoolean("management.enabled", true); } - + public void setManagementEnabled(boolean enabled) { getConfig().setProperty("management.enabled", enabled); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/StartupRootMessageLogger.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/StartupRootMessageLogger.java index 0dffde50fa..bfb122985b 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/StartupRootMessageLogger.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/StartupRootMessageLogger.java @@ -39,4 +39,11 @@ public class StartupRootMessageLogger extends RootMessageLoggerImpl return true; } + @Override + public boolean isMessageEnabled(LogActor actor) + { + return true; + } + + } 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 42b3b05ac5..aea9ab43ea 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 @@ -379,6 +379,8 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry try { _cs.stop(); + CurrentActor.get().message(ManagementConsoleMessages.MNG_1003("RMI Registry", _cs.getAddress().getPort() - PORT_EXPORT_OFFSET)); + CurrentActor.get().message(ManagementConsoleMessages.MNG_1003("RMI ConnectorServer", _cs.getAddress().getPort())); } catch (IOException e) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java index 5f52805414..569dd8bdbd 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/BasicACLPlugin.java @@ -109,10 +109,4 @@ public abstract class BasicACLPlugin implements ACLPlugin // no-op } - public boolean supportsTag(String name) - { - // This plugin doesn't support any tags - return false; - } - } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java index fa6b2285eb..9ab3592628 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java @@ -195,10 +195,22 @@ public class VirtualHost implements Accessable // perform a createExchange twice with the same details in the // MessageStore(RoutingTable) as some instances may not like that. // Derby being one. + // todo this can be removed with the resolution fo QPID-2096 configFileRT.exchange.clear(); initialiseModel(hostConfig); + //todo REMOVE Work Around for QPID-2096 + // This means that all durable exchanges declared in the configuration + // will not be stored in the MessageStore. + // They will still be created/registered/available on startup for as + // long as they are contained in the configuration. However, when they + // are removed from the configuration they will no longer exist. + // This differs from durable queues as they will be writen to to the + // store. After QPID-2096 has been resolved exchanges will mirror that + // functionality. + configFileRT.exchange.clear(); + if (store != null) { _messageStore = store; diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java index 8cb0837b39..ebfa80d139 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java @@ -54,7 +54,7 @@ public class ServerConfigurationTest extends TestCase _config = new XMLConfiguration(); } - + @Override public void tearDown() throws Exception { @@ -727,7 +727,7 @@ public class ServerConfigurationTest extends TestCase assertEquals(true, config.getQpidNIO()); // From the second file, not // present in the first } - + public void testVariableInterpolation() throws Exception { File mainFile = File.createTempFile(getClass().getName(), null); @@ -742,7 +742,7 @@ public class ServerConfigurationTest extends TestCase out.close(); ServerConfiguration config = new ServerConfiguration(mainFile.getAbsoluteFile()); - assertEquals("Did not get correct interpolated value", + assertEquals("Did not get correct interpolated value", "foo", config.getManagementKeyStorePath()); } @@ -783,7 +783,7 @@ public class ServerConfigurationTest extends TestCase out.write("\t</virtualhosts>\n"); out.write("</broker>\n"); out.close(); - + // Load config ApplicationRegistry reg = new ConfigurationFileApplicationRegistry(mainFile); ApplicationRegistry.initialise(reg, 1); @@ -795,15 +795,15 @@ public class ServerConfigurationTest extends TestCase TestIoSession iosession = new TestIoSession(); iosession.setAddress("127.0.0.1"); - + AMQProtocolSession session = new AMQMinaProtocolSession(iosession, virtualHostRegistry, codecFactory); assertFalse(reg.getAccessManager().authoriseConnect(session, virtualHost)); - + iosession.setAddress("127.1.2.3"); session = new AMQMinaProtocolSession(iosession, virtualHostRegistry, codecFactory); assertTrue(reg.getAccessManager().authoriseConnect(session, virtualHost)); } - + public void testCombinedConfigurationFirewall() throws Exception { // Write out config @@ -870,7 +870,7 @@ public class ServerConfigurationTest extends TestCase TestIoSession iosession = new TestIoSession(); iosession.setAddress("127.0.0.1"); - + AMQProtocolSession session = new AMQMinaProtocolSession(iosession, virtualHostRegistry, codecFactory); assertFalse(reg.getAccessManager().authoriseConnect(session, virtualHost)); } @@ -947,22 +947,22 @@ public class ServerConfigurationTest extends TestCase fileBRandom.setLength(0); fileBRandom.seek(0); fileBRandom.close(); - + out = new FileWriter(fileB); out.write("<firewall>\n"); out.write("\t<rule access=\"allow\" network=\"127.0.0.1\"/>"); out.write("</firewall>\n"); out.close(); - + reg.getConfiguration().reparseConfigFile(); - + assertTrue(reg.getAccessManager().authoriseConnect(session, virtualHost)); - + fileBRandom = new RandomAccessFile(fileB, "rw"); fileBRandom.setLength(0); fileBRandom.seek(0); fileBRandom.close(); - + out = new FileWriter(fileB); out.write("<firewall>\n"); out.write("\t<rule access=\"deny\" network=\"127.0.0.1\"/>"); @@ -970,17 +970,17 @@ public class ServerConfigurationTest extends TestCase out.close(); reg.getConfiguration().reparseConfigFile(); - + assertFalse(reg.getAccessManager().authoriseConnect(session, virtualHost)); } public void testnewParserOutputVsOldParserOutput() throws ConfigurationException { String configDir = System.getProperty("QPID_HOME")+"/etc"; - - XMLConfiguration oldConfig = new XMLConfiguration(configDir +"/sample-parsed-config.xml"); - Configuration newConfig = new ServerConfiguration(new File(configDir+"/persistent_config-config-test.xml")).getConfig(); - + + XMLConfiguration oldConfig = new XMLConfiguration(configDir +"/config-systests-ServerConfigurationTest-Old.xml"); + Configuration newConfig = new ServerConfiguration(new File(configDir+"/config-systests-ServerConfigurationTest-New.xml")).getConfig(); + Iterator xmlKeys = oldConfig.getKeys(); while (xmlKeys.hasNext()) { @@ -988,6 +988,21 @@ public class ServerConfigurationTest extends TestCase assertEquals("Incorrect value for "+key, oldConfig.getProperty(key), newConfig.getProperty(key)); } } - - + + + public void testNoVirtualhostXMLFile() throws Exception + { + int REGISTRY=1; + + File configFile = new File(System.getProperty("QPID_HOME")+"/etc/config.xml"); + assertTrue(configFile.exists()); + + ApplicationRegistry.initialise(new ConfigurationFileApplicationRegistry(configFile), REGISTRY); + + VirtualHostRegistry virtualHostRegistry = ApplicationRegistry.getInstance(REGISTRY).getVirtualHostRegistry(); + + assertEquals("Incorrect virtualhost count", 3 , virtualHostRegistry.getVirtualHosts().size()); + } + + } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.java index a964fa931c..0e754e2c87 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/ExchangeDenier.java @@ -47,17 +47,4 @@ public class ExchangeDenier extends AllowAll { return AuthzResult.DENIED; } - - @Override - public String getPluginName() - { - return getClass().getSimpleName(); - } - - @Override - public boolean supportsTag(String name) - { - return name.equals("exchangeDenier"); - } - } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java index 05ac3dca9e..6bae0166d1 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java @@ -39,9 +39,4 @@ public class AMQAuthenticationException extends AMQException { super(error, msg, cause); } - public boolean isHardError() - { - return true; - } - } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java index 118be75705..2e3e417c95 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java @@ -65,6 +65,7 @@ import org.apache.qpid.AMQDisconnectedException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQInvalidArgumentException; import org.apache.qpid.AMQInvalidRoutingKeyException; +import org.apache.qpid.AMQChannelClosedException; import org.apache.qpid.client.failover.FailoverException; import org.apache.qpid.client.failover.FailoverNoopSupport; import org.apache.qpid.client.failover.FailoverProtectedOperation; @@ -205,9 +206,9 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic */ protected static final boolean DEFAULT_MANDATORY = Boolean.parseBoolean(System.getProperty("qpid.default_mandatory", "true")); - protected static final boolean DECLARE_QUEUES = + protected final boolean DECLARE_QUEUES = Boolean.parseBoolean(System.getProperty("qpid.declare_queues", "true")); - protected static final boolean DECLARE_EXCHANGES = + protected final boolean DECLARE_EXCHANGES = Boolean.parseBoolean(System.getProperty("qpid.declare_exchanges", "true")); /** System property to enable strict AMQP compliance. */ @@ -629,6 +630,11 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic */ public void close(long timeout) throws JMSException { + close(timeout, true); + } + + private void close(long timeout, boolean sendClose) throws JMSException + { if (_logger.isInfoEnabled()) { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); @@ -654,9 +660,12 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic // If the connection is open or we are in the process // of closing the connection then send a cance // no point otherwise as the connection will be gone - if (!_connection.isClosed() || _connection.isClosing()) + if (!_connection.isClosed() || _connection.isClosing()) { - sendClose(timeout); + if (sendClose) + { + sendClose(timeout); + } } } catch (AMQException e) @@ -712,25 +721,22 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic if (!_closed.getAndSet(true)) { - synchronized (getFailoverMutex()) + synchronized (_messageDeliveryLock) { - synchronized (_messageDeliveryLock) + // An AMQException has an error code and message already and will be passed in when closure occurs as a + // result of a channel close request + AMQException amqe; + if (e instanceof AMQException) { - // An AMQException has an error code and message already and will be passed in when closure occurs as a - // result of a channel close request - AMQException amqe; - if (e instanceof AMQException) - { - amqe = (AMQException) e; - } - else - { - amqe = new AMQException("Closing session forcibly", e); - } - - _connection.deregisterSession(_channelId); - closeProducersAndConsumers(amqe); + amqe = (AMQException) e; + } + else + { + amqe = new AMQException("Closing session forcibly", e); } + + _connection.deregisterSession(_channelId); + closeProducersAndConsumers(amqe); } } } @@ -1737,6 +1743,11 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic } catch (AMQException e) { + if (e instanceof AMQChannelClosedException) + { + close(-1, false); + } + JMSException ex = new JMSException("Error registering consumer: " + e); ex.setLinkedException(e); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java index fa4e08f62b..d7196c0abb 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java @@ -33,6 +33,7 @@ import org.apache.qpid.client.message.*; import org.apache.qpid.client.message.AMQMessageDelegateFactory; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.client.state.listener.SpecificMethodFrameListener; +import org.apache.qpid.client.state.AMQState; import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.framing.*; import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; @@ -116,12 +117,23 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B public void sendClose(long timeout) throws AMQException, FailoverException { - getProtocolHandler().closeSession(this); - getProtocolHandler().syncWrite(getProtocolHandler().getMethodRegistry().createChannelCloseBody(AMQConstant.REPLY_SUCCESS.getCode(), - new AMQShortString("JMS client closing channel"), 0, 0).generateFrame(_channelId), - ChannelCloseOkBody.class, timeout); - // When control resumes at this point, a reply will have been received that - // indicates the broker has closed the channel successfully. + // we also need to check the state manager for 08/09 as the + // _connection variable may not be updated in time by the error receiving + // thread. + // We can't close the session if we are alreadying in the process of + // closing/closed the connection. + + if (!(getProtocolHandler().getStateManager().getCurrentState().equals(AMQState.CONNECTION_CLOSED) + || getProtocolHandler().getStateManager().getCurrentState().equals(AMQState.CONNECTION_CLOSING))) + { + + getProtocolHandler().closeSession(this); + getProtocolHandler().syncWrite(getProtocolHandler().getMethodRegistry().createChannelCloseBody(AMQConstant.REPLY_SUCCESS.getCode(), + new AMQShortString("JMS client closing channel"), 0, 0).generateFrame(_channelId), + ChannelCloseOkBody.class, timeout); + // When control resumes at this point, a reply will have been received that + // indicates the broker has closed the channel successfully. + } } public void sendCommit() throws AMQException, FailoverException diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java index 2b6745ebe4..2cf19bf391 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java @@ -32,7 +32,6 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ChannelCloseBody; import org.apache.qpid.framing.ChannelCloseOkBody; import org.apache.qpid.protocol.AMQConstant; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,7 +47,7 @@ public class ChannelCloseMethodHandler implements StateAwareMethodListener<Chann } public void methodReceived(AMQProtocolSession session, ChannelCloseBody method, int channelId) - throws AMQException + throws AMQException { _logger.debug("ChannelClose method received"); @@ -59,52 +58,62 @@ public class ChannelCloseMethodHandler implements StateAwareMethodListener<Chann _logger.debug("Channel close reply code: " + errorCode + ", reason: " + reason); } - - ChannelCloseOkBody body = session.getMethodRegistry().createChannelCloseOkBody(); AMQFrame frame = body.generateFrame(channelId); session.writeFrame(frame); - - if (errorCode != AMQConstant.REPLY_SUCCESS) + try { - if (_logger.isDebugEnabled()) - { - _logger.debug("Channel close received with errorCode " + errorCode + ", and reason " + reason); - } - - if (errorCode == AMQConstant.NO_CONSUMERS) - { - throw new AMQNoConsumersException("Error: " + reason, null, null); - } - else if (errorCode == AMQConstant.NO_ROUTE) - { - throw new AMQNoRouteException("Error: " + reason, null, null); - } - else if (errorCode == AMQConstant.INVALID_ARGUMENT) + if (errorCode != AMQConstant.REPLY_SUCCESS) { - _logger.debug("Broker responded with Invalid Argument."); + if (_logger.isDebugEnabled()) + { + _logger.debug("Channel close received with errorCode " + errorCode + ", and reason " + reason); + } + + if (errorCode == AMQConstant.NO_CONSUMERS) + { + throw new AMQNoConsumersException("Error: " + reason, null, null); + } + else if (errorCode == AMQConstant.NO_ROUTE) + { + throw new AMQNoRouteException("Error: " + reason, null, null); + } + else if (errorCode == AMQConstant.INVALID_ARGUMENT) + { + _logger.debug("Broker responded with Invalid Argument."); + + throw new org.apache.qpid.AMQInvalidArgumentException(String.valueOf(reason), null); + } + else if (errorCode == AMQConstant.INVALID_ROUTING_KEY) + { + _logger.debug("Broker responded with Invalid Routing Key."); + + throw new AMQInvalidRoutingKeyException(String.valueOf(reason), null); + } + else + { + + throw new AMQChannelClosedException(errorCode, "Error: " + reason, null); + } - throw new org.apache.qpid.AMQInvalidArgumentException(String.valueOf(reason), null); - } - else if (errorCode == AMQConstant.INVALID_ROUTING_KEY) - { - _logger.debug("Broker responded with Invalid Routing Key."); - - throw new AMQInvalidRoutingKeyException(String.valueOf(reason), null); - } - else - { - throw new AMQChannelClosedException(errorCode, "Error: " + reason, null); } - } - // fixme why is this only done when the close is expected... - // should the above forced closes not also cause a close? - // ---------- - // Closing the session only when it is expected allows the errors to be processed - // Calling this here will prevent failover. So we should do this for all exceptions - // that should never cause failover. Such as authentication errors. - - session.channelClosed(channelId, errorCode, String.valueOf(reason)); + finally + { + // fixme why is this only done when the close is expected... + // should the above forced closes not also cause a close? + // ---------- + // Closing the session only when it is expected allows the errors to be processed + // Calling this here will prevent failover. So we should do this for all exceptions + // that should never cause failover. Such as authentication errors. + // ---- + // 2009-09-07 - ritchiem + // calling channelClosed will only close this session and will not + // prevent failover. If we don't close the session here then we will + // have problems during the session close as it will attempt to + // close the session that the broker has closed, + + session.channelClosed(channelId, errorCode, String.valueOf(reason)); + } } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java index e639a33450..e40cafd72f 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java @@ -40,7 +40,6 @@ public class ConnectionOpenOkMethodHandler implements StateAwareMethodListener<C } public void methodReceived(AMQProtocolSession session, ConnectionOpenOkBody body, int channelId) - throws AMQException { session.getStateManager().changeState(AMQState.CONNECTION_OPEN); } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java index e4e58c317d..287b5957a1 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java @@ -45,7 +45,6 @@ public class ConnectionTuneMethodHandler implements StateAwareMethodListener<Con { } public void methodReceived(AMQProtocolSession session, ConnectionTuneBody frame, int channelId) - throws AMQException { _logger.debug("ConnectionTune frame received"); final MethodRegistry methodRegistry = session.getMethodRegistry(); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQIoTransportProtocolSession.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQIoTransportProtocolSession.java index f2aca58deb..8782e00a12 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQIoTransportProtocolSession.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQIoTransportProtocolSession.java @@ -52,7 +52,7 @@ public class AMQIoTransportProtocolSession extends AMQProtocolSession } @Override - public void closeProtocolSession(boolean waitLast) throws AMQException + public void closeProtocolSession(boolean waitLast) { _ioSender.close(); _protocolHandler.getStateManager().changeState(AMQState.CONNECTION_CLOSED); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java index 2389c9e2da..6f62070fd0 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java @@ -259,7 +259,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter /** * Called when we want to create a new IoTransport session - * @param brokerDetail + * @param brokerDetail */ public void createIoTransportSession(BrokerDetails brokerDetail) { @@ -271,7 +271,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter brokerDetail.useSSL()); _protocolSession.init(); } - + /** * Called when the network connection is closed. This can happen, either because the client explicitly requested * that the connection be closed, in which case nothing is done, or because the connection died. In the case @@ -433,12 +433,11 @@ public class AMQProtocolHandler extends IoHandlerAdapter * @param e the exception to propagate * * @see #propagateExceptionToFrameListeners - * @see #propagateExceptionToStateWaiters */ public void propagateExceptionToAllWaiters(Exception e) { + getStateManager().error(e); propagateExceptionToFrameListeners(e); - propagateExceptionToStateWaiters(e); } /** @@ -469,22 +468,6 @@ public class AMQProtocolHandler extends IoHandlerAdapter } } - /** - * This caters for the case where we only need to propogate an exception to the the state manager to interupt any - * thing waiting for a state change. - * - * Currently (2008-07-15) the state manager is only used during 0-8/0-9 Connection establishement. - * - * Normally the state manager would not need to be notified without notifiying the frame listeners so in normal - * cases {@link #propagateExceptionToAllWaiters} would be the correct choice. - * - * @param e the exception to propagate - */ - public void propagateExceptionToStateWaiters(Exception e) - { - getStateManager().error(e); - } - public void notifyFailoverStarting() { // Set the last exception in the sync block to ensure the ordering with add. @@ -601,7 +584,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter { _protocolLogger.debug(String.format("SEND: [%s] %s", this, message)); } - + final long sentMessages = _messagesOut++; final boolean debug = _logger.isDebugEnabled(); @@ -667,7 +650,8 @@ public class AMQProtocolHandler extends IoHandlerAdapter throw _lastFailoverException; } - if(_stateManager.getCurrentState() == AMQState.CONNECTION_CLOSED) + if(_stateManager.getCurrentState() == AMQState.CONNECTION_CLOSED || + _stateManager.getCurrentState() == AMQState.CONNECTION_CLOSING) { Exception e = _stateManager.getLastException(); if (e != null) @@ -733,25 +717,31 @@ public class AMQProtocolHandler extends IoHandlerAdapter */ public void closeConnection(long timeout) throws AMQException { - getStateManager().changeState(AMQState.CONNECTION_CLOSING); - ConnectionCloseBody body = _protocolSession.getMethodRegistry().createConnectionCloseBody(AMQConstant.REPLY_SUCCESS.getCode(), // replyCode new AMQShortString("JMS client is closing the connection."), 0, 0); final AMQFrame frame = body.generateFrame(0); - try - { - syncWrite(frame, ConnectionCloseOkBody.class, timeout); - _protocolSession.closeProtocolSession(); - } - catch (AMQTimeoutException e) + //If the connection is already closed then don't do a syncWrite + if (getStateManager().getCurrentState().equals(AMQState.CONNECTION_CLOSED)) { _protocolSession.closeProtocolSession(false); } - catch (FailoverException e) + else { - _logger.debug("FailoverException interrupted connection close, ignoring as connection close anyway."); + try + { + syncWrite(frame, ConnectionCloseOkBody.class, timeout); + _protocolSession.closeProtocolSession(); + } + catch (AMQTimeoutException e) + { + _protocolSession.closeProtocolSession(false); + } + catch (FailoverException e) + { + _logger.debug("FailoverException interrupted connection close, ignoring as connection close anyway."); + } } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java index 5e12a5e6f8..0e872170aa 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java @@ -410,12 +410,12 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession return (AMQConnection) _minaProtocolSession.getAttribute(AMQ_CONNECTION); } - public void closeProtocolSession() throws AMQException + public void closeProtocolSession() { closeProtocolSession(true); } - public void closeProtocolSession(boolean waitLast) throws AMQException + public void closeProtocolSession(boolean waitLast) { _logger.debug("Waiting for last write to join."); if (waitLast && (_lastWriteFuture != null)) diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java b/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java index f8645139f2..70d4697f2c 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java @@ -31,6 +31,7 @@ import org.slf4j.LoggerFactory; import java.util.Set; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.io.IOException; /** * The state manager is responsible for managing the state of the protocol session. <p/> @@ -86,7 +87,7 @@ public class AMQStateManager implements AMQMethodListener return _currentState; } - public void changeState(AMQState newState) throws AMQException + public void changeState(AMQState newState) { _logger.debug("State changing to " + newState + " from old state " + _currentState); @@ -136,6 +137,22 @@ public class AMQStateManager implements AMQMethodListener */ public void error(Exception error) { + if (error instanceof AMQException) + { + // AMQException should be being notified before closing the + // ProtocolSession. Which will change the State to CLOSED. + // if we have a hard error. + if (((AMQException)error).isHardError()) + { + changeState(AMQState.CONNECTION_CLOSING); + } + } + else + { + // Be on the safe side here and mark the connection closed + changeState(AMQState.CONNECTION_CLOSED); + } + if (_waiters.size() == 0) { _logger.error("No Waiters for error saving as last error:" + error.getMessage()); diff --git a/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java b/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java index ce79080e97..da44822ec3 100644 --- a/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java +++ b/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java @@ -85,7 +85,7 @@ public class MockAMQConnection extends AMQConnection } @Override - public ProtocolVersion makeBrokerConnection(BrokerDetails brokerDetail) throws IOException, AMQException + public ProtocolVersion makeBrokerConnection(BrokerDetails brokerDetail) throws IOException { _connected = true; _protocolHandler.getStateManager().changeState(AMQState.CONNECTION_OPEN); diff --git a/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java b/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java index 10ec220d9e..fc7f8148f0 100644 --- a/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java +++ b/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java @@ -200,15 +200,8 @@ public class AMQProtocolHandlerTest extends TestCase _handler.getStateManager().error(trigger); _logger.info("Setting state to be CONNECTION_CLOSED."); - try - { - _handler.getStateManager().changeState(AMQState.CONNECTION_CLOSED); - } - catch (AMQException e) - { - _logger.error("Unable to change the state to closed.", e); - fail("Unable to change the state to closed due to :"+e.getMessage()); - } + + _handler.getStateManager().changeState(AMQState.CONNECTION_CLOSED); _logger.info("Firing exception"); _handler.propagateExceptionToFrameListeners(trigger); diff --git a/qpid/java/doc/broker-priority-queue-subscription.dia b/qpid/java/doc/broker-priority-queue-subscription.dia Binary files differnew file mode 100644 index 0000000000..2289899435 --- /dev/null +++ b/qpid/java/doc/broker-priority-queue-subscription.dia diff --git a/qpid/java/doc/broker-queue-subscription.dia b/qpid/java/doc/broker-queue-subscription.dia Binary files differnew file mode 100644 index 0000000000..d146ad136d --- /dev/null +++ b/qpid/java/doc/broker-queue-subscription.dia diff --git a/qpid/java/broker/etc/persistent_config-config-test.xml b/qpid/java/systests/etc/config-systests-ServerConfigurationTest-New.xml index 258f54397d..33cc90b895 100644 --- a/qpid/java/broker/etc/persistent_config-config-test.xml +++ b/qpid/java/systests/etc/config-systests-ServerConfigurationTest-New.xml @@ -87,7 +87,7 @@ <auto_register>true</auto_register> </queue> - <virtualhosts>${conf}/virtualhosts-config-test.xml</virtualhosts> + <virtualhosts>${conf}/virtualhosts-ServerConfigurationTest-New.xml</virtualhosts> </broker> diff --git a/qpid/java/broker/etc/sample-parsed-config.xml b/qpid/java/systests/etc/config-systests-ServerConfigurationTest-Old.xml index 37dfae4d2e..67e0702c41 100644 --- a/qpid/java/broker/etc/sample-parsed-config.xml +++ b/qpid/java/systests/etc/config-systests-ServerConfigurationTest-Old.xml @@ -41,7 +41,7 @@ <principal-database>passwordfile</principal-database> </jmx> </security> -<virtualhosts>${conf}/virtualhosts-config-test.xml +<virtualhosts>${conf}/virtualhosts-ServerConfigurationTest-New.xml <default>dev-only</default> <virtualhost> <name>dev-only</name> diff --git a/qpid/java/broker/etc/acl.config.xml b/qpid/java/systests/etc/config-systests-acl-settings.xml index a2b723fc63..c5374a5c5e 100644 --- a/qpid/java/broker/etc/acl.config.xml +++ b/qpid/java/systests/etc/config-systests-acl-settings.xml @@ -20,76 +20,12 @@ - --> <broker> - <prefix>${QPID_HOME}</prefix> - <work>${QPID_WORK}</work> - <conf>${prefix}/etc</conf> - <connector> - <!-- Uncomment out this block and edit the keystorePath and keystorePassword - to enable SSL support - <ssl> - <enabled>true</enabled> - <sslOnly>true</sslOnly> - <keystorePath>/path/to/keystore.ks</keystorePath> - <keystorePassword>keystorepass</keystorePassword> - </ssl>--> - <qpidnio>false</qpidnio> - <!-- I've had the 0.0 and 0.1 Reader threads continually throwing IOException when client closes--> - <protectio>false</protectio> - <transport>nio</transport> - <port>5672</port> - <sslport>8672</sslport> - <socketReceiveBuffer>32768</socketReceiveBuffer> - <socketSendBuffer>32768</socketSendBuffer> - </connector> - <management> - <enabled>false</enabled> - <jmxport>8999</jmxport> - <security-enabled>false</security-enabled> - </management> - <advanced> - <filterchain enableExecutorPool="true"/> - <enablePooledAllocator>false</enablePooledAllocator> - <enableDirectBuffers>false</enableDirectBuffers> - <framesize>65535</framesize> - <compressBufferOnQueue>false</compressBufferOnQueue> - <enableJMSXUserID>false</enableJMSXUserID> - </advanced> - - <security> - <principal-databases> - <!-- Example use of Base64 encoded MD5 hashes for authentication via CRAM-MD5-Hashed --> - <principal-database> - <name>passwordfile</name> - <class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class> - <attributes> - <attribute> - <name>passwordFile</name> - <value>${conf}/passwd</value> - </attribute> - </attributes> - </principal-database> - </principal-databases> - - <access> - <class>org.apache.qpid.server.security.access.plugins.DenyAll</class> - </access> - - <jmx> - <access>${conf}/jmxremote.access</access> - <principal-database>passwordfile</principal-database> - </jmx> - </security> <virtualhosts> - <directory>${conf}/virtualhosts</directory> <virtualhost> <name>test</name> <test> - <store> - <class>org.apache.qpid.server.store.MemoryMessageStore</class> - </store> - <queues> <exchange>amq.direct</exchange> <!-- 4Mb --> @@ -197,34 +133,7 @@ </security> </test> </virtualhost> - - - <virtualhost> - <name>development</name> - <development> - <store> - <class>org.apache.qpid.server.store.MemoryMessageStore</class> - </store> - </development> - </virtualhost> - - <virtualhost> - <name>localhost</name> - <localhost> - <store> - <class>org.apache.qpid.server.store.MemoryMessageStore</class> - </store> - </localhost> - </virtualhost> - </virtualhosts> - - <heartbeat> - <delay>0</delay> - <timeoutFactor>2.0</timeoutFactor> - </heartbeat> - - <virtualhosts>${conf}/virtualhosts.xml</virtualhosts> </broker> diff --git a/qpid/java/systests/etc/config-systests-acl.xml b/qpid/java/systests/etc/config-systests-acl.xml new file mode 100644 index 0000000000..34104dbe6b --- /dev/null +++ b/qpid/java/systests/etc/config-systests-acl.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + - + - Licensed to the Apache Software Foundation (ASF) under one + - or more contributor license agreements. See the NOTICE file + - distributed with this work for additional information + - regarding copyright ownership. The ASF licenses this file + - to you under the Apache License, Version 2.0 (the + - "License"); you may not use this file except in compliance + - with the License. You may obtain a copy of the License at + - + - http://www.apache.org/licenses/LICENSE-2.0 + - + - Unless required by applicable law or agreed to in writing, + - software distributed under the License is distributed on an + - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + - KIND, either express or implied. See the License for the + - specific language governing permissions and limitations + - under the License. + - + --> +<configuration> + <system/> + <override> + <xml fileName="${test.config}" config-optional="true"/> + <xml fileName="${QPID_HOME}/etc/config-systests-acl-settings.xml"/> + <xml fileName="${QPID_HOME}/etc/config-systests-settings.xml"/> + <xml fileName="${QPID_HOME}/etc/config.xml"/> + </override> +</configuration> diff --git a/qpid/java/systests/etc/config-systests-derby-settings.xml b/qpid/java/systests/etc/config-systests-derby-settings.xml new file mode 100644 index 0000000000..9c25b5682e --- /dev/null +++ b/qpid/java/systests/etc/config-systests-derby-settings.xml @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + - + - Licensed to the Apache Software Foundation (ASF) under one + - or more contributor license agreements. See the NOTICE file + - distributed with this work for additional information + - regarding copyright ownership. The ASF licenses this file + - to you under the Apache License, Version 2.0 (the + - "License"); you may not use this file except in compliance + - with the License. You may obtain a copy of the License at + - + - http://www.apache.org/licenses/LICENSE-2.0 + - + - Unless required by applicable law or agreed to in writing, + - software distributed under the License is distributed on an + - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + - KIND, either express or implied. See the License for the + - specific language governing permissions and limitations + - under the License. + - + --> +<broker> + <virtualhosts> + <directory>${conf}/virtualhosts</directory> + + <virtualhost> + <name>localhost</name> + <localhost> + <store> + <class>org.apache.qpid.server.store.DerbyMessageStore</class> + <environment-path>${work}/derbyDB/localhost-store</environment-path> + </store> + + <housekeeping> + <expiredMessageCheckPeriod>20000</expiredMessageCheckPeriod> + </housekeeping> + + </localhost> + </virtualhost> + + <virtualhost> + <name>development</name> + <development> + <store> + <class>org.apache.qpid.server.store.DerbyMessageStore</class> + <environment-path>${work}/derbyDB/development-store</environment-path> + </store> + </development> + </virtualhost> + + <virtualhost> + <name>test</name> + <test> + <store> + <class>org.apache.qpid.server.store.DerbyMessageStore</class> + <environment-path>${work}/derbyDB/test-store</environment-path> + </store> + </test> + </virtualhost> + + </virtualhosts> +</broker> + + diff --git a/qpid/java/systests/etc/config-systests-derby.xml b/qpid/java/systests/etc/config-systests-derby.xml new file mode 100644 index 0000000000..18ba0c4ad9 --- /dev/null +++ b/qpid/java/systests/etc/config-systests-derby.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + - + - Licensed to the Apache Software Foundation (ASF) under one + - or more contributor license agreements. See the NOTICE file + - distributed with this work for additional information + - regarding copyright ownership. The ASF licenses this file + - to you under the Apache License, Version 2.0 (the + - "License"); you may not use this file except in compliance + - with the License. You may obtain a copy of the License at + - + - http://www.apache.org/licenses/LICENSE-2.0 + - + - Unless required by applicable law or agreed to in writing, + - software distributed under the License is distributed on an + - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + - KIND, either express or implied. See the License for the + - specific language governing permissions and limitations + - under the License. + - + --> +<configuration> + <system/> + <override> + <xml fileName="${test.config}" config-optional="true"/> + <xml fileName="${QPID_HOME}/etc/config-systests-derby-settings.xml"/> + <xml fileName="${QPID_HOME}/etc/config-systests-settings.xml"/> + <xml fileName="${QPID_HOME}/etc/config.xml"/> + </override> +</configuration> diff --git a/qpid/java/systests/etc/config-systests-settings.xml b/qpid/java/systests/etc/config-systests-settings.xml new file mode 100644 index 0000000000..4e9c863fda --- /dev/null +++ b/qpid/java/systests/etc/config-systests-settings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + - + - Licensed to the Apache Software Foundation (ASF) under one + - or more contributor license agreements. See the NOTICE file + - distributed with this work for additional information + - regarding copyright ownership. The ASF licenses this file + - to you under the Apache License, Version 2.0 (the + - "License"); you may not use this file except in compliance + - with the License. You may obtain a copy of the License at + - + - http://www.apache.org/licenses/LICENSE-2.0 + - + - Unless required by applicable law or agreed to in writing, + - software distributed under the License is distributed on an + - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + - KIND, either express or implied. See the License for the + - specific language governing permissions and limitations + - under the License. + - + --> +<broker> + <management> + <enabled>false</enabled> + <ssl> + <enabled>false</enabled> + </ssl> + </management> +</broker> diff --git a/qpid/java/systests/etc/config-systests.xml b/qpid/java/systests/etc/config-systests.xml new file mode 100644 index 0000000000..290c082a4f --- /dev/null +++ b/qpid/java/systests/etc/config-systests.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + - + - Licensed to the Apache Software Foundation (ASF) under one + - or more contributor license agreements. See the NOTICE file + - distributed with this work for additional information + - regarding copyright ownership. The ASF licenses this file + - to you under the Apache License, Version 2.0 (the + - "License"); you may not use this file except in compliance + - with the License. You may obtain a copy of the License at + - + - http://www.apache.org/licenses/LICENSE-2.0 + - + - Unless required by applicable law or agreed to in writing, + - software distributed under the License is distributed on an + - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + - KIND, either express or implied. See the License for the + - specific language governing permissions and limitations + - under the License. + - + --> +<configuration> + <system/> + <override> + <xml fileName="${test.config}" config-optional="true"/> + <xml fileName="${QPID_HOME}/etc/config-systests-settings.xml"/> + <xml fileName="${QPID_HOME}/etc/config.xml"/> + </override> +</configuration> diff --git a/qpid/java/broker/etc/virtualhosts-config-test.xml b/qpid/java/systests/etc/virtualhosts-ServerConfigurationTest-New.xml index 168aa074da..168aa074da 100644 --- a/qpid/java/broker/etc/virtualhosts-config-test.xml +++ b/qpid/java/systests/etc/virtualhosts-ServerConfigurationTest-New.xml diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java index 7a2266902b..2e107ada34 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java @@ -20,24 +20,21 @@ */ package org.apache.qpid.management.jmx; -import org.apache.qpid.commands.objects.AllObjects; -import org.apache.qpid.management.common.JMXConnnectionFactory; import org.apache.qpid.management.common.mbeans.ManagedBroker; import org.apache.qpid.management.common.mbeans.ManagedConnection; import org.apache.qpid.management.common.mbeans.ManagedExchange; import org.apache.qpid.server.logging.AbstractTestLogging; import org.apache.qpid.server.logging.subjects.AbstractTestLogSubject; +import org.apache.qpid.test.utils.JMXTestUtils; import javax.jms.Connection; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; import javax.management.JMException; -import javax.management.MBeanException; -import javax.management.MBeanServerConnection; -import javax.management.MBeanServerInvocationHandler; -import javax.management.ObjectName; -import javax.management.remote.JMXConnector; import java.io.IOException; import java.util.List; -import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Test class to test if any change in the broker JMX code is affesting the management console @@ -46,33 +43,21 @@ import java.util.Set; */ public class ManagementActorLoggingTest extends AbstractTestLogging { - MBeanServerConnection _mbsc; - JMXConnector _jmxc; + private JMXTestUtils _jmxUtils; private static final String USER = "admin"; @Override public void setUp() throws Exception { - setConfigurationProperty("management.enabled", "true"); + _jmxUtils = new JMXTestUtils(this, USER, USER); + _jmxUtils.setUp(); super.setUp(); - - if (isExternalBroker()) - { - _jmxc = JMXConnnectionFactory.getJMXConnection( - 5000, "127.0.0.1", - getManagementPort(getPort()), USER, USER); - - _mbsc = _jmxc.getMBeanServerConnection(); - } } @Override public void tearDown() throws Exception { - if (isExternalBroker()) - { - _jmxc.close(); - } + _jmxUtils.close(); super.tearDown(); } @@ -103,34 +88,31 @@ public class ManagementActorLoggingTest extends AbstractTestLogging */ public void testJMXManagementConsoleConnection() throws IOException { - if (isExternalBroker()) - { - List<String> results = _monitor.findMatches("MNG-1007"); + List<String> results = _monitor.findMatches("MNG-1007"); - assertEquals("Unexpected Management Connection count", 1, results.size()); + assertEquals("Unexpected Management Connection count", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - validateMessageID("MNG-1007", log); + validateMessageID("MNG-1007", log); - assertTrue("User not in log message:" + log, log.endsWith(USER)); - // Extract the id from the log string - // MESSAGE [mng:1(rmi://169.24.29.116)] MNG-1007 : Open : User admin - int connectionID = Integer.parseInt(fromActor(getLog(results.get(0))).charAt(4) + ""); + assertTrue("User not in log message:" + log, log.endsWith(USER)); + // Extract the id from the log string + // MESSAGE [mng:1(rmi://169.24.29.116)] MNG-1007 : Open : User admin + int connectionID = Integer.parseInt(fromActor(getLog(results.get(0))).charAt(4) + ""); - results = _monitor.findMatches("MNG-1008"); + results = _monitor.findMatches("MNG-1008"); - assertEquals("Unexpected Management Connection close count", 0, results.size()); + assertEquals("Unexpected Management Connection close count", 0, results.size()); - _jmxc.close(); + _jmxUtils.close(); - results = _monitor.findMatches("MNG-1008"); + results = _monitor.findMatches("MNG-1008"); - assertEquals("Unexpected Management Connection count", 1, results.size()); + assertEquals("Unexpected Management Connection count", 1, results.size()); - assertEquals("Close does not have same id as open,", connectionID, - Integer.parseInt(fromActor(getLog(results.get(0))).charAt(4) + "")); - } + assertEquals("Close does not have same id as open,", connectionID, + Integer.parseInt(fromActor(getLog(results.get(0))).charAt(4) + "")); } /** @@ -155,38 +137,40 @@ public class ManagementActorLoggingTest extends AbstractTestLogging */ public void testConnectionCloseViaManagement() throws IOException, Exception { - if (isExternalBroker()) + //Create a connection to the broker + Connection connection = getConnection(); + + // Monitor the connection for an exception being thrown + // this should be a DisconnectionException but it is not this tests + // job to valiate that. Only use the exception as a synchronisation + // to check the log file for the Close message + final CountDownLatch exceptionReceived = new CountDownLatch(1); + connection.setExceptionListener(new ExceptionListener() { + public void onException(JMSException e) + { + //Failover being attempted. + exceptionReceived.countDown(); + } + }); - //Create a connection to the broker - Connection connection = getConnection(); - - // Get all active AMQP connections - AllObjects allObject = new AllObjects(_mbsc); - allObject.querystring = "org.apache.qpid:type=VirtualHost.Connection,*"; - - Set<ObjectName> objectNames = allObject.returnObjects(); - - assertEquals("More than one test connection returned", 1, objectNames.size()); + //Remove the connection close from any 0-10 connections + _monitor.reset(); - ObjectName connectionName = objectNames.iterator().next(); + // Get a managedConnection + ManagedConnection mangedConnection = _jmxUtils.getManagedObject(ManagedConnection.class, "org.apache.qpid:type=VirtualHost.Connection,*"); - ManagedConnection mangedConnection = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, connectionName, - ManagedConnection.class, false); + //Close the connection + mangedConnection.closeConnection(); - //Remove the connection close from any 0-10 connections - _monitor.reset(); + //Wait for the connection to close + assertTrue("Timed out waiting for conneciton to report close", + exceptionReceived.await(2, TimeUnit.SECONDS)); - //Close the connection - mangedConnection.closeConnection(); + //Validate results + List<String> results = _monitor.findMatches("CON-1002"); - //Validate results - List<String> results = _monitor.findMatches("CON-1002"); - - - assertEquals("Unexpected Connection Close count", 1, results.size()); - } + assertEquals("Unexpected Connection Close count", 1, results.size()); } /** @@ -212,114 +196,100 @@ public class ManagementActorLoggingTest extends AbstractTestLogging */ public void testCreateExchangeDirectTransientViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous exchange declares - _monitor.reset(); - - createExchange("direct"); + _monitor.reset(); - // Validate + _jmxUtils.createExchange("test", "direct", null, false); - //1 - ID is correct - List<String> results = _monitor.findMatches("EXH-1001"); + // Validate - assertEquals("More than one exchange creation found", 1, results.size()); + //1 - ID is correct + List<String> results = _monitor.findMatches("EXH-1001"); - String log = getLog(results.get(0)); + assertEquals("More than one exchange creation found", 1, results.size()); - // Validate correct exchange name - assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); + String log = getLog(results.get(0)); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + // Validate correct exchange name + assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); - } + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); } public void testCreateExchangeTopicTransientViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous exchange declares - _monitor.reset(); + //Remove any previous exchange declares + _monitor.reset(); - createExchange("topic"); + _jmxUtils.createExchange("test", "topic", null, false); - // Validate + // Validate - //1 - ID is correct - List<String> results = _monitor.findMatches("EXH-1001"); + //1 - ID is correct + List<String> results = _monitor.findMatches("EXH-1001"); - assertEquals("More than one exchange creation found", 1, results.size()); + assertEquals("More than one exchange creation found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct exchange name - assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); + // Validate correct exchange name + assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } } public void testCreateExchangeFanoutTransientViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous exchange declares - _monitor.reset(); + //Remove any previous exchange declares + _monitor.reset(); - createExchange("fanout"); + _jmxUtils.createExchange("test", "fanout", null, false); - // Validate + // Validate - //1 - ID is correct - List<String> results = _monitor.findMatches("EXH-1001"); + //1 - ID is correct + List<String> results = _monitor.findMatches("EXH-1001"); - assertEquals("More than one exchange creation found", 1, results.size()); + assertEquals("More than one exchange creation found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct exchange name - assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); + // Validate correct exchange name + assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } } public void testCreateExchangeHeadersTransientViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous exchange declares - _monitor.reset(); + //Remove any previous exchange declares + _monitor.reset(); - createExchange("headers"); + _jmxUtils.createExchange("test", "headers", null, false); - // Validate + // Validate - //1 - ID is correct - List<String> results = _monitor.findMatches("EXH-1001"); + //1 - ID is correct + List<String> results = _monitor.findMatches("EXH-1001"); - assertEquals("More than one exchange creation found", 1, results.size()); + assertEquals("More than one exchange creation found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct exchange name - assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); + // Validate correct exchange name + assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName())); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } } /** @@ -343,29 +313,26 @@ public class ManagementActorLoggingTest extends AbstractTestLogging */ public void testCreateQueueTransientViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous queue declares - _monitor.reset(); + //Remove any previous queue declares + _monitor.reset(); - createQueue(); + _jmxUtils.createQueue("test", getName(), null, false); - // Validate + // Validate - List<String> results = _monitor.findMatches("QUE-1001"); + List<String> results = _monitor.findMatches("QUE-1001"); - assertEquals("More than one queue creation found", 1, results.size()); + assertEquals("More than one queue creation found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct queue name - String subject = fromSubject(log); - assertEquals("Incorrect queue name created", getName(), AbstractTestLogSubject.getSlice("qu", subject)); + // Validate correct queue name + String subject = fromSubject(log); + assertEquals("Incorrect queue name created", getName(), AbstractTestLogSubject.getSlice("qu", subject)); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); } /** @@ -389,34 +356,29 @@ public class ManagementActorLoggingTest extends AbstractTestLogging */ public void testQueueDeleteViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous queue declares - _monitor.reset(); + //Remove any previous queue declares + _monitor.reset(); - createQueue(); + _jmxUtils.createQueue("test", getName(), null, false); - ManagedBroker managedBroker = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, getVirtualHostManagerObjectName(), - ManagedBroker.class, false); + ManagedBroker managedBroker = _jmxUtils.getManagedBroker("test"); - managedBroker.deleteQueue(getName()); + managedBroker.deleteQueue(getName()); - List<String> results = _monitor.findMatches("QUE-1002"); + List<String> results = _monitor.findMatches("QUE-1002"); - assertEquals("More than one queue deletion found", 1, results.size()); + assertEquals("More than one queue deletion found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct binding - String subject = fromSubject(log); - assertEquals("Incorrect queue named in delete", getName(), AbstractTestLogSubject.getSlice("qu", subject)); + // Validate correct binding + String subject = fromSubject(log); + assertEquals("Incorrect queue named in delete", getName(), AbstractTestLogSubject.getSlice("qu", subject)); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } } /** @@ -440,98 +402,83 @@ public class ManagementActorLoggingTest extends AbstractTestLogging */ public void testBindingCreateOnDirectViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous queue declares - _monitor.reset(); + //Remove any previous queue declares + _monitor.reset(); - createQueue(); + _jmxUtils.createQueue("test", getName(), null, false); - ManagedExchange managedExchange = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, getExchange("amq.direct"), - ManagedExchange.class, false); + ManagedExchange managedExchange = _jmxUtils.getManagedExchange("amq.direct"); - managedExchange.createNewBinding(getName(), getName()); + managedExchange.createNewBinding(getName(), getName()); - List<String> results = _monitor.findMatches("BND-1001"); + List<String> results = _monitor.findMatches("BND-1001"); - assertEquals("More than one bind creation found", 1, results.size()); + assertEquals("More than one bind creation found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct binding - String subject = fromSubject(log); - assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject)); - assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject)); + // Validate correct binding + String subject = fromSubject(log); + assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject)); + assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject)); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); } public void testBindingCreateOnTopicViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous queue declares - _monitor.reset(); + //Remove any previous queue declares + _monitor.reset(); - createQueue(); + _jmxUtils.createQueue("test", getName(), null, false); - ManagedExchange managedExchange = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, getExchange("amq.topic"), - ManagedExchange.class, false); + ManagedExchange managedExchange = _jmxUtils.getManagedExchange("amq.topic"); - managedExchange.createNewBinding(getName(), getName()); + managedExchange.createNewBinding(getName(), getName()); - List<String> results = _monitor.findMatches("BND-1001"); + List<String> results = _monitor.findMatches("BND-1001"); - assertEquals("More than one bind creation found", 1, results.size()); + assertEquals("More than one bind creation found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct binding - String subject = fromSubject(log); - assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject)); - assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject)); + // Validate correct binding + String subject = fromSubject(log); + assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject)); + assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject)); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); } public void testBindingCreateOnFanoutViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - //Remove any previous queue declares - _monitor.reset(); + //Remove any previous queue declares + _monitor.reset(); - createQueue(); + _jmxUtils.createQueue("test", getName(), null, false); - ManagedExchange managedExchange = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, getExchange("amq.fanout"), - ManagedExchange.class, false); + ManagedExchange managedExchange = _jmxUtils.getManagedExchange("amq.fanout"); - managedExchange.createNewBinding(getName(), getName()); + managedExchange.createNewBinding(getName(), getName()); - List<String> results = _monitor.findMatches("BND-1001"); + List<String> results = _monitor.findMatches("BND-1001"); - assertEquals("More than one bind creation found", 1, results.size()); + assertEquals("More than one bind creation found", 1, results.size()); - String log = getLog(results.get(0)); + String log = getLog(results.get(0)); - // Validate correct binding - String subject = fromSubject(log); - assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject)); - assertEquals("Incorrect routing key in create", "*", AbstractTestLogSubject.getSlice("rk", subject)); + // Validate correct binding + String subject = fromSubject(log); + assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject)); + assertEquals("Incorrect routing key in create", "*", AbstractTestLogSubject.getSlice("rk", subject)); - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); } /** @@ -556,114 +503,28 @@ public class ManagementActorLoggingTest extends AbstractTestLogging */ public void testUnRegisterExchangeViaManagementConsole() throws IOException, JMException { - if (isExternalBroker()) - { - - //Remove any previous queue declares - _monitor.reset(); + //Remove any previous queue declares + _monitor.reset(); - createExchange("direct"); + _jmxUtils.createExchange("test", "direct", null, false); - ManagedBroker managedBroker = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, getVirtualHostManagerObjectName(), - ManagedBroker.class, false); + ManagedBroker managedBroker = _jmxUtils.getManagedBroker("test"); - managedBroker.unregisterExchange(getName()); + managedBroker.unregisterExchange(getName()); - List<String> results = _monitor.findMatches("EXH-1002"); + List<String> results = _monitor.findMatches("EXH-1002"); - assertEquals("More than one exchange deletion found", 1, results.size()); - - String log = getLog(results.get(0)); - - // Validate correct binding - String subject = fromSubject(log); - assertEquals("Incorrect exchange named in delete", "direct/" + getName(), AbstractTestLogSubject.getSlice("ex", subject)); - - // Validate it was a management actor. - String actor = fromActor(log); - assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); - } - } - - /** - * Create a non-durable test exchange with the current test name - * - * @throws JMException - is thrown if a exchange with this testName already exists - * @throws IOException - if there is a problem with the JMX Connection - * @throws javax.management.MBeanException - * - if there is another problem creating the exchange - */ - private void createExchange(String type) - throws JMException, IOException, MBeanException - { - ManagedBroker managedBroker = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, getVirtualHostManagerObjectName(), - ManagedBroker.class, false); - - managedBroker.createNewExchange(getName(), type, false); - } - - /** - * Create a non-durable queue (with no owner) that is named after the - * creating test. - * - * @throws JMException - is thrown if a queue with this testName already exists - * @throws IOException - if there is a problem with the JMX Connection - */ - private void createQueue() - throws JMException, IOException - { - ManagedBroker managedBroker = MBeanServerInvocationHandler. - newProxyInstance(_mbsc, getVirtualHostManagerObjectName(), - ManagedBroker.class, false); - - managedBroker.createNewQueue(getName(), null, false); - } - - /** - * Retrive the ObjectName for the test Virtualhost. - * - * This is then use to create aproxy to the ManagedBroker MBean. - * - * @return the ObjectName for the 'test' VirtualHost. - */ - private ObjectName getVirtualHostManagerObjectName() - { - // Get the name of the test manager - AllObjects allObject = new AllObjects(_mbsc); - allObject.querystring = "org.apache.qpid:type=VirtualHost.VirtualHostManager,VirtualHost=test,*"; - - Set<ObjectName> objectNames = allObject.returnObjects(); - - assertEquals("Incorrect number test vhosts returned", 1, objectNames.size()); - - // We have verified we have only one value in objectNames so return it - return objectNames.iterator().next(); - } - - /** - * Retrive the ObjectName for the given Exchange on the test Virtualhost. - * - * This is then use to create aproxy to the ManagedExchange MBean. - * - * @param exchange The exchange to retireve e.g. 'direct' - * - * @return the ObjectName for the given exchange on the test VirtualHost. - */ - private ObjectName getExchange(String exchange) - { - // Get the name of the test manager - AllObjects allObject = new AllObjects(_mbsc); - allObject.querystring = "org.apache.qpid:type=VirtualHost.Exchange,VirtualHost=test,name=" + exchange + ",*"; + assertEquals("More than one exchange deletion found", 1, results.size()); - Set<ObjectName> objectNames = allObject.returnObjects(); + String log = getLog(results.get(0)); - assertEquals("Incorrect number of exchange with name '" + exchange + - "' returned", 1, objectNames.size()); + // Validate correct binding + String subject = fromSubject(log); + assertEquals("Incorrect exchange named in delete", "direct/" + getName(), AbstractTestLogSubject.getSlice("ex", subject)); - // We have verified we have only one value in objectNames so return it - return objectNames.iterator().next(); + // Validate it was a management actor. + String actor = fromActor(log); + assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng")); } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/configuration/ServerConfigurationFileTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/configuration/ServerConfigurationFileTest.java index c4803e121e..0a88ef391c 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/configuration/ServerConfigurationFileTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/configuration/ServerConfigurationFileTest.java @@ -40,6 +40,8 @@ public class ServerConfigurationFileTest extends QpidTestCase { fail("Unable to test without config file:" + _configFile); } + + saveTestConfiguration(); _serverConfig = new ServerConfiguration(_configFile); } 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 620b2a5161..683abee4da 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 @@ -94,7 +94,7 @@ public class AlertingTest extends AbstractTestLogging { _connection = getConnection(); _session = _connection.createSession(true, Session.SESSION_TRANSACTED); - _destination = _session.createQueue("testQueue"); + _destination = _session.createQueue(getTestQueueName()); // Consumer is only used to actually create the destination _session.createConsumer(_destination).close(); @@ -116,14 +116,12 @@ public class AlertingTest extends AbstractTestLogging // Add the current contents of the log file to test output message.append(_monitor.readFile()); - // Write the server config file to test output - message.append("Server configuration file in use:\n"); - message.append(FileUtils.readFileAsString(_configFile)); + // Write the test config file to test output + message.append("Server configuration overrides in use:\n"); + message.append(FileUtils.readFileAsString(getTestConfigFile())); - // Write the virtualhost config file to test output - message.append("\nVirtualhost configuration file in use:\n"); - message.append(FileUtils.readFileAsString(ServerConfiguration. - flatConfig(_configFile).getString("virtualhosts"))); + message.append("\nVirtualhost maxMessageCount:\n"); + message.append((new ServerConfiguration(_configFile)).getConfig().getString("virtualhosts.virtualhost." + VIRTUALHOST + ".queues.maximumMessageCount")); fail(message.toString()); } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java index 254ec9693d..cc3993249c 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java @@ -28,6 +28,7 @@ import javax.jms.Connection; import javax.jms.Queue; import javax.jms.Session; import java.util.List; +import java.io.File; /** * The MessageStore test suite validates that the follow log messages as @@ -56,9 +57,9 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest //We call super.setUp but this will not start the broker as that is //part of the test case. - // Load current configuration file to get the list of defined vhosts - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List<String> vhosts = configuration.getList("virtualhosts.virtualhost.name"); + // Load the default configuration file to get the list of defined vhosts + ServerConfiguration configuration = new ServerConfiguration(new File(_configFile.getParent() + "/config.xml")); + List<String> vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); // Make them all persistent i.e. Use DerbyMessageStore and // test that it logs correctly. @@ -97,8 +98,8 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest assertTrue("MST messages not logged", results.size() > 0); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List<String> vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List<String> vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1002"); @@ -117,7 +118,7 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); @@ -160,8 +161,8 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest assertTrue("MST messages not logged", results.size() > 0); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List<String> vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List<String> vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1004"); @@ -186,7 +187,7 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); @@ -227,8 +228,8 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest assertTrue("MST messages not logged", results.size() > 0); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List<String> vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List<String> vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1006"); @@ -253,7 +254,7 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); @@ -293,8 +294,8 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest assertTrue("MST messages not logged", results.size() > 0); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List<String> vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List<String> vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1004 : Recovery Start :"); @@ -316,7 +317,7 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); @@ -358,8 +359,8 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest assertTrue("MST messages not logged", results.size() > 0); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List<String> vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List<String> vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1006 : Recovery Complete :"); @@ -381,7 +382,7 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); @@ -500,8 +501,8 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest assertTrue("MST messages not logged", results.size() > 0); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List<String> vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List<String> vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1004 : Recovery Start : " + queueName); @@ -542,7 +543,7 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java index 11c003a2a7..8b7c881a32 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java @@ -24,6 +24,7 @@ import junit.framework.AssertionFailedError; import org.apache.qpid.util.LogMonitor; import java.util.List; +import java.io.File; /** * Management Console Test Suite @@ -308,9 +309,8 @@ public class ManagementLoggingTest extends AbstractTestLogging // We expect the RMIConnector Server port to be 100 higher than // the RMI Server Port - int mPort = getPort() + (DEFAULT_MANAGEMENT_PORT - DEFAULT_PORT) + 100; - assertTrue("SSL Keystore entry expected(" + mPort + ").:" + getMessageString(log), - getMessageString(log).endsWith(getConfigurationStringProperty("management.ssl.keyStorePath"))); + assertTrue("SSL Keystore entry expected.:" + getMessageString(log), + getMessageString(log).endsWith(new File(getConfigurationStringProperty("management.ssl.keyStorePath")).getName())); } catch (AssertionFailedError afe) { diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/MemoryMessageStoreLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/MemoryMessageStoreLoggingTest.java index a1cbeca6de..2298ba4da0 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/MemoryMessageStoreLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/MemoryMessageStoreLoggingTest.java @@ -89,8 +89,8 @@ public class MemoryMessageStoreLoggingTest extends AbstractTestLogging assertEquals("MST-1001 is not the first MST message", "MST-1001", getMessageID(fromMessage(log))); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List<String> vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List<String> vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1001"); @@ -109,7 +109,7 @@ public class MemoryMessageStoreLoggingTest extends AbstractTestLogging // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); @@ -156,8 +156,8 @@ public class MemoryMessageStoreLoggingTest extends AbstractTestLogging assertTrue("MST messages not logged", results.size() > 0); // Load VirtualHost list from file. - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List<String> vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List<String> vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("MST-1003"); @@ -176,7 +176,7 @@ public class MemoryMessageStoreLoggingTest extends AbstractTestLogging // the virtualhost name, found above. AND // the index that the virtualhost is within the configuration. // we can retrive that from the vhosts list previously extracted. - String fullStoreName = configuration.getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); + String fullStoreName = configuration.getConfig().getString("virtualhosts.virtualhost(" + vhosts.indexOf(vhostName) + ")." + vhostName + ".store.class"); // Get the Simple class name from the expected class name of o.a.q.s.s.MMS String storeName = fullStoreName.substring(fullStoreName.lastIndexOf(".") + 1); 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 033c0533f8..5dd56fb0f9 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 @@ -29,6 +29,7 @@ import javax.jms.MessageConsumer; import javax.jms.Queue; import javax.jms.Session; import javax.jms.Topic; +import javax.jms.Message; import java.io.IOException; import java.util.List; @@ -327,51 +328,59 @@ public class SubscriptionLoggingTest extends AbstractTestLogging int PREFETCH = 15; //Create new session with small prefetch - _session = ((AMQConnection) _connection).createSession(true, Session.AUTO_ACKNOWLEDGE, PREFETCH); + _session = ((AMQConnection) _connection).createSession(true, Session.SESSION_TRANSACTED, PREFETCH); MessageConsumer consumer = _session.createConsumer(_queue); _connection.start(); + //Start the dispatcher & Unflow the channel. + consumer.receiveNoWait(); + //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); + // subscription to become active + // Previously we set this to 17 so that it would return to a suspended + // state. However, testing has shown that the state change can occur + // sufficiently quickly that logging does not occur consistently enough + // for testing. + int SEND_COUNT = 16; + sendMessage(_session, _queue, SEND_COUNT); _session.commit(); // Retreive the first message, and start the flow of messages - assertNotNull("First message not retreived", consumer.receive(1000)); + Message msg = consumer.receive(1000); + assertNotNull("First message not retreived", msg); _session.commit(); - - //Validate - List<String> results = _monitor.findMatches("SUB-1003"); - - int i = 0; - while (results.size() != 3 && i < 10) + // Drain the queue to ensure there is time for the ACTIVE log message + // Check that we can received all the messages + int receivedCount = 0; + while (msg != null) { - try - { - Thread.sleep(1500); - } - catch (InterruptedException e) - { - - } - results = _monitor.findMatches("SUB-1003"); - i++; + receivedCount++; + msg = consumer.receive(1000); + _session.commit(); } + //Validate we received all the messages + assertEquals("Not all sent messages received.", SEND_COUNT, receivedCount); + + // Fill the queue again to suspend the consumer + sendMessage(_session, _queue, SEND_COUNT); + _session.commit(); + + //Validate + List<String> results = _monitor.findMatches("SUB-1003"); + 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 - // 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 - // As a result validating the actor is more complicated and doesn't add anything. The goal of this test is - // to ensure the State is correct not that a particular Actor performs the logging. -// 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 + // The Actor can be any one of the following depending on the exactly what is going on on the broker. + // Ideally we would test that we can get all of them but setting up + // the timing to do this in a consistent way is not benefitial. + // Ensuring the State is as expected is sufficient. +// INFO - MESSAGE [vh(/test)/qu(example.queue)] [sub:6(qu(example.queue))] SUB-1003 : State : +// INFO - MESSAGE [con:6(guest@anonymous(26562441)/test)/ch:3] [sub:6(qu(example.queue))] SUB-1003 : State : +// INFO - MESSAGE [sub:6(vh(test)/qu(example.queue))] [sub:6(qu(example.queue))] SUB-1003 : State : assertEquals("Result set not expected size:", 3, results.size()); @@ -380,19 +389,10 @@ public class SubscriptionLoggingTest extends AbstractTestLogging String log = getLog(results.get(0)); validateSubscriptionState(log, expectedState); - // Validate that the logActor is the the queue - String actor = fromActor(log); - assertTrue("Actor string does not contain expected queue(" - + _queue.getQueueName() + ") name." + actor, - actor.contains("qu(" + _queue.getQueueName() + ")")); - // After being suspended the subscription should become active. expectedState = "ACTIVE"; log = getLog(results.get(1)); validateSubscriptionState(log, expectedState); - // Validate we have a connection Actor - actor = fromActor(log); - assertTrue("The actor is not a connection actor:" + actor, actor.startsWith("con:")); // Validate that it was re-suspended expectedState = "SUSPENDED"; @@ -411,6 +411,9 @@ public class SubscriptionLoggingTest extends AbstractTestLogging } _connection.close(); + //Ensure the queue is drained before the test ends + drainQueue(_queue); + } /** diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java index 7bf644508e..f4a0c8b27d 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java @@ -64,8 +64,8 @@ public class VirtualHostLoggingTest extends AbstractTestLogging try { // Validation - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List<String> vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List<String> vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("VHT-1001"); @@ -117,8 +117,8 @@ public class VirtualHostLoggingTest extends AbstractTestLogging { // Validation - Configuration configuration = ServerConfiguration.flatConfig(_configFile); - List<String> vhosts = configuration.getList("virtualhosts.virtualhost.name"); + ServerConfiguration configuration = new ServerConfiguration(_configFile); + List<String> vhosts = configuration.getConfig().getList("virtualhosts.virtualhost.name"); //Validate each vhost logs a creation results = _monitor.findMatches("VHT-1002"); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ModelTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ModelTest.java new file mode 100644 index 0000000000..078b8f43ce --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ModelTest.java @@ -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. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.management.common.mbeans.ManagedQueue; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.QpidTestCase; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Session; +import javax.management.JMException; +import javax.management.MBeanException; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.UndeclaredThrowableException; + +/** + * This Test validates the Queue Model on the broker. + * Currently it has some basic queue creation / deletion tests. + * However, it should be expanded to include other tests that relate to the + * model. i.e. + * + * The Create and Delete tests should ensure that the requisite logging is + * performed. + * + * Additions to this suite would be to complete testing of creations, validating + * fields such as owner/exclusive, autodelete and priority are correctly set. + * + * Currently this test uses the JMX interface to validate that the queue has + * been declared as expected so these tests cannot run against a CPP broker. + * + * + * Tests should ensure that they clean up after themselves. + * e,g. Durable queue creation test should perform a queue delete. + */ +public class ModelTest extends QpidTestCase +{ + + private static final String USER = "admin"; + private JMXTestUtils _jmxUtils; + private static final String VIRTUALHOST_NAME = "test"; + + @Override + public void setUp() throws Exception + { + // Create a JMX Helper + _jmxUtils = new JMXTestUtils(this, USER, USER); + _jmxUtils.setUp(); + super.setUp(); + + // Open the JMX Connection + _jmxUtils.open(); + } + + @Override + public void tearDown() throws Exception + { + // Close the JMX Connection + _jmxUtils.close(); + super.tearDown(); + } + + /** + * Test that a transient queue can be created via AMQP. + * + * @throws Exception On unexpected error + */ + public void testQueueCreationTransientViaAMQP() throws Exception + { + Connection connection = getConnection(); + + String queueName = getTestQueueName(); + boolean durable = false; + boolean autoDelete = false; + boolean exclusive = false; + + createViaAMQPandValidateViaJMX(connection, queueName, durable, + autoDelete, exclusive); + } + + /** + * Test that a durable queue can be created via AMQP. + * + * @throws Exception On unexpected error + */ + + public void testQueueCreationDurableViaAMQP() throws Exception + { + Connection connection = getConnection(); + + String queueName = getTestQueueName(); + boolean durable = true; + boolean autoDelete = false; + boolean exclusive = false; + + createViaAMQPandValidateViaJMX(connection, queueName, durable, + autoDelete, exclusive); + + // Clean up + ManagedBroker managedBroker = + _jmxUtils.getManagedBroker(VIRTUALHOST_NAME); + managedBroker.deleteQueue(queueName); + } + + /** + * Test that a transient queue can be created via JMX. + * + * @throws IOException if there is a problem via the JMX connection + * @throws javax.management.JMException if there is a problem with the JMX command + */ + public void testCreationTransientViaJMX() throws IOException, JMException + { + String name = getName(); + String owner = null; + boolean durable = false; + + createViaJMXandValidateViaJMX(name, owner, durable, durable); + } + + /** + * Test that a durable queue can be created via JMX. + * + * @throws IOException if there is a problem via the JMX connection + * @throws javax.management.JMException if there is a problem with the JMX command + */ + public void testCreationDurableViaJMX() throws IOException, JMException + { + String name = getName(); + String owner = null; + boolean durable = true; + + createViaJMXandValidateViaJMX(name, owner, durable, durable); + + // Clean up + ManagedBroker managedBroker = + _jmxUtils.getManagedBroker(VIRTUALHOST_NAME); + managedBroker.deleteQueue(name); + } + + /** + * Test that a transient queue can be deleted via JMX. + * + * @throws IOException if there is a problem via the JMX connection + * @throws javax.management.JMException if there is a problem with the JMX command + */ + public void testDeletionTransientViaJMX() throws IOException, JMException + { + String name = getName(); + + _jmxUtils.createQueue(VIRTUALHOST_NAME, name, null, false); + + ManagedBroker managedBroker = _jmxUtils. + getManagedBroker(VIRTUALHOST_NAME); + + try + { + managedBroker.deleteQueue(name); + } + catch (UndeclaredThrowableException e) + { + fail(((MBeanException) ((InvocationTargetException) + e.getUndeclaredThrowable()).getTargetException()).getTargetException().getMessage()); + } + } + + /** + * Test that a durable queue can be created via JMX. + * + * @throws IOException if there is a problem via the JMX connection + * @throws javax.management.JMException if there is a problem with the JMX command + */ + public void testDeletionDurableViaJMX() throws IOException, JMException + { + String name = getName(); + + _jmxUtils.createQueue(VIRTUALHOST_NAME, name, null, true); + + ManagedBroker managedBroker = _jmxUtils. + getManagedBroker(VIRTUALHOST_NAME); + + try + { + managedBroker.deleteQueue(name); + } + catch (UndeclaredThrowableException e) + { + fail(((MBeanException) ((InvocationTargetException) + e.getUndeclaredThrowable()).getTargetException()).getTargetException().getMessage()); + } + } + + /* + * Helper Methods + */ + + /** + * Using the provided JMS Connection create a queue using the AMQP extension + * with the given properties and then validate it was created correctly via + * the JMX Connection + * + * @param connection Qpid JMS Connection + * @param queueName String the desired QueueName + * @param durable boolean if the queue should be durable + * @param autoDelete boolean if the queue is an autoDelete queue + * @param exclusive boolean if the queue is exclusive + * + * @throws AMQException if there is a problem with the createQueue call + * @throws JMException if there is a problem with the JMX validatation + * @throws IOException if there is a problem with the JMX connection + * @throws JMSException if there is a problem creating the JMS Session + */ + private void createViaAMQPandValidateViaJMX(Connection connection, + String queueName, + boolean durable, + boolean autoDelete, + boolean exclusive) + throws AMQException, JMException, IOException, JMSException + { + AMQSession session = (AMQSession) connection.createSession(false, + Session.AUTO_ACKNOWLEDGE); + + session.createQueue(new AMQShortString(queueName), + autoDelete, durable, exclusive); + + validateQueueViaJMX(queueName, exclusive ? ((AMQConnection) connection). + getUsername() : null, durable, autoDelete); + } + + /** + * Use the JMX Helper to create a queue with the given properties and then + * validate it was created correctly via the JMX Connection + * + * @param queueName String the desired QueueName + * @param owner String the owner value that should be set + * @param durable boolean if the queue should be durable + * @param autoDelete boolean if the queue is an autoDelete queue + * + * @throws JMException if there is a problem with the JMX validatation + * @throws IOException if there is a problem with the JMX connection + */ + private void createViaJMXandValidateViaJMX(String queueName, String owner, + boolean durable, boolean autoDelete) + throws JMException, IOException + { + _jmxUtils.createQueue(VIRTUALHOST_NAME, queueName, owner, durable); + + validateQueueViaJMX(queueName, owner, durable, autoDelete); + } + + /** + * Validate that a queue with the given properties exists on the broker + * + * @param queueName String the desired QueueName + * @param owner String the owner value that should be set + * @param durable boolean if the queue should be durable + * @param autoDelete boolean if the queue is an autoDelete queue + * + * @throws JMException if there is a problem with the JMX validatation + * @throws IOException if there is a problem with the JMX connection + */ + private void validateQueueViaJMX(String queueName, String owner, boolean durable, boolean autoDelete) + throws JMException, IOException + { + ManagedQueue managedQueue = _jmxUtils. + getManagedObject(ManagedQueue.class, + _jmxUtils.getQueueObjectName(VIRTUALHOST_NAME, + queueName)); + + assertEquals(queueName, managedQueue.getName()); + assertEquals(String.valueOf(owner), managedQueue.getOwner()); + assertEquals(durable, managedQueue.isDurable()); + assertEquals(autoDelete, managedQueue.isAutoDelete()); + } + +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java index b5c0a87b0f..a755bbfaa7 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java @@ -21,67 +21,57 @@ package org.apache.qpid.server.security.acl; -import junit.framework.TestCase; -import org.apache.qpid.client.transport.TransportConnection; -import org.apache.qpid.client.*; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; import org.apache.qpid.AMQException; -import org.apache.qpid.test.utils.QpidTestCase; +import org.apache.qpid.AMQConnectionFailureException; +import org.apache.qpid.client.AMQAuthenticationException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.jms.ConnectionListener; +import org.apache.qpid.test.utils.QpidTestCase; import org.apache.qpid.url.URLSyntaxException; -import javax.jms.*; +import javax.jms.Connection; +import javax.jms.DeliveryMode; import javax.jms.IllegalStateException; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.NamingException; import java.io.File; public class SimpleACLTest extends QpidTestCase implements ConnectionListener { - private String BROKER = "vm://:"+ApplicationRegistry.DEFAULT_INSTANCE;//"tcp://localhost:5672"; - public void setUp() throws Exception { - //Shutdown the QTC broker - stopBroker(); - - // Initialise ACLs. - final String QpidExampleHome = System.getProperty("QPID_EXAMPLE_HOME"); - final File defaultaclConfigFile = new File(QpidExampleHome, "etc/acl.config.xml"); + final String QPID_HOME = System.getProperty("QPID_HOME"); - if (!defaultaclConfigFile.exists()) - { - System.err.println("Configuration file not found:" + defaultaclConfigFile); - fail("Configuration file not found:" + defaultaclConfigFile); - } - - if (System.getProperty("QPID_HOME") == null) + if (QPID_HOME == null) { fail("QPID_HOME not set"); } - ConfigurationFileApplicationRegistry config = new ConfigurationFileApplicationRegistry(defaultaclConfigFile); - ApplicationRegistry.initialise(config, ApplicationRegistry.DEFAULT_INSTANCE); - TransportConnection.createVMBroker(ApplicationRegistry.DEFAULT_INSTANCE); - } + // Initialise ACLs. + _configFile = new File(QPID_HOME, "etc/config-systests-acl.xml"); - public void tearDown() - { - TransportConnection.killVMBroker(ApplicationRegistry.DEFAULT_INSTANCE); - ApplicationRegistry.remove(ApplicationRegistry.DEFAULT_INSTANCE); + super.setUp(); } - public String createConnectionString(String username, String password, String broker) + public String createConnectionString(String username, String password) { - return "amqp://" + username + ":" + password + "@clientid/test?brokerlist='" + broker + "?retries='0''"; + return "amqp://" + username + ":" + password + "@clientid/test?brokerlist='" + getBroker() + "?retries='0''"; } public void testAccessAuthorized() throws AMQException, URLSyntaxException { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); Session sesh = conn.createSession(true, Session.SESSION_TRANSACTED); @@ -94,28 +84,32 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener } catch (Exception e) { - fail("Connection was not created due to:" + e.getMessage()); + fail("Connection was not created due to:" + e); } } - public void testAccessNoRights() throws URLSyntaxException, JMSException + public void testAccessNoRights() throws Exception { try { - Connection conn = createConnection("guest", "guest"); + Connection conn = getConnection("guest", "guest"); //Attempt to do do things to test connection. Session sesh = conn.createSession(true, Session.SESSION_TRANSACTED); conn.start(); sesh.rollback(); - conn.close(); fail("Connection was created."); } - catch (AMQException amqe) + catch (JMSException jmse) { - Throwable cause = amqe.getCause(); - assertEquals("Exception was wrong type", AMQAuthenticationException.class, cause.getClass()); + Throwable linkedException = jmse.getLinkedException(); + assertNotNull("Cause was null", linkedException); + + assertEquals("Linked Exception was wrong type", AMQConnectionFailureException.class, linkedException.getClass()); + + Throwable cause = linkedException.getCause(); + assertEquals("Cause was wrong type", AMQAuthenticationException.class, cause.getClass()); assertEquals("Incorrect error code thrown", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode()); } } @@ -124,7 +118,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -140,11 +134,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener } } - public void testClientConsumeFromNamedQueueInvalid() throws AMQException, URLSyntaxException + public void testClientConsumeFromNamedQueueInvalid() throws NamingException { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); //Prevent Failover ((AMQConnection) conn).setConnectionListener(this); @@ -171,7 +165,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -189,11 +183,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener } } - public void testClientCreateNamedQueue() throws JMSException, URLSyntaxException, AMQException + public void testClientCreateNamedQueue() throws NamingException, JMSException, AMQException { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -207,6 +201,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener } catch (AMQAuthenticationException amqe) { + amqe.printStackTrace(); assertEquals("Incorrect error code thrown", 403, ((AMQAuthenticationException) amqe).getErrorCode().getCode()); } } @@ -215,7 +210,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); ((AMQConnection) conn).setConnectionListener(this); @@ -242,7 +237,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); ((AMQConnection) conn).setConnectionListener(this); @@ -268,11 +263,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener } } - public void testClientPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException + public void testClientPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException, NamingException { try { - Connection conn = createConnection("client", "guest"); + Connection conn = getConnection("client", "guest"); ((AMQConnection) conn).setConnectionListener(this); @@ -317,7 +312,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener { try { - Connection conn = createConnection("server", "guest"); + Connection conn = getConnection("server", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -333,11 +328,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener } } - public void testServerConsumeFromNamedQueueInvalid() throws AMQException, URLSyntaxException + public void testServerConsumeFromNamedQueueInvalid() throws AMQException, URLSyntaxException, NamingException { try - { - Connection conn = createConnection("client", "guest"); + { + Connection conn = getConnection("client", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -358,11 +353,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener } } - public void testServerConsumeFromTemporaryQueue() throws AMQException, URLSyntaxException + public void testServerConsumeFromTemporaryQueue() throws AMQException, URLSyntaxException, NamingException { try { - Connection conn = createConnection("server","guest"); + Connection conn = getConnection("server", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -382,30 +377,22 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener } } - private Connection createConnection(String username, String password) throws AMQException + @Override + public Connection getConnection(String username, String password) throws NamingException, JMSException { - AMQConnection connection = null; - try - { - connection = new AMQConnection(createConnectionString(username, password, BROKER)); - } - catch (URLSyntaxException e) - { - // This should never happen as we generate the URLs. - fail(e.getMessage()); - } + AMQConnection connection = (AMQConnection) super.getConnection(username, password); //Prevent Failover connection.setConnectionListener(this); - return (Connection)connection; + return (Connection) connection; } public void testServerCreateNamedQueueValid() throws JMSException, URLSyntaxException { try { - Connection conn = createConnection("server", "guest"); + Connection conn = getConnection("server", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -422,11 +409,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener } } - public void testServerCreateNamedQueueInvalid() throws JMSException, URLSyntaxException, AMQException + public void testServerCreateNamedQueueInvalid() throws JMSException, URLSyntaxException, AMQException, NamingException { try { - Connection conn = createConnection("server", "guest"); + Connection conn = getConnection("server", "guest"); Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -444,18 +431,18 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener } } - public void testServerCreateTemporaryQueueInvalid() throws JMSException, URLSyntaxException, AMQException + public void testServerCreateTemporaryQueueInvalid() throws NamingException { try { - Connection conn = createConnection("server", "guest"); + Connection conn = getConnection("server", "guest"); Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); session.createTemporaryQueue(); - + fail("Test failed as creation succeded."); //conn will be automatically closed } @@ -469,19 +456,19 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener } } - public void testServerCreateAutoDeleteQueueInvalid() throws JMSException, URLSyntaxException, AMQException + public void testServerCreateAutoDeleteQueueInvalid() throws NamingException, JMSException, AMQException { Connection connection = null; try { - connection = createConnection("server", "guest"); + connection = getConnection("server", "guest"); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); connection.start(); ((AMQSession) session).createQueue(new AMQShortString("again_ensure_auto_delete_queue_for_temporary"), - true, false, false); + true, false, false); fail("Test failed as creation succeded."); //connection will be automatically closed @@ -489,7 +476,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener catch (AMQAuthenticationException amqe) { assertEquals("Incorrect error code thrown", 403, amqe.getErrorCode().getCode()); - } + } } /** @@ -500,10 +487,10 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener * @throws URLSyntaxException * @throws JMSException */ - public void testServerPublishUsingTransactionSuccess() throws AMQException, URLSyntaxException, JMSException + public void testServerPublishUsingTransactionSuccess() throws AMQException, URLSyntaxException, JMSException, NamingException { //Set up the Server - Connection serverConnection = createConnection("server", "guest"); + Connection serverConnection = getConnection("server", "guest"); ((AMQConnection) serverConnection).setConnectionListener(this); @@ -516,7 +503,7 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener serverConnection.start(); //Set up the consumer - Connection clientConnection = createConnection("client", "guest"); + Connection clientConnection = getConnection("client", "guest"); //Send a test mesage Session clientSession = clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); @@ -557,8 +544,6 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener //Send the message using a transaction as this will allow us to retrieve any errors that occur on the broker. serverSession.commit(); - - //Ensure Response is received. Message clientResponseMsg = clientResponse.receive(2000); assertNotNull("Client did not receive response message,", clientResponseMsg); @@ -582,11 +567,11 @@ public class SimpleACLTest extends QpidTestCase implements ConnectionListener } } - public void testServerPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException + public void testServerPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException, NamingException { try { - Connection conn = createConnection("server", "guest"); + Connection conn = getConnection("server", "guest"); ((AMQConnection) conn).setConnectionListener(this); 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 5a5e23baa5..d76d3858b3 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 @@ -1,31 +1,300 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ package org.apache.qpid.test.client.message; +import java.util.concurrent.CountDownLatch; + import javax.jms.Connection; +import javax.jms.DeliveryMode; import javax.jms.Destination; +import javax.jms.InvalidSelectorException; +import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; +import javax.jms.MessageListener; import javax.jms.MessageProducer; import javax.jms.Session; import junit.framework.Assert; -import org.apache.log4j.Logger; +import org.apache.log4j.BasicConfigurator; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.BasicMessageProducer; import org.apache.qpid.test.utils.QpidTestCase; +import org.apache.qpid.url.URLSyntaxException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class SelectorTest extends QpidTestCase +public class SelectorTest extends QpidTestCase implements MessageListener { - private static final Logger _logger = Logger.getLogger(SelectorTest.class); + private static final Logger _logger = LoggerFactory.getLogger(SelectorTest.class); + + private AMQConnection _connection; + private AMQDestination _destination; + private AMQSession _session; + private int count; + public String _connectionString = "vm://:1"; + private static final String INVALID_SELECTOR = "Cost LIKE 5"; + CountDownLatch _responseLatch = new CountDownLatch(1); + + private static final String BAD_MATHS_SELECTOR = " 1 % 5"; + + private static final long RECIEVE_TIMEOUT = 1000; + + protected void setUp() throws Exception + { + super.setUp(); + BasicConfigurator.configure(); + init((AMQConnection) getConnection("guest", "guest")); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + if (_session != null) + { + try + { + _session.close(); + } + catch (JMSException e) + { + fail("Error cleaning up:" + e.getMessage()); + } + } + if (_connection != null) + { + try + { + _connection.close(); + } + catch (JMSException e) + { + fail("Error cleaning up:" + e.getMessage()); + } + } + } + + private void init(AMQConnection connection) throws JMSException + { + init(connection, new AMQQueue(connection, randomize("SessionStartTest"), true)); + } + + private void init(AMQConnection connection, AMQDestination destination) throws JMSException + { + _connection = connection; + _destination = destination; + connection.start(); + + String selector = null; + selector = "Cost = 2 AND \"property-with-hyphen\" = 'wibble'"; + // selector = "JMSType = Special AND Cost = 2 AND AMQMessageID > 0 AND JMSDeliveryMode=" + DeliveryMode.NON_PERSISTENT; + + _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + // _session.createConsumer(destination).setMessageListener(this); + _session.createConsumer(destination, selector).setMessageListener(this); + } + + public void test() throws Exception + { + try + { + init((AMQConnection) getConnection("guest", "guest", randomize("Client"))); + + Message msg = _session.createTextMessage("Message"); + msg.setJMSPriority(1); + msg.setIntProperty("Cost", 2); + msg.setStringProperty("property-with-hyphen", "wibble"); + msg.setJMSType("Special"); + + _logger.info("Sending Message:" + msg); + + ((BasicMessageProducer) _session.createProducer(_destination)).send(msg, DeliveryMode.NON_PERSISTENT); + _logger.info("Message sent, waiting for response..."); + + _responseLatch.await(); + + if (count > 0) + { + _logger.info("Got message"); + } + + if (count == 0) + { + fail("Did not get message!"); + // throw new RuntimeException("Did not get message!"); + } + } + catch (JMSException e) + { + _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + if (!(e instanceof InvalidSelectorException)) + { + fail("Wrong exception:" + e.getMessage()); + } + else + { + System.out.println("SUCCESS!!"); + } + } + catch (InterruptedException e) + { + _logger.debug("IE :" + e.getClass().getSimpleName() + ":" + e.getMessage()); + } + catch (URLSyntaxException e) + { + _logger.debug("URL:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + fail("Wrong exception"); + } + catch (AMQException e) + { + _logger.debug("AMQ:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + fail("Wrong exception"); + } + + finally + { + if (_session != null) + { + _session.close(); + } + if (_connection != null) + { + _connection.close(); + } + } + } + + public void testUnparsableSelectors() throws Exception + { + Connection connection = getConnection("guest", "guest", randomize("Client")); + _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + boolean caught = false; + //Try Creating a Browser + try + { + _session.createBrowser(_session.createQueue("Ping"), INVALID_SELECTOR); + } + catch (JMSException e) + { + _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + if (!(e instanceof InvalidSelectorException)) + { + fail("Wrong exception:" + e.getMessage()); + } + caught = true; + } + assertTrue("No exception thrown!", caught); + caught = false; + + //Try Creating a Consumer + try + { + _session.createConsumer(_session.createQueue("Ping"), INVALID_SELECTOR); + } + catch (JMSException e) + { + _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + if (!(e instanceof InvalidSelectorException)) + { + fail("Wrong exception:" + e.getMessage()); + } + caught = true; + } + assertTrue("No exception thrown!", caught); + caught = false; + + //Try Creating a Receiever + try + { + _session.createReceiver(_session.createQueue("Ping"), INVALID_SELECTOR); + } + catch (JMSException e) + { + _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + if (!(e instanceof InvalidSelectorException)) + { + fail("Wrong exception:" + e.getMessage()); + } + caught = true; + } + assertTrue("No exception thrown!", caught); + caught = false; + + try + { + _session.createReceiver(_session.createQueue("Ping"), BAD_MATHS_SELECTOR); + } + catch (JMSException e) + { + _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); + if (!(e instanceof InvalidSelectorException)) + { + fail("Wrong exception:" + e.getMessage()); + } + caught = true; + } + assertTrue("No exception thrown!", caught); + caught = false; + + } + + public void testRuntimeSelectorError() throws JMSException + { + MessageConsumer consumer = _session.createConsumer(_destination , "testproperty % 5 = 1"); + MessageProducer producer = _session.createProducer(_destination); + Message sentMsg = _session.createTextMessage(); + + sentMsg.setIntProperty("testproperty", 1); // 1 % 5 + producer.send(sentMsg); + Message recvd = consumer.receive(RECIEVE_TIMEOUT); + assertNotNull(recvd); + + sentMsg.setStringProperty("testproperty", "hello"); // "hello" % 5 makes no sense + producer.send(sentMsg); + try + { + recvd = consumer.receive(RECIEVE_TIMEOUT); + assertNull(recvd); + } + catch (Exception e) + { + + } + assertTrue("Connection should be closed", _connection.isClosed()); + } + public void testSelectorWithJMSMessageID() throws Exception { Connection conn = getConnection(); conn.start(); Session session = conn.createSession(true, Session.AUTO_ACKNOWLEDGE); - - Destination dest = session.createQueue("SelectorQueue"); - MessageProducer prod = session.createProducer(dest); - MessageConsumer consumer = session.createConsumer(dest,"JMSMessageID IS NOT NULL"); + MessageProducer prod = session.createProducer(_destination); + MessageConsumer consumer = session.createConsumer(_destination,"JMSMessageID IS NOT NULL"); for (int i=0; i<2; i++) { @@ -54,7 +323,7 @@ public class SelectorTest extends QpidTestCase Message msg3 = consumer.receive(1000); Assert.assertNull("Msg3 should be null", msg3); session.commit(); - consumer = session.createConsumer(dest,"JMSMessageID IS NULL"); + consumer = session.createConsumer(_destination,"JMSMessageID IS NULL"); Message msg4 = consumer.receive(1000); Message msg5 = consumer.receive(1000); @@ -62,4 +331,49 @@ public class SelectorTest extends QpidTestCase Assert.assertNotNull("Msg4 should not be null", msg4); Assert.assertNotNull("Msg5 should not be null", msg5); } + + public void onMessage(Message message) + { + count++; + _logger.info("Got Message:" + message); + _responseLatch.countDown(); + } + + private static String randomize(String in) + { + return in + System.currentTimeMillis(); + } + + public static void main(String[] argv) throws Exception + { + SelectorTest test = new SelectorTest(); + test._connectionString = (argv.length == 0) ? "localhost:3000" : argv[0]; + + try + { + while (true) + { + if (test._connectionString.contains("vm://:1")) + { + test.setUp(); + } + test.test(); + + if (test._connectionString.contains("vm://:1")) + { + test.tearDown(); + } + } + } + catch (Exception e) + { + System.err.println(e.getMessage()); + e.printStackTrace(); + } + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(SelectorTest.class); + } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/timeouts/SyncWaitDelayTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/timeouts/SyncWaitDelayTest.java index b13170efc9..a123fb290c 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/timeouts/SyncWaitDelayTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/timeouts/SyncWaitDelayTest.java @@ -57,26 +57,13 @@ public class SyncWaitDelayTest extends QpidTestCase public void setUp() throws Exception { - super.setUp(); - stopBroker(); - if (!_configFile.exists()) - { - fail("Unable to test without config file:" + _configFile); - } - XMLConfiguration configuration = new XMLConfiguration(_configFile); - configuration.setProperty("virtualhosts.virtualhost." + VIRTUALHOST+".store.class", "org.apache.qpid.server.store.SlowMessageStore"); - configuration.setProperty("virtualhosts.virtualhost." + VIRTUALHOST+".store.delays.commitTran.post", POST_COMMIT_DELAY); - configuration.setProperty("management.enabled", "false"); + setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST+".store.class", "org.apache.qpid.server.store.SlowMessageStore"); + setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST+".store.delays.commitTran.post", String.valueOf(POST_COMMIT_DELAY)); + setConfigurationProperty("management.enabled", "false"); - File tmpFile = File.createTempFile("configFile", "test"); - tmpFile.deleteOnExit(); - configuration.save(tmpFile); - - _configFile = tmpFile; - - startBroker(1); + super.setUp(); //Set the syncWrite timeout to be just larger than the delay on the commitTran. setSystemProperty("amqj.default_syncwrite_timeout", String.valueOf(SYNC_WRITE_TIMEOUT)); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/SelectorTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/SelectorTest.java deleted file mode 100644 index c42e4c7582..0000000000 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/SelectorTest.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.unit.basic; - -import org.apache.qpid.AMQException; -import org.apache.qpid.test.utils.QpidTestCase; -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQDestination; -import org.apache.qpid.client.AMQQueue; -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.client.BasicMessageProducer; -import org.apache.qpid.client.state.StateWaiter; -import org.apache.qpid.url.URLSyntaxException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.jms.Connection; -import javax.jms.DeliveryMode; -import javax.jms.InvalidSelectorException; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageListener; -import java.util.concurrent.CountDownLatch; - -public class SelectorTest extends QpidTestCase implements MessageListener -{ - private static final Logger _logger = LoggerFactory.getLogger(SelectorTest.class); - - private AMQConnection _connection; - private AMQDestination _destination; - private AMQSession _session; - private int count; - public String _connectionString = "vm://:1"; - private static final String INVALID_SELECTOR = "Cost LIKE 5"; - CountDownLatch _responseLatch = new CountDownLatch(1); - - protected void setUp() throws Exception - { - super.setUp(); - init((AMQConnection) getConnection("guest", "guest")); - } - - protected void tearDown() throws Exception - { - super.tearDown(); - } - - private void init(AMQConnection connection) throws JMSException - { - init(connection, new AMQQueue(connection, randomize("SessionStartTest"), true)); - } - - private void init(AMQConnection connection, AMQDestination destination) throws JMSException - { - _connection = connection; - _destination = destination; - connection.start(); - - String selector = null; - selector = "Cost = 2 AND \"property-with-hyphen\" = 'wibble'"; - // selector = "JMSType = Special AND Cost = 2 AND AMQMessageID > 0 AND JMSDeliveryMode=" + DeliveryMode.NON_PERSISTENT; - - _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); - // _session.createConsumer(destination).setMessageListener(this); - _session.createConsumer(destination, selector).setMessageListener(this); - } - - public void test() throws Exception - { - try - { - - init((AMQConnection) getConnection("guest", "guest", randomize("Client"))); - - Message msg = _session.createTextMessage("Message"); - msg.setJMSPriority(1); - msg.setIntProperty("Cost", 2); - msg.setStringProperty("property-with-hyphen", "wibble"); - msg.setJMSType("Special"); - - _logger.info("Sending Message:" + msg); - - ((BasicMessageProducer) _session.createProducer(_destination)).send(msg, DeliveryMode.NON_PERSISTENT); - _logger.info("Message sent, waiting for response..."); - - _responseLatch.await(); - - if (count > 0) - { - _logger.info("Got message"); - } - - if (count == 0) - { - fail("Did not get message!"); - // throw new RuntimeException("Did not get message!"); - } - } - catch (JMSException e) - { - _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); - if (!(e instanceof InvalidSelectorException)) - { - fail("Wrong exception:" + e.getMessage()); - } - else - { - System.out.println("SUCCESS!!"); - } - } - catch (InterruptedException e) - { - _logger.debug("IE :" + e.getClass().getSimpleName() + ":" + e.getMessage()); - } - catch (URLSyntaxException e) - { - _logger.debug("URL:" + e.getClass().getSimpleName() + ":" + e.getMessage()); - fail("Wrong exception"); - } - catch (AMQException e) - { - _logger.debug("AMQ:" + e.getClass().getSimpleName() + ":" + e.getMessage()); - fail("Wrong exception"); - } - - finally - { - if (_session != null) - { - _session.close(); - } - if (_connection != null) - { - _connection.close(); - } - } - } - - - public void testInvalidSelectors() throws Exception - { - Connection connection = null; - - try - { - connection = getConnection("guest", "guest", randomize("Client")); - _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); - } - catch (JMSException e) - { - fail(e.getMessage()); - } - catch (AMQException e) - { - fail(e.getMessage()); - } - catch (URLSyntaxException e) - { - fail("Error:" + e.getMessage()); - } - - //Try Creating a Browser - try - { - _session.createBrowser(_session.createQueue("Ping"), INVALID_SELECTOR); - } - catch (JMSException e) - { - _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); - if (!(e instanceof InvalidSelectorException)) - { - fail("Wrong exception:" + e.getMessage()); - } - else - { - _logger.debug("SUCCESS!!"); - } - } - - //Try Creating a Consumer - try - { - _session.createConsumer(_session.createQueue("Ping"), INVALID_SELECTOR); - } - catch (JMSException e) - { - _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); - if (!(e instanceof InvalidSelectorException)) - { - fail("Wrong exception:" + e.getMessage()); - } - else - { - _logger.debug("SUCCESS!!"); - } - } - - //Try Creating a Receiever - try - { - _session.createReceiver(_session.createQueue("Ping"), INVALID_SELECTOR); - } - catch (JMSException e) - { - _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage()); - if (!(e instanceof InvalidSelectorException)) - { - fail("Wrong exception:" + e.getMessage()); - } - else - { - _logger.debug("SUCCESS!!"); - } - } - - finally - { - if (_session != null) - { - try - { - _session.close(); - } - catch (JMSException e) - { - fail("Error cleaning up:" + e.getMessage()); - } - } - if (_connection != null) - { - try - { - _connection.close(); - } - catch (JMSException e) - { - fail("Error cleaning up:" + e.getMessage()); - } - } - } - } - - public void onMessage(Message message) - { - count++; - _logger.info("Got Message:" + message); - _responseLatch.countDown(); - } - - private static String randomize(String in) - { - return in + System.currentTimeMillis(); - } - - public static void main(String[] argv) throws Exception - { - SelectorTest test = new SelectorTest(); - test._connectionString = (argv.length == 0) ? "localhost:3000" : argv[0]; - - try - { - while (true) - { - if (test._connectionString.contains("vm://:1")) - { - test.setUp(); - } - test.test(); - - if (test._connectionString.contains("vm://:1")) - { - test.tearDown(); - } - } - } - catch (Exception e) - { - System.err.println(e.getMessage()); - e.printStackTrace(); - } - } - - public static junit.framework.Test suite() - { - return new junit.framework.TestSuite(SelectorTest.class); - } -} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java new file mode 100644 index 0000000000..c9810e7304 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java @@ -0,0 +1,88 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.client; + +import org.apache.qpid.test.utils.QpidTestCase; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.Session; + +/** + * QPID-155 + * + * Test to validate that setting the respective qpid.declare_queues, + * qpid.declare_exchanges system properties functions as expected. + * + */ +public class DynamicQueueExchangeCreateTest extends QpidTestCase +{ + + public void testQueueDeclare() throws Exception + { + setSystemProperty("qpid.declare_queues", "false"); + + Connection connection = getConnection(); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + Queue queue = session.createQueue(getTestQueueName()); + + try + { + session.createConsumer(queue); + fail("JMSException should be thrown as the queue does not exist"); + } + catch (JMSException e) + { + assertTrue("Exception should be that the queue does not exist :" + + e.getMessage(), + e.getMessage().contains("does not exist")); + + } + } + + public void testExchangeDeclare() throws Exception + { + setSystemProperty("qpid.declare_exchanges", "false"); + + Connection connection = getConnection(); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + String EXCHANGE_TYPE = "test.direct"; + Queue queue = session.createQueue("direct://" + EXCHANGE_TYPE + "/queue/queue"); + + try + { + session.createConsumer(queue); + fail("JMSException should be thrown as the exchange does not exist"); + } + catch (JMSException e) + { + assertTrue("Exception should be that the exchange does not exist :" + + e.getMessage(), + e.getMessage().contains("Exchange " + EXCHANGE_TYPE + " does not exist")); + } + } + +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java new file mode 100644 index 0000000000..3fb6cd3526 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java @@ -0,0 +1,119 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.close; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ExchangeDeclareBody; +import org.apache.qpid.framing.ExchangeDeclareOkBody; +import org.apache.qpid.test.utils.QpidTestCase; + +import javax.jms.Session; + +/** QPID-1809 + * + * Race condition on error handling and close logic. + * + * See most often with SimpleACLTest as this test is the expects the server to + * shut the connection/channels. This sort of testing is not performed by many, + * if any, of the other system tests. + * + * The problem is that we have two threads + * + * MainThread Exception(Mina)Thread + * | | + * Performs | + * ACtion | + * | Receives Server + * | Close + * Blocks for | + * Response | + * | Starts To Notify + * | client + * | | + * | <----- Notify Main Thread + * Notification | + * wakes client | + * | | + * Client then | + * processes Error. | + * | | + * Potentially Attempting Close Channel/Connection + * Connection Close + * + * The two threads both attempt to close the connection but the main thread does + * so assuming that the connection is open and valid. + * + * The Exception thread must modify the connection so that no furter syncWait + * commands are performed. + * + * This test sends an ExchangeDeclare that is Asynchronous and will fail and + * so cause a ChannelClose error but we perform a syncWait so that we can be + * sure to test that the BlockingWaiter is correctly awoken. + * + */ +public class JavaServerCloseRaceConditionTest extends QpidTestCase +{ + private static final String EXCHANGE_NAME = "NewExchangeNametoFailLookup"; + + public void test() throws Exception + { + + AMQConnection connection = (AMQConnection) getConnection(); + + AMQSession session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Set no wait true so that we block the connection + // Also set a different exchange class string so the attempt to declare + // the exchange causes an exchange. + ExchangeDeclareBody body = session.getMethodRegistry().createExchangeDeclareBody(session.getTicket(), new AMQShortString(EXCHANGE_NAME), null, + true, false, false, false, true, null); + + AMQFrame exchangeDeclare = body.generateFrame(session.getChannelId()); + + try + { + // block our thread so that can times out + connection.getProtocolHandler().syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class); + } + catch (Exception e) + { + assertTrue("Exception should say the exchange is not known.", e.getMessage().contains("Unknown exchange: " + EXCHANGE_NAME)); + } + + try + { + // Depending on if the notification thread has closed the connection + // or not we may get an exception here when we attempt to close the + // connection. If we do get one then it should be the same as above + // an AMQAuthenticationException. + connection.close(); + } + catch (Exception e) + { + assertTrue("Exception should say the exchange is not known.", e.getMessage().contains("Unknown exchange: " + EXCHANGE_NAME)); + } + + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java index 9c755fcb41..b603455644 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java @@ -479,7 +479,7 @@ public class CommitRollbackTest extends QpidTestCase _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT)); _pubSession.commit(); - assertNotNull(_consumer.receive(100)); + assertNotNull(_consumer.receive(1000)); _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT)); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java new file mode 100644 index 0000000000..3f8cdb9c25 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java @@ -0,0 +1,208 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.utils; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.commands.objects.AllObjects; +import org.apache.qpid.management.common.JMXConnnectionFactory; +import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.management.common.mbeans.ManagedExchange; + +import javax.management.JMException; +import javax.management.MBeanException; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerInvocationHandler; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; +import java.io.IOException; +import java.util.Set; + +/** + * + */ +public class JMXTestUtils +{ + QpidTestCase _test; + MBeanServerConnection _mbsc; + JMXConnector _jmxc; + + private String USER; + private String PASSWORD; + + public JMXTestUtils(QpidTestCase test, String user, String password) + { + _test = test; + USER = user; + PASSWORD = password; + } + + public void setUp() throws IOException, ConfigurationException, Exception + { + _test.setConfigurationProperty("management.enabled", "true"); + } + + public void open() throws Exception + { + _jmxc = JMXConnnectionFactory.getJMXConnection( + 5000, "127.0.0.1", + _test.getManagementPort(_test.getPort()), USER, PASSWORD); + + _mbsc = _jmxc.getMBeanServerConnection(); + } + + public void close() throws IOException + { + _jmxc.close(); + } + + /** + * Create a non-durable test exchange with the current test name + * + * @throws javax.management.JMException - is thrown if a exchange with this testName already exists + * @throws java.io.IOException - if there is a problem with the JMX Connection + * @throws javax.management.MBeanException + * - if there is another problem creating the exchange + */ + public void createExchange(String virtualHostName, String name, String type, boolean durable) + throws JMException, IOException, MBeanException + { + ManagedBroker managedBroker = getManagedBroker(virtualHostName); + + managedBroker.createNewExchange(name, type, durable); + } + + /** + * Create a non-durable queue (with no owner) that is named after the + * creating test. + * + * @throws JMException - is thrown if a queue with this testName already exists + * @throws IOException - if there is a problem with the JMX Connection + */ + public void createQueue(String virtualHostName, String name, String owner, boolean durable) + throws JMException, IOException + { + ManagedBroker managedBroker = getManagedBroker(virtualHostName); + + managedBroker.createNewQueue(name, owner, durable); + } + + /** + * Retrive the ObjectName for the test Virtualhost. + * + * This is then use to create aproxy to the ManagedBroker MBean. + * + * @return the ObjectName for the 'test' VirtualHost. + */ + public ObjectName getVirtualHostManagerObjectName(String vhostName) + { + // Get the name of the test manager + AllObjects allObject = new AllObjects(_mbsc); + allObject.querystring = "org.apache.qpid:type=VirtualHost.VirtualHostManager,VirtualHost=" + vhostName + ",*"; + + Set<ObjectName> objectNames = allObject.returnObjects(); + + _test.assertEquals("Incorrect number test vhosts returned", 1, objectNames.size()); + + // We have verified we have only one value in objectNames so return it + return objectNames.iterator().next(); + } + + /** + * Retrive the ObjectName for the given Exchange on the test Virtualhost. + * + * This is then use to create aproxy to the ManagedExchange MBean. + * + * @param queue The exchange to retireve e.g. 'direct' + * + * @return the ObjectName for the given exchange on the test VirtualHost. + */ + public ObjectName getQueueObjectName(String virtualHostName, String queue) + { + // Get the name of the test manager + AllObjects allObject = new AllObjects(_mbsc); + allObject.querystring = "org.apache.qpid:type=VirtualHost.Queue,VirtualHost=" + virtualHostName + ",name=" + queue + ",*"; + + Set<ObjectName> objectNames = allObject.returnObjects(); + + _test.assertEquals("Incorrect number of exchange with name '" + queue + + "' returned", 1, objectNames.size()); + + // We have verified we have only one value in objectNames so return it + return objectNames.iterator().next(); + } + + /** + * Retrive the ObjectName for the given Exchange on the test Virtualhost. + * + * This is then use to create aproxy to the ManagedExchange MBean. + * + * @param virtualHostName + * @param exchange The exchange to retireve e.g. 'direct' + * + * @return the ObjectName for the given exchange on the test VirtualHost. + */ + public ObjectName getExchangeObjectName(String virtualHostName, String exchange) + { + // Get the name of the test manager + AllObjects allObject = new AllObjects(_mbsc); + allObject.querystring = "org.apache.qpid:type=VirtualHost.Exchange,VirtualHost=" + virtualHostName + ",name=" + exchange + ",*"; + + Set<ObjectName> objectNames = allObject.returnObjects(); + + _test.assertEquals("Incorrect number of exchange with name '" + exchange + + "' returned", 1, objectNames.size()); + + // We have verified we have only one value in objectNames so return it + return objectNames.iterator().next(); + } + + public <T> T getManagedObject(Class<T> managedClass, String queryString) + { + AllObjects allObject = new AllObjects(_mbsc); + allObject.querystring = queryString; + + Set<ObjectName> objectNames = allObject.returnObjects(); + + _test.assertEquals("More than one " + managedClass + " returned", 1, objectNames.size()); + + ObjectName objectName = objectNames.iterator().next(); + + return getManagedObject(managedClass, objectName); + } + + public <T> T getManagedObject(Class<T> managedClass, ObjectName objectName) + { + return MBeanServerInvocationHandler. + newProxyInstance(_mbsc, objectName, managedClass, false); + } + + public ManagedBroker getManagedBroker(String virtualHost) + { + return getManagedObject(ManagedBroker.class, getVirtualHostManagerObjectName(virtualHost).toString()); + } + + public ManagedExchange getManagedExchange(String exchangeName) + { + return MBeanServerInvocationHandler. + newProxyInstance(_mbsc, getExchangeObjectName("test", exchangeName), + ManagedExchange.class, false); + } +} 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 db096710dc..bed5d3242a 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 @@ -21,6 +21,7 @@ import junit.framework.TestCase; import junit.framework.TestResult; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; +import org.apache.qpid.AMQException; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQConnectionFactory; import org.apache.qpid.client.transport.TransportConnection; @@ -30,6 +31,7 @@ import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; import org.apache.qpid.server.store.DerbyMessageStore; +import org.apache.qpid.url.URLSyntaxException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -73,6 +75,7 @@ public class QpidTestCase extends TestCase protected long RECEIVE_TIMEOUT = 1000l; private Map<String, String> _setProperties = new HashMap<String, String>(); + private XMLConfiguration _testConfiguration = new XMLConfiguration(); /** * Some tests are excluded when the property test.excludes is set to true. @@ -183,8 +186,7 @@ public class QpidTestCase extends TestCase public static final String QUEUE = "queue"; public static final String TOPIC = "topic"; /** Map to hold test defined environment properties */ - private Map<String,String> _env; - + private Map<String, String> _env; public QpidTestCase(String name) { @@ -335,7 +337,7 @@ public class QpidTestCase extends TestCase latch.countDown(); } - if (latch != null && line.contains(stopped)) + if (!seenReady && line.contains(stopped)) { stopLine = line; } @@ -368,7 +370,9 @@ public class QpidTestCase extends TestCase /** * Return the management portin use by the broker on this main port + * * @param mainPort the broker's main port. + * * @return the management port that corresponds to the broker on the given port */ protected int getManagementPort(int mainPort) @@ -415,9 +419,14 @@ public class QpidTestCase extends TestCase { port = getPort(port); + // Save any configuratio changes that have been made + saveTestConfiguration(); + Process process = null; if (_broker.equals(VM)) { + setConfigurationProperty("management.jmxport", String.valueOf(getManagementPort(port))); + saveTestConfiguration(); // create an in_VM broker ApplicationRegistry.initialise(new ConfigurationFileApplicationRegistry(_configFile), port); TransportConnection.createVMBroker(port); @@ -438,15 +447,35 @@ public class QpidTestCase extends TestCase env.put("PATH", env.get("PATH").concat(File.pathSeparator + qpidHome + "/bin")); //Add the test name to the broker run. - env.put("QPID_PNAME", "-DPNAME=\"" + _testName + "\""); + // DON'T change PNAME, qpid.stop needs this value. + env.put("QPID_PNAME", "-DPNAME=QPBRKR -DTNAME=\"" + _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()) + for (Map.Entry<String, String> entry : _env.entrySet()) + { + env.put(entry.getKey(), entry.getValue()); + } + } + + String QPID_OPTS = " "; + // Add all the specified system properties to QPID_OPTS + if (!_setProperties.isEmpty()) + { + for (String key : _setProperties.keySet()) + { + QPID_OPTS += "-D" + key + "=" + System.getProperty(key) + " "; + } + + if (env.containsKey("QPID_OPTS")) { - env.put(entry.getKey() ,entry.getValue()); + env.put("QPID_OPTS", env.get("QPID_OPTS") + QPID_OPTS); + } + else + { + env.put("QPID_OPTS", QPID_OPTS); } } @@ -484,6 +513,27 @@ public class QpidTestCase extends TestCase _brokers.put(port, process); } + public String getTestConfigFile() + { + String path = _output == null ? System.getProperty("java.io.tmpdir") : _output; + return path + "/" + getTestQueueName() + ".xml"; + } + + protected void saveTestConfiguration() throws ConfigurationException + { + String testConfig = getTestConfigFile(); + //Specifiy the test configuration + setSystemProperty("test.config", testConfig); + + // This is a work + if (_testConfiguration.isEmpty()) + { + _testConfiguration.addProperty("test", getTestQueueName()); + } + + _testConfiguration.save(getTestConfigFile()); + } + public void cleanBroker() { if (_brokerClean != null) @@ -565,18 +615,12 @@ public class QpidTestCase extends TestCase storeClass = bdb; } - // First we munge the config file and, if we're in a VM, set up an additional logfile - XMLConfiguration configuration = new XMLConfiguration(_configFile); - configuration.setProperty("virtualhosts.virtualhost." + virtualhost + + + _testConfiguration.setProperty("virtualhosts.virtualhost." + virtualhost + ".store.class", storeClass.getName()); - configuration.setProperty("virtualhosts.virtualhost." + virtualhost + + _testConfiguration.setProperty("virtualhosts.virtualhost." + virtualhost + ".store." + DerbyMessageStore.ENVIRONMENT_PATH_PROPERTY, - "${work}/" + virtualhost); - - File tmpFile = File.createTempFile("configFile", "test"); - tmpFile.deleteOnExit(); - configuration.save(tmpFile); - _configFile = tmpFile; + "${QPID_WORK}/" + virtualhost); } /** @@ -591,6 +635,10 @@ public class QpidTestCase extends TestCase */ protected String getConfigurationStringProperty(String property) throws ConfigurationException { + // Call save Configuration to be sure we have saved the test specific + // file. As the optional status + saveTestConfiguration(); + ServerConfiguration configuration = new ServerConfiguration(_configFile); return configuration.getConfig().getString(property); } @@ -613,48 +661,9 @@ public class QpidTestCase extends TestCase protected void setConfigurationProperty(String property, String value) throws ConfigurationException, IOException { - XMLConfiguration configuration = new XMLConfiguration(_configFile); - - // If we are modifying a virtualhost value then we need to do so in - // the virtualhost.xml file as these values overwrite the values in - // the main config.xml file - if (property.startsWith("virtualhosts")) - { - // So locate the virtualhost.xml file and use the ServerConfiguration - // flatConfig method to get the interpolated value. - String vhostConfigFile = ServerConfiguration. - flatConfig(_configFile).getString("virtualhosts"); - - // Load the vhostConfigFile - XMLConfiguration vhostConfiguration = new XMLConfiguration(vhostConfigFile); - - // Set the value specified in to the vhostConfig. - // Remembering that property will be 'virtualhosts.virtulhost....' - // so we need to take off the 'virtualhosts.' from the start. - vhostConfiguration.setProperty(property.substring(property.indexOf(".") + 1), value); - - // Write out the new virtualhost config file - File tmpFile = File.createTempFile("virtualhost-configFile", ".xml"); - tmpFile.deleteOnExit(); - vhostConfiguration.save(tmpFile); - - // Change the property and value to be the new virtualhosts file - // so that then update the value in the main config file. - property = "virtualhosts"; - value = tmpFile.getAbsolutePath(); - } - - configuration.setProperty(property, value); - - // Write the new server config file - File tmpFile = File.createTempFile("configFile", ".xml"); - tmpFile.deleteOnExit(); - configuration.save(tmpFile); - - _logger.info("Qpid Test Case now using configuration File:" - + tmpFile.getAbsolutePath()); - - _configFile = tmpFile; + //Write the value in to this configuration file which will override the + // defaults. + _testConfiguration.setProperty(property, value); } /** @@ -695,14 +704,13 @@ 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 + * @param value the value to set it to */ protected void setBrokerEnvironment(String property, String value) { - _env.put(property,value); + _env.put(property, value); } - /** * Check whether the broker is an 0.8 * @@ -720,7 +728,7 @@ public class QpidTestCase extends TestCase protected boolean isJavaBroker() { - return _brokerLanguage.equals("java"); + return _brokerLanguage.equals("java") || _broker.equals("vm"); } protected boolean isCppBroker() @@ -831,7 +839,7 @@ public class QpidTestCase extends TestCase * * @throws Exception if there is an error getting the connection */ - public Connection getConnection(String username, String password) throws Exception + public Connection getConnection(String username, String password) throws JMSException, NamingException { _logger.info("get Connection"); Connection con = getConnectionFactory().createConnection(username, password); @@ -840,7 +848,7 @@ public class QpidTestCase extends TestCase return con; } - public Connection getConnection(String username, String password, String id) throws Exception + public Connection getConnection(String username, String password, String id) throws JMSException, URLSyntaxException, AMQException, NamingException { _logger.info("get Connection"); Connection con; @@ -860,6 +868,7 @@ public class QpidTestCase extends TestCase /** * Return a uniqueName for this test. * In this case it returns a queue Named by the TestCase and TestName + * * @return String name for a queue */ protected String getTestQueueName() 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 b721e27726..2b9fe8e039 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 @@ -158,18 +158,6 @@ public class LogMonitorTest extends TestCase validateLogDoesNotContainsMessage(_monitor, notLogged); } - public void testWaitForMessage_Found() throws IOException - { - String message = getName() + ": Test Message"; - - long TIME_OUT = 2000; - - logMessageWithDelay(message, TIME_OUT / 2); - - assertTrue("Message was not logged ", - _monitor.waitForMessage(message, TIME_OUT)); - } - public void testWaitForMessage_Timeout() throws IOException { String message = getName() + ": Test Message"; diff --git a/qpid/java/test-profiles/010Excludes b/qpid/java/test-profiles/010Excludes index e238934124..f03d62667d 100644 --- a/qpid/java/test-profiles/010Excludes +++ b/qpid/java/test-profiles/010Excludes @@ -83,3 +83,12 @@ org.apache.qpid.server.logging.* // CPP Broker does not have a JMX interface to test org.apache.qpid.management.jmx.* +// JMX is used in this test for validation +org.apache.qpid.server.queue.ModelTest#* + + +// 0-10 is not supported by the MethodRegistry +org.apache.qpid.test.unit.close.JavaServerCloseRaceConditionTest#* + +// QPID-2084 : this test needs more work for 0-10 +org.apache.qpid.test.unit.client.DynamicQueueExchangeCreateTest#* diff --git a/qpid/java/test-profiles/08Excludes b/qpid/java/test-profiles/08Excludes index 0866694854..b277c6d929 100644 --- a/qpid/java/test-profiles/08Excludes +++ b/qpid/java/test-profiles/08Excludes @@ -15,3 +15,8 @@ org.apache.qpid.server.persistent.NoLocalAfterRecoveryTest#* org.apache.qpid.client.SessionCreateTest#* org.apache.qpid.test.client.RollbackOrderTest#* + +// QPID-2097 exclude it from the InVM test runs until InVM JMX Interface is reliable +org.apache.qpid.management.jmx.ManagementActorLoggingTest#* +org.apache.qpid.server.queue.ModelTest#* + diff --git a/qpid/java/test-profiles/Excludes b/qpid/java/test-profiles/Excludes index 7ef2a15e51..a72d3bc86c 100644 --- a/qpid/java/test-profiles/Excludes +++ b/qpid/java/test-profiles/Excludes @@ -13,5 +13,9 @@ org.apache.qpid.server.logging.BrokerLoggingTest#testBrokerShutdownListeningTCPS org.apache.qpid.server.logging.BrokerLoggingTest#testBrokerShutdownStopped org.apache.qpid.server.logging.VirtualHostLoggingTest#testVirtualhostClosure org.apache.qpid.server.logging.MemoryMessageStoreLoggingTest#testMessageStoreClose -org.apache.qpid.server.logging.DerbyMessageStoreLoggingTest#testMessageStoreClose +// QPID-XXX : Test fails to start external broker due to Derby Exception. +org.apache.qpid.server.logging.DerbyMessageStoreLoggingTest#* + +// QPID-2081 :The configuration changes are now highlighting the close race condition +org.apache.qpid.server.security.acl.SimpleACLTest#* diff --git a/qpid/java/test-profiles/cpp.testprofile b/qpid/java/test-profiles/cpp.testprofile index 1d5416fe19..b3b979c786 100644 --- a/qpid/java/test-profiles/cpp.testprofile +++ b/qpid/java/test-profiles/cpp.testprofile @@ -8,6 +8,7 @@ broker.executable=${broker.dir}/qpidd broker.module.ssl=${module.dir}/ssl.so broker.module.cluster=${module.dir}/cluster.so broker.module.store=${store.module.dir}/msgstore.so +broker.stopped=Exception constructed broker.modules= broker.args= diff --git a/qpid/java/test-profiles/default.testprofile b/qpid/java/test-profiles/default.testprofile index 49d4a25b82..86a5b2efb3 100644 --- a/qpid/java/test-profiles/default.testprofile +++ b/qpid/java/test-profiles/default.testprofile @@ -18,9 +18,10 @@ log4j.debug=false test.port=15672 test.mport=18999 +#Note : Management will start open second port on: mport + 100 : 19099 test.port.ssl=15671 -test.port.alt=15772 -test.port.alt.ssl=15771 +test.port.alt=25672 +test.port.alt.ssl=25671 test.exclude=true profile.excludes=08TransientExcludes diff --git a/qpid/python/Makefile b/qpid/python/Makefile index 380115db41..31547c8f57 100644 --- a/qpid/python/Makefile +++ b/qpid/python/Makefile @@ -46,6 +46,12 @@ $(BUILD)/%.py: %.py build: $(TARGETS) +.PHONY: doc + +doc: + @mkdir -p $(BUILD) + epydoc qpid.messaging -o $(BUILD)/doc --no-private --no-sourcecode --include-log + install: build install -d $(PYTHON_LIB) diff --git a/qpid/python/qpid/concurrency.py b/qpid/python/qpid/concurrency.py new file mode 100644 index 0000000000..00cdb6b953 --- /dev/null +++ b/qpid/python/qpid/concurrency.py @@ -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. +# + +import inspect, time + +def synchronized(meth): + args, vargs, kwargs, defs = inspect.getargspec(meth) + scope = {} + scope["meth"] = meth + exec """ +def %s%s: + %s + %s._lock.acquire() + try: + return meth%s + finally: + %s._lock.release() +""" % (meth.__name__, inspect.formatargspec(args, vargs, kwargs, defs), + repr(inspect.getdoc(meth)), args[0], + inspect.formatargspec(args, vargs, kwargs, defs, + formatvalue=lambda x: ""), + args[0]) in scope + return scope[meth.__name__] + +class Waiter(object): + + def __init__(self, condition): + self.condition = condition + + def wait(self, predicate, timeout=None): + passed = 0 + start = time.time() + while not predicate(): + if timeout is None: + # using the timed wait prevents keyboard interrupts from being + # blocked while waiting + self.condition.wait(3) + elif passed < timeout: + self.condition.wait(timeout - passed) + else: + return False + passed = time.time() - start + return True + + def notify(self): + self.condition.notify() + + def notifyAll(self): + self.condition.notifyAll() diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index f832ddae34..61643715e4 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -234,6 +234,24 @@ class RangedSet: def add(self, lower, upper = None): self.add_range(Range(lower, upper)) + def empty(self): + for r in self.ranges: + if r.lower <= r.upper: + return False + return True + + def max(self): + if self.ranges: + return self.ranges[-1].upper + else: + return None + + def min(self): + if self.ranges: + return self.ranges[0].lower + else: + return None + def __iter__(self): return iter(self.ranges) diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index c74cc5a945..14111a88df 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -139,12 +139,18 @@ class Server(Delegate): class Client(Delegate): + ppid = 0 + try: + ppid = os.getppid() + except: + pass + PROPERTIES = {"product": "qpid python client", "version": "development", "platform": os.name, "qpid.client_process": os.path.basename(sys.argv[0]), "qpid.client_pid": os.getpid(), - "qpid.client_ppid": os.getppid()} + "qpid.client_ppid": ppid} def __init__(self, connection, username="guest", password="guest", mechanism="PLAIN", heartbeat=None): diff --git a/qpid/python/qpid/driver.py b/qpid/python/qpid/driver.py new file mode 100644 index 0000000000..2e07c82a0d --- /dev/null +++ b/qpid/python/qpid/driver.py @@ -0,0 +1,444 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import compat, connection, socket, sys, time +from concurrency import synchronized +from datatypes import RangedSet, Message as Message010 +from exceptions import Timeout +from logging import getLogger +from messaging import get_codec, ConnectError, Message, Pattern, UNLIMITED +from ops import delivery_mode +from session import Client, INCOMPLETE, SessionDetached +from threading import Condition, Thread +from util import connect + +log = getLogger("qpid.messaging") + +def parse_addr(address): + parts = address.split("/", 1) + if len(parts) == 1: + return parts[0], None + else: + return parts[0], parts[i1] + +def reply_to2addr(reply_to): + if reply_to.routing_key is None: + return reply_to.exchange + elif reply_to.exchange in (None, ""): + return reply_to.routing_key + else: + return "%s/%s" % (reply_to.exchange, reply_to.routing_key) + +class Attachment: + + def __init__(self, target): + self.target = target + +DURABLE_DEFAULT=True + +FILTER_DEFAULTS = { + "topic": Pattern("*") + } + +def delegate(handler, session): + class Delegate(Client): + + def message_transfer(self, cmd): + return handler._message_transfer(session, cmd) + return Delegate + +class Driver: + + def __init__(self, connection): + self.connection = connection + self._lock = self.connection._lock + 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 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.connection._waiter.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.connection._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] + ssn.closed = True + + 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() + + # XXX: we're ignoring acks that get lost when disconnected + 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? + snd.acked += 1 + + @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 + if rcv.impending is not UNLIMITED: + assert rcv.received < rcv.impending + rcv.received += 1 + log.debug("RECV [%s] %s", ssn, msg) + ssn.incoming.append(msg) + self.connection._waiter.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 diff --git a/qpid/python/qpid/messaging.py b/qpid/python/qpid/messaging.py index 3c41a2c417..d755aa5054 100644 --- a/qpid/python/qpid/messaging.py +++ b/qpid/python/qpid/messaging.py @@ -6,9 +6,9 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -30,64 +30,18 @@ Areas that still need work: - protocol negotiation/multiprotocol impl """ -import connection, time, socket, sys, compat from codec010 import StringCodec -from datatypes import timestamp, uuid4, RangedSet, Message as Message010, Serial -from exceptions import Timeout +from concurrency import synchronized, Waiter +from datatypes import timestamp, uuid4, Serial from logging import getLogger -from ops import PRIMITIVE, delivery_mode -from session import Client, INCOMPLETE, SessionDetached +from ops import PRIMITIVE from threading import Thread, RLock, Condition -from util import connect +from util import default log = getLogger("qpid.messaging") static = staticmethod -def synchronized(meth): - def sync_wrapper(self, *args, **kwargs): - self.lock() - try: - return meth(self, *args, **kwargs) - finally: - self.unlock() - return sync_wrapper - -class Lockable(object): - - def lock(self): - self._lock.acquire() - - def unlock(self): - self._lock.release() - - def wait(self, predicate, timeout=None): - passed = 0 - start = time.time() - while not predicate(): - if timeout is None: - # using the timed wait prevents keyboard interrupts from being - # blocked while waiting - self._condition.wait(3) - elif passed < timeout: - self._condition.wait(timeout - passed) - else: - return False - passed = time.time() - start - return True - - def notify(self): - self._condition.notify() - - def notifyAll(self): - self._condition.notifyAll() - -def default(value, default): - if value is None: - return default - else: - return value - AMQP_PORT = 5672 AMQPS_PORT = 5671 @@ -103,12 +57,19 @@ class Constant: UNLIMITED = Constant("UNLIMITED", 0xFFFFFFFFL) class ConnectionError(Exception): + """ + The base class for all connection related exceptions. + """ pass class ConnectError(ConnectionError): + """ + Exception raised when there is an error connecting to the remote + peer. + """ pass -class Connection(Lockable): +class Connection: """ A Connection manages a group of L{Sessions<Session>} and connects @@ -153,27 +114,27 @@ class Connection(Lockable): self._connected = False self._lock = RLock() self._condition = Condition(self._lock) + self._waiter = Waiter(self._condition) self._modcount = Serial(0) self.error = None + from driver import Driver self._driver = Driver(self) self._driver.start() - def wakeup(self): + def _wait(self, predicate, timeout=None): + return self._waiter.wait(predicate, timeout=timeout) + + 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): + 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) + def _ewait(self, predicate, timeout=None, exc=ConnectionError): + result = self._wait(lambda: self.error or predicate(), timeout) + self._check_error(exc) return result @synchronized @@ -200,7 +161,7 @@ class Connection(Lockable): else: ssn = Session(self, name, self.started, transactional=transactional) self.sessions[name] = ssn - self.wakeup() + self._wakeup() return ssn @synchronized @@ -213,8 +174,8 @@ class Connection(Lockable): Connect to the remote endpoint. """ self._connected = True - self.wakeup() - self.ewait(lambda: self._driver._connected, exc=ConnectError) + self._wakeup() + self._ewait(lambda: self._driver._connected, exc=ConnectError) @synchronized def disconnect(self): @@ -222,8 +183,8 @@ class Connection(Lockable): Disconnect from the remote endpoint. """ self._connected = False - self.wakeup() - self.ewait(lambda: not self._driver._connected) + self._wakeup() + self._ewait(lambda: not self._driver._connected) @synchronized def connected(self): @@ -273,17 +234,6 @@ class Pattern: ssn.exchange_bind(exchange=exchange, queue=queue, binding_key=self.value.replace("*", "#")) -FILTER_DEFAULTS = { - "topic": Pattern("*") - } - -def delegate(handler, session): - class Delegate(Client): - - def message_transfer(self, cmd): - handler._message_transfer(session, cmd) - return Delegate - class SessionError(Exception): pass @@ -304,7 +254,7 @@ class NontransactionalSession(SessionError): class TransactionAborted(SessionError): pass -class Session(Lockable): +class Session: """ Sessions provide a linear context for sending and receiving @@ -329,12 +279,14 @@ class Session(Lockable): self.incoming = [] self.unacked = [] self.acked = [] + # XXX: I hate this name. + self.ack_capacity = UNLIMITED self.closing = False self.closed = False self._lock = connection._lock - self._condition = connection._condition + self.running = True self.thread = Thread(target = self.run) self.thread.setDaemon(True) self.thread.start() @@ -342,17 +294,17 @@ class Session(Lockable): def __repr__(self): return "<Session %s>" % self.name - def wakeup(self): - self.connection.wakeup() + def _wait(self, predicate, timeout=None): + return self.connection._wait(predicate, timeout=timeout) - def catchup(self, exc=SessionError): - self.connection.catchup(exc) + def _wakeup(self): + self.connection._wakeup() - def check_error(self, exc=SessionError): - self.connection.check_error(exc) + def _check_error(self, exc=SessionError): + self.connection._check_error(exc) - def ewait(self, predicate, timeout=None, exc=SessionError): - return self.connection.ewait(predicate, timeout, exc) + def _ewait(self, predicate, timeout=None, exc=SessionError): + return self.connection._ewait(predicate, timeout, exc) @synchronized def sender(self, target): @@ -367,7 +319,7 @@ class Session(Lockable): """ sender = Sender(self, len(self.senders), target) self.senders.append(sender) - self.wakeup() + 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 @@ -388,7 +340,7 @@ class Session(Lockable): receiver = Receiver(self, len(self.receivers), source, filter, self.started) self.receivers.append(receiver) - self.wakeup() + self._wakeup() return receiver @synchronized @@ -416,8 +368,8 @@ class Session(Lockable): @synchronized def _get(self, predicate, timeout=None): - if self.wait(lambda: ((self._peek(predicate) is not None) or self.closing), - timeout): + if self._wait(lambda: ((self._peek(predicate) is not None) or self.closing), + timeout): msg = self._pop(predicate) if msg is not None: msg._receiver.returned += 1 @@ -427,13 +379,15 @@ class Session(Lockable): return None @synchronized - def acknowledge(self, message=None): + def acknowledge(self, message=None, sync=True): """ Acknowledge the given L{Message}. If message is None, then all unacknowledged messages on the session are acknowledged. @type message: Message @param message: the message to acknowledge or None + @type sync: boolean + @param sync: if true then block until the message(s) are acknowledged """ if message is None: messages = self.unacked[:] @@ -441,12 +395,18 @@ class Session(Lockable): messages = [message] for m in messages: + if self.ack_capacity is not UNLIMITED: + if self.ack_capacity <= 0: + # XXX: this is currently a SendError, maybe it should be a SessionError? + raise InsufficientCapacity("ack_capacity = %s" % self.ack_capacity) + self._wakeup() + self._ewait(lambda: len(self.acked) < self.ack_capacity) 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() + self._wakeup() + if sync: + self._ewait(lambda: not [m for m in messages if m in self.acked]) @synchronized def commit(self): @@ -457,8 +417,8 @@ class Session(Lockable): if not self.transactional: raise NontransactionalSession() self.committing = True - self.wakeup() - self.ewait(lambda: not self.committing) + self._wakeup() + self._ewait(lambda: not self.committing) if self.aborted: raise TransactionAborted() assert self.committed @@ -472,8 +432,8 @@ class Session(Lockable): if not self.transactional: raise NontransactionalSession() self.aborting = True - self.wakeup() - self.ewait(lambda: not self.aborting) + self._wakeup() + self._ewait(lambda: not self.aborting) assert self.aborted @synchronized @@ -493,7 +453,7 @@ class Session(Lockable): for rcv in self.receivers: rcv.stop() # TODO: think about stopping individual receivers in listen mode - self.wait(lambda: self._peek(self._pred) is None) + self._wait(lambda: self._peek(self._pred) is None) self.started = False def _pred(self, m): @@ -501,6 +461,7 @@ class Session(Lockable): @synchronized def run(self): + self.running = True try: while True: msg = self._get(self._pred) @@ -509,10 +470,10 @@ class Session(Lockable): else: msg._receiver.listener(msg) if self._peek(self._pred) is None: - self.notifyAll() + self.connection._waiter.notifyAll() finally: - self.closed = True - self.notifyAll() + self.running = False + self.connection._waiter.notifyAll() @synchronized def close(self): @@ -523,33 +484,22 @@ class Session(Lockable): link.close() self.closing = True - self.wakeup() - self.catchup() - self.wait(lambda: self.closed) + self._wakeup() + self._ewait(lambda: self.closed and not self.running) while self.thread.isAlive(): self.thread.join(3) self.thread = None + # XXX: should be able to express this condition through API calls + self._ewait(lambda: not self.outgoing and not self.acked) self.connection._remove_session(self) -def parse_addr(address): - parts = address.split("/", 1) - if len(parts) == 1: - return parts[0], None - else: - return parts[0], parts[i1] - -def reply_to2addr(reply_to): - if reply_to.routing_key is None: - return reply_to.exchange - elif reply_to.exchange in (None, ""): - return reply_to.routing_key - else: - return "%s/%s" % (reply_to.exchange, reply_to.routing_key) - class SendError(SessionError): pass -class Sender(Lockable): +class InsufficientCapacity(SendError): + pass + +class Sender: """ Sends outgoing messages. @@ -559,32 +509,50 @@ class Sender(Lockable): self.session = session self.index = index self.target = target + self.capacity = UNLIMITED + self.queued = Serial(0) + self.acked = Serial(0) self.closed = False self._lock = self.session._lock - self._condition = self.session._condition - def wakeup(self): - self.session.wakeup() + def _wakeup(self): + self.session._wakeup() - def catchup(self, exc=SendError): - self.session.catchup(exc) + def _check_error(self, exc=SendError): + self.session._check_error(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) - def ewait(self, predicate, timeout=None, exc=SendError): - return self.session.ewait(predicate, timeout, exc) + @synchronized + def pending(self): + """ + Returns the number of messages awaiting acknowledgment. + @rtype: int + @return: the number of unacknowledged messages + """ + return self.queued - self.acked @synchronized - def send(self, object): + def send(self, object, sync=True, timeout=None): """ Send a message. If the object passed in is of type L{unicode}, L{str}, L{list}, or L{dict}, it will automatically be wrapped in a L{Message} and sent. If it is of type L{Message}, it will be sent - directly. + directly. If the sender capacity is not L{UNLIMITED} then send + will block until there is available capacity to send the message. + If the timeout parameter is specified, then send will throw an + L{InsufficientCapacity} exception if capacity does not become + available within the specified time. @type object: unicode, str, list, dict, Message @param object: the message or content to send + + @type sync: boolean + @param sync: if true then block until the message is sent + + @type timeout: float + @param timeout: the time to wait for available capacity """ if not self.session.connection._connected or self.session.closing: @@ -595,12 +563,23 @@ class Sender(Lockable): else: message = Message(object) + if self.capacity is not UNLIMITED: + if self.capacity <= 0: + raise InsufficientCapacity("capacity = %s" % self.capacity) + if not self._ewait(lambda: self.pending() < self.capacity, timeout=timeout): + raise InsufficientCapacity("capacity = %s" % self.capacity) + # XXX: what if we send the same message to multiple senders? message._sender = self self.session.outgoing.append(message) + self.queued += 1 + mno = self.queued + + self._wakeup() - self.wakeup() - self.ewait(lambda: message not in self.session.outgoing) + if sync: + self._ewait(lambda: self.acked >= mno) + assert message not in self.session.outgoing @synchronized def close(self): @@ -622,7 +601,7 @@ class Empty(ReceiveError): """ pass -class Receiver(Lockable): +class Receiver: """ Receives incoming messages from a remote source. Messages may be @@ -649,22 +628,25 @@ class Receiver(Lockable): self.closed = False self.listener = None self._lock = self.session._lock - self._condition = self.session._condition - def wakeup(self): - self.session.wakeup() + def _wakeup(self): + self.session._wakeup() - def catchup(self, exc=ReceiveError): - self.session.catchup() + def _check_error(self, exc=ReceiveError): + self.session._check_error(exc) - 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) + def _ewait(self, predicate, timeout=None, exc=ReceiveError): + return self.session._ewait(predicate, timeout, exc) @synchronized def pending(self): + """ + Returns the number of messages available to be fetched by the + application. + + @rtype: int + @return: the number of available messages + """ return self.received - self.returned def _capacity(self): @@ -700,23 +682,23 @@ class Receiver(Lockable): """ if self._capacity() == 0: self.granted = self.returned + 1 - self.wakeup() - self.ewait(lambda: self.impending == self.granted) + self._wakeup() + self._ewait(lambda: self.impending >= self.granted) msg = self.session._get(self._pred, timeout=timeout) if msg is None: self.drain = True self.granted = self.received - self.wakeup() - self.ewait(lambda: self.impending == self.received) + self._wakeup() + self._ewait(lambda: self.impending == self.received) self.drain = False self._grant() - self.wakeup() + 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() + self._wakeup() return msg def _grant(self): @@ -736,7 +718,7 @@ class Receiver(Lockable): """ self.started = True self._grant() - self.wakeup() + self._wakeup() @synchronized def stop(self): @@ -745,8 +727,8 @@ class Receiver(Lockable): """ self.started = False self._grant() - self.wakeup() - self.ewait(lambda: self.impending == self.received) + self._wakeup() + self._ewait(lambda: self.impending == self.received) @synchronized def close(self): @@ -754,9 +736,9 @@ class Receiver(Lockable): Close the receiver. """ self.closing = True - self.wakeup() + self._wakeup() try: - self.ewait(lambda: self.closed) + self._ewait(lambda: self.closed) finally: self.session.receivers.remove(self) @@ -843,391 +825,7 @@ 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"] +__all__ = ["Connection", "Session", "Sender", "Receiver", "Pattern", "Message", + "ConnectionError", "ConnectError", "SessionError", "Disconnected", + "SendError", "InsufficientCapacity", "ReceiveError", "Empty", + "timestamp", "uuid4", "UNLIMITED", "AMQP_PORT", "AMQPS_PORT"] diff --git a/qpid/python/qpid/ops.py b/qpid/python/qpid/ops.py index 447f9953df..11e7d11fe9 100644 --- a/qpid/python/qpid/ops.py +++ b/qpid/python/qpid/ops.py @@ -74,10 +74,7 @@ class Compound(object): def dispatch(self, target, *args): handler = "do_%s" % self.NAME - if hasattr(target, handler): - getattr(target, handler)(self, *args) - else: - print "UNHANDLED:", target, args + getattr(target, handler)(self, *args) def __repr__(self, extras=()): return "%s(%s)" % (self.__class__.__name__, diff --git a/qpid/python/qpid/tests/messaging.py b/qpid/python/qpid/tests/messaging.py index 6062895519..7623c1f93b 100644 --- a/qpid/python/qpid/tests/messaging.py +++ b/qpid/python/qpid/tests/messaging.py @@ -23,7 +23,8 @@ import time from qpid.tests import Test from qpid.harness import Skipped -from qpid.messaging import Connection, ConnectError, Disconnected, Empty, Message, UNLIMITED, uuid4 +from qpid.messaging import Connection, ConnectError, Disconnected, Empty, \ + InsufficientCapacity, Message, UNLIMITED, uuid4 from Queue import Queue, Empty as QueueEmpty class Base(Test): @@ -71,13 +72,15 @@ class Base(Test): ssn.acknowledge() assert msg.content == content, "expected %r, got %r" % (content, msg.content) - def drain(self, rcv, limit=None): + def drain(self, rcv, limit=None, timeout=0, expected=None): contents = [] try: while limit is None or len(contents) < limit: - contents.append(rcv.fetch(0).content) + contents.append(rcv.fetch(timeout=timeout).content) except Empty: pass + if expected is not None: + assert expected == contents, "expected %s, got %s" % (expected, contents) return contents def assertEmpty(self, rcv): @@ -224,27 +227,27 @@ class SessionTests(Base): # XXX, we need a convenient way to assert that required queues are # empty on setup, and possibly also to drain queues on teardown - def testAcknowledge(self): + def ackTest(self, acker, ack_capacity=None): # send a bunch of messages snd = self.ssn.sender("test-ack-queue") - tid = "a" - contents = ["testAcknowledge[%s, %s]" % (i, tid) for i in range(10)] + contents = [self.content("ackTest", i) for i in range(15)] for c in contents: snd.send(c) # drain the queue, verify the messages are there and then close # without acking rcv = self.ssn.receiver(snd.target) - assert contents == self.drain(rcv) + self.drain(rcv, expected=contents) self.ssn.close() # drain the queue again, verify that they are all the messages # were requeued, and ack this time before closing self.ssn = self.conn.session() + if ack_capacity is not None: + self.ssn.ack_capacity = ack_capacity rcv = self.ssn.receiver("test-ack-queue") - drained = self.drain(rcv) - assert contents == drained, "expected %s, got %s" % (contents, drained) - self.ssn.acknowledge() + self.drain(rcv, expected=contents) + acker(self.ssn) self.ssn.close() # drain the queue a final time and verify that the messages were @@ -253,6 +256,33 @@ class SessionTests(Base): rcv = self.ssn.receiver("test-ack-queue") self.assertEmpty(rcv) + def testAcknowledge(self): + self.ackTest(lambda ssn: ssn.acknowledge()) + + def testAcknowledgeAsync(self): + self.ackTest(lambda ssn: ssn.acknowledge(sync=False)) + + def testAcknowledgeAsyncAckCap0(self): + try: + try: + self.ackTest(lambda ssn: ssn.acknowledge(sync=False), 0) + assert False, "acknowledge shouldn't succeed with ack_capacity of zero" + except InsufficientCapacity: + pass + finally: + self.ssn.ack_capacity = UNLIMITED + self.drain(self.ssn.receiver("test-ack-queue")) + self.ssn.acknowledge() + + def testAcknowledgeAsyncAckCap1(self): + self.ackTest(lambda ssn: ssn.acknowledge(sync=False), 1) + + def testAcknowledgeAsyncAckCap5(self): + self.ackTest(lambda ssn: ssn.acknowledge(sync=False), 5) + + def testAcknowledgeAsyncAckCapUNLIMITED(self): + self.ackTest(lambda ssn: ssn.acknowledge(sync=False), UNLIMITED) + def send(self, ssn, queue, base, count=1): snd = ssn.sender(queue) contents = [] @@ -543,6 +573,48 @@ class SenderTests(Base): def testSendMap(self): self.checkContent({"testSendMap": self.test_id, "pie": "blueberry", "pi": 3.14}) + def asyncTest(self, capacity): + self.snd.capacity = capacity + msgs = [self.content("asyncTest", i) for i in range(15)] + for m in msgs: + self.snd.send(m, sync=False) + drained = self.drain(self.rcv, timeout=self.delay()) + assert msgs == drained, "expected %s, got %s" % (msgs, drained) + self.ssn.acknowledge() + + def testSendAsyncCapacity0(self): + try: + self.asyncTest(0) + assert False, "send shouldn't succeed with zero capacity" + except InsufficientCapacity: + # this is expected + pass + + def testSendAsyncCapacity1(self): + self.asyncTest(1) + + def testSendAsyncCapacity5(self): + self.asyncTest(5) + + def testSendAsyncCapacityUNLIMITED(self): + self.asyncTest(UNLIMITED) + + def testCapacityTimeout(self): + self.snd.capacity = 1 + msgs = [] + caught = False + while len(msgs) < 100: + m = self.content("testCapacity", len(msgs)) + try: + self.snd.send(m, sync=False, timeout=0) + msgs.append(m) + except InsufficientCapacity: + caught = True + break + self.drain(self.rcv, expected=msgs) + self.ssn.acknowledge() + assert caught, "did not exceed capacity" + class MessageTests(Base): def testCreateString(self): diff --git a/qpid/python/qpid/util.py b/qpid/python/qpid/util.py index c46716b88f..3409d777f9 100644 --- a/qpid/python/qpid/util.py +++ b/qpid/python/qpid/util.py @@ -134,3 +134,9 @@ class URL: if self.port: s += ":%s" % self.port return s + +def default(value, default): + if value is None: + return default + else: + return value diff --git a/qpid/python/tests/datatypes.py b/qpid/python/tests/datatypes.py index b00e5e78f8..00e649d6cf 100644 --- a/qpid/python/tests/datatypes.py +++ b/qpid/python/tests/datatypes.py @@ -148,6 +148,34 @@ class RangedSetTest(TestCase): assert range.lower == 0 assert range.upper == 8 + def testEmpty(self): + s = RangedSet() + assert s.empty() + s.add(0, -1) + assert s.empty() + s.add(0, 0) + assert not s.empty() + + def testMinMax(self): + s = RangedSet() + assert s.max() is None + assert s.min() is None + s.add(0, 10) + assert s.max() == 10 + assert s.min() == 0 + s.add(0, 5) + assert s.max() == 10 + assert s.min() == 0 + s.add(0, 11) + assert s.max() == 11 + assert s.min() == 0 + s.add(15, 20) + assert s.max() == 20 + assert s.min() == 0 + s.add(-10, -5) + assert s.max() == 20 + assert s.min() == -10 + class RangeTest(TestCase): def testIntersect1(self): diff --git a/qpid/python/tests_0-10/alternate_exchange.py b/qpid/python/tests_0-10/alternate_exchange.py index 3b75145907..4d8617eb8e 100644 --- a/qpid/python/tests_0-10/alternate_exchange.py +++ b/qpid/python/tests_0-10/alternate_exchange.py @@ -141,7 +141,61 @@ class AlternateExchangeTests(TestBase010): session.exchange_delete(exchange="e") session.exchange_delete(exchange="alternate") self.assertEquals(530, e.args[0].error_code) - + + + def test_modify_existing_exchange_alternate(self): + """ + Ensure that attempting to modify an exhange to change + the alternate throws an exception + """ + session = self.session + session.exchange_declare(exchange="alt1", type="direct") + session.exchange_declare(exchange="alt2", type="direct") + session.exchange_declare(exchange="onealternate", type="fanout", alternate_exchange="alt1") + try: + # attempt to change the alternate on an already existing exchange + session.exchange_declare(exchange="onealternate", type="fanout", alternate_exchange="alt2") + self.fail("Expected changing an alternate on an existing exchange to fail") + except SessionException, e: + self.assertEquals(530, e.args[0].error_code) + session = self.conn.session("alternate", 2) + session.exchange_delete(exchange="onealternate") + session.exchange_delete(exchange="alt2") + session.exchange_delete(exchange="alt1") + + + def test_add_alternate_to_exchange(self): + """ + Ensure that attempting to modify an exhange by adding + an alternate throws an exception + """ + session = self.session + session.exchange_declare(exchange="alt1", type="direct") + session.exchange_declare(exchange="noalternate", type="fanout") + try: + # attempt to add an alternate on an already existing exchange + session.exchange_declare(exchange="noalternate", type="fanout", alternate_exchange="alt1") + self.fail("Expected adding an alternate on an existing exchange to fail") + except SessionException, e: + self.assertEquals(530, e.args[0].error_code) + session = self.conn.session("alternate", 2) + session.exchange_delete(exchange="noalternate") + session.exchange_delete(exchange="alt1") + + + def test_del_alternate_to_exchange(self): + """ + Ensure that attempting to modify an exhange by declaring + it again without an alternate does nothing + """ + session = self.session + session.exchange_declare(exchange="alt1", type="direct") + session.exchange_declare(exchange="onealternate", type="fanout", alternate_exchange="alt1") + # attempt to re-declare without an alternate - silently ignore + session.exchange_declare(exchange="onealternate", type="fanout" ) + session.exchange_delete(exchange="onealternate") + session.exchange_delete(exchange="alt1") + def assertEmpty(self, queue): try: diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py index 51c2a687cb..53573f309e 100644 --- a/qpid/python/tests_0-10/management.py +++ b/qpid/python/tests_0-10/management.py @@ -295,3 +295,25 @@ class ManagementTest (TestBase010): sleep(1) self.assertEqual(handler.check(), "pass") + def test_connection_close(self): + """ + Test management method for closing connection + """ + self.startQmf() + conn = self.connect() + session = conn.session("my-named-session") + + #using qmf find named session and close the corresponding connection: + qmf_ssn_object = self.qmf.getObjects(_class="session", name="my-named-session")[0] + qmf_ssn_object._connectionRef_.close() + + #check that connection is closed + try: + conn.session("another-session") + self.fail("Expected failure from closed connection") + except: None + + #make sure that the named session has been closed and the name can be re-used + conn = self.connect() + session = conn.session("my-named-session") + session.queue_declare(queue="whatever", exclusive=True, auto_delete=True) diff --git a/qpid/specs/management-schema.xml b/qpid/specs/management-schema.xml index e72ba1cdd7..c25aca67ed 100644 --- a/qpid/specs/management-schema.xml +++ b/qpid/specs/management-schema.xml @@ -169,7 +169,7 @@ <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="altExchange" type="objId" references="Exchange" 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"/> diff --git a/qpid/wcf/QpidWcf.sln b/qpid/wcf/QpidWcf.sln new file mode 100644 index 0000000000..54d8dd7b0b --- /dev/null +++ b/qpid/wcf/QpidWcf.sln @@ -0,0 +1,114 @@ +
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License
+#
+
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AmqpTypes", "src\Apache\Qpid\AmqpTypes\AmqpTypes.csproj", "{820BFC34-A40F-46BA-B86B-05334854CA17}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Interop", "src\Apache\Qpid\Interop\Interop.vcproj", "{C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}"
+ ProjectSection(ProjectDependencies) = postProject
+ {820BFC34-A40F-46BA-B86B-05334854CA17} = {820BFC34-A40F-46BA-B86B-05334854CA17}
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunctionalTests", "test\Apache\Qpid\Test\Channel\Functional\FunctionalTests.csproj", "{E2D8C779-E417-40BA-BEE1-EE034268482F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Channel", "src\Apache\Qpid\Channel\Channel.csproj", "{8AABAB30-7D1E-4539-B7D1-05450262BAD2}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|Mixed Platforms = Debug|Mixed Platforms
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|Mixed Platforms = Release|Mixed Platforms
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {820BFC34-A40F-46BA-B86B-05334854CA17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {820BFC34-A40F-46BA-B86B-05334854CA17}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {820BFC34-A40F-46BA-B86B-05334854CA17}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {820BFC34-A40F-46BA-B86B-05334854CA17}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {820BFC34-A40F-46BA-B86B-05334854CA17}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {820BFC34-A40F-46BA-B86B-05334854CA17}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {820BFC34-A40F-46BA-B86B-05334854CA17}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {820BFC34-A40F-46BA-B86B-05334854CA17}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {820BFC34-A40F-46BA-B86B-05334854CA17}.Release|Any CPU.Build.0 = Release|Any CPU
+ {820BFC34-A40F-46BA-B86B-05334854CA17}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {820BFC34-A40F-46BA-B86B-05334854CA17}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {820BFC34-A40F-46BA-B86B-05334854CA17}.Release|Win32.ActiveCfg = Release|Any CPU
+ {820BFC34-A40F-46BA-B86B-05334854CA17}.Release|x64.ActiveCfg = Release|Any CPU
+ {820BFC34-A40F-46BA-B86B-05334854CA17}.Release|x86.ActiveCfg = Release|Any CPU
+ {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|Any CPU.ActiveCfg = Debug|Win32
+ {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|Any CPU.Build.0 = Debug|Win32
+ {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+ {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|Mixed Platforms.Build.0 = Debug|Win32
+ {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|Win32.ActiveCfg = Debug|Win32
+ {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|Win32.Build.0 = Debug|Win32
+ {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|x64.ActiveCfg = Debug|Win32
+ {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Debug|x86.ActiveCfg = Debug|Win32
+ {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Release|Any CPU.ActiveCfg = Release|Win32
+ {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+ {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Release|Mixed Platforms.Build.0 = Release|Win32
+ {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Release|Win32.ActiveCfg = Release|Win32
+ {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Release|Win32.Build.0 = Release|Win32
+ {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Release|x64.ActiveCfg = Release|Win32
+ {C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}.Release|x86.ActiveCfg = Release|Win32
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|x64.Build.0 = Debug|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Debug|x86.Build.0 = Debug|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|Win32.ActiveCfg = Release|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|x64.ActiveCfg = Release|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|x64.Build.0 = Release|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|x86.ActiveCfg = Release|Any CPU
+ {E2D8C779-E417-40BA-BEE1-EE034268482F}.Release|x86.Build.0 = Release|Any CPU
+ {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Release|Win32.ActiveCfg = Release|Any CPU
+ {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Release|x64.ActiveCfg = Release|Any CPU
+ {8AABAB30-7D1E-4539-B7D1-05450262BAD2}.Release|x86.ActiveCfg = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/qpid/wcf/ReadMe.txt b/qpid/wcf/ReadMe.txt new file mode 100644 index 0000000000..0ef3e06ce5 --- /dev/null +++ b/qpid/wcf/ReadMe.txt @@ -0,0 +1,162 @@ +1. WCF supported features +========================= + +1. WCF service model programming using one way contracts +2. WCF channel model programming using IInputChannel and IOutputChannel based factories +3. Programmatic access to AMQP message properties on WCF messages +4. AMQP version 0-10 (as provided by the Qpid C++ native client library) +5. Shared connections for multiple channels based on binding parameters +6. WCF to WCF applications (using SOAP message encoders) +7. WCF to non-WCF applications (using raw content encoders) +8. Rudimentary AMQP type support for headers (Int and String) +9. Channel functional tests using NUnit +10. Programming samples + + +2. Planned features (not yet available) +======================================= + +1. Full AMQP type support, including maps and arrays +2. System.Transactions integration (local and distributed with dynamic escalation) +3. Prefetch window for inbound messages +4. Shared sessions +5. Connection failover with AMQP broker clusters +6. Temporary queues +7. Broker management +8. System logging and tracing +9. CMake build system support +10. Transport and message based security + + +3. Prerequisites +================ + +1. Qpid C++ client and common libraries for Windows including BOOST +Ensure the location of the Boost library (e.g. %BOOST_ROOT%\lib) is +included in your PATH environment variable. + +2. .NET Framework 3.5 SP1 +Install the .NET Framework from http://www.microsoft.com/net/ + +3. Windows SDK +Install the Windows SDK for the version of Windows that you are using +from http://msdn.microsoft.com/en-us/windows/bb980924.aspx + +4. NUnit +Install NUnit from http://www.nunit.org + +NOTE: In the following instructions %QPID_ROOT% refers to the root of +qpid source code location e.g. C:\trunk\qpid + +5. Build Qpid cpp +Run CMake and choose "%QPID_ROOT%\cpp\build" as the location for "Where to +build the binaries". Build at least the "qpidd", "qpidclient" and +"qpidcommon" projects. + + +4. Building the solution file +============================= + +Option 1: Using MSBuild + +1. %systemroot%\Microsoft.NET\Framework\v3.5\MSBuild.exe %QPID_ROOT%\wcf\QpidWcf.sln +2. %systemroot%\Microsoft.NET\Framework\v3.5\MSBuild.exe %QPID_ROOT%\wcf\tools\QCreate\QCreate.sln + + +Option 2: Using Visual Studio 2008 (the Professional Edition, Team +System Development Edition, or Team System Team Suite SKU) + +1. Open the solution file QpidWcf.sln in Visual Studio. +2. Make sure that the reference to 'nunit.framework.dll' by the 'FunctionalTests' + project is appropriately resolved. +3. Select the Debug configuration. +3. Right-click the solution file in the Solution Explorer and select 'Build Solution'. +4. Follow the above steps to build %QPID_ROOT%\wcf\tools\QCreate.sln as well. + + +5. Executing tests +================== + +1. Make sure that the batch file + %QPID_ROOT%\wcf\test\Apache\Qpid\Test\Channel\Functional\RunTests.bat has the correct + values for the nunit_exe, qpid_dll_location and configuration_name variables as per + your installation. +2. Start the qpid broker from the qpid build folder e.g. %QPID_ROOT%\cpp\build\src\Debug. +3. Execute RunTests.bat from its location e.g. %QPID_ROOT%\wcf\test\Apache\Qpid\Test\Channel\Functional. + + +6. Building and executing samples +================================= + +WCFToWCFDirect + +1. Copy the dlls Apache.Qpid.Channel.dll and Apache.Qpid.Interop.dll that you built + in step 2 to the %QPID_ROOT%\wcf\samples\Channel\WCFToWCFDirect folder. + +2. Build the solution WCFToWCFDirect.sln. + +3. Copy qpidclient.dll and qpidcommon.dll from the Qpid build folder + e.g. %QPID_ROOT%\cpp\build\src\Debug to the same location as the exe files + e.g. bin\Debug of each of the projects. These dlls are needed at runtime. + +4. Copy qpidclient.dll and qpidcommon.dll to %QPID_ROOT%\wcf\tools\QCreate\Debug folder. + +5. Start the qpid broker from the qpid build folder e.g. %QPID_ROOT%\cpp\build\src\Debug. + +6. Create queue required using the QCreate tool located at + %QPID_ROOT%\wcf\tools\QCreate\Debug. The syntax is QCreate %QPID_ROOT%. For + this sample you should do + + QCreate amq.direct routing_key message_queue + +7. Start Service.exe from + %QPID_ROOT%\wcf\samples\Channel\WCFToWCFDirect\Service\bin\Debug. + +8. Start Client.exe from + %QPID_ROOT%\wcf\samples\Channel\WCFToWCFDirect\Client\bin\Debug. + + +WCFToWCFPubSub + +1. Copy the dlls Apache.Qpid.Channel.dll and Apache.Qpid.Interop.dll that you built + in step 2 to the %QPID_ROOT%\wcf\samples\Channel\WCFToWCFPubSub folder. + +2. Build the solution WCFToWCFPubSub.sln. + +3. Copy qpidclient.dll and qpidcommon.dll from the Qpid build folder + e.g. %QPID_ROOT%\cpp\build\src\Debug to the same location as the exe files + e.g. bin\Debug of each of the projects. These dlls are needed at runtime. + +4. Copy qpidclient.dll and qpidcommon.dll to %QPID_ROOT%\wcf\tools\QCreate\Debug folder. + +5. Start the qpid broker from the qpid build folder e.g. %QPID_ROOT%\cpp\build\src\Debug. + +6. Create queues required using the QCreate tool located at + \wcf\tools\QCreate\Debug. The syntax is QCreate %QPID_ROOT%. For this sample you + should do + + QCreate amq.topic usa.# usa + QCreate amq.topic #.news news + +7. Start Topic_Consumer.exe from + %QPID_ROOT%\wcf\samples\Channel\WCFToWCFPubSub\Topic_Consumer\bin\Debug. + +8. Start Another_Topic_Consumer.exe from + %QPID_ROOT%\wcf\samples\Channel\WCFToWCFPubSub\Another_Topic_Consumer\bin\Debug. + +9. Start Topic_Producer.exe from + %QPID_ROOT%\wcf\samples\Channel\WCFToWCFPubSub\Topic_Producer\bin\Debug. + + +7. Known Issues +=============== + +1. The Release configuration of the build (specified using the + /p:Configuration=Release switch with MSBuild) fails. + +2. The AmqpChannelListener is limited to single threaded use and the async methods + throw NotImplementedException. + +3. The AmqpChannelListener can hang on close for 60 seconds. + + diff --git a/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Client.cs b/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Client.cs new file mode 100644 index 0000000000..93ac97bc66 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Client.cs @@ -0,0 +1,68 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +namespace Apache.Qpid.Samples.Channel.WCFToWCFDirect +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using Apache.Qpid.Channel; + + class Client + { + static void Main(string[] args) + { + try + { + // Create binding for the service endpoint. + CustomBinding amqpBinding = new CustomBinding(); + amqpBinding.Elements.Add(new BinaryMessageEncodingBindingElement()); + amqpBinding.Elements.Add(new AmqpTransportBindingElement()); + + // Create endpoint address. + Uri amqpClientUri = new Uri("amqp:amq.direct?routingkey=routing_key"); + EndpointAddress endpointAddress = new EndpointAddress(amqpClientUri); + + // Create a client with given client endpoint configuration. + ChannelFactory<IHelloService> channelFactory = new ChannelFactory<IHelloService>(amqpBinding, endpointAddress); + IHelloService clientProxy = channelFactory.CreateChannel(); + + Console.WriteLine(); + + string name = "name"; + for (int i = 0; i < 5; i++) + { + Console.WriteLine("Sending message: " + name + (i + 1)); + clientProxy.SayHello(name + (i + 1)); + } + + Console.WriteLine(); + Console.WriteLine("Press <ENTER> to terminate client."); + Console.ReadLine(); + + channelFactory.Close(); + } + catch (Exception e) + { + Console.WriteLine("Exception: {0}", e); + } + } + } +} diff --git a/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Client.csproj b/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Client.csproj new file mode 100644 index 0000000000..7e1d2d9f5d --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Client.csproj @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{0CCD5711-2072-47B8-B902-07EC12C04159}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Client</RootNamespace>
+ <AssemblyName>Client</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Apache.Qpid.Channel, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\Apache.Qpid.Channel.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.ServiceModel">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Client.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Service\Service.csproj">
+ <Project>{D0A46136-B4E3-4C50-AB6D-FB2BC6683D6E}</Project>
+ <Name>Service</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
\ No newline at end of file diff --git a/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Properties/AssemblyInfo.cs b/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..414a3b5858 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFDirect/Client/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Client")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("MSIT")] +[assembly: AssemblyProduct("Client")] +[assembly: AssemblyCopyright("Copyright © MSIT 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c3743ce0-3054-4188-8cd7-3a22734ee313")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Properties/AssemblyInfo.cs b/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..2b75210ce3 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Service")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("MSIT")] +[assembly: AssemblyProduct("Service")] +[assembly: AssemblyCopyright("Copyright © MSIT 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5447546e-8547-4b0c-981a-1757ab8d9ec5")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Service.cs b/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Service.cs new file mode 100644 index 0000000000..0342097ed9 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Service.cs @@ -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. +*/ + + +namespace Apache.Qpid.Samples.Channel.WCFToWCFDirect +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Description; + using Apache.Qpid.Channel; + + // Define a service contract. + [ServiceContract] + public interface IHelloService + { + [OperationContract(IsOneWay = true, Action="*")] + void SayHello(string name); + } + + // Service class which implements the service contract. + [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)] + public class HelloService : IHelloService + { + [OperationBehavior] + public void SayHello(string name) + { + Console.WriteLine("Hello " + name); + } + } + + class Service + { + static void Main(string[] args) + { + // Create binding for the service endpoint. + AmqpBinding amqpBinding = new AmqpBinding(); + + // Create ServiceHost. + ServiceHost serviceHost = new ServiceHost(typeof(HelloService), new Uri[] { new Uri("http://localhost:8080/HelloService") }); + + // Add behavior for our MEX endpoint. + ServiceMetadataBehavior mexBehavior = new ServiceMetadataBehavior(); + mexBehavior.HttpGetEnabled = true; + serviceHost.Description.Behaviors.Add(mexBehavior); + + // Add MEX endpoint. + serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), new BasicHttpBinding(), "MEX"); + + // Add AMQP endpoint. + Uri amqpUri = new Uri("amqp:message_queue"); + serviceHost.AddServiceEndpoint(typeof(IHelloService), amqpBinding, amqpUri.ToString()); + + serviceHost.Open(); + + Console.WriteLine(); + Console.WriteLine("The service is ready."); + Console.WriteLine("Press <ENTER> to terminate service."); + Console.WriteLine(); + Console.ReadLine(); + + if (serviceHost.State != CommunicationState.Faulted) + { + serviceHost.Close(); + } + } + } +} diff --git a/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Service.csproj b/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Service.csproj new file mode 100644 index 0000000000..3252380c98 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFDirect/Service/Service.csproj @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{D0A46136-B4E3-4C50-AB6D-FB2BC6683D6E}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Service</RootNamespace>
+ <AssemblyName>Service</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Apache.Qpid.Channel, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\Apache.Qpid.Channel.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Messaging" />
+ <Reference Include="System.ServiceModel">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Service.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
\ No newline at end of file diff --git a/qpid/wcf/samples/Channel/WCFToWCFDirect/WCFToWCFDirect.sln b/qpid/wcf/samples/Channel/WCFToWCFDirect/WCFToWCFDirect.sln new file mode 100644 index 0000000000..6f30a5e053 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFDirect/WCFToWCFDirect.sln @@ -0,0 +1,46 @@ +
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License
+#
+
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{0CCD5711-2072-47B8-B902-07EC12C04159}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "Service\Service.csproj", "{D0A46136-B4E3-4C50-AB6D-FB2BC6683D6E}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {0CCD5711-2072-47B8-B902-07EC12C04159}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0CCD5711-2072-47B8-B902-07EC12C04159}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0CCD5711-2072-47B8-B902-07EC12C04159}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0CCD5711-2072-47B8-B902-07EC12C04159}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D0A46136-B4E3-4C50-AB6D-FB2BC6683D6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D0A46136-B4E3-4C50-AB6D-FB2BC6683D6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D0A46136-B4E3-4C50-AB6D-FB2BC6683D6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D0A46136-B4E3-4C50-AB6D-FB2BC6683D6E}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Another_Topic_Consumer.cs b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Another_Topic_Consumer.cs new file mode 100644 index 0000000000..c1e3ebbc88 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Another_Topic_Consumer.cs @@ -0,0 +1,67 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +namespace Apache.Qpid.Samples.Channel.WCFToWCFPubSub +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Description; + using Apache.Qpid.Channel; + + class Another_Topic_Consumer + { + static void Main(string[] args) + { + // Create binding for the service endpoint. + CustomBinding amqpBinding = new CustomBinding(); + amqpBinding.Elements.Add(new BinaryMessageEncodingBindingElement()); + amqpBinding.Elements.Add(new AmqpTransportBindingElement()); + + // Create ServiceHost. + ServiceHost serviceHost = new ServiceHost(typeof(HelloService), new Uri[] { new Uri("http://localhost:8080/HelloService2") }); + + // Add behavior for our MEX endpoint. + ServiceMetadataBehavior mexBehavior = new ServiceMetadataBehavior(); + mexBehavior.HttpGetEnabled = true; + serviceHost.Description.Behaviors.Add(mexBehavior); + + // Add MEX endpoint. + serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), new BasicHttpBinding(), "MEX"); + + // Add AMQP endpoint. + Uri amqpUri = new Uri("amqp:news"); + serviceHost.AddServiceEndpoint(typeof(IHelloService), amqpBinding, amqpUri.ToString()); + + serviceHost.Open(); + + Console.WriteLine(); + Console.WriteLine("The consumer is now listening on the queue \"news\"."); + Console.WriteLine("Press <ENTER> to terminate service."); + Console.WriteLine(); + Console.ReadLine(); + + if (serviceHost.State != CommunicationState.Faulted) + { + serviceHost.Close(); + } + } + } +} diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Another_Topic_Consumer.csproj b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Another_Topic_Consumer.csproj new file mode 100644 index 0000000000..47769e086d --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Another_Topic_Consumer.csproj @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{6AC32E9D-EFB2-4DEF-81D7-F70A0D7A606F}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Another_Topic_Consumer</RootNamespace>
+ <AssemblyName>Another_Topic_Consumer</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Apache.Qpid.Channel, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\Apache.Qpid.Channel.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.ServiceModel">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Another_Topic_Consumer.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Topic_Consumer\Topic_Consumer.csproj">
+ <Project>{248A3A0B-FDC4-4E70-8428-BE0AF5AB021B}</Project>
+ <Name>Topic_Consumer</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
\ No newline at end of file diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Properties/AssemblyInfo.cs b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..8c22cb6d1f --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Another_Topic_Consumer/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Another_Topic_Consumer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("MSIT")] +[assembly: AssemblyProduct("Another_Topic_Consumer")] +[assembly: AssemblyCopyright("Copyright © MSIT 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ba584c88-26a8-4910-a9a1-b4632b9adf01")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Properties/AssemblyInfo.cs b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..19fea85618 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Topic_Consumer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("MSIT")] +[assembly: AssemblyProduct("Topic_Consumer")] +[assembly: AssemblyCopyright("Copyright © MSIT 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("3facd6d1-f604-4ac9-ace3-7b7acff471eb")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Topic_Consumer.cs b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Topic_Consumer.cs new file mode 100644 index 0000000000..c4dd1e2256 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Topic_Consumer.cs @@ -0,0 +1,85 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +namespace Apache.Qpid.Samples.Channel.WCFToWCFPubSub +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Description; + using Apache.Qpid.Channel; + + // Define a service contract. + [ServiceContract] + public interface IHelloService + { + [OperationContract(IsOneWay = true)] + void SayHello(string name); + } + + // Service class which implements the service contract. + public class HelloService : IHelloService + { + [OperationBehavior] + public void SayHello(string name) + { + Console.WriteLine("Hello " + name); + } + } + + class Consumer + { + static void Main(string[] args) + { + // Create binding for the service endpoint. + CustomBinding amqpBinding = new CustomBinding(); + amqpBinding.Elements.Add(new BinaryMessageEncodingBindingElement()); + amqpBinding.Elements.Add(new AmqpTransportBindingElement()); + + // Create ServiceHost. + ServiceHost serviceHost = new ServiceHost(typeof(HelloService), new Uri[] { new Uri("http://localhost:8080/HelloService1") }); + + // Add behavior for our MEX endpoint. + ServiceMetadataBehavior mexBehavior = new ServiceMetadataBehavior(); + mexBehavior.HttpGetEnabled = true; + serviceHost.Description.Behaviors.Add(mexBehavior); + + // Add MEX endpoint. + serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), new BasicHttpBinding(), "MEX"); + + // Add AMQP endpoint. + Uri amqpUri = new Uri("amqp:usa"); + serviceHost.AddServiceEndpoint(typeof(IHelloService), amqpBinding, amqpUri.ToString()); + + serviceHost.Open(); + + Console.WriteLine(); + Console.WriteLine("The consumer is now listening on the queue \"usa\"."); + Console.WriteLine("Press <ENTER> to terminate service."); + Console.WriteLine(); + Console.ReadLine(); + + if (serviceHost.State != CommunicationState.Faulted) + { + serviceHost.Close(); + } + } + } +} diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Topic_Consumer.csproj b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Topic_Consumer.csproj new file mode 100644 index 0000000000..b2151c0631 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Consumer/Topic_Consumer.csproj @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{248A3A0B-FDC4-4E70-8428-BE0AF5AB021B}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Topic_Consumer</RootNamespace>
+ <AssemblyName>Topic_Consumer</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Apache.Qpid.Channel, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\Apache.Qpid.Channel.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.ServiceModel">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Topic_Consumer.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
\ No newline at end of file diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Properties/AssemblyInfo.cs b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..87310bf92a --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Topic_Producer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("MSIT")] +[assembly: AssemblyProduct("Topic_Producer")] +[assembly: AssemblyCopyright("Copyright © MSIT 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a70e852d-a510-4e00-af72-68bb8547696f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Topic_Producer.cs b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Topic_Producer.cs new file mode 100644 index 0000000000..e3850eb4c0 --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Topic_Producer.cs @@ -0,0 +1,68 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +namespace Apache.Qpid.Samples.Channel.WCFToWCFPubSub +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using Apache.Qpid.Channel; + + class Topic_Producer + { + static void Main(string[] args) + { + try + { + // Create binding for the service endpoint. + CustomBinding amqpBinding = new CustomBinding(); + amqpBinding.Elements.Add(new BinaryMessageEncodingBindingElement()); + amqpBinding.Elements.Add(new AmqpTransportBindingElement()); + + // Create endpoint address. + Uri amqpClientUri = new Uri("amqp:amq.topic?routingkey=usa.news"); + EndpointAddress endpointAddress = new EndpointAddress(amqpClientUri); + + // Create a client with given client endpoint configuration. + ChannelFactory<IHelloService> channelFactory = new ChannelFactory<IHelloService>(amqpBinding, endpointAddress); + IHelloService clientProxy = channelFactory.CreateChannel(); + + Console.WriteLine(); + + string name = "name"; + for (int i = 0; i < 5; i++) + { + Console.WriteLine("Sending message: " + name + (i + 1)); + clientProxy.SayHello(name + (i+1)); + } + + Console.WriteLine(); + Console.WriteLine("Press <ENTER> to terminate client."); + Console.ReadLine(); + + channelFactory.Close(); + } + catch (Exception e) + { + Console.WriteLine("Exception: {0}", e); + } + } + } +} diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Topic_Producer.csproj b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Topic_Producer.csproj new file mode 100644 index 0000000000..b4318ead3f --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/Topic_Producer/Topic_Producer.csproj @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{67B413EF-3B9C-4988-87DE-0386C209D368}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Topic_Producer</RootNamespace>
+ <AssemblyName>Topic_Producer</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Apache.Qpid.Channel, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\Apache.Qpid.Channel.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.ServiceModel">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Topic_Producer.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Topic_Consumer\Topic_Consumer.csproj">
+ <Project>{248A3A0B-FDC4-4E70-8428-BE0AF5AB021B}</Project>
+ <Name>Topic_Consumer</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
\ No newline at end of file diff --git a/qpid/wcf/samples/Channel/WCFToWCFPubSub/WCFToWCFPubSub.sln b/qpid/wcf/samples/Channel/WCFToWCFPubSub/WCFToWCFPubSub.sln new file mode 100644 index 0000000000..d8a56ea8db --- /dev/null +++ b/qpid/wcf/samples/Channel/WCFToWCFPubSub/WCFToWCFPubSub.sln @@ -0,0 +1,52 @@ +
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License
+#
+
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Topic_Consumer", "Topic_Consumer\Topic_Consumer.csproj", "{248A3A0B-FDC4-4E70-8428-BE0AF5AB021B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Topic_Producer", "Topic_Producer\Topic_Producer.csproj", "{67B413EF-3B9C-4988-87DE-0386C209D368}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Another_Topic_Consumer", "Another_Topic_Consumer\Another_Topic_Consumer.csproj", "{6AC32E9D-EFB2-4DEF-81D7-F70A0D7A606F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {248A3A0B-FDC4-4E70-8428-BE0AF5AB021B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {248A3A0B-FDC4-4E70-8428-BE0AF5AB021B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {248A3A0B-FDC4-4E70-8428-BE0AF5AB021B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {248A3A0B-FDC4-4E70-8428-BE0AF5AB021B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {67B413EF-3B9C-4988-87DE-0386C209D368}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {67B413EF-3B9C-4988-87DE-0386C209D368}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {67B413EF-3B9C-4988-87DE-0386C209D368}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {67B413EF-3B9C-4988-87DE-0386C209D368}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6AC32E9D-EFB2-4DEF-81D7-F70A0D7A606F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6AC32E9D-EFB2-4DEF-81D7-F70A0D7A606F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6AC32E9D-EFB2-4DEF-81D7-F70A0D7A606F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6AC32E9D-EFB2-4DEF-81D7-F70A0D7A606F}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpBoolean.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpBoolean.cs new file mode 100644 index 0000000000..980ae78361 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpBoolean.cs @@ -0,0 +1,57 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.AmqpTypes +{ + using System; + using System.IO; + using System.Collections.Generic; + using System.Text; + + public class AmqpBoolean : AmqpType + { + bool value; + + public AmqpBoolean(bool i) + { + this.value = i; + } + + public override void Encode(byte[] bufer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override int EncodedSize + { + get { throw new NotImplementedException(); } + } + + public override AmqpType Clone() + { + return new AmqpBoolean(this.value); + } + + public bool Value + { + get { return this.value; } + set { this.value = value; } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpInt.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpInt.cs new file mode 100644 index 0000000000..c114e98a71 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpInt.cs @@ -0,0 +1,57 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.AmqpTypes +{ + using System; + using System.IO; + using System.Collections.Generic; + using System.Text; + + public class AmqpInt : AmqpType + { + int value; + + public AmqpInt(int i) + { + this.value = i; + } + + public override void Encode(byte[] bufer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override int EncodedSize + { + get { throw new NotImplementedException(); } + } + + public override AmqpType Clone() + { + return new AmqpInt(this.value); + } + + public int Value + { + get { return this.value; } + set { this.value = value; } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpProperties.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpProperties.cs new file mode 100644 index 0000000000..0f649dcd36 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpProperties.cs @@ -0,0 +1,292 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.AmqpTypes +{ + using System; + using System.IO; + using System.Collections.Generic; + using System.Text; + + public class AmqpProperties + { + // AMQP 0-10 delivery properties + private bool durable; + private Nullable<TimeSpan> timeToLive; + private string routingKey; + + // AMQP 0-10 message properties + private string replyToExchange; + private string replyToRoutingKey; + private byte[] userId; + private byte[] correlationId; + private string contentType; + + // for application and vendor properties + Dictionary<String, AmqpType> propertyMap; + + public AmqpProperties() + { + } + + // AMQP 0-10 "message.delivery-properties + internal bool HasDeliveryProperties + { + get + { + return ((this.routingKey != null) || this.durable || this.timeToLive.HasValue); + } + } + + internal bool HasMappedProperties + { + get + { + if (this.propertyMap != null) + { + if (this.propertyMap.Count > 0) + { + return true; + } + } + + return false; + } + } + + // AMQP 0-10 "message.message-properties" + internal bool HasMessageProperties + { + get + { + if ((this.replyToExchange != null) || + (this.replyToRoutingKey != null) || + (this.userId != null) || + (this.correlationId != null) || + (this.contentType != null)) + { + return true; + } + + if (this.propertyMap == null) + { + return false; + } + + return (this.propertyMap.Count != 0); + } + } + + public Dictionary<String, AmqpType> PropertyMap + { + get + { + if (this.propertyMap == null) + { + this.propertyMap = new Dictionary<string, AmqpType>(); + } + return propertyMap; + } + set { this.propertyMap = value; } + } + + internal bool Empty + { + get + { + if (this.HasDeliveryProperties || this.HasMessageProperties) + { + return true; + } + return false; + } + } + + public string ContentType + { + get { return contentType; } + // TODO: validate + set { contentType = value; } + } + + public byte[] CorrelationId + { + get { return correlationId; } + set + { + if (value != null) + { + if (value.Length > 65535) + { + throw new ArgumentException("CorrelationId too big"); + } + } + correlationId = value; + } + } + + public byte[] UserId + { + get { return userId; } + set + { + if (value != null) + { + if (value.Length > 65535) + { + throw new ArgumentException("UserId too big"); + } + } + userId = value; + } + } + + public TimeSpan? TimeToLive + { + get { return this.timeToLive; } + set { this.timeToLive = value; } + } + + public string RoutingKey + { + get { return this.routingKey; } + set { this.routingKey = value; } + } + + public string ReplyToExchange + { + get { return this.replyToExchange; } + } + + public string ReplyToRoutingKey + { + get { return this.replyToRoutingKey; } + } + + // this changes from 0-10 to 1.0 + public void SetReplyTo(string exchange, string routingKey) + { + if ((exchange == null && routingKey == null)) + { + throw new ArgumentNullException("SetReplyTo"); + } + + this.replyToExchange = exchange; + this.replyToRoutingKey = routingKey; + } + + public bool Durable + { + get { return durable; } + set { durable = value; } + } + + public void Clear() + { + this.timeToLive = null; + this.routingKey = null; + this.replyToRoutingKey = null; + this.replyToExchange = null; + this.durable = false; + this.contentType = null; + this.userId = null; + this.correlationId = null; + this.propertyMap = null; + } + + public AmqpProperties Clone() + { + // memberwise clone ok for string, byte[], and value types + AmqpProperties clonedProps = (AmqpProperties)this.MemberwiseClone(); + + // deeper copy for the dictionary + if (this.propertyMap != null) + { + if (this.propertyMap.Count > 0) + { + Dictionary<string, AmqpType> clonedDictionary = new Dictionary<string, AmqpType>(this.propertyMap.Count); + foreach (KeyValuePair<string, AmqpType> original in this.propertyMap) + { + clonedDictionary.Add(original.Key, original.Value.Clone()); + } + + clonedProps.propertyMap = clonedDictionary; + } + else + { + clonedProps.propertyMap = null; + } + } + return clonedProps; + } + + // adds/replaces from the other AmqpProperty object. + // just inserts references, i.e. provides shallow copy semantics (see Clone for deep copy) + public void MergeFrom(AmqpProperties other) + { + if (other.timeToLive.HasValue) + { + this.timeToLive = other.timeToLive; + } + + if ((other.replyToRoutingKey != null) || (other.replyToExchange != null)) + { + this.replyToExchange = other.replyToExchange; + this.replyToRoutingKey = other.replyToRoutingKey; + } + + if (other.routingKey != null) + { + this.routingKey = other.routingKey; + } + + if (other.durable) + { + this.durable = true; + } + + if (other.contentType != null) + { + this.contentType = other.contentType; + } + + if (other.correlationId != null) + { + this.correlationId = other.correlationId; + } + + if (other.userId != null) + { + this.userId = other.userId; + } + + if (other.propertyMap != null) + { + if (other.propertyMap.Count > 0) + { + Dictionary<string, AmqpType> thisMap = this.PropertyMap; + foreach (KeyValuePair<string, AmqpType> kvp in other.propertyMap) + { + thisMap[kvp.Key] = kvp.Value; + } + } + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpString.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpString.cs new file mode 100644 index 0000000000..87cebe878c --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpString.cs @@ -0,0 +1,91 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.AmqpTypes +{ + using System; + using System.IO; + using System.Collections.Generic; + using System.Text; + + // for big strings: str16 in 0-10 and str32 in 1.0 + + public class AmqpString : AmqpType + { + string value; + Encoding encoding; + + public AmqpString(string s) + { + this.value = s; + this.encoding = Encoding.UTF8; + } + + public AmqpString(string s, Encoding enc) + { + ValidateEncoding(enc); + this.value = s; + this.encoding = enc; + } + + public Encoding Encoding + { + get { return encoding; } + set + { + ValidateEncoding(value); + encoding = value; + } + } + + private void ValidateEncoding(Encoding enc) + { + if (value == null) + { + throw new ArgumentNullException("Encoding"); + } + + if ((enc != Encoding.UTF8) && (enc != Encoding.Unicode)) + { + throw new ArgumentException("Encoding not one of UTF8 or Unicode"); + } + } + + public override void Encode(byte[] bufer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override int EncodedSize + { + get { throw new NotImplementedException(); } + } + + public override AmqpType Clone() + { + return new AmqpString(this.value); + } + + public string Value + { + get { return this.value; } + set { this.value = value; } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpType.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpType.cs new file mode 100644 index 0000000000..8cd3ac9e4a --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpType.cs @@ -0,0 +1,33 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.AmqpTypes +{ + using System; + using System.IO; + using System.Collections.Generic; + using System.Text; + + public abstract class AmqpType + { + public abstract void Encode(byte[] bufer, int offset, int count); + public abstract int EncodedSize { get; } + public abstract AmqpType Clone(); + } +} diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpTypes.csproj b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpTypes.csproj new file mode 100644 index 0000000000..9c13d47296 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpTypes.csproj @@ -0,0 +1,153 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{820BFC34-A40F-46BA-B86B-05334854CA17}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Apache.Qpid.AmqpTypes</RootNamespace>
+ <AssemblyName>Apache.Qpid.AmqpTypes</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="AmqpBoolean.cs" />
+ <Compile Include="AmqpInt.cs" />
+ <Compile Include="AmqpProperties.cs" />
+ <Compile Include="AmqpString.cs" />
+ <Compile Include="AmqpType.cs" />
+ <Compile Include="AmqpUbyte.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="PropertyName.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+<Message Text="yet another debug line" />
+ </Target>
+ <Target Name="AfterBuild"
+ >
+<Message Text="a debug line before banana.netmodule" />
+ <PropertyGroup Condition="('$(TargetFrameworkVersion)' != 'v1.0') and ('$(TargetFrameworkVersion)' != 'v1.1')">
+ <NoWarn>$(NoWarn);1701;1702</NoWarn>
+ </PropertyGroup>
+
+ <Csc
+ AdditionalLibPaths="$(AdditionalLibPaths)"
+ AddModules="@(AddModules)"
+ AllowUnsafeBlocks="$(AllowUnsafeBlocks)"
+ BaseAddress="$(BaseAddress)"
+ CheckForOverflowUnderflow="$(CheckForOverflowUnderflow)"
+ CodePage="$(CodePage)"
+ DebugType="$(DebugType)"
+ DefineConstants="$(DefineConstants)"
+ DelaySign="$(DelaySign)"
+ DisabledWarnings="$(NoWarn)"
+ DocumentationFile="@(DocFileItem)"
+ EmitDebugInformation="$(DebugSymbols)"
+ ErrorReport="$(ErrorReport)"
+ FileAlignment="$(FileAlignment)"
+ GenerateFullPaths="$(GenerateFullPaths)"
+ KeyContainer="$(KeyContainerName)"
+ KeyFile="$(KeyOriginatorFile)"
+ LangVersion="$(LangVersion)"
+ MainEntryPoint="$(StartupObject)"
+ ModuleAssemblyName="banana"
+ NoConfig="true"
+ NoLogo="$(NoLogo)"
+ NoStandardLib="$(NoStdLib)"
+ NoWin32Manifest="$(NoWin32Manifest)"
+ Optimize="$(Optimize)"
+ OutputAssembly="@(IntermediateAssembly)"
+ PdbFile="$(PdbFile)"
+ Platform="$(PlatformTarget)"
+ References="@(ReferencePath)"
+ Resources="@(_CoreCompileResourceInputs);@(CompiledLicenseFile)"
+ ResponseFiles="$(CompilerResponseFile)"
+ Sources="@(Compile)"
+ TargetType="module"
+ ToolExe="$(CscToolExe)"
+ ToolPath="$(CscToolPath)"
+ TreatWarningsAsErrors="$(TreatWarningsAsErrors)"
+ UseHostCompilerIfAvailable="$(UseHostCompilerIfAvailable)"
+ Utf8Output="$(Utf8Output)"
+ WarningLevel="$(WarningLevel)"
+ WarningsAsErrors="$(WarningsAsErrors)"
+ WarningsNotAsErrors="$(WarningsNotAsErrors)"
+ Win32Icon="$(ApplicationIcon)"
+ Win32Manifest="$(Win32Manifest)"
+ Win32Resource="$(Win32Resource)"
+ />
+
+ <ItemGroup>
+ <_CoreCompileResourceInputs Remove="@(_CoreCompileResourceInputs)" />
+ </ItemGroup>
+
+<Message Text="a debug line after banana.netmodule" />
+ </Target>
+ -->
+ <PropertyGroup>
+ <PostBuildEvent>cd "$(ProjectDir)bin\$(ConfigurationName)"
+del $(AssemblyName).dll
+del $(AssemblyName).pdb
+cd "$(ProjectDir)obj\$(ConfigurationName)"
+del $(AssemblyName).dll
+del $(AssemblyName).pdb
+cd "$(ProjectDir)"
+CreateNetModule.bat</PostBuildEvent>
+ </PropertyGroup>
+</Project>
\ No newline at end of file diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpUbyte.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpUbyte.cs new file mode 100644 index 0000000000..5ec8a732cf --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/AmqpUbyte.cs @@ -0,0 +1,57 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.AmqpTypes +{ + using System; + using System.IO; + using System.Collections.Generic; + using System.Text; + + public class AmqpUbyte : AmqpType + { + byte value; + + public AmqpUbyte(byte i) + { + this.value = i; + } + + public override void Encode(byte[] bufer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override int EncodedSize + { + get { throw new NotImplementedException(); } + } + + public override AmqpType Clone() + { + return new AmqpUbyte(this.value); + } + + public byte Value + { + get { return this.value; } + set { this.value = value; } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/CreateNetModule.bat b/qpid/wcf/src/Apache/Qpid/AmqpTypes/CreateNetModule.bat new file mode 100755 index 0000000000..ddbe1407a7 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/CreateNetModule.bat @@ -0,0 +1,19 @@ + +REM Licensed to the Apache Software Foundation (ASF) under one +REM or more contributor license agreements. See the NOTICE file +REM distributed with this work for additional information +REM regarding copyright ownership. The ASF licenses this file +REM to you under the Apache License, Version 2.0 (the +REM "License"); you may not use this file except in compliance +REM with the License. You may obtain a copy of the License at +REM +REM http://www.apache.org/licenses/LICENSE-2.0 +REM +REM Unless required by applicable law or agreed to in writing, +REM software distributed under the License is distributed on an +REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +REM KIND, either express or implied. See the License for the +REM specific language governing permissions and limitations +REM under the License. + +%systemroot%\Microsoft.NET\Framework\v3.5\Csc.exe /noconfig /nowarn:1701,1702 /errorreport:prompt /warn:4 /define:DEBUG;TRACE /reference:"%programfiles%\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll" /reference:"%programfiles%\Reference Assemblies\Microsoft\Framework\v3.5\System.Data.DataSetExtensions.dll" /reference:%systemroot%\Microsoft.NET\Framework\v2.0.50727\System.Data.dll /reference:%systemroot%\Microsoft.NET\Framework\v2.0.50727\System.dll /reference:%systemroot%\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll /reference:"%programfiles%\Reference Assemblies\Microsoft\Framework\v3.5\System.Xml.Linq.dll" /debug+ /debug:full /filealign:512 /optimize- /out:obj\Debug\Apache.Qpid.AmqpTypes.netmodule /target:module *.cs
\ No newline at end of file diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/Properties/AssemblyInfo.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..0bce6f9795 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Apache.Qpid.AmqpTypes")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("79b8b5d9-047d-4f3b-8610-7fe112ce6416")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/src/Apache/Qpid/AmqpTypes/PropertyName.cs b/qpid/wcf/src/Apache/Qpid/AmqpTypes/PropertyName.cs new file mode 100644 index 0000000000..b80f8b9e9e --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/AmqpTypes/PropertyName.cs @@ -0,0 +1,35 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.AmqpTypes +{ + using System; + using System.IO; + using System.Collections.Generic; + using System.Text; + + public sealed class PropertyName + { + public const string Priority = "amqpx.priority"; + public const string ContentType = "amqp.content-type"; + public const string ReplyTo = "amqp.reply-to"; + public const string ReplyToExchange = "amqpx.qpid0-10.reply-to-exchange"; + public const string RoutingKey = "amqpx.qpid0-10.routing-key"; + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBinding.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBinding.cs new file mode 100644 index 0000000000..e207f2fe45 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBinding.cs @@ -0,0 +1,60 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Configuration; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Configuration; + + using Apache.Qpid.AmqpTypes; + + public class AmqpBinaryBinding : AmqpBinding + { + public AmqpBinaryBinding() +: base (new RawMessageEncodingBindingElement()) + { + } + + public AmqpBinaryBinding(string configurationName) + : this() + { + ApplyConfiguration(configurationName); + } + + private void ApplyConfiguration(string configurationName) + { + AmqpBinaryBindingCollectionElement section = (AmqpBinaryBindingCollectionElement)ConfigurationManager.GetSection(AmqpConstants.AmqpBinaryBindingSectionName); + AmqpBinaryBindingConfigurationElement element = section.Bindings[configurationName]; + if (element == null) + { + throw new ConfigurationErrorsException(string.Format(System.Globalization.CultureInfo.CurrentCulture, + "There is no binding named {0} at {1}.", configurationName, section.BindingName)); + } + else + { + element.ApplyConfiguration(this); + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBindingCollectionElement.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBindingCollectionElement.cs new file mode 100644 index 0000000000..de263bc4ef --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBindingCollectionElement.cs @@ -0,0 +1,29 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + /// <summary> + /// Implement application configuration of bindingExtensions for AmqpBinaryBinding + /// </summary> + public class AmqpBinaryBindingCollectionElement + : System.ServiceModel.Configuration.StandardBindingCollectionElement<AmqpBinaryBinding, AmqpBinaryBindingConfigurationElement> + { + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBindingConfigurationElement.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBindingConfigurationElement.cs new file mode 100644 index 0000000000..a537a6c6c3 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinaryBindingConfigurationElement.cs @@ -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. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Configuration; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Configuration; + using Apache.Qpid.AmqpTypes; + + public class AmqpBinaryBindingConfigurationElement : AmqpBindingConfigurationElement + { + public AmqpBinaryBindingConfigurationElement(string configurationName) + : base(configurationName) + { + } + + public AmqpBinaryBindingConfigurationElement() + : this(null) + { + } + + protected override Type BindingElementType + { + get { return typeof(AmqpBinaryBinding); } + } + + protected override ConfigurationPropertyCollection Properties + { + get + { + ConfigurationPropertyCollection properties = base.Properties; + + return properties; + } + } + + protected override void InitializeFrom(Binding binding) + { + base.InitializeFrom(binding); + AmqpBinaryBinding amqpBinding = (AmqpBinaryBinding)binding; + } + + protected override void OnApplyConfiguration(Binding binding) + { + if (binding == null) + throw new ArgumentNullException("binding"); + + if (binding.GetType() != typeof(AmqpBinaryBinding)) + { + throw new ArgumentException(string.Format("Invalid type for configuring an AMQP binding. Expected type: {0}. Type passed in: {1}.", + typeof(AmqpBinaryBinding).AssemblyQualifiedName, + binding.GetType().AssemblyQualifiedName)); + } + + base.OnApplyConfiguration(binding); + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinding.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinding.cs new file mode 100644 index 0000000000..b952faf9e5 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBinding.cs @@ -0,0 +1,115 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Configuration; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Configuration; + + using Apache.Qpid.AmqpTypes; + + public class AmqpBinding : Binding + { + protected AmqpTransportBindingElement transport; + protected MessageEncodingBindingElement encoding; + + public AmqpBinding() + { + transport = new AmqpTransportBindingElement(); + encoding = new BinaryMessageEncodingBindingElement(); + } + + protected AmqpBinding(MessageEncodingBindingElement encoding) + { + this.encoding = encoding; + transport = new AmqpTransportBindingElement(); + } + + public AmqpBinding(string configurationName) + : this() + { + ApplyConfiguration(configurationName); + } + + public string BrokerHost + { + get { return transport.BrokerHost; } + set { transport.BrokerHost = value; } + } + + public int BrokerPort + { + get { return transport.BrokerPort; } + set { transport.BrokerPort = value; } + } + + public bool Shared + { + get { return transport.Shared; } + set { transport.Shared = value; } + } + + public TransferMode TransferMode + { + get { return transport.TransferMode; } + set { transport.TransferMode = value; } + } + + public AmqpProperties DefaultMessageProperties + { + get { return transport.DefaultMessageProperties; } + set { transport.DefaultMessageProperties = value; } + } + + public override string Scheme + { + get { return AmqpConstants.Scheme; } + } + + public override BindingElementCollection CreateBindingElements() + { + BindingElementCollection bindingElements = new BindingElementCollection(); + + bindingElements.Add(encoding); + bindingElements.Add(transport); + + return bindingElements.Clone(); + } + + private void ApplyConfiguration(string configurationName) + { + AmqpBindingCollectionElement section = (AmqpBindingCollectionElement)ConfigurationManager.GetSection(AmqpConstants.AmqpBindingSectionName); + AmqpBindingConfigurationElement element = section.Bindings[configurationName]; + if (element == null) + { + throw new ConfigurationErrorsException(string.Format(System.Globalization.CultureInfo.CurrentCulture, + "There is no binding named {0} at {1}.", configurationName, section.BindingName)); + } + else + { + element.ApplyConfiguration(this); + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingCollectionElement.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingCollectionElement.cs new file mode 100644 index 0000000000..e8d3b6fad4 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingCollectionElement.cs @@ -0,0 +1,29 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + /// <summary> + /// Implement application configuration of bindingExtensions for AmqpBinding + /// </summary> + public class AmqpBindingCollectionElement + : System.ServiceModel.Configuration.StandardBindingCollectionElement<AmqpBinding, AmqpBindingConfigurationElement> + { + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingConfigurationElement.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingConfigurationElement.cs new file mode 100644 index 0000000000..3ec62e809d --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpBindingConfigurationElement.cs @@ -0,0 +1,258 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Configuration; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Configuration; + using Apache.Qpid.AmqpTypes; + + public class AmqpBindingConfigurationElement : StandardBindingElement + { + // not regular config elements. See PostDeserialize + string brokerHost; + int brokerPort; + + public AmqpBindingConfigurationElement(string configurationName) + : base(configurationName) + { + brokerHost = AmqpDefaults.BrokerHost; + brokerPort = AmqpDefaults.BrokerPort; + } + + public AmqpBindingConfigurationElement() + : this(null) + { + } + + protected override Type BindingElementType + { + get { return typeof(AmqpBinding); } + } + + public string BrokerHost + { + get { return brokerHost; } + set { brokerHost = value; } + } + + public int BrokerPort + { + get { return brokerPort; } + set { brokerPort = value; } + } + + [ConfigurationProperty(AmqpConfigurationStrings.Shared, DefaultValue = false)] + public bool Shared + { + get { return (bool)base[AmqpConfigurationStrings.Shared]; } + set { base[AmqpConfigurationStrings.Shared] = value; } + } + + [ConfigurationProperty(AmqpConfigurationStrings.TransferMode, DefaultValue = AmqpDefaults.TransferMode)] + public TransferMode TransferMode + { + get { return (TransferMode)base[AmqpConfigurationStrings.TransferMode]; } + set { base[AmqpConfigurationStrings.TransferMode] = value; } + } + + [ConfigurationProperty(AmqpConfigurationStrings.Brokers)] + public BrokerCollection Brokers + { + get + { + return (BrokerCollection)base[AmqpConfigurationStrings.Brokers]; + } + set + { + base[AmqpConfigurationStrings.Brokers] = value; + } + } + + protected override ConfigurationPropertyCollection Properties + { + get + { + ConfigurationPropertyCollection properties = base.Properties; + properties.Add(new ConfigurationProperty(AmqpConfigurationStrings.Shared, + typeof(bool), false, null, null, ConfigurationPropertyOptions.None)); + properties.Add(new ConfigurationProperty(AmqpConfigurationStrings.TransferMode, + typeof(TransferMode), AmqpDefaults.TransferMode, null, null, ConfigurationPropertyOptions.None)); + properties.Add(new ConfigurationProperty("brokers", typeof(BrokerCollection), null)); + return properties; + } + } + + protected override void InitializeFrom(Binding binding) + { + base.InitializeFrom(binding); + AmqpBinding amqpBinding = (AmqpBinding)binding; + this.BrokerHost = amqpBinding.BrokerHost; + this.BrokerPort = amqpBinding.BrokerPort; + this.TransferMode = amqpBinding.TransferMode; + this.Shared = amqpBinding.Shared; + + AmqpProperties props = amqpBinding.DefaultMessageProperties; + } + + protected override void OnApplyConfiguration(Binding binding) + { + if (binding == null) + throw new ArgumentNullException("binding"); + + if (!(binding is AmqpBinding)) + { + throw new ArgumentException(string.Format("Invalid type for configuring an AMQP binding. Expected type: {0}. Type passed in: {1}.", + typeof(AmqpBinding).AssemblyQualifiedName, + binding.GetType().AssemblyQualifiedName)); + } + + AmqpBinding amqpBinding = (AmqpBinding)binding; + amqpBinding.BrokerHost = this.BrokerHost; + amqpBinding.BrokerPort = this.BrokerPort; + amqpBinding.TransferMode = this.TransferMode; + amqpBinding.Shared = this.Shared; + } + + protected override void PostDeserialize() + { + base.PostDeserialize(); + + BrokerCollection brokers = Brokers; + if (brokers != null) + { + if (brokers.Count > 0) + { + // just grab the first element until failover is supported + System.Collections.IEnumerator brokersEnum = brokers.GetEnumerator(); + // move to first element + brokersEnum.MoveNext(); + BrokerElement be = (BrokerElement)brokersEnum.Current; + this.BrokerHost = be.Host; + this.BrokerPort = be.Port; + } + } + } + } + + public class BrokerCollection : ConfigurationElementCollection + { + public BrokerCollection() + { + //this.AddElementName = "broker"; + } + + protected override ConfigurationElement CreateNewElement() + { + return new BrokerElement(); + } + + protected override void BaseAdd(ConfigurationElement element) + { + BrokerElement be = (BrokerElement)element; + if (this.BaseGet((Object)be.Key) != null) + { + throw new ConfigurationErrorsException("duplicate broker definition at line " + element.ElementInformation.LineNumber); + } + base.BaseAdd(element); + } + + protected override Object GetElementKey(ConfigurationElement element) + { + BrokerElement be = (BrokerElement) element; + return be.Key; + } + + protected override void PostDeserialize() + { + base.PostDeserialize(); + if (this.Count == 0) + { + throw new ArgumentException("Brokers collection requires at least one broker"); + } + if (this.Count > 1) + { + Console.WriteLine("Warning: multiple brokers not supported, selecting first instance"); + } + BrokerElement be = (BrokerElement)this.BaseGet(0); + } + + protected override string ElementName + { + get + { + return "broker"; + } + } + + public override ConfigurationElementCollectionType CollectionType + { + get + { + return ConfigurationElementCollectionType.BasicMap; + } + } + } + + public class BrokerElement : ConfigurationElement + { + string key; + + public BrokerElement() + { + Properties.Add(new ConfigurationProperty(AmqpConfigurationStrings.BrokerHost, + typeof(string), AmqpDefaults.BrokerHost, null, null, ConfigurationPropertyOptions.None)); + Properties.Add(new ConfigurationProperty(AmqpConfigurationStrings.BrokerPort, + typeof(int), AmqpDefaults.BrokerPort, null, null, ConfigurationPropertyOptions.None)); + + } + + [ConfigurationProperty(AmqpConfigurationStrings.BrokerHost, DefaultValue = AmqpDefaults.BrokerHost)] + public string Host + { + get { return (string)base[AmqpConfigurationStrings.BrokerHost]; } + set { base[AmqpConfigurationStrings.BrokerHost] = value; } + } + + [ConfigurationProperty(AmqpConfigurationStrings.BrokerPort, DefaultValue = AmqpDefaults.BrokerPort)] + public int Port + { + get { return (int)base[AmqpConfigurationStrings.BrokerPort]; } + set { base[AmqpConfigurationStrings.BrokerPort] = value; } + } + + public string Key + { + get + { + if (this.key == null) + { + this.key = this.Host + ':' + this.Port; + } + return this.key; + } + } + + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelFactory.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelFactory.cs new file mode 100644 index 0000000000..b8e2811527 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelFactory.cs @@ -0,0 +1,98 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.Collections.Generic; + using System.Collections.ObjectModel; + + class AmqpChannelFactory<TChannel> : ChannelFactoryBase<TChannel> + { + MessageEncoderFactory messageEncoderFactory; + AmqpTransportBindingElement bindingElement; + AmqpChannelProperties channelProperties; + long maxBufferPoolSize; + bool shared; + + internal AmqpChannelFactory(AmqpTransportBindingElement bindingElement, BindingContext context) + : base(context.Binding) + { + this.bindingElement = bindingElement; + this.channelProperties = bindingElement.ChannelProperties.Clone(); + this.shared = bindingElement.Shared; + this.maxBufferPoolSize = bindingElement.MaxBufferPoolSize; + Collection<MessageEncodingBindingElement> messageEncoderBindingElements + = context.BindingParameters.FindAll<MessageEncodingBindingElement>(); + + if(messageEncoderBindingElements.Count > 1) + { + throw new InvalidOperationException("More than one MessageEncodingBindingElement was found in the BindingParameters of the BindingContext"); + } + else if (messageEncoderBindingElements.Count == 1) + { + this.messageEncoderFactory = messageEncoderBindingElements[0].CreateMessageEncoderFactory(); + } + else + { + this.messageEncoderFactory = new TextMessageEncodingBindingElement().CreateMessageEncoderFactory(); + } + } + + + public override T GetProperty<T>() + { + T mep = messageEncoderFactory.Encoder.GetProperty<T>(); + if (mep != null) + { + return mep; + } + + if (typeof(T) == typeof(MessageVersion)) + { + return (T)(object)messageEncoderFactory.Encoder.MessageVersion; + } + + return base.GetProperty<T>(); + } + + protected override void OnOpen(TimeSpan timeout) + { + } + + protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state) + { + throw new NotImplementedException("AmqpChannelFactory OnBeginOpen"); + //// return null; + } + + protected override void OnEndOpen(IAsyncResult result) + { + throw new NotImplementedException("AmqpChannelFactory OnEndOpen"); + } + + protected override TChannel OnCreateChannel(EndpointAddress remoteAddress, Uri via) + { + return (TChannel)(object) new AmqpTransportChannel(this, this.channelProperties, remoteAddress, this.messageEncoderFactory.Encoder, this.maxBufferPoolSize, this.shared); + } + + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelHelpers.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelHelpers.cs new file mode 100644 index 0000000000..f1de30406a --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelHelpers.cs @@ -0,0 +1,142 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.Net; + using System.Net.Sockets; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.Globalization; + + using Apache.Qpid.AmqpTypes; + + /// <summary> + /// Collection of constants used by the Amqp Channel classes + /// </summary> + static class AmqpConstants + { + internal const string Scheme = "amqp"; + internal const string AmqpBindingSectionName = "system.serviceModel/bindings/amqpBinding"; + internal const string AmqpBinaryBindingSectionName = "system.serviceModel/bindings/amqpBinaryBinding"; + internal const string AmqpTransportSectionName = "amqpTransport"; + } + + static class AmqpConfigurationStrings + { + public const string BrokerHost = "host"; + public const string BrokerPort = "port"; + public const string TransferMode = "transferMode"; + public const string Brokers = "brokers"; + public const string Shared = "shared"; + public const string MaxBufferPoolSize = "maxBufferPoolSize"; + public const string MaxReceivedMessageSize = "maxReceivedMessageSize"; + } + + static class AmqpDefaults + { + internal const string BrokerHost = "localhost"; + internal const int BrokerPort = 5672; + internal const TransferMode TransferMode = System.ServiceModel.TransferMode.Buffered; + internal const byte Priority = 4; + internal const long MaxBufferPoolSize = 64 * 1024; + internal const int MaxReceivedMessageSize = 5 * 1024 * 1024; //64 * 1024; + } + + // parking spot for properties that may be shared by separate channels on a single AMQP connection + internal class AmqpChannelProperties + { + string brokerHost; + int brokerPort; + TransferMode transferMode; + AmqpProperties defaultMessageProperties; + + long maxBufferPoolSize; + int maxReceivedMessageSize; + + internal AmqpChannelProperties() + { + this.brokerHost = AmqpDefaults.BrokerHost; + this.brokerPort = AmqpDefaults.BrokerPort; + this.transferMode = AmqpDefaults.TransferMode; + this.defaultMessageProperties = null; + this.maxBufferPoolSize = AmqpDefaults.MaxBufferPoolSize; + this.maxReceivedMessageSize = AmqpDefaults.MaxReceivedMessageSize; + } + + public AmqpChannelProperties Clone() + { + AmqpChannelProperties props = (AmqpChannelProperties) this.MemberwiseClone(); + if (this.defaultMessageProperties != null) + { + props.defaultMessageProperties = this.defaultMessageProperties.Clone(); + } + + return props; + } + + internal string BrokerHost + { + get { return this.brokerHost; } + set { this.brokerHost = value; } + } + + internal int BrokerPort + { + get { return this.brokerPort; } + set { this.brokerPort = value; } + } + + internal TransferMode TransferMode + { + get { return this.transferMode; } + set { this.transferMode = value; } + } + + internal AmqpProperties DefaultMessageProperties + { + get { return this.defaultMessageProperties; } + set { this.defaultMessageProperties = value; } + } + + internal long MaxBufferPoolSize + { + get { return this.maxBufferPoolSize; } + set { this.maxBufferPoolSize = value; } + } + + internal int MaxReceivedMessageSize + { + get { return this.maxReceivedMessageSize; } + set { this.maxReceivedMessageSize = value; } + } + } + + static class AmqpChannelHelpers + { + internal static void ValidateTimeout(TimeSpan timeout) + { + if (timeout < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException("timeout", timeout, "Timeout must be greater than or equal to TimeSpan.Zero. To disable timeout, specify TimeSpan.MaxValue."); + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelListener.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelListener.cs new file mode 100644 index 0000000000..44fecdaf62 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpChannelListener.cs @@ -0,0 +1,174 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.Threading; + using System.Collections.Generic; + using System.Collections.ObjectModel; + + class AmqpChannelListener : ChannelListenerBase<IInputChannel> + { + MessageEncoderFactory messageEncoderFactory; + AmqpTransportBindingElement bindingElement; + AmqpChannelProperties channelProperties; + bool shared; + long maxBufferPoolSize; + Uri uri; + AmqpTransportChannel amqpTransportChannel; + delegate IInputChannel AsyncOnAcceptCaller (TimeSpan timeout); + AsyncOnAcceptCaller asyncOnAcceptCaller; + ManualResetEvent acceptWaitEvent; + + internal AmqpChannelListener(AmqpTransportBindingElement bindingElement, BindingContext context) + : base(context.Binding) + { + this.bindingElement = bindingElement; + this.channelProperties = bindingElement.ChannelProperties.Clone(); + this.shared = bindingElement.Shared; + + this.maxBufferPoolSize = bindingElement.MaxBufferPoolSize; + + // TODO: review this. Should be unique hostname based + this.uri = context.ListenUriBaseAddress; + this.asyncOnAcceptCaller = new AsyncOnAcceptCaller(this.OnAcceptChannel); + this.acceptWaitEvent = new ManualResetEvent(false); + + Collection<MessageEncodingBindingElement> messageEncoderBindingElements + = context.BindingParameters.FindAll<MessageEncodingBindingElement>(); + + if(messageEncoderBindingElements.Count > 1) + { + throw new InvalidOperationException("More than one MessageEncodingBindingElement was found in the BindingParameters of the BindingContext"); + } + else if (messageEncoderBindingElements.Count == 1) + { + this.messageEncoderFactory = messageEncoderBindingElements[0].CreateMessageEncoderFactory(); + } + else + { + this.messageEncoderFactory = new TextMessageEncodingBindingElement().CreateMessageEncoderFactory(); + } + } + + public override Uri Uri + { + get + { + return this.uri; + } + } + + + + public override T GetProperty<T>() + { + T mep = messageEncoderFactory.Encoder.GetProperty<T>(); + if (mep != null) + { + return mep; + } + + if (typeof(T) == typeof(MessageVersion)) + { + return (T)(object)messageEncoderFactory.Encoder.MessageVersion; + } + + return base.GetProperty<T>(); + } + + protected override void OnOpen(TimeSpan timeout) + { + } + + protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state) + { + throw new NotImplementedException("AmqpChannelListener OnBeginOpen"); + //// return null; + } + + protected override void OnEndOpen(IAsyncResult result) + { + throw new NotImplementedException("AmqpChannelListener OnEndOpen"); + } + + protected override bool OnWaitForChannel(TimeSpan timeout) + { + throw new NotImplementedException("AmqpChannelListener OnWaitForChannel"); + } + + protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state) + { + throw new NotImplementedException("AmqpChannelListener OnBeginWaitForChannel"); + } + + protected override bool OnEndWaitForChannel(IAsyncResult result) + { + throw new NotImplementedException("AmqpChannelListener OnEndWaitForChannel"); + } + + protected override IInputChannel OnAcceptChannel(TimeSpan timeout) + { + if (amqpTransportChannel == null) + { + amqpTransportChannel = new AmqpTransportChannel(this, this.channelProperties, + new EndpointAddress(uri), messageEncoderFactory.Encoder, + maxBufferPoolSize, this.shared); + return (IInputChannel)(object) amqpTransportChannel; + } + + // TODO: remove "max one channel" restriction, add timeout processing + acceptWaitEvent.WaitOne(); + return null; + } + + protected override IAsyncResult OnBeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state) + { + return asyncOnAcceptCaller.BeginInvoke(timeout, callback, state); + } + + protected override IInputChannel OnEndAcceptChannel(IAsyncResult result) + { + return asyncOnAcceptCaller.EndInvoke(result); + } + + protected override void OnClose(TimeSpan timeout) + { + // TODO: (+ OnAbort) + } + + protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state) + { + throw new NotImplementedException("AmqpChannelListener OnBeginClose"); + } + + protected override void OnEndClose(IAsyncResult result) + { + throw new NotImplementedException("AmqpChannelListener OnEndClose"); + } + + protected override void OnAbort() + { + // TODO: + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportBindingElement.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportBindingElement.cs new file mode 100644 index 0000000000..f23b8072e9 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportBindingElement.cs @@ -0,0 +1,145 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Description; + using Apache.Qpid.AmqpTypes; + + public class AmqpTransportBindingElement : TransportBindingElement + { + AmqpChannelProperties channelProperties; + bool shared; + + public AmqpTransportBindingElement() + { + // start with default properties + channelProperties = new AmqpChannelProperties(); + } + + protected AmqpTransportBindingElement(AmqpTransportBindingElement other) + : base(other) + { + this.channelProperties = other.channelProperties.Clone(); + this.shared = other.shared; + } + + public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + + return (IChannelFactory<TChannel>)(object)new AmqpChannelFactory<TChannel>(this, context); + } + + public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + + return (IChannelListener<TChannel>)(object)new AmqpChannelListener(this, context); + } + + + + public override bool CanBuildChannelFactory<TChannel>(BindingContext context) + { + return ((typeof(TChannel) == typeof(IOutputChannel)) || + (typeof(TChannel) == typeof(IInputChannel))); + } + + public override bool CanBuildChannelListener<TChannel>(BindingContext context) + { + return ((typeof(TChannel) == typeof(IInputChannel))); + } + + public override BindingElement Clone() + { + return new AmqpTransportBindingElement(this); + } + + internal AmqpChannelProperties ChannelProperties + { + get { return channelProperties; } + } + + public string BrokerHost + { + get { return this.channelProperties.BrokerHost; } + set { this.channelProperties.BrokerHost = value; } + } + + public int BrokerPort + { + get { return this.channelProperties.BrokerPort; } + set { this.channelProperties.BrokerPort = value; } + } + + public bool Shared + { + get { return this.shared; } + set { this.shared = value; } + } + + public TransferMode TransferMode + { + get { return this.channelProperties.TransferMode; } + set { this.channelProperties.TransferMode = value; } + } + + public AmqpProperties DefaultMessageProperties + { + get { return this.channelProperties.DefaultMessageProperties; } + + set { this.channelProperties.DefaultMessageProperties = value; } + } + + public override T GetProperty<T>(BindingContext context) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + + if (typeof(T) == typeof(MessageVersion)) + { + return (T)(object)MessageVersion.Default; + } + + + return context.GetInnerProperty<T>(); + } + + public override string Scheme + { + get + { + return AmqpConstants.Scheme; + } + } + + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportChannel.cs b/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportChannel.cs new file mode 100644 index 0000000000..ca9c10be69 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/AmqpTransportChannel.cs @@ -0,0 +1,592 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// TODO: flow control +// timeout handling +// transactions +// check if should split into separate input and output classes (little overlap) + +namespace Apache.Qpid.Channel +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.Text; + using System.Threading; + using System.Globalization; + using System.Xml; + + // the thin interop layer that provides access to the Qpid AMQP client libraries + using Apache.Qpid.Interop; + using Apache.Qpid.AmqpTypes; + + /// <summary> + /// WCF client transport channel for accessing AMQP brokers using the Qpid C++ library + /// </summary> + public class AmqpTransportChannel : ChannelBase, IOutputChannel, IInputChannel + { + private static readonly EndpointAddress AnonymousAddress = + new EndpointAddress("http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"); + + private EndpointAddress remoteAddress; + private MessageEncoder encoder; + private AmqpChannelProperties factoryChannelProperties; + private bool shared; + private string encoderContentType; + + // input = 0-10 queue, output = 0-10 exchange + private string queueName; + + private String routingKey; + private BufferManager bufferManager; + private AmqpProperties outputMessageProperties; + + private InputLink inputLink; + private OutputLink outputLink; + + private bool isInputChannel; + private bool streamed; + + private AsyncTimeSpanCaller asyncOpenCaller; + private AsyncTimeSpanCaller asyncCloseCaller; + + internal AmqpTransportChannel(ChannelManagerBase factory, AmqpChannelProperties channelProperties, EndpointAddress remoteAddress, MessageEncoder msgEncoder, long maxBufferPoolSize, bool sharedConnection) + : base(factory) + { + this.isInputChannel = (factory is ChannelListenerBase) || (factory is AmqpChannelFactory<IInputChannel>); + + if (remoteAddress == null) + { + throw new ArgumentException("Null Endpoint Address"); + } + + this.factoryChannelProperties = channelProperties; + this.shared = sharedConnection; + this.remoteAddress = remoteAddress; + + // pull out host, port, queue, and connection arguments + this.ParseAmqpUri(remoteAddress.Uri); + + this.encoder = msgEncoder; + string ct = String.Empty; + if (this.encoder != null) + { + ct = this.encoder.ContentType; + if (ct != null) + { + int pos = ct.IndexOf(';'); + if (pos != -1) + { + ct = ct.Substring(0, pos).Trim(); + } + } + else + { + ct = "application/octet-stream"; + } + } + + this.encoderContentType = ct; + + if (this.factoryChannelProperties.TransferMode == TransferMode.Streamed) + { + this.streamed = true; + } + else + { + if (!(this.factoryChannelProperties.TransferMode == TransferMode.Buffered)) + { + throw new ArgumentException("TransferMode mode must be \"Streamed\" or \"Buffered\""); + } + + this.streamed = false; + } + + this.bufferManager = BufferManager.CreateBufferManager(maxBufferPoolSize, int.MaxValue); + + this.asyncOpenCaller = new AsyncTimeSpanCaller(this.OnOpen); + this.asyncCloseCaller = new AsyncTimeSpanCaller(this.OnClose); + + if (this.isInputChannel) + { + this.inputLink = ConnectionManager.GetInputLink(this.factoryChannelProperties, shared, false, this.queueName); + } + else + { + this.outputLink = ConnectionManager.GetOutputLink(this.factoryChannelProperties, shared, false, this.queueName); + } + } + + private delegate bool AsyncTryReceiveCaller(TimeSpan timeout, out Message message); + + private delegate void AsyncTimeSpanCaller(TimeSpan timeout); + + EndpointAddress IOutputChannel.RemoteAddress + { + get + { + return this.remoteAddress; + } + } + + // i.e what you would insert into a ReplyTo header to reach + // here. Presumably should be exchange/link and routing info, + // rather than the actual input queue name. + EndpointAddress IInputChannel.LocalAddress + { + get + { + // TODO: something better + return AnonymousAddress; + } + } + + AmqpProperties OutputMessageProperties + { + get + { + if (this.outputMessageProperties == null) + { + this.outputMessageProperties = this.factoryChannelProperties.DefaultMessageProperties; + if (this.outputMessageProperties == null) + { + this.outputMessageProperties = new AmqpProperties(); + } + } + + return this.outputMessageProperties; + } + } + + Uri IOutputChannel.Via + { + get + { + return this.remoteAddress.Uri; + } + } + + public override T GetProperty<T>() + { + if (typeof(T) == typeof(IInputChannel)) + { + if (this.isInputChannel) + { + return (T)(object)this; + } + } + else if (typeof(T) == typeof(IOutputChannel)) + { + if (!this.isInputChannel) + { + return (T)(object)this; + } + } + + return base.GetProperty<T>(); + } + + public void Send(Message message, TimeSpan timeout) + { + this.ThrowIfDisposedOrNotOpen(); + AmqpChannelHelpers.ValidateTimeout(timeout); + + try + { + using (AmqpMessage amqpMessage = this.WcfToQpid(message)) + { + this.outputLink.Send(amqpMessage, timeout); + } + } + finally + { + message.Close(); + } + } + + public void Send(Message message) + { + this.Send(message, this.DefaultSendTimeout); + } + + public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state) + { + this.ThrowIfDisposedOrNotOpen(); + AmqpChannelHelpers.ValidateTimeout(timeout); + + try + { + using (AmqpMessage amqpMessage = this.WcfToQpid(message)) + { + return this.outputLink.BeginSend(amqpMessage, timeout, callback, state); + } + } + finally + { + message.Close(); + } + } + + public IAsyncResult BeginSend(Message message, AsyncCallback callback, object state) + { + return this.BeginSend(message, this.DefaultSendTimeout, callback, state); + } + + public void EndSend(IAsyncResult result) + { + this.outputLink.EndSend(result); + } + + public Message Receive(TimeSpan timeout) + { + Message message; + if (this.TryReceive(timeout, out message)) + { + return message; + } + else + { + throw new TimeoutException("Receive"); + } + } + + public Message Receive() + { + return this.Receive(this.DefaultReceiveTimeout); + } + + public bool TryReceive(TimeSpan timeout, out Message message) + { + this.ThrowIfDisposedOrNotOpen(); + AmqpMessage amqpMessage; + message = null; + + if (this.inputLink.TryReceive(timeout, out amqpMessage)) + { + message = this.QpidToWcf(amqpMessage); + return true; + } + + return false; + } + + public IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback, object state) + { + return this.inputLink.BeginTryReceive(timeout, callback, state); + } + + public bool EndTryReceive(IAsyncResult result, out Message message) + { + AmqpMessage amqpMessage = null; + if (!this.inputLink.EndTryReceive(result, out amqpMessage)) + { + message = null; + return false; + } + message = QpidToWcf(amqpMessage); + return true; + } + + public bool WaitForMessage(TimeSpan timeout) + { + return this.inputLink.WaitForMessage(timeout); + } + + public IAsyncResult BeginReceive(TimeSpan timeout, AsyncCallback callback, object state) + { + return this.inputLink.BeginTryReceive(timeout, callback, state); + } + + public IAsyncResult BeginReceive(AsyncCallback callback, object state) + { + return this.BeginReceive(this.DefaultReceiveTimeout, callback, state); + } + + public IAsyncResult BeginWaitForMessage(TimeSpan timeout, AsyncCallback callback, object state) + { + return this.inputLink.BeginWaitForMessage(timeout, callback, state); + } + + public Message EndReceive(IAsyncResult result) + { + Message message; + if (this.EndTryReceive(result, out message)) + { + return message; + } + else + { + throw new TimeoutException("EndReceive"); + } + } + + public bool EndWaitForMessage(IAsyncResult result) + { + return this.inputLink.EndWaitForMessage(result); + } + + public void CloseEndPoint() + { + if (this.inputLink != null) + { + this.inputLink.Close(); + } + if (this.outputLink != null) + { + this.outputLink.Close(); + } + } + + /// <summary> + /// Open connection to Broker + /// </summary> + protected override void OnOpen(TimeSpan timeout) + { + // TODO: move open logic to here from constructor + } + + protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state) + { + return this.asyncOpenCaller.BeginInvoke(timeout, callback, state); + } + + protected override void OnEndOpen(IAsyncResult result) + { + this.asyncOpenCaller.EndInvoke(result); + } + + protected override void OnAbort() + { + //// TODO: check for network-less qpid teardown or launch special thread + this.Cleanup(); + } + + /// <summary> + /// Shutdown gracefully + /// </summary> + protected override void OnClose(TimeSpan timeout) + { + this.CloseEndPoint(); + this.Cleanup(); + } + + protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state) + { + return this.asyncCloseCaller.BeginInvoke(timeout, callback, state); + } + + protected override void OnEndClose(IAsyncResult result) + { + this.asyncCloseCaller.EndInvoke(result); + } + + private AmqpMessage WcfToQpid(Message wcfMessage) + { + object obj; + AmqpProperties applicationProperties = null; + bool success = false; + AmqpMessage amqpMessage = null; + + if (wcfMessage.Properties.TryGetValue("AmqpProperties", out obj)) + { + applicationProperties = obj as AmqpProperties; + } + + try + { + AmqpProperties outgoingProperties = new AmqpProperties(); + + // Start with AMQP properties from the binding and the URI + if (this.factoryChannelProperties.DefaultMessageProperties != null) + { + outgoingProperties.MergeFrom(this.factoryChannelProperties.DefaultMessageProperties); + } + + if (this.routingKey != null) + { + outgoingProperties.RoutingKey = this.routingKey; + } + + // Add the Properties set by the application on this particular message. + // Application properties trump channel properties + if (applicationProperties != null) + { + outgoingProperties.MergeFrom(applicationProperties); + } + + amqpMessage = this.outputLink.CreateMessage(); + amqpMessage.Properties = outgoingProperties; + + // copy the WCF message body to the AMQP message body + if (this.streamed) + { + this.encoder.WriteMessage(wcfMessage, amqpMessage.BodyStream); + } + else + { + ArraySegment<byte> encodedBody = this.encoder.WriteMessage(wcfMessage, int.MaxValue, this.bufferManager); + try + { + amqpMessage.BodyStream.Write(encodedBody.Array, encodedBody.Offset, encodedBody.Count); + } + finally + { + this.bufferManager.ReturnBuffer(encodedBody.Array); + } + } + + success = true; + } + finally + { + if (!success && (amqpMessage != null)) + { + amqpMessage.Dispose(); + } + } + return amqpMessage; + } + + + private Message QpidToWcf(AmqpMessage amqpMessage) + { + if (amqpMessage == null) + { + return null; + } + + Message wcfMessage = null; + byte[] managedBuffer = null; + + try + { + if (this.streamed) + { + wcfMessage = this.encoder.ReadMessage(amqpMessage.BodyStream, int.MaxValue); + } + else + { + int count = (int)amqpMessage.BodyStream.Length; + managedBuffer = this.bufferManager.TakeBuffer(count); + int nr = amqpMessage.BodyStream.Read(managedBuffer, 0, count); + ArraySegment<byte> bufseg = new ArraySegment<byte>(managedBuffer, 0, count); + + wcfMessage = this.encoder.ReadMessage(bufseg, this.bufferManager); + + // set to null for finally{} block, since the encoder is now responsible for + // returning the BufferManager memory + managedBuffer = null; + } + + // This message will be discarded unless the "To" header matches + // the WCF endpoint dispatcher's address filter (or the service is + // AddressFilterMode=AddressFilterMode.Any). + + this.remoteAddress.ApplyTo(wcfMessage); + + if (amqpMessage.Properties != null) + { + wcfMessage.Properties.Add("AmqpProperties", amqpMessage.Properties); + } + } + catch (XmlException xmlException) + { + throw new ProtocolException( + "There is a problem with the XML that was received from the network. See inner exception for more details.", + xmlException); + } + catch (Exception e) + { + // TODO: logging + Console.WriteLine("TX channel encoder exception " + e); + } + finally + { + // close the amqpMessage unless the body will be read at a later time. + if (!this.streamed || wcfMessage == null) + { + amqpMessage.Close(); + } + + // the handoff to the encoder failed + if (managedBuffer != null) + { + this.bufferManager.ReturnBuffer(managedBuffer); + } + } + + return wcfMessage; + } + + private void Cleanup() + { + this.bufferManager.Clear(); + } + + // "amqp:queue1" | "amqp:stocks@broker1.com" | "amqp:queue3?routingkey=key" + private void ParseAmqpUri(Uri uri) + { + if (uri.Scheme != AmqpConstants.Scheme) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, + "The scheme {0} specified in address is not supported.", uri.Scheme), "uri"); + } + + this.queueName = uri.LocalPath; + + if ((this.queueName.IndexOf('@') != -1) && this.isInputChannel) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, + "Invalid input queue name: \"{0}\" specified.", this.queueName), "uri"); + } + + // search out session parameters in the query portion of the URI + + string routingParseKey = "routingkey="; + char[] charSeparators = new char[] { '?', ';' }; + string[] args = uri.Query.Split(charSeparators, StringSplitOptions.RemoveEmptyEntries); + foreach (string s in args) + { + if (s.StartsWith(routingParseKey)) + { + this.routingKey = s.Substring(routingParseKey.Length); + } + } + + if (this.queueName == String.Empty) + { + if (this.isInputChannel) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, + "Empty queue target specifier not allowed."), "uri"); + } + else + { + if (this.routingKey == null) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, + "No target queue or routing key specified."), "uri"); + } + } + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/Channel.csproj b/qpid/wcf/src/Apache/Qpid/Channel/Channel.csproj new file mode 100644 index 0000000000..0b04eba986 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/Channel.csproj @@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{8AABAB30-7D1E-4539-B7D1-05450262BAD2}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Apache.Qpid.Channel</RootNamespace>
+ <AssemblyName>Apache.Qpid.Channel</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <StartupObject>
+ </StartupObject>
+ <SignAssembly>false</SignAssembly>
+ <AssemblyOriginatorKeyFile>
+ </AssemblyOriginatorKeyFile>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+ <ItemGroup>
+ <Compile Include="AmqpBinaryBinding.cs" />
+ <Compile Include="AmqpBinaryBindingCollectionElement.cs" />
+ <Compile Include="AmqpBinaryBindingConfigurationElement.cs" />
+ <Compile Include="AmqpChannelFactory.cs" />
+ <Compile Include="AmqpChannelHelpers.cs" />
+ <Compile Include="AmqpChannelListener.cs" />
+ <Compile Include="AmqpBinding.cs" />
+ <Compile Include="AmqpBindingCollectionElement.cs" />
+ <Compile Include="AmqpBindingConfigurationElement.cs" />
+ <Compile Include="AmqpTransportBindingElement.cs" />
+ <Compile Include="AmqpTransportChannel.cs" />
+ <Compile Include="ConnectionManager.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="RawMessage.cs" />
+ <Compile Include="RawMessageEncoder.cs" />
+ <Compile Include="RawMessageEncoderFactory.cs" />
+ <Compile Include="RawMessageEncodingBindingElement.cs" />
+ <Compile Include="RawXmlReader.cs" />
+ <Compile Include="RawXmlWriter.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.configuration" />
+ <Reference Include="System.Runtime.Serialization">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.ServiceModel">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.XML" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Interop\Interop.vcproj">
+ <Project>{C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}</Project>
+ <Name>Interop</Name>
+ </ProjectReference>
+ </ItemGroup>
+</Project>
\ No newline at end of file diff --git a/qpid/wcf/src/Apache/Qpid/Channel/ConnectionManager.cs b/qpid/wcf/src/Apache/Qpid/Channel/ConnectionManager.cs new file mode 100644 index 0000000000..a63e5333f4 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/ConnectionManager.cs @@ -0,0 +1,266 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Threading; + + using Apache.Qpid.Interop; + + // The ConnectionManager looks after a shareable pool of AmqpConnection and AmqpSession + // objects. If two connection requests could be shared (see MakeKey() properties), and + // are designated as shareable, then they will be paired up. Each shared connection is + // a separate instance of a ManagedConnection. All unshared connections use a single + // instance of ManagedConnection with locking turned off. The ManagedConnection object + // registers for notifictation when a connection goes idle (all grandchild InputLink and + // OutputLink objects have been closed), and closes the connection. + + // TODO: the session sharing is roughed-in via comments but needs completing. + + internal sealed class ConnectionManager + { + // A side effect of creating InputLinks and OutputLinks is that counters + // in the respective AmqpSession and AmqpConnection are updated, so care + // must be taken to hold the lock across acquiring a session and opening + // a link on it. + + // one for each shared connection + private static Dictionary<string, ManagedConnection> sharedInstances; + + // this one creates and releases connections that are not shared. No locking required. + private static ManagedConnection unsharedInstance; + + // lock for finding or creating ManagedConnection instances + private static Object connectionLock; + + static ConnectionManager() + { + unsharedInstance = null; + sharedInstances = new Dictionary<string, ManagedConnection>(); + connectionLock = new Object(); + } + + private static string MakeKey(AmqpChannelProperties props) + { + return props.BrokerHost + ':' + props.BrokerPort + ':' + props.TransferMode; + } + + private static ManagedConnection GetManagedConnection(AmqpChannelProperties channelProperties, bool connectionSharing) + { + if (connectionSharing) + { + string key = MakeKey(channelProperties); + lock (connectionLock) + { + ManagedConnection mc = null; + if (!sharedInstances.TryGetValue(key, out mc)) + { + mc = new ManagedConnection(true); + sharedInstances.Add(key, mc); + } + return mc; + } + } + else + { + lock (connectionLock) + { + if (unsharedInstance == null) + { + unsharedInstance = new ManagedConnection(false); + } + return unsharedInstance; + } + } + } + + public static OutputLink GetOutputLink(AmqpChannelProperties channelProperties, bool connectionSharing, bool sessionSharing, string qname) + { + ManagedConnection mc = GetManagedConnection(channelProperties, connectionSharing); + return (OutputLink)mc.GetLink(channelProperties, sessionSharing, null, qname); + } + + public static InputLink GetInputLink(AmqpChannelProperties channelProperties, bool connectionSharing, bool sessionSharing, string qname) + { + ManagedConnection mc = GetManagedConnection(channelProperties, connectionSharing); + return (InputLink)mc.GetLink(channelProperties, sessionSharing, qname, null); + } + + + + class ManagedConnection + { + private Boolean shared; + private AmqpConnection sharedConnection; + //private Dictionary<string, AmqpSession> sharedSessions; + + public ManagedConnection(bool shared) + { + this.shared = shared; + } + + + public object GetLink(AmqpChannelProperties channelProperties, bool sessionSharing, string inputQueue, string outputQueue) + { + AmqpConnection connection = null; + AmqpSession session = null; + Object link = null; + bool newConnection = false; + //bool newSession = false; + bool success = false; + + // when called in the non-shared case, only stack variables should be used for holding connections/sessions/links + + if (this.shared) + { + Monitor.Enter(this); // lock + } + + try + { + if (this.shared) + { + // TODO: check shared connection not closed (i.e. network drop) and refresh this instance if needed + if (sessionSharing) + { + throw new NotImplementedException("shared session"); + /* * ... once we have a defined shared session config parameter: + + // lazilly create + if (this.sharedSessions == null) + { + this.sharedSessions = new Dictionary<string, AmqpSession>(); + } + + alreadydeclaredstring sessionKey = channelProperties.name_of_key_goes_here; + this.sharedSessions.TryGetValue(sessionKey, out session); + + * */ + } + + if (this.sharedConnection != null) + { + connection = this.sharedConnection; + } + } + + if (connection == null) + { + connection = new AmqpConnection(channelProperties.BrokerHost, channelProperties.BrokerPort); + newConnection = true; + if (this.shared) + { + connection.OnConnectionIdle += new ConnectionIdleEventHandler(this.IdleConnectionHandler); + } + else + { + connection.OnConnectionIdle += new ConnectionIdleEventHandler(UnsharedIdleConnectionHandler); + } + } + + if (session == null) + { + session = connection.CreateSession(); + //newSession = true; + } + + if (inputQueue != null) + { + link = session.CreateInputLink(inputQueue); + } + else + { + link = session.CreateOutputLink(outputQueue); + } + + if (this.shared) + { + if (newConnection) + { + this.sharedConnection = connection; + } + /* + if (newSession) + { + sharedSessions.Add(foo, session); + } + * */ + } + + success = true; + } + finally + { + if (this.shared) + { + Monitor.Exit(this); + } + if (!success) + { + /* + if (newSession) + { + session.Close(); + } + */ + if (newConnection) + { + connection.Close(); + } + } + } + + return link; + } + + + static void UnsharedIdleConnectionHandler(Object sender, EventArgs empty) + { + if (sender is AmqpConnection) + { + AmqpConnection connection = (AmqpConnection)sender; + connection.Close(); + } + } + + void IdleConnectionHandler(Object sender, EventArgs empty) + { + lock (this) + { + if (sharedConnection != sender || sharedConnection == null) + { + return; + } + if (!sharedConnection.IsIdle) + { + // Another thread made the connection busy again. + // That's OK. Another idle event will come along later. + return; + } + sharedConnection.Close(); // also closes all child sessions + sharedConnection = null; + //sharedSessions = null; + } + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/Properties/AssemblyInfo.cs b/qpid/wcf/src/Apache/Qpid/Channel/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..bc047d59b3 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/Properties/AssemblyInfo.cs @@ -0,0 +1,52 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Apache.Qpid.Channel")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ac02bbb0-2c19-43fb-a36c-b1b0a50eaf1a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/src/Apache/Qpid/Channel/RawMessage.cs b/qpid/wcf/src/Apache/Qpid/Channel/RawMessage.cs new file mode 100644 index 0000000000..5925fa47dc --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/RawMessage.cs @@ -0,0 +1,374 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.IO; + using System.ServiceModel.Channels; + using System.Xml; + + // This incoming Message is backed either by a Stream (bodyStream) or a byte array (bodyBytes). + // If bodyBytes belongs to a BufferManager, we must return it when done. + // The pay-off is OnGetReaderAtBodyContents(). + // Most of the complexity is dealing with the OnCreateBufferedCopy() machinery. + internal class RawMessage : Message + { + private MessageHeaders headers; + private MessageProperties properties; + private XmlDictionaryReaderQuotas readerQuotas; + private Stream bodyStream; + private byte[] bodyBytes; + private int index; + private int count; + private BufferManager bufferManager; + + public RawMessage(byte[] buffer, int index, int count, BufferManager bufferManager, XmlDictionaryReaderQuotas quotas) + { + // this constructor supports MessageEncoder.ReadMessage(ArraySegment<byte> b, BufferManager mgr, string contentType) + if (quotas == null) + { + quotas = new XmlDictionaryReaderQuotas(); + } + + this.headers = new MessageHeaders(MessageVersion.None); + this.properties = new MessageProperties(); + this.readerQuotas = quotas; + this.bodyBytes = buffer; + this.index = index; + this.count = count; + this.bufferManager = bufferManager; + } + + public RawMessage(Stream stream, XmlDictionaryReaderQuotas quotas) + { + // this constructor supports MessageEncoder.ReadMessage(System.IO.Stream s, int max, string contentType) + if (quotas == null) + { + quotas = new XmlDictionaryReaderQuotas(); + } + + this.headers = new MessageHeaders(MessageVersion.None); + this.properties = new MessageProperties(); + this.bodyStream = stream; + } + + public RawMessage(MessageHeaders headers, MessageProperties properties, byte[] bytes, int index, int count, XmlDictionaryReaderQuotas quotas) + { + // this constructor supports internal needs for CreateBufferedCopy().CreateMessage() + this.headers = new MessageHeaders(headers); + this.properties = new MessageProperties(properties); + this.bodyBytes = bytes; + this.index = index; + this.count = count; + this.readerQuotas = quotas; + } + + public override MessageHeaders Headers + { + get + { + if (this.IsDisposed) + { + throw new ObjectDisposedException("message"); + } + + return this.headers; + } + } + + public override bool IsEmpty + { + get + { + if (this.IsDisposed) + { + throw new ObjectDisposedException("message"); + } + + return false; + } + } + + public override bool IsFault + { + get + { + if (this.IsDisposed) + { + throw new ObjectDisposedException("message"); + } + + return false; + } + } + + public override MessageProperties Properties + { + get + { + if (this.IsDisposed) + { + throw new ObjectDisposedException("message"); + } + + return this.properties; + } + } + + public override MessageVersion Version + { + get + { + if (this.IsDisposed) + { + throw new ObjectDisposedException("message"); + } + + return MessageVersion.None; + } + } + + protected override void OnBodyToString(XmlDictionaryWriter writer) + { + if (this.bodyStream != null) + { + writer.WriteString("Stream"); + } + else + { + writer.WriteStartElement(RawMessageEncoder.StreamElementName, string.Empty); + writer.WriteBase64(this.bodyBytes, this.index, this.count); + writer.WriteEndElement(); + } + } + + protected override void OnClose() + { + Exception deferEx = null; + try + { + base.OnClose(); + } + catch (Exception e) + { + deferEx = e; + } + + try + { + if (this.properties != null) + { + this.properties.Dispose(); + } + } + catch (Exception e) + { + if (deferEx == null) + { + deferEx = e; + } + } + + try + { + if (this.bufferManager != null) + { + this.bufferManager.ReturnBuffer(this.bodyBytes); + this.bufferManager = null; + } + } + catch (Exception e) + { + if (deferEx == null) + { + deferEx = e; + } + } + + if (deferEx != null) + { + throw deferEx; + } + } + + protected override MessageBuffer OnCreateBufferedCopy(int maxBufferSize) + { + if (this.bodyStream != null) + { + int len = (int)this.bodyStream.Length; + byte[] buf = new byte[len]; + this.bodyStream.Read(buf, 0, len); + this.bodyStream = null; + this.bodyBytes = buf; + this.count = len; + this.index = 0; + } + else + { + if (this.bufferManager != null) + { + // we could take steps to share the buffer among copies and release the memory + // after the last user finishes by a reference count or such, but we are already + // far from the intended optimized use. Make one GC managed memory copy that is + // shared by all. + byte[] buf = new byte[this.count]; + + Buffer.BlockCopy(this.bodyBytes, this.index, buf, 0, this.count); + this.bufferManager.ReturnBuffer(this.bodyBytes); + this.bufferManager = null; + this.bodyBytes = buf; + this.index = 0; + } + } + + return new RawMessageBuffer(this.headers, this.properties, this.bodyBytes, this.index, this.count, this.readerQuotas); + } + + protected override XmlDictionaryReader OnGetReaderAtBodyContents() + { + Stream readerStream = null; + bool ownsStream; + + if (this.bodyStream != null) + { + readerStream = this.bodyStream; + ownsStream = false; + } + else + { + // create stream for duration of XmlReader. + ownsStream = true; + if (this.bufferManager != null) + { + readerStream = new RawMemoryStream(this.bodyBytes, this.index, this.count, this.bufferManager); + this.bufferManager = null; + } + else + { + readerStream = new MemoryStream(this.bodyBytes, this.index, this.count, false); + } + } + + return new RawXmlReader(readerStream, this.readerQuotas, ownsStream); + } + + protected override void OnWriteBodyContents(XmlDictionaryWriter writer) + { + writer.WriteStartElement(RawMessageEncoder.StreamElementName, string.Empty); + if (this.bodyStream != null) + { + int len = (int)this.bodyStream.Length; + byte[] buf = new byte[len]; + this.bodyStream.Read(buf, 0, len); + writer.WriteBase64(buf, 0, len); + } + else + { + writer.WriteBase64(this.bodyBytes, this.index, this.count); + } + + writer.WriteEndElement(); + } + + private class RawMemoryStream : MemoryStream + { + private BufferManager bufferManager; + private byte[] buffer; + + public RawMemoryStream(byte[] bytes, int index, int count, BufferManager mgr) + : base(bytes, index, count, false) + { + this.bufferManager = mgr; + this.buffer = bytes; + } + + protected override void Dispose(bool disposing) + { + if (this.bufferManager != null) + { + try + { + this.bufferManager.ReturnBuffer(this.buffer); + } + finally + { + this.bufferManager = null; + base.Dispose(disposing); + } + } + } + } + + private class RawMessageBuffer : MessageBuffer + { + private bool closed; + private MessageHeaders headers; + private MessageProperties properties; + private byte[] bodyBytes; + private int index; + private int count; + private XmlDictionaryReaderQuotas readerQuotas; + + public RawMessageBuffer(MessageHeaders headers, MessageProperties properties, byte[] bytes, int index, int count, XmlDictionaryReaderQuotas quotas) + : base() + { + this.headers = new MessageHeaders(headers); + this.properties = new MessageProperties(properties); + this.bodyBytes = bytes; + this.index = index; + this.count = count; + this.readerQuotas = new XmlDictionaryReaderQuotas(); + quotas.CopyTo(this.readerQuotas); + } + + public override int BufferSize + { + get { return this.count; } + } + + public override void Close() + { + if (!this.closed) + { + this.closed = true; + this.headers = null; + if (this.properties != null) + { + this.properties.Dispose(); + this.properties = null; + } + + this.bodyBytes = null; + this.readerQuotas = null; + } + } + + public override Message CreateMessage() + { + if (this.closed) + { + throw new ObjectDisposedException("message"); + } + + return new RawMessage(this.headers, this.properties, this.bodyBytes, this.index, this.count, this.readerQuotas); + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncoder.cs b/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncoder.cs new file mode 100644 index 0000000000..76dae6f6c7 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncoder.cs @@ -0,0 +1,113 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.IO; + using System.ServiceModel.Channels; + using System.ServiceModel; + using System.Xml; + + + class RawMessageEncoder : MessageEncoder + { + public const string StreamElementName = "Binary"; + + XmlDictionaryReaderQuotas readerQuotas; + + public RawMessageEncoder(XmlDictionaryReaderQuotas quotas) + { + this.readerQuotas = new XmlDictionaryReaderQuotas(); + if (quotas != null) + { + quotas.CopyTo(this.readerQuotas); + } + } + + public override string ContentType + { + get { return null; } + } + + public override bool IsContentTypeSupported(string contentType) + { + return true; + } + + public override string MediaType + { + get { return null; } + } + + public override MessageVersion MessageVersion + { + get { return MessageVersion.None; } + } + + public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType) + { + RawMessage message = new RawMessage(buffer.Array, buffer.Offset, buffer.Count, bufferManager, readerQuotas); + message.Properties.Encoder = this; + return message; + } + + public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType) + { + RawMessage message = new RawMessage(stream, readerQuotas); + message.Properties.Encoder = this; + return message; + } + + private void CheckType(XmlDictionaryReader reader, XmlNodeType type) + { + if (reader.NodeType != type) + { + throw new System.IO.InvalidDataException(String.Format("RawMessageEncoder xml check {0} type should be {1}", type, reader.NodeType)); + } + } + + public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset) + { + MemoryStream tempStream = new MemoryStream(); + this.WriteMessage(message, tempStream); + int len = messageOffset + (int)tempStream.Length; + byte[] buf = bufferManager.TakeBuffer(len); + MemoryStream targetStream = new MemoryStream(buf); + if (messageOffset > 0) + { + targetStream.Seek(messageOffset, SeekOrigin.Begin); + } + + tempStream.WriteTo(targetStream); + targetStream.Close(); + + return new ArraySegment<byte>(buf, messageOffset, len - messageOffset); + } + + public override void WriteMessage(Message message, Stream stream) + { + using (XmlWriter writer = new RawXmlWriter(stream)) + { + message.WriteMessage(writer); + writer.Flush(); + } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncoderFactory.cs b/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncoderFactory.cs new file mode 100644 index 0000000000..5c015f9a1b --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncoderFactory.cs @@ -0,0 +1,45 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.Xml; + using System.ServiceModel.Channels; + + internal class RawMessageEncoderFactory : MessageEncoderFactory + { + RawMessageEncoder encoder; + + public RawMessageEncoderFactory(XmlDictionaryReaderQuotas quotas) + { + this.encoder = new RawMessageEncoder(quotas); + } + + public override MessageEncoder Encoder + { + get { return this.encoder; } + } + + public override MessageVersion MessageVersion + { + get { return encoder.MessageVersion; } + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncodingBindingElement.cs b/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncodingBindingElement.cs new file mode 100644 index 0000000000..5ec10a976d --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/RawMessageEncodingBindingElement.cs @@ -0,0 +1,102 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.ServiceModel.Channels; + + public class RawMessageEncodingBindingElement : MessageEncodingBindingElement + { + + public RawMessageEncodingBindingElement() + : base() + { + } + + RawMessageEncodingBindingElement(RawMessageEncodingBindingElement originalBindingElement) + { + } + + public override MessageEncoderFactory CreateMessageEncoderFactory() + { + return new RawMessageEncoderFactory(null); + } + + + public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + + context.BindingParameters.Add(this); + return context.BuildInnerChannelFactory<TChannel>(); + } + + public override bool CanBuildChannelFactory<TChannel>(BindingContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + + return context.CanBuildInnerChannelFactory<TChannel>(); + } + + public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + + context.BindingParameters.Add(this); + return context.BuildInnerChannelListener<TChannel>(); + } + + public override bool CanBuildChannelListener<TChannel>(BindingContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + + context.BindingParameters.Add(this); + return context.CanBuildInnerChannelListener<TChannel>(); + } + + + public override BindingElement Clone() + { + return new RawMessageEncodingBindingElement(this); + } + + + + public override MessageVersion MessageVersion + { + get + { + return MessageVersion.None; + } + + set + { + if (value != MessageVersion.None) + throw new ArgumentException("Unsupported message version"); + } + } + + + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/RawXmlReader.cs b/qpid/wcf/src/Apache/Qpid/Channel/RawXmlReader.cs new file mode 100644 index 0000000000..8fadfce441 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/RawXmlReader.cs @@ -0,0 +1,353 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.IO; + using System.Xml; + + internal class RawXmlReader : XmlDictionaryReader + { + ////this class presents a hardcoded XML InfoSet: "<rawtag>X</rawtag>" where X is the entire stream content + + private Stream stream; + private bool closed; + private bool streamOwner; + private ReaderPosition position; + private string contentAsBase64; + private XmlNameTable xmlNameTable; + private XmlDictionaryReaderQuotas readerQuotas; + + public RawXmlReader(Stream stream, XmlDictionaryReaderQuotas quotas, bool streamOwner) + { + this.stream = stream; + this.streamOwner = streamOwner; + if (quotas == null) + { + this.readerQuotas = new XmlDictionaryReaderQuotas(); + } + else + { + this.readerQuotas = quotas; + } + } + + private enum ReaderPosition + { + None, + StartElement, + Content, + EndElement, + EOF + } + + public override int AttributeCount + { + get { return 0; } + } + + public override string BaseURI + { + get { return string.Empty; } + } + + public override int Depth + { + get { return (this.position == ReaderPosition.Content) ? 1 : 0; } + } + + public override bool EOF + { + get { return this.position == ReaderPosition.EOF; } + } + + public override bool HasAttributes + { + get { return false; } + } + + public override bool HasValue + { + get { return this.position == ReaderPosition.Content; } + } + + public override bool IsEmptyElement + { + get { return false; } + } + + public override string LocalName + { + get + { + if (this.position == ReaderPosition.StartElement) + { + return RawMessageEncoder.StreamElementName; + } + + return null; + } + } + + public override string NamespaceURI + { + get { return string.Empty; } + } + + public override XmlNameTable NameTable + { + get + { + if (this.xmlNameTable == null) + { + this.xmlNameTable = new NameTable(); + this.xmlNameTable.Add(RawMessageEncoder.StreamElementName); + } + + return this.xmlNameTable; + } + } + + public override XmlNodeType NodeType + { + get + { + switch (this.position) + { + case ReaderPosition.StartElement: + return XmlNodeType.Element; + case ReaderPosition.Content: + return XmlNodeType.Text; + case ReaderPosition.EndElement: + return XmlNodeType.EndElement; + default: + // and StreamPosition.EOF + return XmlNodeType.None; + } + } + } + + public override string Prefix + { + get { return string.Empty; } + } + + public override ReadState ReadState + { + get + { + switch (this.position) + { + case ReaderPosition.None: + return ReadState.Initial; + case ReaderPosition.StartElement: + case ReaderPosition.Content: + case ReaderPosition.EndElement: + return ReadState.Interactive; + case ReaderPosition.EOF: + return ReadState.Closed; + default: + return ReadState.Error; + } + } + } + + public override string Value + { + get + { + switch (this.position) + { + case ReaderPosition.Content: + if (this.contentAsBase64 == null) + { + this.contentAsBase64 = Convert.ToBase64String(this.ReadContentAsBase64()); + } + + return this.contentAsBase64; + + default: + return string.Empty; + } + } + } + + public override void Close() + { + if (!this.closed) + { + this.closed = true; + this.position = ReaderPosition.EOF; + this.readerQuotas = null; + if (this.streamOwner) + { + this.stream.Close(); + } + } + } + + public override string GetAttribute(int i) + { + throw new ArgumentOutOfRangeException("i", i, "Argument not in set of valid values"); + } + + public override string GetAttribute(string name, string namespaceURI) + { + return null; + } + + public override string GetAttribute(string name) + { + return null; + } + + public override string LookupNamespace(string prefix) + { + if (prefix == string.Empty) + { + return string.Empty; + } + else if (prefix == "xml") + { + return "http://www.w3.org/XML/1998/namespace"; + } + else if (prefix == "xmlns") + { + return "http://www.w3.org/2000/xmlns/"; + } + else + { + return null; + } + } + + public override bool MoveToAttribute(string name, string ns) + { + return false; + } + + public override bool MoveToAttribute(string name) + { + return false; + } + + public override bool MoveToElement() + { + if (this.position == ReaderPosition.None) + { + this.position = ReaderPosition.StartElement; + return true; + } + + return false; + } + + public override bool MoveToFirstAttribute() + { + return false; + } + + public override bool MoveToNextAttribute() + { + return false; + } + + public override bool Read() + { + switch (this.position) + { + case ReaderPosition.None: + this.position = ReaderPosition.StartElement; + return true; + case ReaderPosition.StartElement: + this.position = ReaderPosition.Content; + return true; + case ReaderPosition.Content: + this.position = ReaderPosition.EndElement; + return true; + case ReaderPosition.EndElement: + this.position = ReaderPosition.EOF; + return false; + case ReaderPosition.EOF: + return false; + default: + return false; + } + } + + public override bool ReadAttributeValue() + { + return false; + } + + public override int ReadContentAsBase64(byte[] buffer, int index, int count) + { + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + if (this.position != ReaderPosition.Content) + { + throw new InvalidOperationException("XML reader not in Element"); + } + + if (count == 0) + { + return 0; + } + + int readCount = this.stream.Read(buffer, index, count); + if (readCount == 0) + { + this.position = ReaderPosition.EndElement; + } + + return readCount; + } + + public override int ReadContentAsBinHex(byte[] buffer, int index, int count) + { + throw new NotSupportedException(); + } + + public override void ResolveEntity() + { + throw new NotSupportedException(); + } + + public override bool TryGetBase64ContentLength(out int length) + { + // The whole stream is this one element + if (!this.closed && this.stream.CanSeek) + { + long streamLength = this.stream.Length; + if (streamLength <= int.MaxValue) + { + length = (int)streamLength; + return true; + } + } + + length = -1; + return false; + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Channel/RawXmlWriter.cs b/qpid/wcf/src/Apache/Qpid/Channel/RawXmlWriter.cs new file mode 100644 index 0000000000..7d05b70807 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Channel/RawXmlWriter.cs @@ -0,0 +1,221 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Channel +{ + using System; + using System.IO; + using System.Xml; + + internal sealed class RawXmlWriter : XmlDictionaryWriter + { + + WriteState state; + Stream stream; + bool closed; + bool rawWritingEnabled; + + public RawXmlWriter(Stream stream) + { + if (stream == null) + { + throw new ArgumentNullException("Stream"); + } + + this.stream = stream; + this.state = WriteState.Start; + } + + public override WriteState WriteState + { + get + { + return this.state; + } + } + + public override void Close() + { + if (!this.closed) + { + this.closed = true; + this.state = WriteState.Closed; + this.rawWritingEnabled = false; + } + } + + public override void Flush() + { + this.ThrowIfClosed(); + this.stream.Flush(); + } + + public override string LookupPrefix(string ns) + { + return null; + } + + public override void WriteBase64(byte[] buffer, int index, int count) + { + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + ThrowIfClosed(); + + if (!this.rawWritingEnabled) + { + throw new InvalidOperationException("XmlWriter not in Element"); + } + + this.stream.Write(buffer, index, count); + this.state = WriteState.Content; + } + + public override void WriteStartElement(string prefix, string localName, string ns) + { + ThrowIfClosed(); + if (this.state != WriteState.Start) + { + throw new InvalidOperationException("Start Element Already Called"); + } + + if (!string.IsNullOrEmpty(prefix) || !string.IsNullOrEmpty(ns) || localName != RawMessageEncoder.StreamElementName) + { + throw new XmlException("Wrong XML Start Element Name"); + } + this.state = WriteState.Element; + this.rawWritingEnabled = true; + } + + public override void WriteEndElement() + { + ThrowIfClosed(); + if (!this.rawWritingEnabled) + { + throw new InvalidOperationException("Unexpected End Element"); + } + this.rawWritingEnabled = false; + } + + public override void WriteFullEndElement() + { + this.WriteEndElement(); + } + + public override void WriteEndDocument() + { + this.rawWritingEnabled = false; + this.ThrowIfClosed(); + } + + public override void WriteStartDocument() + { + this.rawWritingEnabled = false; + this.ThrowIfClosed(); + } + + public override void WriteStartDocument(bool standalone) + { + this.rawWritingEnabled = false; + this.ThrowIfClosed(); + } + + private void ThrowIfClosed() + { + if (this.closed) + { + throw new InvalidOperationException("XML Writer closed"); + } + } + + + public override void WriteString(string text) + { + throw new NotSupportedException(); + } + + public override void WriteCData(string text) + { + throw new NotSupportedException(); + } + + public override void WriteCharEntity(char ch) + { + throw new NotSupportedException(); + } + + public override void WriteChars(char[] buffer, int index, int count) + { + throw new NotSupportedException(); + } + + public override void WriteComment(string text) + { + throw new NotSupportedException(); + } + + public override void WriteDocType(string name, string pubid, string sysid, string subset) + { + throw new NotSupportedException(); + } + + public override void WriteEndAttribute() + { + throw new NotSupportedException(); + } + + public override void WriteEntityRef(string name) + { + throw new NotSupportedException(); + } + + + public override void WriteProcessingInstruction(string name, string text) + { + throw new NotSupportedException(); + } + + public override void WriteRaw(string data) + { + throw new NotSupportedException(); + } + + public override void WriteRaw(char[] buffer, int index, int count) + { + throw new NotSupportedException(); + } + + public override void WriteStartAttribute(string prefix, string localName, string ns) + { + throw new NotSupportedException(); + } + + public override void WriteSurrogateCharEntity(char lowChar, char highChar) + { + throw new NotSupportedException(); + } + + public override void WriteWhitespace(string ws) + { + throw new NotSupportedException(); + } + } +} diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AmqpConnection.cpp b/qpid/wcf/src/Apache/Qpid/Interop/AmqpConnection.cpp new file mode 100644 index 0000000000..02d6c7ab18 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/AmqpConnection.cpp @@ -0,0 +1,165 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#include <windows.h> +#include <msclr\lock.h> + +#include "qpid/client/AsyncSession.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/MessageListener.h" +#include "qpid/framing/FrameSet.h" + +#include "AmqpConnection.h" +#include "AmqpSession.h" +#include "QpidMarshal.h" +#include "QpidException.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace msclr; + +using namespace qpid::client; +using namespace std; + + +// Note on locks: Use "this" for fast counting and idle/busy +// notifications. Use the "sessions" list to serialize session +// creation/reaping and overall tear down. +// TODO: switch "this" lock to separate non-visible Object. + + +AmqpConnection::AmqpConnection(String^ server, int port) : + connectionp(NULL), + busyCount(0), + disposed(false) +{ + bool success = false; + System::Exception^ openException = nullptr; + sessions = gcnew Collections::Generic::List<AmqpSession^>(); + + try { + connectionp = new Connection; + connectionp->open (QpidMarshal::ToNative(server), port); + // TODO: registerFailureCallback for failover + success = true; + const ConnectionSettings& settings = connectionp->getNegotiatedSettings(); + this->maxFrameSize = settings.maxFrameSize; + } catch (const qpid::Exception& error) { + String^ errmsg = gcnew String(error.what()); + openException = gcnew QpidException(errmsg); + } finally { + if (!success) { + Cleanup(); + if (openException == nullptr) { + openException = gcnew QpidException ("unknown connection failure"); + } + throw openException; + } + } +} + +void AmqpConnection::Cleanup() +{ + { + lock l(sessions); + if (disposed) + return; + disposed = true; + } + + try { + // let the child sessions clean up + for each(AmqpSession^ s in sessions) { + s->ConnectionClosed(); + } + } + finally + { + if (connectionp != NULL) { + connectionp->close(); + delete connectionp; + connectionp = NULL; + } + } +} + +AmqpConnection::~AmqpConnection() +{ + Cleanup(); +} + +AmqpConnection::!AmqpConnection() +{ + Cleanup(); +} + +void AmqpConnection::Close() +{ + // Simulate Dispose()... + Cleanup(); + GC::SuppressFinalize(this); +} + +AmqpSession^ AmqpConnection::CreateSession() +{ + lock l(sessions); + if (disposed) { + throw gcnew ObjectDisposedException("AmqpConnection"); + } + AmqpSession^ session = gcnew AmqpSession(this, connectionp); + sessions->Add(session); + return session; +} + +// called whenever a child session becomes newly busy (a first reader or writer since last idle) + +void AmqpConnection::NotifyBusy() +{ + bool changed = false; + { + lock l(this); + if (busyCount++ == 0) + changed = true; + } +} + +// called whenever a child session becomes newly idle (a last reader or writer has closed) +// The connection is idle when none of its child sessions are busy + +void AmqpConnection::NotifyIdle() +{ + bool connectionIdle = false; + { + lock l(this); + if (--busyCount == 0) + connectionIdle = true; + } + if (connectionIdle) { + OnConnectionIdle(this, System::EventArgs::Empty); + } +} + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AmqpConnection.h b/qpid/wcf/src/Apache/Qpid/Interop/AmqpConnection.h new file mode 100644 index 0000000000..2641391e82 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/AmqpConnection.h @@ -0,0 +1,71 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace std; +using namespace qpid::client; + +ref class AmqpSession; + +public delegate void ConnectionIdleEventHandler(Object^ sender, EventArgs^ eventArgs); + +public ref class AmqpConnection +{ +private: + Connection* connectionp; + void Cleanup(); + bool disposed; + Collections::Generic::List<AmqpSession^>^ sessions; + bool isOpen; + int busyCount; + int maxFrameSize; + + internal: + void NotifyBusy(); + void NotifyIdle(); + + property int MaxFrameSize { + int get () { return maxFrameSize; } + } + +public: + AmqpConnection(System::String^ server, int port); + ~AmqpConnection(); + !AmqpConnection(); + void Close(); + AmqpSession^ CreateSession(); + event ConnectionIdleEventHandler^ OnConnectionIdle; + + property bool IsOpen { + bool get() { return isOpen; } + }; + + property bool IsIdle { + bool get() { return (busyCount == 0); } + } +}; + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.cpp b/qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.cpp new file mode 100644 index 0000000000..5c333aff60 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.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 <windows.h> +#include <msclr\lock.h> + +#include "qpid/client/AsyncSession.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/AMQFrame.h" + +#include "MessageBodyStream.h" +#include "AmqpMessage.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Threading; +using namespace msclr; + +using namespace Apache::Qpid::AmqpTypes; + +AmqpMessage::AmqpMessage(MessageBodyStream ^mbs) : + messageBodyStream(mbs), + disposed(false) +{ +} + +void AmqpMessage::Cleanup() +{ + { + lock l(this); + if (disposed) + return; + + disposed = true; + } + + messageBodyStream->Close(); +} + +AmqpMessage::~AmqpMessage() +{ + Cleanup(); +} + +AmqpMessage::!AmqpMessage() +{ + Cleanup(); +} + +void AmqpMessage::Close() +{ + // Simulate Dispose()... + Cleanup(); + GC::SuppressFinalize(this); +} + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.h b/qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.h new file mode 100644 index 0000000000..f0801d30dc --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/AmqpMessage.h @@ -0,0 +1,61 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; + +using namespace qpid::client; +using namespace std; + + + +public ref class AmqpMessage +{ +private: + MessageBodyStream^ messageBodyStream; + AmqpTypes::AmqpProperties^ amqpProperties; + bool disposed; + void Cleanup(); + +internal: + AmqpMessage(MessageBodyStream ^bstream); + +public: + ~AmqpMessage(); + !AmqpMessage(); + void Close(); + + property AmqpTypes::AmqpProperties^ Properties { + AmqpTypes::AmqpProperties^ get () { return amqpProperties; } + void set(AmqpTypes::AmqpProperties^ p) { amqpProperties = p; } + } + + property System::IO::Stream^ BodyStream { + System::IO::Stream^ get() { return messageBodyStream; } + } +}; + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.cpp b/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.cpp new file mode 100644 index 0000000000..bab73da74e --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.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 <windows.h> +#include <msclr\lock.h> + +#include "qpid/client/AsyncSession.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Connection.h" +#include "qpid/client/SessionImpl.h" +#include "qpid/client/SessionBase_0_10Access.h" +#include "qpid/client/Message.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/client/Future.h" + +#include "AmqpConnection.h" +#include "AmqpSession.h" +#include "AmqpMessage.h" +#include "MessageBodyStream.h" +#include "InputLink.h" +#include "OutputLink.h" +#include "QpidMarshal.h" +#include "QpidException.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace msclr; + +using namespace qpid::client; +using namespace std; + + +AmqpSession::AmqpSession(AmqpConnection^ conn, qpid::client::Connection* qpidConnectionp) : + connection(conn), + sessionp(NULL), + sessionImplp(NULL), + subs_mgrp(NULL), + openCount(0) +{ + bool success = false; + + try { + sessionp = new qpid::client::AsyncSession; + *sessionp = qpidConnectionp->newSession(); + subs_mgrp = new SubscriptionManager (*sessionp); + success = true; + waiters = gcnew Collections::Generic::List<CompletionWaiter^>(); + } finally { + if (!success) { + Cleanup(); + throw gcnew QpidException ("session creation failure"); + } + } +} + + +void AmqpSession::Cleanup() +{ + if (subscriptionp != NULL) { + subscriptionp->cancel(); + delete subscriptionp; + subscriptionp=NULL; + } + + if (subs_mgrp != NULL) { + subs_mgrp->stop(); + delete subs_mgrp; + subs_mgrp = NULL; + } + + if (localQueuep != NULL) { + delete localQueuep; + localQueuep = NULL; + } + + if (sessionp != NULL) { + sessionp->close(); + delete sessionp; + sessionp = NULL; + sessionImplp = NULL; + } + + if (connectionp != NULL) { + connectionp->close(); + delete connectionp; + connectionp = NULL; + } +} + + +// Called by the parent AmqpConnection + +void AmqpSession::ConnectionClosed() +{ + Cleanup(); +} + +InputLink^ AmqpSession::CreateInputLink(System::String^ sourceQueue) +{ + return CreateInputLink(sourceQueue, true, false, nullptr, nullptr); +} + +InputLink^ AmqpSession::CreateInputLink(System::String^ sourceQueue, bool exclusive, bool temporary, + System::String^ filterKey, System::String^ exchange) +{ + InputLink^ link = gcnew InputLink (this, sourceQueue, sessionp, subs_mgrp, exclusive, temporary, filterKey, exchange); + { + lock l(waiters); + if (openCount == 0) { + connection->NotifyBusy(); + } + openCount++; + } + return link; +} + +OutputLink^ AmqpSession::CreateOutputLink(System::String^ targetQueue) +{ + OutputLink^ link = gcnew OutputLink (this, targetQueue); + + lock l(waiters); + + if (sessionImplp == NULL) { + // not needed unless sending messages + SessionBase_0_10Access sa(*sessionp); + boost::shared_ptr<SessionImpl> sip = sa.get(); + sessionImplp = sip.get(); + } + + if (openCount == 0) { + connection->NotifyBusy(); + } + openCount++; + + return link; +} + + +// called whenever a child InputLink or OutputLink is closed or finalized +void AmqpSession::NotifyClosed() +{ + lock l(waiters); + openCount--; + if (openCount == 0) { + connection->NotifyIdle(); + } +} + + +CompletionWaiter^ AmqpSession::SendMessage (System::String^ queue, MessageBodyStream ^mbody, TimeSpan timeout, bool async, AsyncCallback^ callback, Object^ state) +{ + lock l(waiters); + if (sessionp == NULL) + throw gcnew ObjectDisposedException("Send"); + + // create an AMQP message.transfer command to use with the partial frameset from the MessageBodyStream + + std::string exname = QpidMarshal::ToNative(queue); + FrameSet *framesetp = (FrameSet *) mbody->GetFrameSet().ToPointer(); + uint8_t acceptMode=1; + uint8_t acquireMode=0; + MessageTransferBody mtcmd(ProtocolVersion(0,10), exname, acceptMode, acquireMode); + // ask for a command completion + mtcmd.setSync(true); + + //send it + + Future *futurep = NULL; + try { + futurep = new Future(sessionImplp->send(mtcmd, *framesetp)); + + CompletionWaiter^ waiter = nullptr; + if (async || (timeout != TimeSpan::MaxValue)) { + waiter = gcnew CompletionWaiter(this, timeout, (IntPtr) futurep, callback, state); + // waiter is responsible for releasing the Future native resource + futurep = NULL; + addWaiter(waiter); + } + + l.release(); + + if (waiter != nullptr) + return waiter; + + // synchronous send with no timeout: no need to involve the asyncHelper thread + + internalWaitForCompletion((IntPtr) futurep); + } + finally { + if (futurep != NULL) + delete (futurep); + } + return nullptr; +} + +void AmqpSession::Bind(System::String^ queue, System::String^ exchange, System::String^ filterKey) +{ + sessionp->exchangeBind(arg::queue=QpidMarshal::ToNative(queue), + arg::exchange=QpidMarshal::ToNative(exchange), + arg::bindingKey=QpidMarshal::ToNative(filterKey)); + +} + + +void AmqpSession::internalWaitForCompletion(IntPtr fp) +{ + lock l(waiters); + if (sessionp == NULL) + throw gcnew ObjectDisposedException("AmqpSession"); + + // increment the smart pointer count to sessionImplp to guard agains async close + Session sessionCopy(*sessionp); + + l.release(); + // Qpid native lib call to wait for the command completion + ((Future *)fp.ToPointer())->wait(*sessionImplp); +} + +// call with lock held +void AmqpSession::addWaiter(CompletionWaiter^ waiter) +{ + waiters->Add(waiter); + if (!helperRunning) { + helperRunning = true; + ThreadPool::QueueUserWorkItem(gcnew WaitCallback(this, &AmqpSession::asyncHelper)); + } +} + + +void AmqpSession::removeWaiter(CompletionWaiter^ waiter) +{ + // a waiter can be removed from anywhere in the list if timed out + + lock l(waiters); + int idx = waiters->IndexOf(waiter); + if (idx == -1) { + // TODO: assert or log + } + else { + waiters->RemoveAt(idx); + } +} + + +// process CompletionWaiter list one at a time. + +void AmqpSession::asyncHelper(Object ^unused) +{ + lock l(waiters); + + while (true) { + if (waiters->Count == 0) { + helperRunning = false; + return; + } + + CompletionWaiter^ waiter = waiters[0]; + l.release(); + // can block, but for short time + // the waiter removes itself from the list, possibly as the timer thread on timeout + waiter->Run(); + l.acquire(); + } +} + + +}}} // namespace Apache::Qpid::Cli diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.h b/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.h new file mode 100644 index 0000000000..b959a4123a --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/AmqpSession.h @@ -0,0 +1,80 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +#include "AmqpConnection.h" +#include "MessageBodyStream.h" +#include "CompletionWaiter.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; + +using namespace qpid::client; +using namespace std; + +ref class InputLink; +ref class OutputLink; + +public ref class AmqpSession +{ +private: + AmqpConnection^ connection; + Connection* connectionp; + AsyncSession* sessionp; + SessionImpl* sessionImplp; + SubscriptionManager* subs_mgrp; + Subscription* subscriptionp; + LocalQueue* localQueuep; + Collections::Generic::List<CompletionWaiter^>^ waiters; + bool helperRunning; + int openCount; + + void Cleanup(); + void asyncHelper(Object ^); + void addWaiter(CompletionWaiter^ waiter); + +public: + OutputLink^ CreateOutputLink(System::String^ targetQueue); + InputLink^ CreateInputLink(System::String^ sourceQueue); + + // 0-10 specific support + InputLink^ CreateInputLink(System::String^ sourceQueue, bool exclusive, bool temporary, System::String^ filterKey, System::String^ exchange); + void Bind(System::String^ queue, System::String^ exchange, System::String^ filterKey); + +internal: + AmqpSession(AmqpConnection^ connection, qpid::client::Connection* qpidConnection); + void NotifyClosed(); + CompletionWaiter^ SendMessage (System::String^ queue, MessageBodyStream ^mbody, TimeSpan timeout, bool async, AsyncCallback^ callback, Object^ state); + void ConnectionClosed(); + void internalWaitForCompletion(IntPtr Future); + void removeWaiter(CompletionWaiter^ waiter); + + property AmqpConnection^ Connection { + AmqpConnection^ get () { return connection; } + } + + +}; + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/AssemblyInfo.cpp b/qpid/wcf/src/Apache/Qpid/Interop/AssemblyInfo.cpp new file mode 100644 index 0000000000..91c23ae30a --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/AssemblyInfo.cpp @@ -0,0 +1,57 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using namespace System; +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; +using namespace System::Security::Permissions; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly:AssemblyTitleAttribute("Apache.Qpid.Interop")]; +[assembly:AssemblyDescriptionAttribute("")]; +[assembly:AssemblyConfigurationAttribute("")]; +[assembly:AssemblyCompanyAttribute("")]; +[assembly:AssemblyProductAttribute("")]; +[assembly:AssemblyCopyrightAttribute("")]; +[assembly:AssemblyTrademarkAttribute("")]; +[assembly:AssemblyCultureAttribute("")]; + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the value or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly:AssemblyVersionAttribute("1.0.*")]; + +[assembly:ComVisible(false)]; + +[assembly:CLSCompliantAttribute(true)]; + +[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)]; diff --git a/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.cpp b/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.cpp new file mode 100644 index 0000000000..e39ee1b1ae --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.cpp @@ -0,0 +1,145 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#include <windows.h> +#include <msclr\lock.h> + +#include "qpid/client/AsyncSession.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/MessageListener.h" +#include "qpid/client/Demux.h" +#include "qpid/client/SessionImpl.h" +#include "qpid/client/SessionBase_0_10Access.h" + +#include "MessageBodyStream.h" +#include "AmqpMessage.h" +#include "AmqpSession.h" +#include "InputLink.h" +#include "CompletionWaiter.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Threading; +using namespace msclr; + +// A class to provide IAsyncResult semantics for a qpid AsyncSession command (i.e. 0-10 messageTransfer) +// when the client session receives a "Completion" notification from the Broker. + + +CompletionWaiter::CompletionWaiter(AmqpSession^ parent, TimeSpan timeSpan, IntPtr future, AsyncCallback^ callback, Object^ state) +{ + this->qpidFuture = future; + this->asyncCallback = callback; + this->state = state; + this->parent = parent; + this->thisLock = gcnew Object(); + // do this after the Completion Waiter is fully initialized, in case of + // very small timespan + if (timeSpan != TimeSpan::MaxValue) { + this->timer = gcnew Timer(timeoutCallback, this, timeSpan, TimeSpan::FromMilliseconds(-1)); + } +} + + +void CompletionWaiter::WaitForCompletion() +{ + if (isCompleted) + return; + + lock l(thisLock); + while (!isCompleted) { + Monitor::Wait(thisLock); + } +} + +void CompletionWaiter::Run() +{ + // no locks required in this method + if (isCompleted) + return; + + try { + // Wait for the arrival of the "AMQP Completion" indication from the Broker + parent->internalWaitForCompletion(qpidFuture); + } + catch (System::Exception^ e) { + runException = e; + } + finally { + delete(qpidFuture.ToPointer()); + qpidFuture = (IntPtr) NULL; + } + + if (timer != nullptr) { + timer->~Timer(); + timer = nullptr; + } + + Complete(false); +} + + +// "Complete" here means complete the AsyncResult, which may precede broker "command completion" if timed out + +void CompletionWaiter::Complete(bool isTimerThread) +{ + lock l(thisLock); + if (isCompleted) + return; + + isCompleted = true; + if (isTimerThread) + timedOut = true; + + Monitor::PulseAll(thisLock); + + // do this check and signal while locked + if (asyncWaitHandle != nullptr) + asyncWaitHandle->Set(); + + l.release(); + + parent->removeWaiter(this); + + if (asyncCallback != nullptr) { + // guard against application callback exception + try { + asyncCallback(this); + } + catch (System::Exception^) { + // log it? + } + } +} + + +void CompletionWaiter::TimeoutCallback(Object^ state) +{ + CompletionWaiter^ waiter = (CompletionWaiter^) state; + waiter->Complete(true); +} + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.h b/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.h new file mode 100644 index 0000000000..197ac632b0 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/CompletionWaiter.h @@ -0,0 +1,99 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Threading; + +public ref class CompletionWaiter : IAsyncResult +{ +private: + bool timedOut; + // has an owner thread + bool assigned; + // can Run (i.e. earlier CompletionWaiters in the queue have completed) + System::Exception^ runException; + AsyncCallback^ asyncCallback; + Threading::Timer ^timer; + bool isCompleted; + Object^ state; + Object^ thisLock; + ManualResetEvent^ asyncWaitHandle; + AmqpSession^ parent; + IntPtr qpidFuture; + void Complete(bool isTimerThread); + static void TimeoutCallback(Object^ state); + static TimerCallback^ timeoutCallback = gcnew TimerCallback(CompletionWaiter::TimeoutCallback); + + internal: + CompletionWaiter(AmqpSession^ parent, TimeSpan timeSpan, IntPtr future, AsyncCallback ^callback, Object^ state); + + void Run(); + void WaitForCompletion(); + + property bool Assigned { + bool get () { return assigned; } + } + + property bool TimedOut { + bool get () { return timedOut; } + } + + + public: + + virtual property bool IsCompleted { + bool get () { return isCompleted; } + } + + virtual property bool CompletedSynchronously { + bool get () { return false; } + } + + virtual property WaitHandle^ AsyncWaitHandle { + WaitHandle^ get () { + if (asyncWaitHandle != nullptr) { + return asyncWaitHandle; + } + + msclr::lock l(thisLock); + if (asyncWaitHandle == nullptr) { + asyncWaitHandle = gcnew ManualResetEvent(isCompleted); + } + return asyncWaitHandle; + } + } + + + virtual property Object^ AsyncState { + Object^ get () { return state; } + } + + + + +}; + +}}} // namespace Apache::Qpid::Interop + diff --git a/qpid/wcf/src/Apache/Qpid/Interop/InputLink.cpp b/qpid/wcf/src/Apache/Qpid/Interop/InputLink.cpp new file mode 100644 index 0000000000..cee394b05d --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/InputLink.cpp @@ -0,0 +1,685 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#include <windows.h> +#include <msclr\lock.h> + +#include "qpid/client/AsyncSession.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/MessageListener.h" +#include "qpid/client/Demux.h" +#include "qpid/client/SessionImpl.h" +#include "qpid/client/SessionBase_0_10Access.h" + +#include "MessageBodyStream.h" +#include "AmqpMessage.h" +#include "AmqpSession.h" +#include "InputLink.h" +#include "QpidMarshal.h" +#include "QpidException.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace System::Threading; +using namespace msclr; + +using namespace qpid::client; +using namespace qpid::framing; + +using namespace std; + +using namespace Apache::Qpid::AmqpTypes; + +// Scalability note: When using async methods, an async helper thread is created +// to block on the Demux BlockingQueue. This design should be revised in line +// with proposed changes to the native library to reduce the number of servicing +// threads for large numbers of subscriptions. + + +// The folowing def must match the "Frames" private typedef. +// TODO, make Qpid-cpp "Frames" definition visible. +typedef qpid::InlineVector<AMQFrame, 4> FrameSetFrames; + +InputLink::InputLink(AmqpSession^ session, System::String^ sourceQueue, + qpid::client::AsyncSession *qpidSessionp, qpid::client::SubscriptionManager *qpidSubsMgrp, + bool exclusive, + bool temporary, System::String^ filterKey, System::String^ exchange) : + amqpSession(session), + subscriptionp(NULL), + localQueuep(NULL), + queuePtrp(NULL), + dequeuedFrameSetpp(NULL), + disposed(false), + finalizing(false) +{ + bool success = false; + System::Exception^ linkException = nullptr; + + waiters = gcnew Collections::Generic::List<MessageWaiter^>(); + + try { + std::string qname = QpidMarshal::ToNative(sourceQueue); + + if (temporary) { + qpidSessionp->queueDeclare(arg::queue=qname, arg::durable=false, arg::autoDelete=true, arg::exclusive=true); + qpidSessionp->exchangeBind(arg::exchange=QpidMarshal::ToNative(exchange), + arg::queue=qname, arg::bindingKey=QpidMarshal::ToNative(filterKey)); + qpidSessionp->sync(); + } + + localQueuep = new LocalQueue; + SubscriptionSettings settings; + settings.flowControl = FlowControl::messageCredit(0); + Subscription sub = qpidSubsMgrp->subscribe(*localQueuep, qname, settings); + subscriptionp = new Subscription (sub); // copy smart pointer for later IDisposable cleanup + + // the roundabout way to obtain localQueuep->queue + SessionBase_0_10Access sa(*qpidSessionp); + boost::shared_ptr<SessionImpl> simpl = sa.get(); + queuePtrp = new Demux::QueuePtr(simpl->getDemux().get(sub.getName())); + + success = true; + } finally { + if (!success) { + Cleanup(); + linkException = gcnew QpidException ("InputLink creation failure"); + throw linkException; + } + } +} + +void InputLink::ReleaseNative() +{ + // involves talking to the Broker unless the connection is broken + if (subscriptionp != NULL) { + try { + subscriptionp->cancel(); + } + catch (const std::exception& error) { + // TODO: log this properly + std::cout << "shutdown error " << error.what() << std::endl; + } + } + + // free native mem (or smart pointers) that we own + if (subscriptionp != NULL) + delete subscriptionp; + if (queuePtrp != NULL) + delete queuePtrp; + if (localQueuep != NULL) + delete localQueuep; + if (dequeuedFrameSetpp != NULL) + delete dequeuedFrameSetpp; +} + +void InputLink::Cleanup() +{ + { + lock l(waiters); + if (disposed) + return; + + disposed = true; + + // if the asyncHelper exists and is idle, unblock it + if (asyncHelperWaitHandle != nullptr) { + asyncHelperWaitHandle->Set(); + } + + // wakeup anyone waiting for messages + if (queuePtrp != NULL) + (*queuePtrp)->close(); + + try {} + finally + { + ReleaseNative(); + } + + } + amqpSession->NotifyClosed(); +} + +InputLink::~InputLink() +{ + Cleanup(); +} + +InputLink::!InputLink() +{ + Cleanup(); +} + +void InputLink::Close() +{ + // Simulate Dispose()... + Cleanup(); + GC::SuppressFinalize(this); +} + +// call with lock held +bool InputLink::haveMessage() +{ + if (dequeuedFrameSetpp != NULL) + return true; + + if (queuePtrp != NULL) { + if ((*queuePtrp)->size() > 0) + return true; + } + return false; +} + +IntPtr InputLink::nextLocalMessage() +{ + lock l(waiters); + if (disposed) + return (IntPtr) NULL; + + // A message already pulled off BlockingQueue? + if (dequeuedFrameSetpp != NULL) { + QpidFrameSetPtr* rv = dequeuedFrameSetpp; + dequeuedFrameSetpp = NULL; + return (IntPtr) rv; + } + + if ((*queuePtrp)->empty()) + return (IntPtr) NULL; + + bool received = false; + QpidFrameSetPtr* frameSetpp = new QpidFrameSetPtr; + + try { + received = (*queuePtrp)->pop(*frameSetpp, qpid::sys::TIME_INFINITE); + if (received) { + QpidFrameSetPtr* rv = frameSetpp; + // no need to free native in finally block + frameSetpp = NULL; + return (IntPtr) rv; + } + } catch(const std::exception& error) { + // should be no async tampering with queue since we hold the lock and have a + // smart pointer ref to the native LocalQueue, even if the network connection fails... + cout << "unknown exception in InputLink.nextLocalMessage() " << error.what() <<endl; + // TODO: log this + } + finally { + if (frameSetpp != NULL) { + delete frameSetpp; + } + } + + return (IntPtr) NULL; +} + + + +void InputLink::unblockWaiter() +{ + // to be followed by resetQueue() below + lock l(waiters); + if (disposed) + return; + (*queuePtrp)->close(); +} + + + +// Set things right after unblockWaiter(). Closing and opening a Qpid BlockingQueue unsticks +// a blocking thread without interefering with queue contents or the ability to push +// new incoming messages. + +void InputLink::resetQueue() +{ + lock l(waiters); + if (disposed) + return; + if ((*queuePtrp)->isClosed()) { + (*queuePtrp)->open(); + } +} + + +// returns true if there is a message to consume, i.e. nextLocalMessage() won't block + +bool InputLink::internalWaitForMessage() +{ + Demux::QueuePtr demuxQueuePtr; + + bool received = false; + QpidFrameSetPtr* frameSetpp = NULL; + try { + lock l(waiters); + if (disposed) + return false; + if (haveMessage()) + return true; + + // TODO: prefetch window of messages, compatible with both 0-10 and 1.0. + subscriptionp->grantMessageCredit(1); + + // get a scoped smart ptr ref to guard against async close or hangup + demuxQueuePtr = *queuePtrp; + frameSetpp = new QpidFrameSetPtr; + + l.release(); + // Async cleanup is now possible. Only use demuxQueuePtr until lock reacquired. + received = demuxQueuePtr->pop(*frameSetpp, qpid::sys::TIME_INFINITE); + l.acquire(); + + if (received) { + dequeuedFrameSetpp = frameSetpp; + frameSetpp = NULL; // native will eventually be freed in Cleanup or MessageBodyStream + } + + return true; + } catch(const std::exception& ) { + // timeout or connection closed + return false; + } + finally { + if (frameSetpp != NULL) { + delete frameSetpp; + } + } + + return false; +} + + +// call with lock held +void InputLink::addWaiter(MessageWaiter^ waiter) +{ + waiters->Add(waiter); + if (waiters->Count == 1) { + // mark this waiter as ready to run + // Only the waiter at the head of the queue is active. + waiter->Activate(); + } + + if (waiter->Assigned) + return; + + if (asyncHelperWaitHandle == nullptr) { + asyncHelperWaitHandle = gcnew ManualResetEvent(false); + ThreadStart^ threadDelegate = gcnew ThreadStart(this, &InputLink::asyncHelper); + (gcnew Thread(threadDelegate))->Start(); + } + + if (waiters->Count == 1) { + // wake up the asyncHelper + asyncHelperWaitHandle->Set(); + } +} + + +void InputLink::removeWaiter(MessageWaiter^ waiter) { + // a waiter can be removed from anywhere in the list if timed out + + lock l(waiters); + int idx = waiters->IndexOf(waiter); + if (idx == -1) { + // TODO: assert or log + if (asyncHelperWaitHandle != nullptr) { + // just in case. + asyncHelperWaitHandle->Set(); + } + return; + } + waiters->RemoveAt(idx); + + // let the next waiter know it's his turn. + if (waiters->Count > 0) { + MessageWaiter^ nextWaiter = waiters[0]; + + // wakeup the asyncHelper thread to help out if necessary. + if (!nextWaiter->Assigned) { + asyncHelperWaitHandle->Set(); + } + + l.release(); + nextWaiter->Activate(); + return; + } + else { + if (disposed && (asyncHelperWaitHandle != nullptr)) { + asyncHelperWaitHandle->Set(); + } + } +} + + +void InputLink::asyncHelper() +{ + lock l(waiters); + + while (true) { + if (disposed && (waiters->Count == 0)) { + asyncHelperWaitHandle = nullptr; + return; + } + + if (waiters->Count > 0) { + MessageWaiter^ waiter = waiters[0]; + + l.release(); + if (waiter->AcceptForWork()) { + waiter->Run(); + } + l.acquire(); + } + + // sleep if more work may be coming or it is currently someone else's turn + if (((waiters->Count == 0) && !disposed) || ((waiters->Count != 0) && waiters[0]->Assigned)) { + // wait for something to do + asyncHelperWaitHandle->Reset(); + l.release(); + asyncHelperWaitHandle->WaitOne(); + l.acquire(); + } + } +} + +void InputLink::sync() +{ + // for the timeout thread + lock l(waiters); +} + + +AmqpMessage^ InputLink::createAmqpMessage(IntPtr msgp) +{ + QpidFrameSetPtr* fspp = (QpidFrameSetPtr*) msgp.ToPointer(); + bool ownFrameSet = true; + bool haveProperties = false; + + try { + MessageBodyStream^ mstream = gcnew MessageBodyStream(fspp); + ownFrameSet = false; // stream releases on close/dispose + + AmqpMessage^ amqpMessage = gcnew AmqpMessage(mstream); + + AMQHeaderBody* headerBodyp = (*fspp)->getHeaders(); + uint64_t contentSize = (*fspp)->getContentSize(); + SequenceSet frameSetID((*fspp)->getId()); + + // target managed representation + AmqpProperties^ amqpProperties = gcnew AmqpProperties(); + + // source native representation + const DeliveryProperties* deliveryProperties = headerBodyp->get<DeliveryProperties>(); + const qpid::framing::MessageProperties* messageProperties = headerBodyp->get<qpid::framing::MessageProperties>(); + + if (deliveryProperties) { + if (deliveryProperties->hasRoutingKey()) { + haveProperties = true; + + amqpProperties->RoutingKey = gcnew String(deliveryProperties->getRoutingKey().c_str()); + } + + if (deliveryProperties->hasDeliveryMode()) { + if (deliveryProperties->getDeliveryMode() == qpid::framing::PERSISTENT) + amqpProperties->Durable = true; + } + + if (deliveryProperties->hasTtl()) { + long long ticks = deliveryProperties->getTtl() * TimeSpan::TicksPerMillisecond; + amqpProperties->TimeToLive = Nullable<TimeSpan>(TimeSpan::FromTicks(ticks)); + } + } + + if (messageProperties) { + + if (messageProperties->hasReplyTo()) { + haveProperties = true; + const ReplyTo& rpto = messageProperties->getReplyTo(); + String^ rk = nullptr; + String^ ex = nullptr; + if (rpto.hasRoutingKey()) { + rk = gcnew String(rpto.getRoutingKey().c_str()); + } + if (rpto.hasExchange()) { + ex = gcnew String(rpto.getExchange().c_str()); + } + amqpProperties->SetReplyTo(ex,rk); + } + + if (messageProperties->hasContentType()) { + haveProperties = true; + amqpProperties->ContentType = gcnew String(messageProperties->getContentType().c_str()); + + if (messageProperties->hasContentEncoding()) { + String^ enc = gcnew String(messageProperties->getContentEncoding().c_str()); + if (!String::IsNullOrEmpty(enc)) { + // TODO: properly assemble 1.0 style to 0-10 for all cases + amqpProperties->ContentType += "; charset=" + enc; + } + } + } + + if (messageProperties->hasCorrelationId()) { + haveProperties = true; + const std::string& ncid = messageProperties->getCorrelationId(); + int len = ncid.size(); + array<unsigned char>^ mcid = gcnew array<unsigned char>(len); + Marshal::Copy ((IntPtr) (void *) ncid.data(), mcid, 0, len); + amqpProperties->CorrelationId = mcid; + } + + if (messageProperties->hasUserId()) { + haveProperties = true; + const std::string& nuid = messageProperties->getUserId(); + int len = nuid.size(); + array<unsigned char>^ muid = gcnew array<unsigned char>(len); + Marshal::Copy ((IntPtr) (void *) nuid.data(), muid, 0, len); + amqpProperties->UserId = muid; + } + + if (messageProperties->hasApplicationHeaders()) { + haveProperties = true; + const qpid::framing::FieldTable& fieldTable = messageProperties->getApplicationHeaders(); + int count = fieldTable.count(); + + if (count > 0) { + haveProperties = true; + Collections::Generic::Dictionary<System::String^, AmqpType^>^ mmap = + gcnew Collections::Generic::Dictionary<System::String^, AmqpType^>(count); + + for(qpid::framing::FieldTable::ValueMap::const_iterator i = fieldTable.begin(); i != fieldTable.end(); i++) { + + qpid::framing::FieldValue::Data &data = i->second->getData(); + + // TODO: replace these generic int/string conversions with handler for each AMQP specific type: + // uint8_t dataType = i->second->getType(); + // switch (dataType) { case TYPE_CODE_STR8: ... } + + if (data.convertsToInt()) { + mmap->Add (gcnew String(i->first.data()), gcnew AmqpInt((int) i->second->getData().getInt())); + } + if (data.convertsToString()) { + std::string ns = data.getString(); + String^ ms = gcnew String(ns.data(), 0, ns.size()); + mmap->Add (gcnew String(i->first.data()), gcnew AmqpString(ms)); + } + } + + amqpProperties->PropertyMap = mmap; + } + + } + } + + if (haveProperties) { + amqpMessage->Properties = amqpProperties; + } + + // We have a message we can return to the caller. + // Tell the broker we got it. + subscriptionp->accept(frameSetID); + return amqpMessage; + } + finally { + if (ownFrameSet) + delete (fspp); + } +} + + // As for IInputChannel: + // if success, return true + amqpMessage + // elseif timeout, return false + // elseif closed/EOF, return true and amqpMessage = null + // else throw an Exception + +bool InputLink::TryReceive(TimeSpan timeout, [Out] AmqpMessage^% amqpMessage) +{ + lock l(waiters); + + if (waiters->Count == 0) { + // see if there is a message already available without blocking + IntPtr fspp = nextLocalMessage(); + if (fspp.ToPointer() != NULL) { + amqpMessage = createAmqpMessage(fspp); + return true; + } + } + + MessageWaiter^ waiter = gcnew MessageWaiter(this, timeout, true, false, nullptr, nullptr); + addWaiter(waiter); + + l.release(); + waiter->Run(); + l.acquire(); + + if (waiter->TimedOut) { + return false; + } + + IntPtr waiterMsg = waiter->Message; + if (waiterMsg.ToPointer() == NULL) { + if (disposed) { + // indicate normal EOF on channel + amqpMessage = nullptr; + return true; + } + } + + amqpMessage = createAmqpMessage(waiterMsg); + return true; +} + +IAsyncResult^ InputLink::BeginTryReceive(TimeSpan timeout, AsyncCallback^ callback, Object^ state) +{ + + //TODO: if haveMessage() complete synchronously + + lock l(waiters); + MessageWaiter^ waiter = gcnew MessageWaiter(this, timeout, true, true, callback, state); + addWaiter(waiter); + return waiter; +} + +bool InputLink::EndTryReceive(IAsyncResult^ result, [Out] AmqpMessage^% amqpMessage) +{ + + // TODO: validate result + + MessageWaiter^ waiter = (MessageWaiter ^) result; + + waiter->WaitForCompletion(); + + if (waiter->RunException != nullptr) + throw waiter->RunException; + + if (waiter->TimedOut) { + amqpMessage = nullptr; + return false; + } + + IntPtr waiterMsg = waiter->Message; + if (waiterMsg.ToPointer() == NULL) { + if (disposed) { + // indicate normal EOF on channel + amqpMessage = nullptr; + return true; + } + } + + amqpMessage = createAmqpMessage(waiterMsg); + return true; +} + + +bool InputLink::WaitForMessage(TimeSpan timeout) +{ + lock l(waiters); + + if (waiters->Count == 0) { + // see if there is a message already available without blocking + if (haveMessage()) + return true; + } + + // Same as for TryReceive, except consuming = false + MessageWaiter^ waiter = gcnew MessageWaiter(this, timeout, false, false, nullptr, nullptr); + addWaiter(waiter); + + l.release(); + waiter->Run(); + l.acquire(); + + if (waiter->TimedOut) { + return false; + } + + return true; +} + +IAsyncResult^ InputLink::BeginWaitForMessage(TimeSpan timeout, AsyncCallback^ callback, Object^ state) +{ + lock l(waiters); + + // Same as for BeginTryReceive, except consuming = false + MessageWaiter^ waiter = gcnew MessageWaiter(this, timeout, false, true, callback, state); + addWaiter(waiter); + return waiter; +} + +bool InputLink::EndWaitForMessage(IAsyncResult^ result) +{ + MessageWaiter^ waiter = (MessageWaiter ^) result; + + waiter->WaitForCompletion(); + + if (waiter->TimedOut) { + return false; + } + + return true; +} + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/InputLink.h b/qpid/wcf/src/Apache/Qpid/Interop/InputLink.h new file mode 100644 index 0000000000..366780c137 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/InputLink.h @@ -0,0 +1,85 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +#include "MessageWaiter.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Threading; +using namespace System::Runtime::InteropServices; + +using namespace qpid::client; +using namespace std; + +// smart pointer to the low level AMQP 0-10 frames of the message +typedef qpid::framing::FrameSet::shared_ptr QpidFrameSetPtr; + +public ref class InputLink +{ +private: + AmqpSession^ amqpSession; + Subscription* subscriptionp; + LocalQueue* localQueuep; + Demux::QueuePtr* queuePtrp; + Collections::Generic::List<MessageWaiter^>^ waiters; + bool disposed; + bool finalizing; + QpidFrameSetPtr* dequeuedFrameSetpp; + ManualResetEvent^ asyncHelperWaitHandle; + + void Cleanup(); + void ReleaseNative(); + bool haveMessage(); + void addWaiter(MessageWaiter^ waiter); + void asyncHelper(); + AmqpMessage^ createAmqpMessage(IntPtr msgp); + +internal: + InputLink(AmqpSession^ session, System::String^ sourceQueue, qpid::client::AsyncSession *qpidSessionp, + qpid::client::SubscriptionManager *qpidSubsMgrp, bool exclusive, bool temporary, System::String^ filterKey, + System::String^ exchange); + + bool internalWaitForMessage(); + void unblockWaiter(); + void resetQueue(); + IntPtr nextLocalMessage(); + void removeWaiter(MessageWaiter^ waiter); + void sync(); + +public: + ~InputLink(); + !InputLink(); + void Close(); + + bool TryReceive(TimeSpan timeout, [Out] AmqpMessage ^% amqpMessage); + IAsyncResult^ BeginTryReceive(TimeSpan timeout, AsyncCallback^ callback, Object^ state); + bool EndTryReceive(IAsyncResult^ result, [Out] AmqpMessage^% amqpMessage); + + bool WaitForMessage(TimeSpan timeout); + IAsyncResult^ BeginWaitForMessage(TimeSpan timeout, AsyncCallback^ callback, Object^ state); + bool EndWaitForMessage(IAsyncResult^ result); + +}; + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/Interop.vcproj b/qpid/wcf/src/Apache/Qpid/Interop/Interop.vcproj new file mode 100644 index 0000000000..32f78c8344 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/Interop.vcproj @@ -0,0 +1,302 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="Interop"
+ ProjectGUID="{C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}"
+ RootNamespace="Interop"
+ Keyword="ManagedCProj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ProjectDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ ManagedExtensions="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine="copy ..\AmqpTypes\obj\$(ConfigurationName)\Apache.Qpid.AmqpTypes.netmodule $(ConfigurationName)"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/FU Debug\Apache.Qpid.AmqpTypes.netmodule"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\..\..\..\cpp\build\include;..\..\..\..\..\cpp\build\src;..\..\..\..\..\cpp\include;..\..\..\..\..\cpp\src;"$(BOOST_ROOT)""
+ PreprocessorDefinitions="WIN32;_DEBUG;_CRT_NONSTDC_NO_WARNINGS;WIN32_LEAN_AND_MEAN;NOMINMAX;_SCL_SECURE_NO_WARNINGS;BOOST_ALL_DYN_LINK"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="..\..\..\..\..\cpp\build\src\Debug\qpidcommon.lib ..\..\..\..\..\cpp\build\src\Debug\qpidclient.lib Debug\Apache.Qpid.AmqpTypes.netmodule"
+ AdditionalDependencies="$(NoInherit)"
+ OutputFile="$(OutDir)\Apache.Qpid.Interop.dll"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories=""$(BOOST_ROOT)\lib""
+ GenerateDebugInformation="true"
+ AssemblyDebug="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ ManagedExtensions="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="2"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(NoInherit)"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ <AssemblyReference
+ RelativePath="System.dll"
+ AssemblyName="System, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"
+ MinFrameworkVersion="131072"
+ />
+ <AssemblyReference
+ RelativePath="System.Data.dll"
+ AssemblyName="System.Data, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=x86"
+ MinFrameworkVersion="131072"
+ />
+ <AssemblyReference
+ RelativePath="System.XML.dll"
+ AssemblyName="System.Xml, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"
+ MinFrameworkVersion="131072"
+ />
+ <AssemblyReference
+ RelativePath="System.Runtime.Serialization.dll"
+ AssemblyName="System.Runtime.Serialization, Version=3.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"
+ MinFrameworkVersion="196608"
+ />
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\AmqpConnection.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\AmqpMessage.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\AmqpSession.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\AssemblyInfo.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\CompletionWaiter.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\InputLink.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\MessageBodyStream.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\MessageWaiter.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\OutputLink.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\AmqpConnection.h"
+ >
+ </File>
+ <File
+ RelativePath=".\AmqpMessage.h"
+ >
+ </File>
+ <File
+ RelativePath=".\AmqpSession.h"
+ >
+ </File>
+ <File
+ RelativePath=".\CompletionWaiter.h"
+ >
+ </File>
+ <File
+ RelativePath=".\InputLink.h"
+ >
+ </File>
+ <File
+ RelativePath=".\MessageBodyStream.h"
+ >
+ </File>
+ <File
+ RelativePath=".\MessageWaiter.h"
+ >
+ </File>
+ <File
+ RelativePath=".\OutputLink.h"
+ >
+ </File>
+ <File
+ RelativePath=".\QpidException.h"
+ >
+ </File>
+ <File
+ RelativePath=".\QpidMarshal.h"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/qpid/wcf/src/Apache/Qpid/Interop/MessageBodyStream.cpp b/qpid/wcf/src/Apache/Qpid/Interop/MessageBodyStream.cpp new file mode 100644 index 0000000000..f2cb5740d3 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/MessageBodyStream.cpp @@ -0,0 +1,337 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#include <windows.h> +#include <msclr\lock.h> + +#include "qpid/client/AsyncSession.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/AMQFrame.h" + +#include "MessageBodyStream.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace System::Threading; +using namespace msclr; + +using namespace qpid::client; +using namespace qpid::framing; + +// Thefolowing def must match "Frames" private typedef. +// TODO: make "Frames" publicly visible. +typedef qpid::InlineVector<AMQFrame, 4> FrameSetFrames; + +using namespace std; + +static void ThrowIfBadArgs (array<unsigned char>^ buffer, int offset, int count) +{ + if (buffer == nullptr) + throw gcnew ArgumentNullException("buffer"); + + if (offset < 0) + throw gcnew ArgumentOutOfRangeException("offset"); + + if (count < 0) + throw gcnew ArgumentOutOfRangeException("count"); + + if ((offset + count) > buffer->Length) + throw gcnew ArgumentException("offset + count"); +} + + +// Input stream constructor + +MessageBodyStream::MessageBodyStream(FrameSet::shared_ptr *fspp) +{ + isInputStream = true; + frameSetpp = fspp; + fragmentCount = 0; + length = 0; + position = 0; + currentFramep = NULL; + + const std::string *datap; // pointer to the fragment's string variable that holds the content + + for(FrameSetFrames::const_iterator i = (*frameSetpp)->begin(); i != (*frameSetpp)->end(); i++) { + if (i->getBody()->type() == CONTENT_BODY) { + fragmentCount++; + datap = &(i->castBody<AMQContentBody>()->getData()); + length += datap->size(); + } + } + + // fragmentCount can be zero for an empty message + + fragmentIndex = 0; + fragmentPosition = 0; + + if (fragmentCount == 0) { + currentFragment = NULL; + fragmentLength = 0; + } + else if (fragmentCount == 1) { + currentFragment = datap->data(); + fragmentLength = (int) length; + } + else { + fragments = gcnew array<IntPtr>(fragmentCount); + fragmentIndex = 0; + for(FrameSetFrames::const_iterator i = (*frameSetpp)->begin(); i != (*frameSetpp)->end(); i++) { + if (i->getBody()->type() == CONTENT_BODY) { + datap = &(i->castBody<AMQContentBody>()->getData()); + fragments[fragmentIndex++] = (IntPtr) (void *) datap; + } + } + fragmentIndex = 0; + datap = (const std::string *) fragments[0].ToPointer(); + currentFragment = datap->data(); + fragmentLength = datap->size(); + } +} + + +int MessageBodyStream::Read(array<unsigned char>^ buffer, int offset, int count) +{ + if (!isInputStream) + throw gcnew NotSupportedException(); + if (disposed) + throw gcnew ObjectDisposedException("Stream"); + if (count == 0) + return 0; + ThrowIfBadArgs(buffer, offset, count); + + int nRead = 0; + int remaining = count; + + while (nRead < count) { + int fragAvail = fragmentLength - fragmentPosition; + int copyCount = min (fragAvail, remaining); + if (copyCount == 0) { + // no more to read + return nRead; + } + + // copy from native space + IntPtr nativep = (IntPtr) (void *) (currentFragment + fragmentPosition); + Marshal::Copy (nativep, buffer, offset, copyCount); + nRead += copyCount; + remaining -= copyCount; + fragmentPosition += copyCount; + offset += copyCount; + + // advance to next fragment? + if (fragmentPosition == fragmentLength) { + if (++fragmentIndex < fragmentCount) { + const std::string *datap = (const std::string *) fragments[fragmentIndex].ToPointer(); + currentFragment = datap->data(); + fragmentLength = datap->size(); + fragmentPosition = 0; + } + } + } + + return nRead; +} + + +void MessageBodyStream::pushCurrentFrame(bool lastFrame) +{ + // set flags as in SessionImpl::sendContent. + if (currentFramep->getBody()->type() == CONTENT_BODY) { + + if ((fragmentCount == 1) && lastFrame) { + // only one content frame + currentFramep->setFirstSegment(false); + } + else { + currentFramep->setFirstSegment(false); + currentFramep->setLastSegment(true); + if (fragmentCount != 1) { + currentFramep->setFirstFrame(false); + } + if (!lastFrame) { + currentFramep->setLastFrame(false); + } + } + } + else { + // the header frame + currentFramep->setFirstSegment(false); + if (!lastFrame) { + // there will be at least one content frame + currentFramep->setLastSegment(false); + } + } + + // add to frame set. This makes a copy and ref counts the body + (*frameSetpp)->append(*currentFramep); + + delete currentFramep; + + currentFramep = NULL; +} + + +IntPtr MessageBodyStream::GetFrameSet() +{ + if (currentFramep != NULL) { + // No more content. Tidy up the pending (possibly single header) frame. + pushCurrentFrame(true); + } + + if (frameSetpp == NULL) { + return (IntPtr) NULL; + } + + // shared_ptr.get() + return (IntPtr) (void *) (*frameSetpp).get(); +} + +IntPtr MessageBodyStream::GetHeader() +{ + return (IntPtr) headerBodyp; +} + + +// Ouput stream constructor + +MessageBodyStream::MessageBodyStream(int maxFrameSize) +{ + isInputStream = false; + + maxFrameContentSize = maxFrameSize - AMQFrame::frameOverhead(); + SequenceNumber unused; // only meaningful on incoming frames + frameSetpp = new FrameSet::shared_ptr(new FrameSet(unused)); + fragmentCount = 0; + length = 0; + position = 0; + + // header goes first in the outgoing frameset + + boost::intrusive_ptr<AMQBody> headerBody(new AMQHeaderBody); + currentFramep = new AMQFrame(headerBody); + headerBodyp = static_cast<AMQHeaderBody*>(headerBody.get()); + + // mark this header frame as "full" to force the first write to create a new content frame + fragmentPosition = maxFrameContentSize; +} + +void MessageBodyStream::Write(array<unsigned char>^ buffer, int offset, int count) +{ + if (isInputStream) + throw gcnew NotSupportedException(); + if (disposed) + throw gcnew ObjectDisposedException("Stream"); + if (count == 0) + return; + ThrowIfBadArgs(buffer, offset, count); + + if (currentFramep == NULL) { + // GetFrameSet() has been called and we no longer exclusively own the underlying frames. + throw gcnew InvalidOperationException ("Mesage Body output already completed"); + } + + if (count <= 0) + return; + + // keep GC memory movement at bay while copying to native space + pin_ptr<unsigned char> pinnedBuf = &buffer[0]; + + string *datap; + + int remaining = count; + while (remaining > 0) { + if (fragmentPosition == maxFrameContentSize) { + // move to a new frame, but not until ready to add new content. + // zero content is valid, or the final write may exactly fill to maxFrameContentSize + + pushCurrentFrame(false); + + currentFramep = new AMQFrame(AMQContentBody()); + fragmentPosition = 0; + fragmentCount++; + } + + int copyCount = min (remaining, (maxFrameContentSize - fragmentPosition)); + datap = &(currentFramep->castBody<AMQContentBody>()->getData()); + + char *outp = (char *) pinnedBuf + offset; + if (fragmentPosition == 0) { + datap->assign(outp, copyCount); + } + else { + datap->append(outp, copyCount); + } + + position += copyCount; + fragmentPosition += copyCount; + remaining -= copyCount; + offset += copyCount; + } +} + + +void MessageBodyStream::Cleanup() +{ + { + lock l(this); + if (disposed) + return; + + disposed = true; + } + + try {} + finally + { + if (frameSetpp != NULL) { + delete frameSetpp; + frameSetpp = NULL; + } + if (currentFramep != NULL) { + delete currentFramep; + currentFramep = NULL; + } + } +} + +MessageBodyStream::~MessageBodyStream() +{ + Cleanup(); +} + +MessageBodyStream::!MessageBodyStream() +{ + Cleanup(); +} + +void MessageBodyStream::Close() +{ + // Simulate Dispose()... + Cleanup(); + GC::SuppressFinalize(this); +} + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/MessageBodyStream.h b/qpid/wcf/src/Apache/Qpid/Interop/MessageBodyStream.h new file mode 100644 index 0000000000..fa8e3f6bde --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/MessageBodyStream.h @@ -0,0 +1,131 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; + +using namespace qpid::client; +using namespace qpid::framing; +using namespace std; + + +// This class provides memory streaming of the message body contents +// between native and managed space. To avoid additional memory copies +// in native space, it reads and writes directly to the low level Qpid +// frames. + +public ref class MessageBodyStream : System::IO::Stream +{ +private: + bool isInputStream; + long long length; + long long position; + + // the boost smart pointer that keeps the message body frames in memory + FrameSet::shared_ptr *frameSetpp; + + int fragmentCount; + int fragmentIndex; + const char* currentFragment; + int fragmentPosition; + int fragmentLength; + array<IntPtr>^ fragments; + + int maxFrameContentSize; + AMQFrame* currentFramep; + void* headerBodyp; + bool disposed; + bool finalizing; + void Cleanup(); + +internal: + // incoming message + MessageBodyStream(FrameSet::shared_ptr *fspp); + // outgoing message + MessageBodyStream(int maxFrameSize); + void pushCurrentFrame(bool last); +public: + ~MessageBodyStream(); + !MessageBodyStream(); + virtual void Close() override; + virtual int Read( + [InAttribute] [OutAttribute] array<unsigned char>^ buffer, + int offset, + int count) override; + + virtual void Write( + array<unsigned char>^ buffer, + int offset, + int count) override; + + + IntPtr GetFrameSet(); + IntPtr GetHeader(); + + virtual void Flush() override {} // noop + + + // TODO: see CanSeek below. + virtual long long Seek( + long long offset, + System::IO::SeekOrigin origin) override {throw gcnew System::NotSupportedException(); } + + // TODO: see CanSeek below. + virtual void SetLength( + long long value) override {throw gcnew System::NotSupportedException(); } + + virtual property long long Length { + long long get() override { return length; } + }; + + virtual property long long Position { + long long get() override { return position; } + void set(long long p) override { throw gcnew System::NotSupportedException(); } + }; + + + virtual property bool CanRead { + bool get () override { return isInputStream; } + } + + virtual property bool CanWrite { + bool get () override { return !isInputStream; } + } + + // Note: this class must return true to signal that the Length property works. + // Required by the raw message encoder. + // "If a class derived from Stream does not support seeking, calls to Length, + // SetLength, Position, and Seek throw a NotSupportedException". + + virtual property bool CanSeek { + bool get () override { return true; } + } + + virtual property bool CanTimeout { + bool get () override { return isInputStream; } + } +}; + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.cpp b/qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.cpp new file mode 100644 index 0000000000..f7a28b0692 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.cpp @@ -0,0 +1,251 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#include <windows.h> +#include <msclr\lock.h> + +#include "qpid/client/AsyncSession.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/MessageListener.h" +#include "qpid/client/Demux.h" +#include "qpid/client/SessionImpl.h" +#include "qpid/client/SessionBase_0_10Access.h" + +#include "MessageBodyStream.h" +#include "AmqpMessage.h" +#include "AmqpSession.h" +#include "InputLink.h" +#include "MessageWaiter.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Threading; +using namespace msclr; + + +MessageWaiter::MessageWaiter(InputLink^ parent, TimeSpan timeSpan, bool consuming, bool async, AsyncCallback ^callback, Object^ state) +{ + this->consuming = consuming; + if (!consuming) { + GC::SuppressFinalize(this); + } + + if (async) { + this->async = true; + this->asyncCallback = callback; + this->state = state; + } + else { + this->assigned = true; + } + this->parent = parent; + this->thisLock = gcnew Object(); + + // do this after the Message Waiter is fully initialized, in case of + // very small timespan + if (timeSpan != TimeSpan::MaxValue) { + this->timer = gcnew Timer(timeoutCallback, this, timeSpan, TimeSpan::FromMilliseconds(-1)); + } +} + +MessageWaiter::~MessageWaiter() +{ + if (message != IntPtr::Zero) { + try{} + finally { + delete message.ToPointer(); + message = IntPtr::Zero; + } + } +} + +MessageWaiter::!MessageWaiter() +{ + this->~MessageWaiter(); +} + + +void MessageWaiter::WaitForCompletion() +{ + if (isCompleted) + return; + + lock l(thisLock); + while (!isCompleted) { + Monitor::Wait(thisLock); + } +} + +void MessageWaiter::Activate() +{ + if (activated) + return; + + lock l(thisLock); + if (!activated) { + activated = true; + Monitor::PulseAll(thisLock); + } +} + + +void MessageWaiter::Run() +{ + lock l(thisLock); + + // wait until Activate(), i.e. our turn in the waiter list or a timeout + while (!activated) { + Monitor::Wait(thisLock); + } + bool haveMessage = false; + bool mustReset = false; + + if (!timedOut) + blocking = true; + + if (blocking) { + l.release(); + + try { + haveMessage = parent->internalWaitForMessage(); + } + catch (System::Exception^ e) { + runException = e; + } + + l.acquire(); + blocking = false; + if (timedOut) { + // TimeoutCallback() called parent->unblockWaiter() + mustReset = true; + // let the timer thread move past critical region + while (processingTimeout) { + Monitor::Wait(thisLock); + } + } + } + + if (timer != nullptr) { + timer->~Timer(); + timer = nullptr; + } + + if (haveMessage) { + timedOut = false; // for the case timeout and message arrival are essentially tied + if (!consuming) { + // just waiting + haveMessage = false; + } + } + + if (haveMessage || mustReset) { + l.release(); + if (haveMessage) { + // hang on to it for when the async caller gets around to retrieving + message = parent->nextLocalMessage(); + } + if (mustReset) { + parent->resetQueue(); + } + l.acquire(); + } + + isCompleted = true; + Monitor::PulseAll(thisLock); + + // do this check and signal while locked + if (asyncWaitHandle != nullptr) + asyncWaitHandle->Set(); + + l.release(); + parent->removeWaiter(this); + + + if (asyncCallback != nullptr) { + // guard against application callback exception + try { + asyncCallback(this); + } + catch (System::Exception^) { + // log it? + } + } + +} + +bool MessageWaiter::AcceptForWork() +{ + lock l(thisLock); + if (!assigned) { + assigned = true; + return true; + } + return false; +} + +void MessageWaiter::TimeoutCallback(Object^ state) +{ + MessageWaiter^ waiter = (MessageWaiter^) state; + if (waiter->isCompleted) + return; + + // make sure parent has finished initializing us before we get going + waiter->parent->sync(); + + lock l(waiter->thisLock); + if (waiter->timer == nullptr) { + // the waiter is in the clean up phase and doesn't need a wakeup + return; + } + + // timedOut, blocking and processingTimeout work as a unit + waiter->timedOut = true; + if (waiter->blocking) { + // let the waiter know that we are busy with an upcoming unblock operation + waiter->processingTimeout = true; + } + + waiter->Activate(); + + if (waiter->processingTimeout) { + // call with lock off + l.release(); + waiter->parent->unblockWaiter(); + + // synchronize with blocked thread + l.acquire(); + waiter->processingTimeout = false; + Monitor::PulseAll(waiter->thisLock); + } + + l.release(); + + // If waiter has no associated thread, we must move it to completion + if (waiter->AcceptForWork()) { + waiter->Run(); // does not block since timedOut == true + } +} + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.h b/qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.h new file mode 100644 index 0000000000..3eb4919646 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/MessageWaiter.h @@ -0,0 +1,127 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Threading; + +public ref class MessageWaiter : IAsyncResult +{ +private: + // Receive() or WaitForMessage() + bool consuming; + bool consumed; + bool timedOut; + bool async; + // has an owner thread + bool assigned; + // can Run (i.e. earlier MessageWaiters in the queue have completed) + bool activated; + // is making a call to internalWaitForMessage() which (usually) blocks + bool blocking; + // the timeout timer thread is lurking + bool processingTimeout; + // the saved exception from within Run() for async delivery + System::Exception^ runException; + AsyncCallback^ asyncCallback; + Threading::Timer ^timer; + bool isCompleted; + bool completedSynchronously; + Object^ state; + Object^ thisLock; + ManualResetEvent^ asyncWaitHandle; + InputLink^ parent; + static void TimeoutCallback(Object^ state); + static TimerCallback^ timeoutCallback = gcnew TimerCallback(MessageWaiter::TimeoutCallback); + IntPtr message; + !MessageWaiter(); + ~MessageWaiter(); + + internal: + MessageWaiter(InputLink^ parent, TimeSpan timeSpan, bool consuming, bool async, AsyncCallback ^callback, Object^ state); + + void Run(); + bool AcceptForWork(); + void Activate(); + void WaitForCompletion(); + +// inline void SetCompletedSynchronously (bool v) { completedSynchronously = v; } + + property IntPtr Message { + IntPtr get () { + if (!consuming || consumed) + throw gcnew InvalidOperationException("Message property"); + consumed = true; + IntPtr v = message; + message = IntPtr::Zero; + GC::SuppressFinalize(this); + return v; + } + // void set (IntPtr v) { message = v; } + } + + property bool Assigned { + bool get () { return assigned; } + } + + property bool TimedOut { + bool get () { return timedOut; } + } + + + property System::Exception^ RunException { + System::Exception^ get() { return runException; } + } + + public: + + virtual property bool IsCompleted { + bool get () { return isCompleted; } + } + + virtual property bool CompletedSynchronously { + bool get () { return completedSynchronously; } + } + + virtual property WaitHandle^ AsyncWaitHandle { + WaitHandle^ get () { + if (asyncWaitHandle != nullptr) { + return asyncWaitHandle; + } + + msclr::lock l(thisLock); + if (asyncWaitHandle == nullptr) { + asyncWaitHandle = gcnew ManualResetEvent(isCompleted); + } + return asyncWaitHandle; + } + } + + virtual property Object^ AsyncState { + Object^ get () { return state; } + } +}; + +}}} // namespace Apache::Qpid::Interop + diff --git a/qpid/wcf/src/Apache/Qpid/Interop/OutputLink.cpp b/qpid/wcf/src/Apache/Qpid/Interop/OutputLink.cpp new file mode 100644 index 0000000000..27725b8207 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/OutputLink.cpp @@ -0,0 +1,251 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#include <windows.h> +#include <msclr\lock.h> + +#include "qpid/client/AsyncSession.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/MessageListener.h" + + +#include "AmqpSession.h" +#include "AmqpMessage.h" +#include "OutputLink.h" +#include "QpidMarshal.h" + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace System::Threading; +using namespace msclr; + +using namespace qpid::client; +using namespace std; + +using namespace Apache::Qpid::AmqpTypes; + + +OutputLink::OutputLink(AmqpSession^ session, String^ defaultQueue) : + amqpSession(session), + queue(defaultQueue), + disposed(false), + maxFrameSize(session->Connection->MaxFrameSize), + finalizing(false) +{ +} + +void OutputLink::Cleanup() +{ + { + lock l(this); + if (disposed) + return; + + disposed = true; + } + + amqpSession->NotifyClosed(); +} + +OutputLink::~OutputLink() +{ + Cleanup(); +} + +OutputLink::!OutputLink() +{ + Cleanup(); +} + +void OutputLink::Close() +{ + // Simulate Dispose()... + Cleanup(); + GC::SuppressFinalize(this); +} + + +AmqpMessage^ OutputLink::CreateMessage() +{ + MessageBodyStream ^mbody = gcnew MessageBodyStream(maxFrameSize); + AmqpMessage ^amqpm = gcnew AmqpMessage(mbody); + return amqpm; +} + + +void OutputLink::ManagedToNative(AmqpMessage^ m) +{ + MessageBodyStream^ messageBodyStream = (MessageBodyStream^ ) m->BodyStream; + + AmqpProperties^ mprops = m->Properties; + + if (mprops != nullptr) { + AMQHeaderBody* bodyp = (AMQHeaderBody*) messageBodyStream->GetHeader().ToPointer(); + + if (mprops->HasDeliveryProperties) { + DeliveryProperties* deliveryPropertiesp = bodyp->get<DeliveryProperties>(true); + + if (mprops->RoutingKey != nullptr) { + deliveryPropertiesp->setRoutingKey(QpidMarshal::ToNative(mprops->RoutingKey)); + } + + if (mprops->Durable) { + deliveryPropertiesp->setDeliveryMode(qpid::framing::PERSISTENT); + } + + if (mprops->TimeToLive.HasValue) { + long long ttl = mprops->TimeToLive.Value.Ticks; + bool was_positive = (ttl > 0); + if (ttl < 0) + ttl = 0; + ttl = ttl / TimeSpan::TicksPerMillisecond; + if ((ttl == 0) && was_positive) + ttl = 1; + deliveryPropertiesp->setTtl(ttl); + } + } + + if (mprops->HasMessageProperties) { + qpid::framing::MessageProperties* messagePropertiesp = + bodyp->get<qpid::framing::MessageProperties>(true); + + String^ replyToExchange = mprops->ReplyToExchange; + String^ replyToRoutingKey = mprops->ReplyToRoutingKey; + if ((replyToExchange != nullptr) || (replyToRoutingKey != nullptr)) { + qpid::framing::ReplyTo nReplyTo; + if (replyToExchange != nullptr) { + nReplyTo.setExchange(QpidMarshal::ToNative(replyToExchange)); + } + if (replyToRoutingKey != nullptr) { + nReplyTo.setRoutingKey(QpidMarshal::ToNative(replyToRoutingKey)); + } + messagePropertiesp->setReplyTo(nReplyTo); + } + + // TODO: properly split 1.0 style to 0-10 content type + encoding + + String^ contentType = mprops->ContentType; + if (contentType != nullptr) { + String^ type = nullptr; + String^ enc = nullptr; + int idx = contentType->IndexOf(';'); + if (idx == -1) { + type = contentType; + } + else { + type = contentType->Substring(0, idx); + contentType = contentType->Substring(idx + 1); + idx = contentType->IndexOf('='); + if (idx != -1) { + enc = contentType->Substring(idx + 1); + enc = enc->Trim(); + } + } + if (!String::IsNullOrEmpty(type)) { + messagePropertiesp->setContentType(QpidMarshal::ToNative(type)); + } + if (!String::IsNullOrEmpty(enc)) { + messagePropertiesp->setContentEncoding(QpidMarshal::ToNative(enc)); + } + } + + + array<unsigned char>^ mbytes = mprops->CorrelationId; + if (mbytes != nullptr) { + pin_ptr<unsigned char> pinnedBuf = &mbytes[0]; + std::string s((char *) pinnedBuf, mbytes->Length); + messagePropertiesp->setCorrelationId(s); + } + + mbytes = mprops->UserId; + if (mbytes != nullptr) { + pin_ptr<unsigned char> pinnedBuf = &mbytes[0]; + std::string s((char *) pinnedBuf, mbytes->Length); + messagePropertiesp->setUserId(s); + } + + if (mprops->HasMappedProperties) { + qpid::framing::FieldTable fieldTable; + // TODO: add support for abitrary AMQP types + for each (Collections::Generic::KeyValuePair<System::String^, AmqpType^> kvp in mprops->PropertyMap) { + Type^ type = kvp.Value->GetType(); + if (type == AmqpInt::typeid) { + fieldTable.setInt(QpidMarshal::ToNative(kvp.Key), + ((AmqpInt ^) kvp.Value)->Value); + } + else if (type == AmqpString::typeid) { + AmqpString^ str = (AmqpString ^) kvp.Value; + // For now, FieldTable supports a single string type + fieldTable.setString(QpidMarshal::ToNative(kvp.Key), QpidMarshal::ToNative(str->Value)); + } + } + + messagePropertiesp->setApplicationHeaders(fieldTable); + } + } + } +} + + + +void OutputLink::Send(AmqpMessage^ amqpMessage, TimeSpan timeout) +{ + // copy properties from managed space to the native counterparts + ManagedToNative(amqpMessage); + + MessageBodyStream^ messageBodyStream = (MessageBodyStream^ ) amqpMessage->BodyStream; + CompletionWaiter^ waiter = amqpSession->SendMessage(queue, messageBodyStream, timeout, false, nullptr, nullptr); + + if (waiter != nullptr) { + waiter->WaitForCompletion(); + if (waiter->TimedOut) { + throw gcnew TimeoutException("Receive"); + } + } + // else: SendMessage() has already waited for the Completion + +} + +IAsyncResult^ OutputLink::BeginSend(AmqpMessage^ amqpMessage, TimeSpan timeout, AsyncCallback^ callback, Object^ state) +{ + ManagedToNative(amqpMessage); + + MessageBodyStream^ messageBodyStream = (MessageBodyStream^ ) amqpMessage->BodyStream; + CompletionWaiter^ waiter = amqpSession->SendMessage(queue, messageBodyStream, timeout, true, callback, state); + return waiter; +} + +void OutputLink::EndSend(IAsyncResult^ result) +{ + CompletionWaiter^ waiter = (CompletionWaiter ^) result; + waiter->WaitForCompletion(); + if (waiter->TimedOut) { + throw gcnew TimeoutException("Receive"); + } +} + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/OutputLink.h b/qpid/wcf/src/Apache/Qpid/Interop/OutputLink.h new file mode 100644 index 0000000000..1f049a7412 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/OutputLink.h @@ -0,0 +1,64 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Runtime::InteropServices; + +using namespace qpid::client; +using namespace std; + + +public ref class OutputLink +{ +private: + AmqpSession^ amqpSession; + String^ queue; + bool disposed; + bool finalizing; + void Cleanup(); + AmqpTypes::AmqpProperties^ defaultProperties; + void ManagedToNative(AmqpMessage^ m); + int maxFrameSize; + +internal: + OutputLink(AmqpSession^ session, String^ defaultQueue); + +public: + ~OutputLink(); + !OutputLink(); + void Close(); + AmqpMessage^ CreateMessage(); + void Send(AmqpMessage^ m, TimeSpan timeout); + IAsyncResult^ BeginSend(AmqpMessage^ amqpMessage, TimeSpan timeout, AsyncCallback^ callback, Object^ state); + void EndSend(IAsyncResult^ result); + + property AmqpTypes::AmqpProperties^ DefaultProperties { + AmqpTypes::AmqpProperties^ get () { return defaultProperties; } + void set(AmqpTypes::AmqpProperties^ p) { defaultProperties = p; } + } +}; + + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/QpidException.h b/qpid/wcf/src/Apache/Qpid/Interop/QpidException.h new file mode 100644 index 0000000000..91677a5e73 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/QpidException.h @@ -0,0 +1,37 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; + +public ref class QpidException : System::Exception +{ + public: + + QpidException() : System::Exception() {} + QpidException(String^ estring) : System::Exception(estring) {} + +}; + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/src/Apache/Qpid/Interop/QpidMarshal.h b/qpid/wcf/src/Apache/Qpid/Interop/QpidMarshal.h new file mode 100644 index 0000000000..3e22af7b39 --- /dev/null +++ b/qpid/wcf/src/Apache/Qpid/Interop/QpidMarshal.h @@ -0,0 +1,53 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +namespace Apache { +namespace Qpid { +namespace Interop { + +using namespace System; +using namespace System::Text; + + +// Helper functions for marshaling. + +private ref class QpidMarshal +{ + public: + + // marshal_as<T> not available in all Visual Studio editions. + + static std::string ToNative (System::String^ managed) { + if (managed->Length == 0) { + return std::string(); + } + array<unsigned char>^ mbytes = Encoding::UTF8->GetBytes(managed); + if (mbytes->Length == 0) { + return std::string(); + } + + pin_ptr<unsigned char> pinnedBuf = &mbytes[0]; + std::string native((char *) pinnedBuf, mbytes->Length); + return native; + } +}; + +}}} // namespace Apache::Qpid::Interop diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/AsyncTest.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/AsyncTest.cs new file mode 100644 index 0000000000..bf20b5083d --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/AsyncTest.cs @@ -0,0 +1,190 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.Threading; + using NUnit.Framework; + + [TestFixture] + public class AsyncTest + { + private const int MessageCount = 20; + private const string Queue = "amqp:amq.direct?routingkey=routing_key"; + private Uri endpoint = new Uri("amqp:message_queue"); + private TimeSpan standardTimeout = TimeSpan.FromSeconds(10.0); // seconds + + [Test] + public void NonTryReceives() + { + this.SendMessages(this.standardTimeout, this.standardTimeout); + this.ReceiveNonTryMessages(this.standardTimeout, this.standardTimeout); + } + + [Test] + public void TryReceives() + { + this.SendMessages(this.standardTimeout, this.standardTimeout); + this.ReceiveTryMessages(this.standardTimeout, this.standardTimeout); + } + + [Test] + public void SmallTimeout() + { + // This code is commented out due to a bug in asynchronous channel open. + ////IChannelListener parentListener; + ////try + ////{ + //// this.RetrieveAsyncChannel(new Uri("amqp:fake_queue_do_not_create"), TimeSpan.FromMilliseconds(10.0), out parentListener); + //// parentListener.Close(); + //// Assert.Fail("Accepting the channel did not time out."); + ////} + ////catch (TimeoutException) + ////{ + //// // Intended exception. + ////} + + try + { + this.ReceiveNonTryMessages(this.standardTimeout, TimeSpan.FromMilliseconds(10.0)); + Assert.Fail("Receiving a message did not time out."); + } + catch (TimeoutException) + { + // Intended exception. + } + } + + private void SendMessages(TimeSpan channelTimeout, TimeSpan messageSendTimeout) + { + ChannelFactory<IOutputChannel> channelFactory = + new ChannelFactory<IOutputChannel>(Util.GetBinding(), Queue); + IOutputChannel proxy = channelFactory.CreateChannel(); + IAsyncResult[] resultArray = new IAsyncResult[MessageCount]; + + for (int i = 0; i < MessageCount; i++) + { + Message toSend = Message.CreateMessage(MessageVersion.Default, string.Empty, i); + resultArray[i] = proxy.BeginSend(toSend, messageSendTimeout, null, null); + } + + for (int j = 0; j < MessageCount; j++) + { + proxy.EndSend(resultArray[j]); + } + + IAsyncResult iocCloseResult = proxy.BeginClose(channelTimeout, null, null); + Thread.Sleep(TimeSpan.FromMilliseconds(50.0)); // Dummy work + proxy.EndClose(iocCloseResult); + + IAsyncResult chanFactCloseResult = channelFactory.BeginClose(channelTimeout, null, null); + Thread.Sleep(TimeSpan.FromMilliseconds(50.0)); // Dummy work + channelFactory.EndClose(chanFactCloseResult); + } + + private void ReceiveNonTryMessages(TimeSpan channelTimeout, TimeSpan messageTimeout) + { + IChannelListener inputChannelParentListener; + IInputChannel inputChannel = this.RetrieveAsyncChannel(this.endpoint, channelTimeout, out inputChannelParentListener); + + inputChannel.Open(); + + IAsyncResult[] resultArray = new IAsyncResult[MessageCount]; + try + { + for (int i = 0; i < MessageCount; i++) + { + resultArray[i] = inputChannel.BeginReceive(messageTimeout, null, null); + } + + for (int j = 0; j < MessageCount; j++) + { + inputChannel.EndReceive(resultArray[j]); + } + } + finally + { + IAsyncResult channelCloseResult = inputChannel.BeginClose(channelTimeout, null, null); + Thread.Sleep(TimeSpan.FromMilliseconds(50.0)); // Dummy work + inputChannel.EndClose(channelCloseResult); + + // Asynchronous listener close has not been implemented. + ////IAsyncResult listenerCloseResult = inputChannelParentListener.BeginClose(channelTimeout, null, null); + ////Thread.Sleep(TimeSpan.FromMilliseconds(50.0)); // Dummy work + ////inputChannelParentListener.EndClose(listenerCloseResult); + + inputChannelParentListener.Close(); + } + } + + private void ReceiveTryMessages(TimeSpan channelAcceptTimeout, TimeSpan messageReceiveTimeout) + { + IChannelListener<IInputChannel> listener = Util.GetBinding().BuildChannelListener<IInputChannel>(this.endpoint, new BindingParameterCollection()); + listener.Open(); + IInputChannel inputChannel = listener.AcceptChannel(channelAcceptTimeout); + IAsyncResult channelResult = inputChannel.BeginOpen(channelAcceptTimeout, null, null); + Thread.Sleep(TimeSpan.FromMilliseconds(50.0)); + inputChannel.EndOpen(channelResult); + + IAsyncResult[] resultArray = new IAsyncResult[MessageCount]; + + for (int i = 0; i < MessageCount; i++) + { + resultArray[i] = inputChannel.BeginTryReceive(messageReceiveTimeout, null, null); + } + + for (int j = 0; j < MessageCount; j++) + { + Message tempMessage; + Assert.True(inputChannel.EndTryReceive(resultArray[j], out tempMessage), "Did not successfully receive message #{0}", j); + } + + inputChannel.Close(); + listener.Close(); + } + + private IInputChannel RetrieveAsyncChannel(Uri queue, TimeSpan timeout, out IChannelListener parentListener) + { + IChannelListener<IInputChannel> listener = + Util.GetBinding().BuildChannelListener<IInputChannel>(queue, new BindingParameterCollection()); + listener.Open(); + IInputChannel inputChannel; + + try + { + IAsyncResult acceptResult = listener.BeginAcceptChannel(timeout, null, null); + Thread.Sleep(TimeSpan.FromMilliseconds(300.0)); // Dummy work + inputChannel = listener.EndAcceptChannel(acceptResult); + } + catch (TimeoutException e) + { + listener.Close(); + throw e; + } + finally + { + parentListener = listener; + } + return inputChannel; + } + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/CustomAmqpBindingTest.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/CustomAmqpBindingTest.cs new file mode 100644 index 0000000000..45a926ce4d --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/CustomAmqpBindingTest.cs @@ -0,0 +1,77 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System; + using System.Collections.Generic; + using System.Reflection; + using System.ServiceModel; + using System.Threading; + using NUnit.Framework; + + [TestFixture] + public class CustomAmqpBindingTest + { + private MessageClient client; + + [SetUp] + public void Setup() + { + // Create client + this.client = new MessageClient(); + this.client.NumberOfMessages = 3; + this.client.NumberOfIterations = 3; + + // Setup and start service + MessageService.EndpointAddress = "amqp:message_queue"; + MessageService.ContractTypes = new List<Type>(); + MessageService.ContractTypes.Add(typeof(IInteropService)); + MessageService.CompletionHandle = new EventWaitHandle(false, EventResetMode.AutoReset); + MessageService.IntendedInvocationCount = this.client.NumberOfIterations * this.client.NumberOfMessages * MessageService.ContractTypes.Count; + MessageService.StartService(Util.GetCustomBinding()); + } + + [Test] + public void Run() + { + // Create the WCF AMQP channel and send messages + MethodInfo runClientMethod = this.client.GetType().GetMethod("RunInteropClient"); + EndpointAddress address = new EndpointAddress("amqp:amq.direct?routingkey=routing_key"); + foreach (Type contractType in MessageService.ContractTypes) + { + MethodInfo runClientT = runClientMethod.MakeGenericMethod(contractType); + runClientT.Invoke(this.client, new object[] { address }); + } + + // Allow the WCF service to process all the messages before validation + MessageService.CompletionHandle.WaitOne(TimeSpan.FromSeconds(10.0), false); + + // Validation + int expectedMethodCallCount = this.client.NumberOfIterations * this.client.NumberOfMessages * MessageService.ContractTypes.Count; + Assert.AreEqual(expectedMethodCallCount, MessageService.TotalMethodCallCount); + } + + [TearDown] + public void Cleanup() + { + MessageService.StopService(); + } + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/FunctionalTests.csproj b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/FunctionalTests.csproj new file mode 100644 index 0000000000..b7f4ed5d0a --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/FunctionalTests.csproj @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{E2D8C779-E417-40BA-BEE1-EE034268482F}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Apache.Qpid.Test.Channel.Functional</RootNamespace>
+ <AssemblyName>Apache.Qpid.Test.Channel.Functional</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="nunit.framework, Version=2.5.2.9222, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL" />
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Runtime.Serialization">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.ServiceModel">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="AsyncTest.cs" />
+ <Compile Include="CustomAmqpBindingTest.cs" />
+ <Compile Include="IGenericObjectService.cs" />
+ <Compile Include="IInteropService.cs" />
+ <Compile Include="IQueuedDatagramService1.cs" />
+ <Compile Include="IQueuedDatagramService2.cs" />
+ <Compile Include="IQueuedDatagramService3.cs" />
+ <Compile Include="MessageBodyTest.cs" />
+ <Compile Include="MessagePropertiesTest.cs" />
+ <Compile Include="MultipleEndpointsSameQueueTest.cs" />
+ <Compile Include="MessageClient.cs" />
+ <Compile Include="MessageService.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Util.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\..\..\..\src\Apache\Qpid\Channel\Channel.csproj">
+ <Project>{8AABAB30-7D1E-4539-B7D1-05450262BAD2}</Project>
+ <Name>Channel</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\..\..\..\src\Apache\Qpid\Interop\Interop.vcproj">
+ <Project>{C9B6AC75-6332-47A4-B82B-0C20E0AF2D34}</Project>
+ <Name>Interop</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+ <PropertyGroup>
+ <PostBuildEvent>
+ </PostBuildEvent>
+ </PropertyGroup>
+</Project>
\ No newline at end of file diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IGenericObjectService.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IGenericObjectService.cs new file mode 100644 index 0000000000..a1ffac50b3 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IGenericObjectService.cs @@ -0,0 +1,30 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System.ServiceModel; + + [ServiceContract(SessionMode = SessionMode.NotAllowed)] + public interface IGenericObjectService + { + [OperationContract(IsOneWay = true)] + void SendObject(object message); + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IInteropService.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IInteropService.cs new file mode 100644 index 0000000000..25f7010a89 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IInteropService.cs @@ -0,0 +1,31 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System.ServiceModel; + using System.ServiceModel.Channels; + + [ServiceContract] + public interface IInteropService + { + [OperationContract(IsOneWay = true, Action = "*")] + void Hello(Message message); + } +}
\ No newline at end of file diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService1.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService1.cs new file mode 100644 index 0000000000..bdbbb82f7e --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService1.cs @@ -0,0 +1,34 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System.ServiceModel; + using System.ServiceModel.Channels; + + [ServiceContract(SessionMode = SessionMode.NotAllowed)] + public interface IQueuedDatagramService1 + { + [OperationContract(IsOneWay = true)] + void Hello(string message); + + [OperationContract(IsOneWay = true)] + void Goodbye(); + } +}
\ No newline at end of file diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService2.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService2.cs new file mode 100644 index 0000000000..565f7aa27b --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService2.cs @@ -0,0 +1,34 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System.ServiceModel; + using System.ServiceModel.Channels; + + [ServiceContract(SessionMode = SessionMode.NotAllowed)] + public interface IQueuedDatagramService2 + { + [OperationContract(IsOneWay = true)] + void Hello(string message); + + [OperationContract(IsOneWay = true)] + void Goodbye(); + } +}
\ No newline at end of file diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService3.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService3.cs new file mode 100644 index 0000000000..3ff2085557 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/IQueuedDatagramService3.cs @@ -0,0 +1,33 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System.ServiceModel; + + [ServiceContract(SessionMode = SessionMode.NotAllowed)] + public interface IQueuedDatagramService3 + { + [OperationContract(IsOneWay = true)] + void Hello(string message); + + [OperationContract(IsOneWay = true)] + void Goodbye(); + } +}
\ No newline at end of file diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageBodyTest.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageBodyTest.cs new file mode 100644 index 0000000000..3fad6b336d --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageBodyTest.cs @@ -0,0 +1,135 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System; + using System.Collections.Generic; + using System.Runtime.Serialization; + using System.ServiceModel; + using System.ServiceModel.Channels; + using NUnit.Framework; + + [TestFixture] + public class MessageBodyTest + { + private const string Queue = "amqp:amq.direct?routingkey=routing_key"; + + [Test] + public void DateVariation() + { + DateTime rightNow = DateTime.UtcNow; + this.SendMessage(rightNow); + this.ReceiveMessage<DateTime>(rightNow); + } + + [Test] + public void EmptyStringVariation() + { + const string TestString = ""; + this.SendMessage(TestString); + this.ReceiveMessage<string>(TestString); + } + + [Test] + public void IntPrimitiveVariation() + { + const int TheAnswer = 42; + this.SendMessage(TheAnswer); + this.ReceiveMessage<int>(TheAnswer); + } + + [Test] + public void MultipleIntVariation() + { + const int NumberOfMessages = 20; + int[] listOfNumbers = new int[NumberOfMessages]; + + for (int i = 0; i < NumberOfMessages; i++) + { + this.SendMessage(i); + listOfNumbers[i] = i; + } + + Assert.True(listOfNumbers[NumberOfMessages - 1] != 0, "Not all messages were sent."); + + for (int j = 0; j < NumberOfMessages; j++) + { + int receivedNumber = this.ReceiveMessage<int>(); + Assert.True(listOfNumbers[j].Equals(receivedNumber), "Received {0} - this number is unknown or has been received more than once.", receivedNumber); + } + } + + [Test] + public void StringVariation() + { + const string TestString = "The darkest of dim, dreary days dost draw deathly deeds. どーも"; + this.SendMessage(TestString); + this.ReceiveMessage<string>(TestString); + } + + private void SendMessage(object objectToSend) + { + IChannelFactory<IOutputChannel> channelFactory = + Util.GetBinding().BuildChannelFactory<IOutputChannel>(); + channelFactory.Open(); + IOutputChannel proxy = channelFactory.CreateChannel(new EndpointAddress(Queue)); + proxy.Open(); + Message toSend = Message.CreateMessage(MessageVersion.Default, string.Empty, objectToSend); + proxy.Send(toSend); + toSend.Close(); + channelFactory.Close(); + } + + private TObjectType ReceiveMessage<TObjectType>() + { + Uri endpoint = new Uri("amqp:message_queue"); + IChannelListener<IInputChannel> listener = Util.GetBinding().BuildChannelListener<IInputChannel>(endpoint, new BindingParameterCollection()); + listener.Open(); + IInputChannel service = listener.AcceptChannel(TimeSpan.FromSeconds(10)); + service.Open(); + Message receivedMessage = service.Receive(TimeSpan.FromSeconds(10)); + Assert.NotNull(receivedMessage, "Message was not received"); + try + { + TObjectType receivedObject = receivedMessage.GetBody<TObjectType>(); + return receivedObject; + } + catch (SerializationException) + { + Assert.Fail("Deserialized object not of correct type"); + } + finally + { + receivedMessage.Close(); + service.Close(); + listener.Close(); + } + + return default(TObjectType); + } + + private TObjectType ReceiveMessage<TObjectType>(TObjectType objectToMatch) + { + TObjectType receivedObject = this.ReceiveMessage<TObjectType>(); + Assert.True(objectToMatch.Equals(receivedObject), "Original and deserialized objects do not match"); + return receivedObject; + } + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageClient.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageClient.cs new file mode 100644 index 0000000000..8f867551b1 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageClient.cs @@ -0,0 +1,101 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System; + using System.Reflection; + using System.ServiceModel; + using System.ServiceModel.Channels; + + public class MessageClient + { + public int NumberOfMessages + { + get; + set; + } + + public int NumberOfIterations + { + get; + set; + } + + public void RunClient<TServiceContract>(EndpointAddress address) + { + Binding amqpBinding = Util.GetBinding(); + Type proxyType = typeof(TServiceContract); + MethodInfo helloMethod = proxyType.GetMethod("Hello"); + MethodInfo goodbyeMethod = proxyType.GetMethod("Goodbye"); + + string[] messages = new string[this.NumberOfMessages]; + for (int i = 0; i < this.NumberOfMessages; ++i) + { + messages[i] = "Message " + i; + } + + for (int i = 0; i < this.NumberOfIterations; ++i) + { + this.CreateChannelAndSendMessages<TServiceContract>(address, amqpBinding, helloMethod, goodbyeMethod, messages); + } + } + + public void RunInteropClient<TServiceContract>(EndpointAddress address) + { + Binding amqpBinding = Util.GetBinding(); + Type proxyType = typeof(TServiceContract); + MethodInfo helloMethod = proxyType.GetMethod("Hello"); + + Message[] messages = new Message[this.NumberOfMessages]; + + for (int i = 0; i < this.NumberOfIterations; ++i) + { + this.CreateInteropChannelAndSendMessages<TServiceContract>(address, amqpBinding, helloMethod, this.NumberOfMessages); + } + } + + private void CreateChannelAndSendMessages<TServiceContract>(EndpointAddress address, Binding amqpBinding, MethodInfo helloMethod, MethodInfo goodbyeMethod, object[] messages) + { + ChannelFactory<TServiceContract> channelFactory = new ChannelFactory<TServiceContract>(amqpBinding, address); + TServiceContract proxy = channelFactory.CreateChannel(); + + foreach (object message in messages) + { + helloMethod.Invoke(proxy, new object[] { message }); + } + + goodbyeMethod.Invoke(proxy, new object[0]); + channelFactory.Close(); + } + + private void CreateInteropChannelAndSendMessages<TServiceContract>(EndpointAddress address, Binding amqpBinding, MethodInfo helloMethod, int messageCount) + { + ChannelFactory<TServiceContract> channelFactory = new ChannelFactory<TServiceContract>(amqpBinding, address); + TServiceContract proxy = channelFactory.CreateChannel(); + + for (int i = 0; i < messageCount; i++) + { + helloMethod.Invoke(proxy, new object[] { Message.CreateMessage(MessageVersion.Soap12WSAddressing10, "*") }); + } + + channelFactory.Close(); + } + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageProperties.txt b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageProperties.txt new file mode 100644 index 0000000000..bd6459ccb9 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageProperties.txt @@ -0,0 +1,22 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +ContentType=Text +Durable=true +RoutingKey=routing_key +TimeToLive=00:00:10
\ No newline at end of file diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessagePropertiesTest.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessagePropertiesTest.cs new file mode 100644 index 0000000000..8e192e90f1 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessagePropertiesTest.cs @@ -0,0 +1,131 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System; + using System.Collections.Generic; + using System.Reflection; + using System.Runtime.Serialization; + using System.ServiceModel; + using System.ServiceModel.Channels; + using Apache.Qpid.AmqpTypes; + using NUnit.Framework; + + [TestFixture] + public class MessagePropertiesTest + { + private const string RoutingKey = "routing_key"; + private const string SendToUri = "amqp:amq.direct?routingkey=" + RoutingKey; + + [Test] + public void DefaultAmqpProperties() + { + const string TestString = "Test Message"; + AmqpProperties messageProperties = new AmqpProperties(); + + this.SendMessage(TestString, messageProperties); + this.ReceiveMessage<string>(TestString, messageProperties); + } + + [Test] + public void NonDefaultAmqpProperties() + { + const string TestString = "Test Message"; + AmqpProperties messageProperties = this.CreateMessageProperties(); + + this.SendMessage(TestString, messageProperties); + this.ReceiveMessage<string>(TestString, messageProperties); + } + + private AmqpProperties CreateMessageProperties() + { + Dictionary<string, string> messageProperties = Util.GetProperties("..\\..\\MessageProperties.txt"); + + AmqpProperties amqpProperties = new AmqpProperties(); + amqpProperties.ContentType = (string)messageProperties["ContentType"]; + amqpProperties.Durable = Convert.ToBoolean((string)messageProperties["Durable"]); + amqpProperties.RoutingKey = (string)messageProperties["RoutingKey"]; + amqpProperties.TimeToLive = TimeSpan.Parse((string)messageProperties["TimeToLive"]); + + return amqpProperties; + } + + private void SendMessage(object objectToSend, AmqpProperties propertiesToSend) + { + ChannelFactory<IOutputChannel> channelFactory = + new ChannelFactory<IOutputChannel>(Util.GetBinding(), SendToUri); + IOutputChannel proxy = channelFactory.CreateChannel(); + proxy.Open(); + + Message toSend = Message.CreateMessage(MessageVersion.Default, string.Empty, objectToSend); + toSend.Properties["AmqpProperties"] = propertiesToSend; + proxy.Send(toSend); + + toSend.Close(); + proxy.Close(); + channelFactory.Close(); + } + + private void ReceiveMessage<TObjectType>(TObjectType objectToMatch, AmqpProperties expectedProperties) + { + Uri receiveFromUri = new Uri("amqp:message_queue"); + IChannelListener<IInputChannel> listener = Util.GetBinding().BuildChannelListener<IInputChannel>(receiveFromUri, new BindingParameterCollection()); + listener.Open(); + IInputChannel service = listener.AcceptChannel(TimeSpan.FromSeconds(10)); + service.Open(); + Message receivedMessage = service.Receive(TimeSpan.FromSeconds(10)); + try + { + TObjectType receivedObject = receivedMessage.GetBody<TObjectType>(); + Assert.True(receivedObject.Equals(objectToMatch), "Original and deserialized objects do not match"); + + AmqpProperties receivedProperties = (AmqpProperties)receivedMessage.Properties["AmqpProperties"]; + PropertyInfo[] propInfo = typeof(AmqpProperties).GetProperties(); + + for (int i = 0; i < propInfo.Length; i++) + { + string propertyName = propInfo[i].Name; + if (propertyName.Equals("RoutingKey", StringComparison.InvariantCultureIgnoreCase)) + { + Assert.AreEqual(RoutingKey, Convert.ToString(propInfo[i].GetValue(receivedProperties, null))); + } + else + { + Assert.AreEqual(Convert.ToString(propInfo[i].GetValue(expectedProperties, null)), Convert.ToString(propInfo[i].GetValue(receivedProperties, null))); + } + } + } + catch (NullReferenceException) + { + Assert.Fail("Message not received"); + } + catch (SerializationException) + { + Assert.Fail("Deserialized object not of correct type"); + } + finally + { + receivedMessage.Close(); + service.Close(); + listener.Close(); + } + } + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageService.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageService.cs new file mode 100644 index 0000000000..a473cad986 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MessageService.cs @@ -0,0 +1,158 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System; + using System.Collections.Generic; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.Threading; + + public class MessageService : IQueuedDatagramService1, IQueuedDatagramService2, IQueuedDatagramService3, IInteropService + { + private static Dictionary<string, int> methodCallCount = new Dictionary<string, int>(); + private static ServiceHost serviceHost; + + public static EventWaitHandle CompletionHandle + { + get; + set; + } + + public static int IntendedInvocationCount + { + get; + set; + } + + public static int TotalMethodCallCount + { + get; + set; + } + + // The test must set these paramters + public static List<Type> ContractTypes + { + get; + set; + } + + public static string EndpointAddress + { + get; + set; + } + + public static void DisplayCounts() + { + Console.WriteLine("Method calls:"); + foreach (string key in methodCallCount.Keys) + { + Console.WriteLine(" {0}: {1}", key, methodCallCount[key]); + } + + Console.WriteLine("Total: {0}", TotalMethodCallCount); + } + + public static void StartService(Binding amqpBinding) + { + MessageService.methodCallCount.Clear(); + MessageService.TotalMethodCallCount = 0; + + serviceHost = new ServiceHost(typeof(MessageService)); + + foreach (Type contractType in ContractTypes) + { + serviceHost.AddServiceEndpoint(contractType, amqpBinding, EndpointAddress); + } + + serviceHost.Open(); + } + + public static void StopService() + { + if (serviceHost.State != CommunicationState.Faulted) + { + serviceHost.Close(); + } + } + + public void UpdateCounts(string method) + { + lock (methodCallCount) + { + if (!methodCallCount.ContainsKey(method)) + { + methodCallCount[method] = 0; + } + + ++methodCallCount[method]; + ++TotalMethodCallCount; + if (TotalMethodCallCount >= IntendedInvocationCount && CompletionHandle != null) + { + CompletionHandle.Set(); + } + } + } + + [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] + void IQueuedDatagramService1.Hello(string message) + { + this.UpdateCounts("IQueuedDatagramService1.Hello"); + } + + [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] + void IQueuedDatagramService1.Goodbye() + { + this.UpdateCounts("IQueuedDatagramService1.Goodbye"); + } + + [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] + void IQueuedDatagramService2.Hello(string message) + { + this.UpdateCounts("IQueuedDatagramService2.Hello"); + } + + [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] + void IQueuedDatagramService2.Goodbye() + { + this.UpdateCounts("IQueuedDatagramService2.Goodbye"); + } + + [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] + void IQueuedDatagramService3.Hello(string message) + { + this.UpdateCounts("IQueuedDatagramService3.Hello"); + } + + [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] + void IQueuedDatagramService3.Goodbye() + { + this.UpdateCounts("IQueuedDatagramService3.Goodbye"); + } + + [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] + void IInteropService.Hello(Message message) + { + this.UpdateCounts("IInteropService.Hello"); + } + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MultipleEndpointsSameQueueTest.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MultipleEndpointsSameQueueTest.cs new file mode 100644 index 0000000000..d09832757a --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/MultipleEndpointsSameQueueTest.cs @@ -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. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System; + using System.Collections.Generic; + using System.Reflection; + using System.ServiceModel; + using System.Threading; + using NUnit.Framework; + + [TestFixture] + public class MultipleEndpointsSameQueueTest + { + private MessageClient client; + + [SetUp] + public void Setup() + { + // Create client + this.client = new MessageClient(); + this.client.NumberOfMessages = 3; + this.client.NumberOfIterations = 5; + + // Setup and start service + MessageService.EndpointAddress = "amqp:message_queue"; + + MessageService.ContractTypes = new List<Type>(); + MessageService.ContractTypes.Add(typeof(IQueuedDatagramService1)); + MessageService.ContractTypes.Add(typeof(IQueuedDatagramService2)); + MessageService.ContractTypes.Add(typeof(IQueuedDatagramService3)); + MessageService.CompletionHandle = new EventWaitHandle(false, EventResetMode.AutoReset); + MessageService.IntendedInvocationCount = this.client.NumberOfIterations * (this.client.NumberOfMessages + 1) * MessageService.ContractTypes.Count; + + MessageService.StartService(Util.GetBinding()); + } + + [Test] + public void Run() + { + // Create wcf amqpchannel and send messages + MethodInfo runClientMethod = this.client.GetType().GetMethod("RunClient"); + EndpointAddress address = new EndpointAddress("amqp:amq.direct?routingkey=routing_key"); + + foreach (Type contractType in MessageService.ContractTypes) + { + MethodInfo runClientT = runClientMethod.MakeGenericMethod(contractType); + runClientT.Invoke(this.client, new object[] { address }); + } + + // Allow the wcf service to process all the messages before validation + MessageService.CompletionHandle.WaitOne(TimeSpan.FromSeconds(10.0), false); + + // Validation + int expectedMethodCallCount = this.client.NumberOfIterations * (this.client.NumberOfMessages + 1) * MessageService.ContractTypes.Count; + + Assert.AreEqual(expectedMethodCallCount, MessageService.TotalMethodCallCount); + } + + [TearDown] + public void Cleanup() + { + MessageService.StopService(); + } + } +} diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/Properties/AssemblyInfo.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..b47a25494f --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Apache.Qpid.Test.Channel.Functional")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("552dca74-b5a3-4ad3-a718-4a1dd03db039")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/RunTests.bat b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/RunTests.bat new file mode 100755 index 0000000000..4b83993257 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/RunTests.bat @@ -0,0 +1,34 @@ +@echo OFF + +REM Licensed to the Apache Software Foundation (ASF) under one +REM or more contributor license agreements. See the NOTICE file +REM distributed with this work for additional information +REM regarding copyright ownership. The ASF licenses this file +REM to you under the Apache License, Version 2.0 (the +REM "License"); you may not use this file except in compliance +REM with the License. You may obtain a copy of the License at +REM +REM http://www.apache.org/licenses/LICENSE-2.0 +REM +REM Unless required by applicable law or agreed to in writing, +REM software distributed under the License is distributed on an +REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +REM KIND, either express or implied. See the License for the +REM specific language governing permissions and limitations +REM under the License. + + +set nunit_exe=%programfiles%\NUnit 2.5.1\bin\net-2.0\nunit-console.exe +set qpid_dll_location=..\..\..\..\..\..\..\cpp\build\src\Debug +set configuration_name=bin\Debug +set qcreate_location=..\..\..\..\..\..\tools\QCreate\Debug + +copy %qpid_dll_location%\qpidclient.dll %configuration_name% +copy %qpid_dll_location%\qpidcommon.dll %configuration_name% + +copy %qpid_dll_location%\qpidclient.dll %qcreate_location% +copy %qpid_dll_location%\qpidcommon.dll %qcreate_location% + +%qcreate_location%\QCreate.exe amq.direct routing_key message_queue + +"%nunit_exe%" %configuration_name%\Apache.Qpid.Test.Channel.Functional.dll diff --git a/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/Util.cs b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/Util.cs new file mode 100644 index 0000000000..97be1fb925 --- /dev/null +++ b/qpid/wcf/test/Apache/Qpid/Test/Channel/Functional/Util.cs @@ -0,0 +1,74 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +namespace Apache.Qpid.Test.Channel.Functional +{ + using System.Collections.Generic; + using System.IO; + using System.ServiceModel; + using System.ServiceModel.Channels; + using Apache.Qpid.Channel; + + internal class Util + { + public static Dictionary<string, string> GetProperties(string path) + { + string fileData = string.Empty; + using (StreamReader sr = new StreamReader(path)) + { + fileData = sr.ReadToEnd().Replace("\r", string.Empty); + } + + Dictionary<string, string> properties = new Dictionary<string, string>(); + string[] kvp; + string[] records = fileData.Split("\n".ToCharArray()); + foreach (string record in records) + { + if (record[0] == '/' || record[0] == '*') + { + continue; + } + + kvp = record.Split("=".ToCharArray()); + properties.Add(kvp[0], kvp[1]); + } + + return properties; + } + + public static Binding GetBinding() + { + return new AmqpBinding(); + } + + public static Binding GetCustomBinding() + { + AmqpTransportBindingElement transportElement = new AmqpTransportBindingElement(); + RawMessageEncodingBindingElement encodingElement = new RawMessageEncodingBindingElement(); + transportElement.BrokerHost = "127.0.0.1"; + transportElement.TransferMode = TransferMode.Streamed; + + CustomBinding brokerBinding = new CustomBinding(); + brokerBinding.Elements.Add(encodingElement); + brokerBinding.Elements.Add(transportElement); + + return brokerBinding; + } + } +} diff --git a/qpid/wcf/tools/QCreate/QCreate.cpp b/qpid/wcf/tools/QCreate/QCreate.cpp new file mode 100644 index 0000000000..7b0231f339 --- /dev/null +++ b/qpid/wcf/tools/QCreate/QCreate.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 "stdafx.h" + +#include <qpid/client/Connection.h> +#include <qpid/client/Session.h> + +#include <cstdlib> +#include <iostream> + +using namespace qpid::client; +using namespace qpid::framing; + + +int main(int argc, char** argv) { + + std::string exchange = argc>1 ? argv[1] : "amq.direct"; + std::string bindingKey = argc>2 ? argv[2] : "routing_key"; + std::string queue = argc>3 ? argv[3] : "message_queue"; + + const char* host = "127.0.0.1"; + int port = 5672; + Connection connection; + + try { + connection.open(host, port); + Session session = connection.newSession(); + + + //--------- Main body of program -------------------------------------------- + + // Create a queue and route all messages whose + // routing key is "routing_key" to this newly created queue. + + session.queueDeclare(arg::queue=queue); + session.exchangeBind(arg::exchange=exchange, arg::queue=queue, arg::bindingKey=bindingKey); + + //----------------------------------------------------------------------------- + + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; + +} + diff --git a/qpid/wcf/tools/QCreate/QCreate.sln b/qpid/wcf/tools/QCreate/QCreate.sln new file mode 100644 index 0000000000..c01675d53a --- /dev/null +++ b/qpid/wcf/tools/QCreate/QCreate.sln @@ -0,0 +1,40 @@ +
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual C++ Express 2008
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License
+#
+
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QCreate", "QCreate.vcproj", "{7CA88318-485A-4D95-A321-43DFBB6EA28B}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {7CA88318-485A-4D95-A321-43DFBB6EA28B}.Debug|Win32.ActiveCfg = Debug|Win32
+ {7CA88318-485A-4D95-A321-43DFBB6EA28B}.Debug|Win32.Build.0 = Debug|Win32
+ {7CA88318-485A-4D95-A321-43DFBB6EA28B}.Release|Win32.ActiveCfg = Release|Win32
+ {7CA88318-485A-4D95-A321-43DFBB6EA28B}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/qpid/wcf/tools/QCreate/QCreate.vcproj b/qpid/wcf/tools/QCreate/QCreate.vcproj new file mode 100644 index 0000000000..e58077d78c --- /dev/null +++ b/qpid/wcf/tools/QCreate/QCreate.vcproj @@ -0,0 +1,232 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="QCreate"
+ ProjectGUID="{7CA88318-485A-4D95-A321-43DFBB6EA28B}"
+ RootNamespace="QCreate"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""$(BOOST_ROOT)\include\$(BOOST_VERSION)";"$(BOOST_ROOT)\.";..\..\..\cpp\include;..\..\..\cpp\build\include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="qpidcommon.lib qpidclient.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories=".;"$(BOOST_ROOT)\lib";..\..\..\cpp\build\src\Debug"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\QCreate.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\stdafx.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\stdafx.h"
+ >
+ </File>
+ <File
+ RelativePath=".\targetver.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ <File
+ RelativePath=".\ReadMe.txt"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/qpid/wcf/tools/QCreate/ReadMe.txt b/qpid/wcf/tools/QCreate/ReadMe.txt new file mode 100644 index 0000000000..b3efb84503 --- /dev/null +++ b/qpid/wcf/tools/QCreate/ReadMe.txt @@ -0,0 +1,52 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +======================================================================== + CONSOLE APPLICATION : QCreate Project Overview +======================================================================== + +AppWizard has created this QCreate application for you. + +This file contains a summary of what you will find in each of the files that +make up your QCreate application. + + +QCreate.vcproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +QCreate.cpp + This is the main application source file. + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named QCreate.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/qpid/wcf/tools/QCreate/stdafx.cpp b/qpid/wcf/tools/QCreate/stdafx.cpp new file mode 100644 index 0000000000..568cd3b7d6 --- /dev/null +++ b/qpid/wcf/tools/QCreate/stdafx.cpp @@ -0,0 +1,27 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// stdafx.cpp : source file that includes just the standard includes +// QCreate.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/qpid/wcf/tools/QCreate/stdafx.h b/qpid/wcf/tools/QCreate/stdafx.h new file mode 100644 index 0000000000..a516e19a10 --- /dev/null +++ b/qpid/wcf/tools/QCreate/stdafx.h @@ -0,0 +1,34 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include <stdio.h> +#include <tchar.h> + + + +// TODO: reference additional headers your program requires here diff --git a/qpid/wcf/tools/QCreate/targetver.h b/qpid/wcf/tools/QCreate/targetver.h new file mode 100644 index 0000000000..9cfb78641b --- /dev/null +++ b/qpid/wcf/tools/QCreate/targetver.h @@ -0,0 +1,32 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. +#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif + |