diff options
author | Rajith Muditha Attapattu <rajith@apache.org> | 2011-05-27 15:44:23 +0000 |
---|---|---|
committer | Rajith Muditha Attapattu <rajith@apache.org> | 2011-05-27 15:44:23 +0000 |
commit | 66765100f4257159622cefe57bed50125a5ad017 (patch) | |
tree | a88ee23bb194eb91f0ebb2d9b23ff423e3ea8e37 /qpid/cpp/bindings/qmf | |
parent | 1aeaa7b16e5ce54f10c901d75c4d40f9f88b9db6 (diff) | |
parent | 88b98b2f4152ef59a671fad55a0d08338b6b78ca (diff) | |
download | qpid-python-rajith_jms_client.tar.gz |
Creating a branch for experimenting with some ideas for JMS client.rajith_jms_client
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/rajith_jms_client@1128369 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/cpp/bindings/qmf')
-rw-r--r-- | qpid/cpp/bindings/qmf/Makefile.am | 33 | ||||
-rw-r--r-- | qpid/cpp/bindings/qmf/python/Makefile.am | 51 | ||||
-rw-r--r-- | qpid/cpp/bindings/qmf/python/python.i | 143 | ||||
-rw-r--r-- | qpid/cpp/bindings/qmf/python/qmf.py | 1680 | ||||
-rw-r--r-- | qpid/cpp/bindings/qmf/qmfengine.i | 59 | ||||
-rw-r--r-- | qpid/cpp/bindings/qmf/ruby/Makefile.am | 45 | ||||
-rw-r--r-- | qpid/cpp/bindings/qmf/ruby/qmf.rb | 1522 | ||||
-rw-r--r-- | qpid/cpp/bindings/qmf/ruby/ruby.i | 106 | ||||
-rw-r--r-- | qpid/cpp/bindings/qmf/tests/Makefile.am | 27 | ||||
-rwxr-xr-x | qpid/cpp/bindings/qmf/tests/agent_ruby.rb | 279 | ||||
-rw-r--r-- | qpid/cpp/bindings/qmf/tests/python_agent.py | 326 | ||||
-rwxr-xr-x | qpid/cpp/bindings/qmf/tests/python_console.py | 311 | ||||
-rwxr-xr-x | qpid/cpp/bindings/qmf/tests/ruby_console.rb | 174 | ||||
-rwxr-xr-x | qpid/cpp/bindings/qmf/tests/ruby_console_test.rb | 397 | ||||
-rwxr-xr-x | qpid/cpp/bindings/qmf/tests/run_interop_tests | 135 | ||||
-rw-r--r-- | qpid/cpp/bindings/qmf/tests/test_base.rb | 82 |
16 files changed, 5370 insertions, 0 deletions
diff --git a/qpid/cpp/bindings/qmf/Makefile.am b/qpid/cpp/bindings/qmf/Makefile.am new file mode 100644 index 0000000000..eebb4b94de --- /dev/null +++ b/qpid/cpp/bindings/qmf/Makefile.am @@ -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. +# + +if HAVE_SWIG + +EXTRA_DIST = qmfengine.i +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 new file mode 100644 index 0000000000..8abad32959 --- /dev/null +++ b/qpid/cpp/bindings/qmf/python/Makefile.am @@ -0,0 +1,51 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +if HAVE_PYTHON_DEVEL + +INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src/qmf -I$(top_srcdir)/src -I$(top_builddir)/src + +generated_file_list = \ + qmfengine.cpp \ + qmfengine.py + +EXTRA_DIST = python.i +BUILT_SOURCES = $(generated_file_list) +SWIG_FLAGS = -w362,401 + +$(generated_file_list): $(srcdir)/python.i $(srcdir)/../qmfengine.i + $(SWIG) -c++ -python $(SWIG_FLAGS) $(INCLUDES) $(QPID_CXXFLAGS) -I$(top_srcdir)/src/qmf -I/usr/include -o qmfengine.cpp $(srcdir)/python.i + +pylibdir = $(PYTHON_LIB) + +lib_LTLIBRARIES = _qmfengine.la +qenginedir = $(pyexecdir) +qengine_PYTHON = qmfengine.py qmf.py + +#_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/libqmf.la +_qmfengine_la_CXXFLAGS = $(INCLUDES) -I$(srcdir)/qmf -I$(PYTHON_INC) -fno-strict-aliasing +nodist__qmfengine_la_SOURCES = qmfengine.cpp + +CLEANFILES = $(generated_file_list) + +endif # HAVE_PYTHON_DEVEL + diff --git a/qpid/cpp/bindings/qmf/python/python.i b/qpid/cpp/bindings/qmf/python/python.i new file mode 100644 index 0000000000..5e25d155f9 --- /dev/null +++ b/qpid/cpp/bindings/qmf/python/python.i @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +%module qmfengine + + +/* 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 +} + + +/* 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..06d3070841 --- /dev/null +++ b/qpid/cpp/bindings/qmf/python/qmf.py @@ -0,0 +1,1680 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 +import logging +from threading import Thread +from threading import RLock +from threading import Condition +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) +from qmfengine import (SEV_EMERG, SEV_ALERT, SEV_CRIT, SEV_ERROR, SEV_WARN, SEV_NOTICE, + SEV_INFORM, SEV_DEBUG) + + +def qmf_to_native(val): + typecode = val.getType() + if typecode == TYPE_UINT8: return val.asUint() + elif typecode == TYPE_UINT16: return val.asUint() + elif typecode == TYPE_UINT32: return val.asUint() + elif typecode == TYPE_UINT64: return val.asUint64() + elif typecode == TYPE_SSTR: return val.asString() + elif typecode == TYPE_LSTR: return val.asString() + elif typecode == TYPE_ABSTIME: return val.asInt64() + elif typecode == TYPE_DELTATIME: return val.asUint64() + elif typecode == TYPE_REF: return ObjectId(val.asObjectId()) + elif typecode == TYPE_BOOL: return val.asBool() + elif typecode == TYPE_FLOAT: return val.asFloat() + elif typecode == TYPE_DOUBLE: return val.asDouble() + elif typecode == TYPE_UUID: return val.asUuid() + elif typecode == TYPE_INT8: return val.asInt() + elif typecode == TYPE_INT16: return val.asInt() + elif typecode == TYPE_INT32: return val.asInt() + elif typecode == TYPE_INT64: return val.asInt64() + elif typecode == TYPE_MAP: return value_to_dict(val) + elif typecode == TYPE_LIST: return value_to_list(val) + else: + # when TYPE_OBJECT + logging.error( "Unsupported type for get_attr? '%s'" % str(val.getType()) ) + return None + + +def native_to_qmf(target, value): + val = None + typecode = None + if target.__class__ == qmfengine.Value: + val = target + typecode = val.getType() + else: + typecode = target + val = qmfengine.Value(typecode) + + if typecode == TYPE_UINT8: val.setUint(value) + elif typecode == TYPE_UINT16: val.setUint(value) + elif typecode == TYPE_UINT32: val.setUint(value) + elif typecode == TYPE_UINT64: val.setUint64(value) + elif typecode == TYPE_SSTR: + if value: val.setString(value) + else: val.setString('') + elif typecode == TYPE_LSTR: + if value: val.setString(value) + else: val.setString('') + elif typecode == TYPE_ABSTIME: val.setInt64(value) + elif typecode == TYPE_DELTATIME: val.setUint64(value) + elif typecode == TYPE_REF: val.setObjectId(value.impl) + elif typecode == TYPE_BOOL: val.setBool(value) + elif typecode == TYPE_FLOAT: val.setFloat(value) + elif typecode == TYPE_DOUBLE: val.setDouble(value) + elif typecode == TYPE_UUID: val.setUuid(value) + elif typecode == TYPE_INT8: val.setInt(value) + elif typecode == TYPE_INT16: val.setInt(value) + elif typecode == TYPE_INT32: val.setInt(value) + elif typecode == TYPE_INT64: val.setInt64(value) + elif typecode == TYPE_MAP: dict_to_value(val, value) + elif typecode == TYPE_LIST: list_to_value(val, value) + else: + # when TYPE_OBJECT + logging.error("Unsupported type for get_attr? '%s'" % str(val.getType())) + return None + return val + + +def pick_qmf_type(value): + if value.__class__ == int: + if value >= 0: + if value < 0x100000000: return TYPE_UINT32 + return TYPE_UINT64 + else: + if value > -0xffffffff: return TYPE_INT32 + return TYPE_INT64 + + if value.__class__ == long: + if value >= 0: return TYPE_UINT64 + return TYPE_INT64 + + if value.__class__ == str: + if len(value) < 256: return TYPE_SSTR + return TYPE_LSTR + + if value.__class__ == float: return TYPE_DOUBLE + if value.__class__ == bool: return TYPE_BOOL + if value == None: return TYPE_BOOL + if value.__class__ == dict: return TYPE_MAP + if value.__class__ == list: return TYPE_LIST + + raise "QMF type not known for native type %s" % value.__class__ + + +def value_to_dict(val): + if not val.isMap(): raise "value_to_dict must be given a map value" + mymap = {} + for i in range(val.keyCount()): + key = val.key(i) + mymap[key] = qmf_to_native(val.byKey(key)) + return mymap + + +def dict_to_value(val, mymap): + for key, value in mymap.items(): + if key.__class__ != str: raise "QMF map key must be a string" + typecode = pick_qmf_type(value) + val.insert(key, native_to_qmf(typecode, value)) + + +def value_to_list(val): + mylist = [] + if val.isList(): + for i in range(val.listItemCount()): + mylist.append(qmf_to_native(val.listItem(i))) + return mylist + #if val.isArray(): + # for i in range(val.arrayItemCount()): + # mylist.append(qmf_to_native(val.arrayItem(i))) + # return mylist + + raise "value_to_list must be given a list value" + + +def list_to_value(val, mylist): + for item in mylist: + typecode = pick_qmf_type(item) + val.appendToList(native_to_qmf(typecode, item)) + + + ##============================================================================== + ## CONNECTION + ##============================================================================== + +class ConnectionSettings(object): + #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) == int: + _v = qmfengine.Value(TYPE_UINT32) + _v.setUint(val) + elif type(val) == bool: + _v = qmfengine.Value(TYPE_BOOL) + _v.setBool(val) + else: + raise Exception("Argument error: value for attribute '%s' has unsupported type: %s" % ( key, type(val))) + + good = self.impl.setAttr(key, _v) + if not good: + raise Exception("Argument error: unsupported attribute '%s'" % key ) + + + def get_attr(self, key): + _v = self.impl.getAttr(key) + if _v.isString(): + return _v.asString() + elif _v.isUint(): + return _v.asUint() + elif _v.isBool(): + return _v.asBool() + else: + raise Exception("Argument error: value for attribute '%s' has unsupported type: %s" % ( key, str(_v.getType()))) + + + def __getattr__(self, name): + return self.get_attr(name) + + + def __setattr__(self, name, value): + if name == "impl": + return super.__setattr__(self, name, value) + return self.set_attr(name, value) + + + +class ConnectionHandler: + def conn_event_connected(self): None + def conn_event_disconnected(self, error): None + def conn_event_visit(self): 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_to_delete = [] + self._conn_handlers = [] + self._connected = False + self._operational = True + self.start() + + + def destroy(self, timeout=None): + logging.debug("Destroying Connection...") + self._operational = False + self.kick() + self.join(timeout) + logging.debug("... Conn Destroyed!" ) + if self.isAlive(): + logging.error("Error: Connection thread '%s' is hung..." % self.getName()) + + + def connected(self): + return self._connected + + + def kick(self): + self.impl.notify() + + + def add_conn_handler(self, handler): + self._lock.acquire() + try: + self._new_conn_handlers.append(handler) + finally: + self._lock.release() + self.kick() + + + def del_conn_handler(self, handler): + self._lock.acquire() + try: + self._conn_handlers_to_delete.append(handler) + finally: + self._lock.release() + self.kick() + + + def run(self): + eventImpl = qmfengine.ResilientConnectionEvent() + new_handlers = [] + del_handlers = [] + bt_count = 0 + + while self._operational: + logging.debug("Connection thread waiting for socket data...") + self._sock.recv(1) + + self._lock.acquire() + try: + new_handlers = self._new_conn_handlers + del_handlers = self._conn_handlers_to_delete + self._new_conn_handlers = [] + self._conn_handlers_to_delete = [] + finally: + self._lock.release() + + for nh in new_handlers: + self._conn_handlers.append(nh) + if self._connected: + nh.conn_event_connected() + new_handlers = [] + + for dh in del_handlers: + if dh in self._conn_handlers: + self._conn_handlers.remove(dh) + del_handlers = [] + + valid = self.impl.getEvent(eventImpl) + while valid: + try: + if eventImpl.kind == qmfengine.ResilientConnectionEvent.CONNECTED: + logging.debug("Connection thread: CONNECTED event received.") + self._connected = True + for h in self._conn_handlers: + h.conn_event_connected() + + elif eventImpl.kind == qmfengine.ResilientConnectionEvent.DISCONNECTED: + logging.debug("Connection thread: DISCONNECTED event received.") + self._connected = False + for h in self._conn_handlers: + h.conn_event_disconnected(eventImpl.errorText) + + elif eventImpl.kind == qmfengine.ResilientConnectionEvent.SESSION_CLOSED: + logging.debug("Connection thread: SESSION_CLOSED event received.") + eventImpl.sessionContext.handler.sess_event_session_closed(eventImpl.sessionContext, eventImpl.errorText) + + elif eventImpl.kind == qmfengine.ResilientConnectionEvent.RECV: + logging.debug("Connection thread: RECV event received.") + eventImpl.sessionContext.handler.sess_event_recv(eventImpl.sessionContext, eventImpl.message) + else: + logging.debug("Connection thread received unknown event: '%s'" % str(eventImpl.kind)) + + except: + import traceback + logging.error( "Exception occurred during Connection event processing:" ) + logging.error( str(sys.exc_info()) ) + if bt_count < 2: + traceback.print_exc() + traceback.print_stack() + bt_count += 1 + + self.impl.popEvent() + valid = self.impl.getEvent(eventImpl) + + for h in self._conn_handlers: + h.conn_event_visit() + + logging.debug("Shutting down Connection thread") + + + +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 destroy(self): + self._conn.impl.destroySession(self.handle) + + + + ##============================================================================== + ## OBJECTS and EVENTS + ##============================================================================== + +class QmfEvent(object): + # attr_reader :impl, :event_class + def __init__(self, cls, kwargs={}): + self._allow_sets = True + if kwargs.has_key("broker"): + self._broker = kwargs["broker"] + else: + self._broker = None + if cls: + self.event_class = cls + self.impl = qmfengine.Event(self.event_class.impl) + elif kwargs.has_key("impl"): + self.impl = qmfengine.Event(kwargs["impl"]) + self.event_class = SchemaEventClass(None, None, 0, + {"impl":self.impl.getClass()}) + else: + raise Exception("Argument error: required parameter ('impl') not supplied") + + + def arguments(self): + list = [] + for arg in self.event_class.arguments: + list.append([arg, self.get_attr(arg.name())]) + return list + + + def get_attr(self, name): + val = self._value(name) + return qmf_to_native(val) + + + def set_attr(self, name, v): + val = self._value(name) + native_to_qmf(val, v) + + + def __getitem__(self, name): + return self.get_attr(name) + + + def __setitem__(self, name, value): + self.set_attr(name, value) + + + def __setattr__(self, name, value): + # + # Ignore the internal attributes, set them normally... + # + if (name[0] == '_' or + name == 'impl' or + name == 'event_class'): + return super.__setattr__(self, name, value) + + if not self._allow_sets: + raise Exception("'Set' operations not permitted on this object") + + # + # If the name matches an argument name, set the value of the argument. + # + # print "set name=%s" % str(name) + for arg in self.event_class.arguments: + if arg.name() == name: + return self.set_attr(name, value) + + # unrecognized name? should I raise an exception? + super.__setattr__(self, name, value) + + + def __getattr__(self, name, *args): + # + # If the name matches an argument name, return the value of the argument. + # + for arg in self.event_class.arguments: + if arg.name() == name: + return self.get_attr(name) + + # + # This name means nothing to us, pass it up the line to the parent + # class's handler. + # + # print "__getattr__=%s" % str(name) + super.__getattr__(self, name) + + + def _value(self, name): + val = self.impl.getValue(name) + if not val: + raise Exception("Argument error: attribute named '%s' not defined for package %s, class %s" % + (name, + self.event_class.impl.getClassKey().getPackageName(), + self.event_class.impl.getClassKey().getClassName())) + return val + + +class QmfObject(object): + # attr_reader :impl, :object_class + def __init__(self, cls, kwargs={}): + self._cv = Condition() + self._sync_count = 0 + self._sync_result = None + self._allow_sets = False + if kwargs.has_key("broker"): + self._broker = kwargs["broker"] + else: + self._broker = None + if cls: + self.object_class = cls + self.impl = qmfengine.Object(self.object_class.impl) + elif kwargs.has_key("impl"): + self.impl = qmfengine.Object(kwargs["impl"]) + self.object_class = SchemaObjectClass(None, + None, + {"impl":self.impl.getClass()}) + else: + raise Exception("Argument error: required parameter ('impl') not supplied") + + + def destroy(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 properties(self): + list = [] + for prop in self.object_class.properties: + list.append([prop, self.get_attr(prop.name())]) + return list + + + def statistics(self): + list = [] + for stat in self.object_class.statistics: + list.append([stat, self.get_attr(stat.name())]) + return list + + + def get_attr(self, name): + val = self._value(name) + return qmf_to_native(val) + + + def set_attr(self, name, v): + val = self._value(name) + native_to_qmf(val, v) + + + 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 __setattr__(self, name, value): + # + # Ignore the internal attributes, set them normally... + # + if (name[0] == '_' or + name == 'impl' or + name == 'object_class'): + return super.__setattr__(self, name, value) + + if not self._allow_sets: + raise Exception("'Set' operations not permitted on this object") + # + # If the name matches a property name, set the value of the property. + # + # print "set name=%s" % str(name) + for prop in self.object_class.properties: + if prop.name() == name: + return self.set_attr(name, value) + # + # otherwise, check for a statistic set... + # + for stat in self.object_class.statistics: + if stat.name() == name: + return self.set_attr(name, value) + + # unrecognized name? should I raise an exception? + super.__setattr__(self, name, value) + + + def __getattr__(self, name, *args): + # + # If the name matches a property name, return the value of the property. + # + for prop in self.object_class.properties: + if prop.name() == name: + return self.get_attr(name) + # + # Do the same for statistics + # + for stat in self.object_class.statistics: + if stat.name() == name: + return self.get_attr(name) + # + # If we still haven't found a match for the name, check to see if + # it matches a method name. If so, marshall up the arguments into + # a map, and invoke the method. + # + for method in self.object_class.methods: + if method.name() == name: + argMap = self._marshall(method, args) + return lambda name, argMap : self._invokeMethod(name, argMap) + + # + # This name means nothing to us, pass it up the line to the parent + # class's handler. + # + # print "__getattr__=%s" % str(name) + super.__getattr__(self, name) + + + def _invokeMethod(self, name, argMap): + """ + Private: Helper function that invokes an object's method, and waits for the result. + """ + self._cv.acquire() + try: + timeout = 30 + self._sync_count = 1 + self.impl.invokeMethod(name, argMap, self) + if self._broker: + self._broker.conn.kick() + self._cv.wait(timeout) + if self._sync_count == 1: + raise Exception("Timed out: waiting for response to method call.") + finally: + self._cv.release() + + return self._sync_result + + + def _method_result(self, result): + """ + Called to return the result of a method call on an object + """ + self._cv.acquire(); + try: + self._sync_result = result + self._sync_count -= 1 + self._cv.notify() + finally: + self._cv.release() + + + def _marshall(schema, args): + ''' + Private: Convert a list of arguments (positional) into a Value object of type "map". + Used to create the argument parameter for an object's method invokation. + ''' + # Build a map of the method's arguments + map = qmfengine.Value(TYPE_MAP) + for arg in schema.arguments: + if arg.direction == DIR_IN or arg.direction == DIR_IN_OUT: + map.insert(arg.name, qmfengine.Value(arg.typecode)) + + # install each argument's value into the map + marshalled = Arguments(map) + idx = 0 + for arg in schema.arguments: + if arg.direction == DIR_IN or arg.direction == DIR_IN_OUT: + if args[idx]: + marshalled[arg.name] = args[idx] + idx += 1 + + return marshalled.map + + + def _value(self, name): + val = self.impl.getValue(name) + if not val: + raise Exception("Argument error: attribute named '%s' not defined for package %s, class %s" % + (name, + self.object_class.impl.getClassKey().getPackageName(), + self.object_class.impl.getClassKey().getClassName())) + return val + + + +class AgentObject(QmfObject): + def __init__(self, cls, kwargs={}): + QmfObject.__init__(self, cls, kwargs) + self._allow_sets = True + + + def destroy(self): + self.impl.destroy() + + + def set_object_id(self, oid): + self.impl.setObjectId(oid.impl) + + + +class ConsoleObject(QmfObject): + # attr_reader :current_time, :create_time, :delete_time + def __init__(self, cls, kwargs={}): + QmfObject.__init__(self, cls, kwargs) + + + def update(self): + if not self._broker: + raise Exception("No linkage to broker") + newer = self._broker.console.objects(Query({"object_id":object_id})) + if newer.size != 1: + raise Exception("Expected exactly one update for this object, %d present" % int(newer.size)) + self.merge_update(newer[0]) + + + def merge_update(self, newObject): + self.impl.merge(new_object.impl) + + + def is_deleted(self): + return self.impl.isDeleted() + + + def key(self): pass + + + +class ObjectId: + def __init__(self, impl=None): + if impl: + self.impl = impl + else: + self.impl = qmfengine.ObjectId() + self.agent_key = "%d.%d" % (self.impl.getBrokerBank(), self.impl.getAgentBank()) + + + def object_num_high(self): + return self.impl.getObjectNumHi() + + + def object_num_low(self): + return self.impl.getObjectNumLo() + + + def agent_key(self): + self.agent_key + + def __eq__(self, other): + if not isinstance(other, self.__class__): return False + return self.impl == other.impl + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return self.impl.str() + + + +class Arguments(object): + def __init__(self, map): + self.map = map + self._by_hash = {} + key_count = self.map.keyCount() + a = 0 + while a < key_count: + key = self.map.key(a) + self._by_hash[key] = qmf_to_native(self.map.byKey(key)) + 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 self._by_hash.__iter__ + + + def __getattr__(self, name): + if name in self._by_hash: + return self._by_hash[name] + return super.__getattr__(self, name) + + + def __setattr__(self, name, value): + # + # ignore local data members + # + if (name[0] == '_' or + name == 'map'): + return super.__setattr__(self, name, value) + + if name in self._by_hash: + self._by_hash[name] = value + return self.set(name, value) + + return super.__setattr__(self, name, value) + + + def set(self, key, value): + val = self.map.byKey(key) + native_to_qmf(val, value) + + + +class MethodResponse(object): + def __init__(self, impl): + self.impl = qmfengine.MethodResponse(impl) + + + def status(self): + return self.impl.getStatus() + + + def exception(self): + return self.impl.getException() + + + def text(self): + return exception().asString() + + + def args(self): + return Arguments(self.impl.getArgs()) + + + def __getattr__(self, name): + myArgs = self.args() + return myArgs.__getattr__(name) + + + def __setattr__(self, name, value): + if name == 'impl': + return super.__setattr__(self, name, value) + + myArgs = self.args() + return myArgs.__setattr__(name, value) + + + + ##============================================================================== + ## QUERY + ##============================================================================== + + +class Query: + def __init__(self, kwargs={}): + if "impl" in kwargs: + self.impl = kwargs["impl"] + else: + package = '' + if "key" in kwargs: + # construct using SchemaClassKey: + self.impl = qmfengine.Query(kwargs["key"]) + elif "object_id" in kwargs: + self.impl = qmfengine.Query(kwargs["object_id"].impl) + else: + if "package" in kwargs: + package = kwargs["package"] + if "class" in kwargs: + self.impl = qmfengine.Query(kwargs["class"], package) + else: + raise Exception("Argument error: invalid arguments, use 'key', 'object_id' or 'class'[,'package']") + + + 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={}): + if "impl" in kwargs: + self.impl = kwargs["impl"] + else: + 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"]) + + + def name(self): + return self.impl.getName() + + + def direction(self): + return self.impl.getDirection() + + + def typecode(self): + return self.impl.getType() + + + def __repr__(self): + return self.name() + + + +class SchemaMethod: + # attr_reader :impl, arguments + def __init__(self, name, kwargs={}): + self.arguments = [] + if "impl" in kwargs: + self.impl = kwargs["impl"] + for i in range(self.impl.getArgumentCount()): + self.arguments.append(SchemaArgument(None,None,{"impl":self.impl.getArgument(i)})) + else: + self.impl = qmfengine.SchemaMethod(name) + if kwargs.has_key("desc"): self.impl.setDesc(kwargs["desc"]) + + + def add_argument(self, arg): + self.arguments.append(arg) + self.impl.addArgument(arg.impl) + + def name(self): + return self.impl.getName() + + def __repr__(self): + return self.name() + + + +class SchemaProperty: + #attr_reader :impl + def __init__(self, name, typecode, kwargs={}): + if "impl" in kwargs: + self.impl = kwargs["impl"] + else: + 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() + + def __repr__(self): + return self.name() + + + +class SchemaStatistic: + # attr_reader :impl + def __init__(self, name, typecode, kwargs={}): + if "impl" in kwargs: + self.impl = kwargs["impl"] + else: + 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"]) + + + def name(self): + return self.impl.getName() + + def __repr__(self): + return self.name() + + + +class SchemaClassKey: + #attr_reader :impl + def __init__(self, i): + self.impl = i + + + def package_name(self): + return self.impl.getPackageName() + + + def class_name(self): + return self.impl.getClassName() + + def __repr__(self): + return self.impl.asString() + + + +class SchemaObjectClass: + # attr_reader :impl, :properties, :statistics, :methods + def __init__(self, package, name, kwargs={}): + self.properties = [] + self.statistics = [] + self.methods = [] + if "impl" in kwargs: + self.impl = kwargs["impl"] + + for i in range(self.impl.getPropertyCount()): + self.properties.append(SchemaProperty(None, None, {"impl":self.impl.getProperty(i)})) + + for i in range(self.impl.getStatisticCount()): + self.statistics.append(SchemaStatistic(None, None, {"impl":self.impl.getStatistic(i)})) + + for i in range(self.impl.getMethodCount()): + self.methods.append(SchemaMethod(None, {"impl":self.impl.getMethod(i)})) + else: + self.impl = qmfengine.SchemaObjectClass(package, name) + + + 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 class_key(self): + return SchemaClassKey(self.impl.getClassKey()) + + + def package_name(self): + return self.impl.getClassKey().getPackageName() + + + def class_name(self): + return self.impl.getClassKey().getClassName() + + + +class SchemaEventClass: + # attr_reader :impl :arguments + def __init__(self, package, name, sev, kwargs={}): + self.arguments = [] + if "impl" in kwargs: + self.impl = kwargs["impl"] + for i in range(self.impl.getArgumentCount()): + self.arguments.append(SchemaArgument(nil, nil, {"impl":self.impl.getArgument(i)})) + else: + self.impl = qmfengine.SchemaEventClass(package, name, sev) + if kwargs.has_key("desc"): self.impl.setDesc(kwargs["desc"]) + + + def add_argument(self, arg): + self.arguments.append(arg) + self.impl.addArgument(arg.impl) + + + def name(self): + return self.impl.getClassKey().getClassName() + + def class_key(self): + return SchemaClassKey(self.impl.getClassKey()) + + + def package_name(self): + return self.impl.getClassKey().getPackageName() + + + def class_name(self): + return self.impl.getClassKey().getClassName() + + + ##============================================================================== + ## 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(Thread): + # attr_reader :impl + def __init__(self, handler=None, kwargs={}): + Thread.__init__(self) + self._handler = handler + self.impl = qmfengine.Console() + self._event = qmfengine.ConsoleEvent() + self._broker_list = [] + self._cv = Condition() + self._sync_count = 0 + self._sync_result = None + self._select = {} + self._cb_cond = Condition() + self._operational = True + self.start() + + + def destroy(self, timeout=None): + logging.debug("Destroying Console...") + self._operational = False + self.start_console_events() # wake thread up + self.join(timeout) + logging.debug("... Console Destroyed!") + if self.isAlive(): + logging.error( "Console thread '%s' is hung..." % self.getName() ) + + + def add_connection(self, conn): + broker = Broker(self, conn) + self._cv.acquire() + try: + self._broker_list.append(broker) + finally: + self._cv.release() + return broker + + + def del_connection(self, broker): + logging.debug("shutting down broker...") + broker.shutdown() + logging.debug("...broker down.") + self._cv.acquire() + try: + self._broker_list.remove(broker) + finally: + self._cv.release() + logging.debug("del_connection() finished") + + + def packages(self): + plist = [] + for i in range(self.impl.packageCount()): + plist.append(self.impl.getPackageName(i)) + return plist + + + def classes(self, package, kind=CLASS_OBJECT): + clist = [] + for i in range(self.impl.classCount(package)): + key = self.impl.getClass(package, i) + class_kind = self.impl.getClassKind(key) + if class_kind == kind: + if kind == CLASS_OBJECT: + clist.append(SchemaObjectClass(None, None, {"impl":self.impl.getObjectClass(key)})) + elif kind == CLASS_EVENT: + clist.append(SchemaEventClass(None, None, 0, {"impl":self.impl.getEventClass(key)})) + return clist + + + def bind_package(self, package): + return self.impl.bindPackage(package) + + + def bind_class(self, kwargs = {}): + if "key" in kwargs: + self.impl.bindClass(kwargs["key"]) + elif "package" in kwargs: + package = kwargs["package"] + if "class" in kwargs: + self.impl.bindClass(package, kwargs["class"]) + else: + self.impl.bindClass(package, "*") + else: + raise Exception("Argument error: invalid arguments, use 'key' or 'package'[,'class']") + + + def bind_event(self, kwargs = {}): + if "key" in kwargs: + self.impl.bindEvent(kwargs["key"]) + elif "package" in kwargs: + package = kwargs["package"] + if "event" in kwargs: + self.impl.bindEvent(package, kwargs["event"]) + else: + self.impl.bindEvent(package, "*") + else: + raise Exception("Argument error: invalid arguments, use 'key' or 'package'[,'event']") + + + def agents(self, broker=None): + blist = [] + if broker: + blist.append(broker) + else: + self._cv.acquire() + try: + # copy while holding lock + blist = self._broker_list[:] + finally: + self._cv.release() + + agents = [] + for b in blist: + for idx in range(b.impl.agentCount()): + agents.append(AgentProxy(b.impl.getAgent(idx), b)) + + return agents + + + def objects(self, query, kwargs = {}): + timeout = 30 + agent = None + temp_args = kwargs.copy() + if type(query) == type({}): + temp_args.update(query) + + if "_timeout" in temp_args: + timeout = temp_args["_timeout"] + temp_args.pop("_timeout") + + if "_agent" in temp_args: + agent = temp_args["_agent"] + temp_args.pop("_agent") + + if type(query) == type({}): + query = Query(temp_args) + + self._select = {} + for k in temp_args.iterkeys(): + if type(k) == str: + self._select[k] = temp_args[k] + + self._cv.acquire() + try: + self._sync_count = 1 + self._sync_result = [] + broker = self._broker_list[0] + broker.send_query(query.impl, None, agent) + self._cv.wait(timeout) + if self._sync_count == 1: + raise Exception("Timed out: waiting for query response") + finally: + self._cv.release() + + return self._sync_result + + + def object(self, query, kwargs = {}): + ''' + Return one and only one object or None. + ''' + objs = objects(query, kwargs) + if len(objs) == 1: + return objs[0] + else: + return None + + + def first_object(self, query, kwargs = {}): + ''' + Return the first of potentially many objects. + ''' + objs = objects(query, kwargs) + if objs: + return objs[0] + else: + return None + + + # Check the object against select to check for a match + def _select_match(self, object): + schema_props = object.properties() + for key in self._select.iterkeys(): + for prop in schema_props: + if key == p[0].name() and self._select[key] != p[1]: + return False + return True + + + def _get_result(self, list, context): + ''' + Called by Broker proxy to return the result of a query. + ''' + self._cv.acquire() + try: + for item in list: + if self._select_match(item): + self._sync_result.append(item) + self._sync_count -= 1 + self._cv.notify() + finally: + self._cv.release() + + + def start_sync(self, query): pass + + + def touch_sync(self, sync): pass + + + def end_sync(self, sync): pass + + + def run(self): + while self._operational: + self._cb_cond.acquire() + try: + self._cb_cond.wait(1) + while self._do_console_events(): + pass + finally: + self._cb_cond.release() + logging.debug("Shutting down Console thread") + + + def start_console_events(self): + self._cb_cond.acquire() + try: + self._cb_cond.notify() + finally: + self._cb_cond.release() + + + def _do_console_events(self): + ''' + Called by the Console thread to poll for events. Passes the events + onto the ConsoleHandler associated with this Console. Is called + periodically, but can also be kicked by Console.start_console_events(). + ''' + count = 0 + valid = self.impl.getEvent(self._event) + while valid: + count += 1 + try: + if self._event.kind == qmfengine.ConsoleEvent.AGENT_ADDED: + logging.debug("Console Event AGENT_ADDED received") + if self._handler: + self._handler.agent_added(AgentProxy(self._event.agent, None)) + elif self._event.kind == qmfengine.ConsoleEvent.AGENT_DELETED: + logging.debug("Console Event AGENT_DELETED received") + if self._handler: + self._handler.agent_deleted(AgentProxy(self._event.agent, None)) + elif self._event.kind == qmfengine.ConsoleEvent.NEW_PACKAGE: + logging.debug("Console Event NEW_PACKAGE received") + if self._handler: + self._handler.new_package(self._event.name) + elif self._event.kind == qmfengine.ConsoleEvent.NEW_CLASS: + logging.debug("Console Event NEW_CLASS received") + if self._handler: + self._handler.new_class(SchemaClassKey(self._event.classKey)) + elif self._event.kind == qmfengine.ConsoleEvent.OBJECT_UPDATE: + logging.debug("Console Event OBJECT_UPDATE received") + if self._handler: + self._handler.object_update(ConsoleObject(None, {"impl":self._event.object}), + self._event.hasProps, self._event.hasStats) + elif self._event.kind == qmfengine.ConsoleEvent.EVENT_RECEIVED: + logging.debug("Console Event EVENT_RECEIVED received") + elif self._event.kind == qmfengine.ConsoleEvent.AGENT_HEARTBEAT: + logging.debug("Console Event AGENT_HEARTBEAT received") + if self._handler: + self._handler.agent_heartbeat(AgentProxy(self._event.agent, None), self._event.timestamp) + elif self._event.kind == qmfengine.ConsoleEvent.METHOD_RESPONSE: + logging.debug("Console Event METHOD_RESPONSE received") + else: + logging.debug("Console thread received unknown event: '%s'" % str(self._event.kind)) + except e: + print "Exception caught in callback thread:", e + self.impl.popEvent() + valid = self.impl.getEvent(self._event) + return count + + + +class AgentProxy: + # attr_reader :broker + def __init__(self, impl, broker): + self.impl = impl + self.broker = broker + self.key = "%d.%d" % (self.impl.getBrokerBank(), self.impl.getAgentBank()) + + + def label(self): + return self.impl.getLabel() + + + def key(self): + return self.key + + +class Broker(ConnectionHandler): + # attr_reader :impl :conn, :console, :broker_bank + def __init__(self, console, conn): + self.broker_bank = 1 + self.console = console + self.conn = conn + self._session = None + self._cv = Condition() + self._stable = 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) + self._operational = True + + + def shutdown(self): + logging.debug("broker.shutdown() called.") + self.console.impl.delConnection(self.impl) + self.conn.del_conn_handler(self) + if self._session: + self.impl.sessionClosed() + logging.debug("broker.shutdown() sessionClosed done.") + self._session.destroy() + logging.debug("broker.shutdown() session destroy done.") + self._session = None + self._operational = False + logging.debug("broker.shutdown() done.") + + + def wait_for_stable(self, timeout = None): + self._cv.acquire() + try: + if self._stable: + return + if timeout: + self._cv.wait(timeout) + if not self._stable: + raise Exception("Timed out: waiting for broker connection to become stable") + else: + while not self._stable: + self._cv.wait() + finally: + self._cv.release() + + + def send_query(self, query, ctx, agent): + agent_impl = None + if agent: + agent_impl = agent.impl + self.impl.sendQuery(query, ctx, agent_impl) + self.conn.kick() + + + def _do_broker_events(self): + count = 0 + valid = self.impl.getEvent(self._event) + while valid: + count += 1 + if self._event.kind == qmfengine.BrokerEvent.BROKER_INFO: + logging.debug("Broker Event BROKER_INFO received"); + elif self._event.kind == qmfengine.BrokerEvent.DECLARE_QUEUE: + logging.debug("Broker Event DECLARE_QUEUE received"); + self.conn.impl.declareQueue(self._session.handle, self._event.name) + elif self._event.kind == qmfengine.BrokerEvent.DELETE_QUEUE: + logging.debug("Broker Event DELETE_QUEUE received"); + self.conn.impl.deleteQueue(self._session.handle, self._event.name) + elif self._event.kind == qmfengine.BrokerEvent.BIND: + logging.debug("Broker Event BIND received"); + self.conn.impl.bind(self._session.handle, self._event.exchange, self._event.name, self._event.bindingKey) + elif self._event.kind == qmfengine.BrokerEvent.UNBIND: + logging.debug("Broker Event UNBIND received"); + self.conn.impl.unbind(self._session.handle, self._event.exchange, self._event.name, self._event.bindingKey) + elif self._event.kind == qmfengine.BrokerEvent.SETUP_COMPLETE: + logging.debug("Broker Event SETUP_COMPLETE received"); + self.impl.startProtocol() + elif self._event.kind == qmfengine.BrokerEvent.STABLE: + logging.debug("Broker Event STABLE received"); + self._cv.acquire() + try: + self._stable = True + self._cv.notify() + finally: + self._cv.release() + elif self._event.kind == qmfengine.BrokerEvent.QUERY_COMPLETE: + result = [] + for idx in range(self._event.queryResponse.getObjectCount()): + result.append(ConsoleObject(None, {"impl":self._event.queryResponse.getObject(idx), "broker":self})) + self.console._get_result(result, self._event.context) + elif self._event.kind == qmfengine.BrokerEvent.METHOD_RESPONSE: + obj = self._event.context + obj._method_result(MethodResponse(self._event.methodResponse())) + + 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 + logging.debug("Broker: sending msg on connection") + 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: + self.console.start_console_events() + bcnt = self._do_broker_events() + mcnt = self._do_broker_messages() + if bcnt == 0 and mcnt == 0: + break; + + + def conn_event_connected(self): + logging.debug("Broker: Connection event CONNECTED") + 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): + logging.debug("Broker: Connection event DISCONNECTED") + pass + + + def conn_event_visit(self): + self._do_events() + + + def sess_event_session_closed(self, context, error): + logging.debug("Broker: Session event CLOSED") + self.impl.sessionClosed() + + + def sess_event_recv(self, context, message): + logging.debug("Broker: Session event MSG_RECV") + if not self._operational: + logging.warning("Unexpected session event message received by Broker proxy: context='%s'" % str(context)) + 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.Agent(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 raise_event(self, event): + self.impl.raiseEvent(event.impl) + + 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({"impl":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): + logging.debug("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): + logging.debug("Agent Connection Lost") + pass + + + def conn_event_visit(self): + self.do_events() + + + def sess_event_session_closed(self, context, error): + logging.debug("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 new file mode 100644 index 0000000000..eb350115a3 --- /dev/null +++ b/qpid/cpp/bindings/qmf/qmfengine.i @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +%{ + +#include "qmf/engine/Agent.h" +#include "qmf/engine/Console.h" +#include "qmf/engine/ResilientConnection.h" + +%} + +%include <qmf/engine/QmfEngineImportExport.h> +%include <qmf/engine/Query.h> +%include <qmf/engine/Message.h> +%include <qmf/engine/Agent.h> +%include <qmf/engine/Console.h> +%include <qmf/engine/ConnectionSettings.h> +%include <qmf/engine/ResilientConnection.h> +%include <qmf/engine/Typecode.h> +%include <qmf/engine/Schema.h> +%include <qmf/engine/Value.h> +%include <qmf/engine/ObjectId.h> +%include <qmf/engine/Object.h> +%include <qmf/engine/Event.h> + + +%inline { + +using namespace std; +using namespace qmf::engine; + +namespace qmf { +namespace engine { + +} +} +} + + +%{ + +%}; + diff --git a/qpid/cpp/bindings/qmf/ruby/Makefile.am b/qpid/cpp/bindings/qmf/ruby/Makefile.am new file mode 100644 index 0000000000..de8c4d10d5 --- /dev/null +++ b/qpid/cpp/bindings/qmf/ruby/Makefile.am @@ -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. +# + +if HAVE_RUBY_DEVEL + +INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src -I$(top_builddir)/src + +EXTRA_DIST = ruby.i +BUILT_SOURCES = qmfengine.cpp +SWIG_FLAGS = -w362,401 + +rubylibdir = $(RUBY_LIB) + +dist_rubylib_DATA = qmf.rb + +qmfengine.cpp: $(srcdir)/ruby.i $(srcdir)/../qmfengine.i + $(SWIG) -ruby -c++ $(SWIG_FLAGS) $(INCLUDES) $(QPID_CXXFLAGS) -I/usr/include -o qmfengine.cpp $(srcdir)/ruby.i + +rubylibarchdir = $(RUBY_LIB_ARCH) +rubylibarch_LTLIBRARIES = qmfengine.la + +qmfengine_la_LDFLAGS = -avoid-version -module -shared -shrext ".$(RUBY_DLEXT)" +qmfengine_la_LIBADD = $(RUBY_LIBS) -L$(top_builddir)/src/.libs -lqpidclient $(top_builddir)/src/libqmfengine.la +qmfengine_la_CXXFLAGS = $(INCLUDES) -I$(RUBY_INC) -I$(RUBY_INC_ARCH) -fno-strict-aliasing +nodist_qmfengine_la_SOURCES = qmfengine.cpp + +CLEANFILES = qmfengine.cpp + +endif # HAVE_RUBY_DEVEL diff --git a/qpid/cpp/bindings/qmf/ruby/qmf.rb b/qpid/cpp/bindings/qmf/ruby/qmf.rb new file mode 100644 index 0000000000..34d3255d8d --- /dev/null +++ b/qpid/cpp/bindings/qmf/ruby/qmf.rb @@ -0,0 +1,1522 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 '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 or + c.index('CLASS_') == 0 or c.index('SEV_') == 0 + const_set(c, Qmfengine.const_get(c)) + end + end + + class Util + def qmf_to_native(val) + 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 ObjectId.new(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 then value_to_dict(val) + when TYPE_LIST then value_to_list(val) + when TYPE_OBJECT + when TYPE_ARRAY + end + end + + def native_to_qmf(target, value) + if target.class == Qmfengine::Value + val = target + typecode = val.getType + else + typecode = target + val = Qmfengine::Value.new(typecode) + end + + case typecode + 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 then dict_to_value(val, value) + when TYPE_LIST then list_to_value(val, value) + when TYPE_OBJECT + when TYPE_ARRAY + end + return val + end + + def pick_qmf_type(value) + if value.class == Fixnum + if value >= 0 + return TYPE_UINT32 if value < 0x100000000 + return TYPE_UINT64 + else + return TYPE_INT32 if value > -0xffffffff + return TYPE_INT64 + end + end + + if value.class == Bignum + return TYPE_UINT64 if value >= 0 + return TYPE_INT64 + end + + if value.class == String + return TYPE_SSTR if value.length < 256 + return TYPE_LSTR + end + + return TYPE_DOUBLE if value.class == Float + + return TYPE_BOOL if value.class == TrueClass + return TYPE_BOOL if value.class == FalseClass + return TYPE_BOOL if value.class == NilClass + + return TYPE_MAP if value.class == Hash + return TYPE_LIST if value.class == Array + + raise ArgumentError, "QMF type not known for native type #{value.class}" + end + + def value_to_dict(val) + # Assume val is of type Qmfengine::Value + raise ArgumentError, "value_to_dict must be given a map value" if !val.isMap + map = {} + for i in 0...val.keyCount + key = val.key(i) + map[key] = qmf_to_native(val.byKey(key)) + end + return map + end + + def dict_to_value(val, map) + map.each do |key, value| + raise ArgumentError, "QMF map key must be a string" if key.class != String + typecode = pick_qmf_type(value) + val.insert(key, native_to_qmf(typecode, value)) + end + end + + def value_to_list(val) + # Assume val is of type Qmfengine::Value + raise ArgumentError, "value_to_dict must be given a map value" if !val.isList + list = [] + for i in 0...val.listItemCount + list.push(qmf_to_native(val.listItem(i))) + end + return list + end + + def list_to_value(val, list) + list.each do |value| + typecode = pick_qmf_type(value) + val.appendToList(native_to_qmf(typecode, value)) + end + end + end + + $util = Util.new + + ##============================================================================== + ## CONNECTION + ##============================================================================== + + 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 + + good = @impl.setAttr(key, v) + raise "Invalid attribute '#{key}'" unless good + end + + def get_attr(key) + _v = @impl.getAttr(key) + if _v.isString() + return _v.asString() + elsif _v.isUint() + return _v.asUint() + elsif _v.isBool() + return _v.asBool() + else + raise Exception("Argument error: value for attribute '#{key}' has unsupported type: #{_v.getType()}") + end + end + + + def method_missing(name_in, *args) + name = name_in.to_s + if name[name.length - 1] == 61 + attr = name[0..name.length - 2] + set_attr(attr, args[0]) + return + else + return get_attr(name) + end + 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 = [] + @connected = nil + + @thread = Thread.new do + run + end + end + + def connected? + @connected + end + + def kick + @impl.notify + end + + def add_conn_handler(handler) + synchronize do + @new_conn_handlers << handler + end + kick + end + + def del_conn_handler(handler) + synchronize do + @conn_handlers_to_delete << handler + end + kick + end + + def run() + eventImpl = Qmfengine::ResilientConnectionEvent.new + 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 + + del_handlers.each do |dh| + d = @conn_handlers.delete(dh) + end + 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 Session + attr_reader :handle, :handler + + def initialize(conn, label, handler) + @conn = conn + @label = label + @handler = handler + @handle = Qmfengine::SessionHandle.new + result = @conn.impl.createSession(label, self, @handle) + end + + def destroy() + @conn.impl.destroySession(@handle) + end + end + + ##============================================================================== + ## OBJECTS and EVENTS + ##============================================================================== + + class QmfEvent + attr_reader :impl, :event_class + def initialize(cls, kwargs={}) + @broker = kwargs[:broker] if kwargs.include?(:broker) + @allow_sets = :true + + if cls: + @event_class = cls + @impl = Qmfengine::Event.new(@event_class.impl) + elsif kwargs.include?(:impl) + @impl = Qmfengine::Event.new(kwargs[:impl]) + @event_class = SchemaEventClass.new(nil, nil, nil, :impl => @impl.getClass) + end + end + + def arguments + list = [] + @event_class.arguments.each do |arg| + list << [arg, get_attr(arg.name)] + end + return list + end + + def get_attr(name) + val = value(name) + $util.qmf_to_native(val) + end + + def set_attr(name, v) + val = value(name) + $util.native_to_qmf(val, v) + end + + def [](name) + get_attr(name) + end + + def []=(name, value) + set_attr(name, value) + end + + 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 an argument name, set or return the value of the argument. + # + @event_class.arguments.each do |arg| + if arg.name == name + if attr_set + return set_attr(name, args[0]) + else + return get_attr(name) + end + end + end + + # + # This name means nothing to us, pass it up the line to the parent + # class's handler. + # + super.method_missing(name_in, args) + end + + private + def value(name) + val = @impl.getValue(name.to_s) + if val.nil? + raise ArgumentError, "Attribute '#{name}' not defined for event #{@event_class.impl.getClassKey.getPackageName}:#{@object_class.impl.getClassKey.getClassName}" + end + return val + end + end + + 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 properties + list = [] + @object_class.properties.each do |prop| + list << [prop, get_attr(prop.name)] + end + return list + end + + def statistics + list = [] + @object_class.statistics.each do |stat| + list << [stat, get_attr(stat.name)] + end + return list + end + + def get_attr(name) + val = value(name) + $util.qmf_to_native(val) + end + + def set_attr(name, v) + val = value(name) + $util.native_to_qmf(val, v) + end + + def [](name) + get_attr(name) + end + + def []=(name, value) + set_attr(name, value) + end + + def inc_attr(name, by=1) + set_attr(name, get_attr(name) + by) + end + + def dec_attr(name, by=1) + set_attr(name, get_attr(name) - by) + end + + 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 + + # + # 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 + + # + # 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 + + # + # 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 + + # + # 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 + + 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 + + return marshalled.map + 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.getClassKey.getPackageName}:#{@object_class.impl.getClassKey.getClassName}" + end + return val + end + end + + class AgentObject < QmfObject + def initialize(cls, kwargs={}) + super(cls, kwargs) + @allow_sets = :true + end + + def destroy + @impl.destroy + end + + def set_object_id(oid) + @impl.setObjectId(oid.impl) + end + end + + class ConsoleObject < QmfObject + attr_reader :current_time, :create_time, :delete_time + + def initialize(cls, kwargs={}) + super(cls, kwargs) + end + + def update() + raise "No linkage to broker" unless @broker + newer = @broker.console.objects(Query.new(:object_id => object_id)) + raise "Expected exactly one update for this object" unless newer.size == 1 + merge_update(newer[0]) + end + + def merge_update(new_object) + @impl.merge(new_object.impl) + end + + def deleted?() + @impl.isDeleted + end + + def key() + end + end + + class ObjectId + attr_reader :impl, :agent_key + def initialize(impl=nil) + if impl + @impl = Qmfengine::ObjectId.new(impl) + else + @impl = Qmfengine::ObjectId.new + end + @agent_key = "#{@impl.getBrokerBank}.#{@impl.getAgentBank}" + end + + def object_num_high + @impl.getObjectNumHi + end + + def object_num_low + @impl.getObjectNumLo + end + + def ==(other) + return @impl == other.impl + end + + def to_s + @impl.str + end + end + + class Arguments + attr_reader :map + def initialize(map) + @map = map + @by_hash = {} + key_count = @map.keyCount + a = 0 + while a < key_count + key = @map.key(a) + @by_hash[key] = $util.qmf_to_native(@map.byKey(key)) + a += 1 + end + end + + def [] (key) + return @by_hash[key] + end + + def []= (key, value) + @by_hash[key] = value + set(key, value) + end + + def each + @by_hash.each { |k, v| yield(k, v) } + end + + def method_missing(name, *args) + if @by_hash.include?(name.to_s) + return @by_hash[name.to_s] + end + + super.method_missing(name, args) + end + + def set(key, value) + val = @map.byKey(key) + $util.native_to_qmf(val, value) + end + end + + class MethodResponse + def initialize(impl) + @impl = Qmfengine::MethodResponse.new(impl) + end + + def status + @impl.getStatus + end + + def exception + @impl.getException + end + + def text + exception.asString + end + + def args + Arguments.new(@impl.getArgs) + end + + def method_missing(name, *extra_args) + args.__send__(name, extra_args) + end + end + + ##============================================================================== + ## QUERY + ##============================================================================== + + class Query + attr_reader :impl + def initialize(kwargs = {}) + if kwargs.include?(:impl) + @impl = Qmfengine::Query.new(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].impl) + 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, :object_id or :class[,:package]" + end + end + end + end + + 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) + @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) + end + end + + def name + @impl.getName + end + + def direction + @impl.getDirection + end + + def typecode + @impl.getType + end + + def to_s + name + 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) + end + end + + def add_argument(arg) + @arguments << arg + @impl.addArgument(arg.impl) + end + + def name + @impl.getName + end + + def to_s + name + 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) + @impl.setOptional(kwargs[:optional]) if kwargs.include?(:optional) + @impl.setUnit(kwargs[:unit]) if kwargs.include?(:unit) + @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) + end + end + + def name + @impl.getName + end + + def to_s + name + 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 + + def name + @impl.getName + end + + def to_s + name + end + end + + class SchemaClassKey + attr_reader :impl + def initialize(i) + @impl = Qmfengine::SchemaClassKey.new(i) + end + + def package_name + @impl.getPackageName + end + + def class_name + @impl.getClassName + end + + def to_s + @impl.asString + 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) + end + 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_method(meth) + @methods << meth + @impl.addMethod(meth.impl) + end + + def class_key + SchemaClassKey.new(@impl.getClassKey) + end + + def package_name + @impl.getClassKey.getPackageName + end + + def class_name + @impl.getClassKey.getClassName + end + end + + class SchemaEventClass + attr_reader :impl, :arguments + def initialize(package, name, sev, 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, sev) + @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc) + end + end + + def add_argument(arg) + @arguments << arg + @impl.addArgument(arg.impl) + end + + def name + @impl.getClassKey.getClassName + end + + def class_key + SchemaClassKey.new(@impl.getClassKey) + end + + def package_name + @impl.getClassKey.getPackageName + end + + def class_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::Console.new + @event = Qmfengine::ConsoleEvent.new + @broker_list = [] + @cv = new_cond + @sync_count = nil + @sync_result = nil + @select = [] + @bt_count = 0 + @cb_cond = new_cond + @cb_thread = Thread.new do + run_cb_thread + end + end + + def add_connection(conn) + broker = Broker.new(self, conn) + synchronize { @broker_list << broker } + return broker + end + + def del_connection(broker) + broker.shutdown + synchronize { @broker_list.delete(broker) } + end + + def packages() + plist = [] + count = @impl.packageCount + for i in 0...count + plist << @impl.getPackageName(i) + end + return plist + end + + def 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, nil, :impl => @impl.getEventClass(key)) + end + end + end + + return clist + end + + def bind_package(package) + @impl.bindPackage(package) + end + + 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 + + def bind_event(kwargs = {}) + if kwargs.include?(:key) + @impl.bindEvent(kwargs[:key]) + elsif kwargs.include?(:package) + package = kwargs[:package] + if kwargs.include?(:event) + @impl.bindEvent(package, kwargs[:event]) + else + @impl.bindEvent(package, "*") + end + else + raise ArgumentError, "Invalid arguments, use :key or :package[,:event]" + end + end + + def agents(broker = nil) + blist = [] + if broker + blist << broker + else + synchronize { blist = @broker_list } + end + + agents = [] + blist.each do |b| + count = b.impl.agentCount + for idx in 0...count + agents << AgentProxy.new(b.impl.getAgent(idx), b) + end + end + + return agents + end + + def objects(query, kwargs = {}) + timeout = 30 + agent = nil + kwargs.merge!(query) if query.class == Hash + + if kwargs.include?(:timeout) + timeout = kwargs[:timeout] + kwargs.delete(:timeout) + end + + if kwargs.include?(:agent) + agent = kwargs[:agent] + kwargs.delete(:agent) + end + + query = Query.new(kwargs) if query.class == Hash + + @select = [] + kwargs.each do |k,v| + @select << [k, v] if k.is_a?(String) + end + + synchronize do + @sync_count = 1 + @sync_result = [] + broker = nil + synchronize { broker = @broker_list[0] } + broker.send_query(query.impl, nil, agent) + unless @cv.wait(timeout) { @sync_count == 0 } + raise "Timed out waiting for response" + end + + return @sync_result + end + end + + # Return one and only one object or nil. + def object(query, kwargs = {}) + objs = objects(query, kwargs) + return objs.length == 1 ? objs[0] : nil + end + + # Return the first of potentially many objects. + def first_object(query, kwargs = {}) + objs = objects(query, kwargs) + return objs.length > 0 ? objs[0] : nil + end + + # Check the object against select to check for a match + def select_match(object) + @select.each do |key, value| + object.properties.each do |prop, propval| + if key == prop.name && value != propval + return nil + end + end + end + return :true + end + + def _get_result(list, context) + synchronize do + list.each do |item| + @sync_result << item if select_match(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 run_cb_thread + while :true + synchronize { @cb_cond.wait(1) } + begin + count = do_console_events + end until count == 0 + end + end + + def start_console_events + synchronize { @cb_cond.signal } + end + + def do_console_events + count = 0 + valid = @impl.getEvent(@event) + while valid + count += 1 + begin + case @event.kind + when Qmfengine::ConsoleEvent::AGENT_ADDED + @handler.agent_added(AgentProxy.new(@event.agent, nil)) if @handler + when Qmfengine::ConsoleEvent::AGENT_DELETED + @handler.agent_deleted(AgentProxy.new(@event.agent, nil)) if @handler + when Qmfengine::ConsoleEvent::NEW_PACKAGE + @handler.new_package(@event.name) if @handler + when Qmfengine::ConsoleEvent::NEW_CLASS + @handler.new_class(SchemaClassKey.new(@event.classKey)) if @handler + when Qmfengine::ConsoleEvent::OBJECT_UPDATE + @handler.object_update(ConsoleObject.new(nil, :impl => @event.object), @event.hasProps, @event.hasStats) if @handler + when Qmfengine::ConsoleEvent::EVENT_RECEIVED + @handler.event_received(QmfEvent.new(nil, :impl => @event.event)) if @handler + when Qmfengine::ConsoleEvent::AGENT_HEARTBEAT + @handler.agent_heartbeat(AgentProxy.new(@event.agent, nil), @event.timestamp) if @handler + when Qmfengine::ConsoleEvent::METHOD_RESPONSE + end + rescue Exception => ex + puts "Exception caught in callback: #{ex}" + if @bt_count < 2 + puts ex.backtrace + @bt_count += 1 + end + end + @impl.popEvent + valid = @impl.getEvent(@event) + end + return count + end + end + + class AgentProxy + attr_reader :impl, :broker, :label, :key + + def initialize(impl, broker) + @impl = Qmfengine::AgentProxy.new(impl) + @broker = broker + @label = @impl.getLabel + @key = "#{@impl.getBrokerBank}.#{@impl.getAgentBank}" + end + end + + class Broker < ConnectionHandler + include MonitorMixin + attr_reader :impl, :conn, :console, :broker_bank + + def initialize(console, conn) + super() + @broker_bank = 1 + @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 wait_for_stable(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 send_query(query, ctx, agent) + agent_impl = agent.impl if agent + @impl.sendQuery(query, ctx, agent_impl) + @conn.kick + end + + def do_broker_events() + count = 0 + valid = @impl.getEvent(@event) + while valid + count += 1 + 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 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 + @console.start_console_events + bcnt = do_broker_events + mcnt = do_broker_messages + end until 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 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::Agent.new(@agentLabel) + @event = Qmfengine::AgentEvent.new + @xmtMessage = Qmfengine::Message.new + end + + 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 raise_event(event) + @impl.raiseEvent(event.impl) + 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/ruby/ruby.i b/qpid/cpp/bindings/qmf/ruby/ruby.i new file mode 100644 index 0000000000..0101861100 --- /dev/null +++ b/qpid/cpp/bindings/qmf/ruby/ruby.i @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +%include stl.i + +%module qmfengine + +%typemap (in) void * +{ + $1 = (void *) $input; +} + +%typemap (out) void * +{ + $result = (VALUE) $1; +} + +%typemap (in) uint16_t +{ + $1 = NUM2UINT ($input); +} + +%typemap (out) uint16_t +{ + $result = UINT2NUM((uint16_t) $1); +} + +%typemap (in) uint32_t +{ + if (TYPE($input) == T_BIGNUM) + $1 = NUM2UINT($input); + else + $1 = FIX2UINT($input); +} + +%typemap (out) uint32_t +{ + $result = UINT2NUM((uint32_t) $1); +} + +%typemap (in) int32_t +{ + if (TYPE($input) == T_BIGNUM) + $1 = NUM2INT($input); + else + $1 = FIX2INT($input); +} + +%typemap (out) int32_t +{ + $result = INT2NUM((int32_t) $1); +} + +%typemap (typecheck, precedence=SWIG_TYPECHECK_INTEGER) uint32_t { + $1 = FIXNUM_P($input); +} + +%typemap (in) uint64_t +{ + if (TYPE($input) == T_BIGNUM) + $1 = NUM2ULL($input); + else + $1 = (uint64_t) FIX2ULONG($input); +} + +%typemap (out) uint64_t +{ + $result = ULL2NUM((uint64_t) $1); +} + +%typemap (in) int64_t +{ + if (TYPE($input) == T_BIGNUM) + $1 = NUM2LL($input); + else + $1 = (int64_t) FIX2LONG($input); +} + +%typemap (out) int64_t +{ + $result = LL2NUM((int64_t) $1); +} + +%typemap (typecheck, precedence=SWIG_TYPECHECK_INTEGER) uint64_t { + $1 = FIXNUM_P($input); +} + + +%include "../qmfengine.i" + diff --git a/qpid/cpp/bindings/qmf/tests/Makefile.am b/qpid/cpp/bindings/qmf/tests/Makefile.am new file mode 100644 index 0000000000..182771e16b --- /dev/null +++ b/qpid/cpp/bindings/qmf/tests/Makefile.am @@ -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. +# + +TESTS = 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 new file mode 100755 index 0000000000..5ee5e371d3 --- /dev/null +++ b/qpid/cpp/bindings/qmf/tests/agent_ruby.rb @@ -0,0 +1,279 @@ +#!/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 Model + attr_reader :parent_class, :child_class, :event_class + + def initialize + @parent_class = Qmf::SchemaObjectClass.new("org.apache.qpid.qmf", "parent") + @parent_class.add_property(Qmf::SchemaProperty.new("name", Qmf::TYPE_SSTR, :index => true)) + @parent_class.add_property(Qmf::SchemaProperty.new("state", Qmf::TYPE_SSTR)) + + @parent_class.add_property(Qmf::SchemaProperty.new("uint64val", Qmf::TYPE_UINT64)) + @parent_class.add_property(Qmf::SchemaProperty.new("uint32val", Qmf::TYPE_UINT32)) + @parent_class.add_property(Qmf::SchemaProperty.new("uint16val", Qmf::TYPE_UINT16)) + @parent_class.add_property(Qmf::SchemaProperty.new("uint8val", Qmf::TYPE_UINT8)) + + @parent_class.add_property(Qmf::SchemaProperty.new("int64val", Qmf::TYPE_INT64)) + @parent_class.add_property(Qmf::SchemaProperty.new("int32val", Qmf::TYPE_INT32)) + @parent_class.add_property(Qmf::SchemaProperty.new("int16val", Qmf::TYPE_INT16)) + @parent_class.add_property(Qmf::SchemaProperty.new("int8val", Qmf::TYPE_INT8)) + + @parent_class.add_property(Qmf::SchemaProperty.new("sstrval", Qmf::TYPE_SSTR)) + @parent_class.add_property(Qmf::SchemaProperty.new("lstrval", Qmf::TYPE_LSTR)) + + @parent_class.add_property(Qmf::SchemaProperty.new("mapval", Qmf::TYPE_MAP)) + @parent_class.add_property(Qmf::SchemaProperty.new("listval", Qmf::TYPE_LIST)) + + @parent_class.add_statistic(Qmf::SchemaStatistic.new("queryCount", Qmf::TYPE_UINT32, :unit => "query", :desc => "Query count")) + + method = Qmf::SchemaMethod.new("echo", :desc => "Check responsiveness of the agent object") + method.add_argument(Qmf::SchemaArgument.new("sequence", Qmf::TYPE_UINT32, :dir => Qmf::DIR_IN_OUT)) + @parent_class.add_method(method) + + method = Qmf::SchemaMethod.new("set_numerics", :desc => "Set the numeric values in the object") + method.add_argument(Qmf::SchemaArgument.new("test", Qmf::TYPE_SSTR, :dir => Qmf::DIR_IN)) + @parent_class.add_method(method) + + method = Qmf::SchemaMethod.new("test_map_list", :desc => "A method call that accepts map and list arguments.") + method.add_argument(Qmf::SchemaArgument.new("inMap", Qmf::TYPE_MAP, :dir => Qmf::DIR_IN)) + method.add_argument(Qmf::SchemaArgument.new("inList", Qmf::TYPE_LIST, :dir => Qmf::DIR_IN)) + method.add_argument(Qmf::SchemaArgument.new("outMap", Qmf::TYPE_MAP, :dir => Qmf::DIR_OUT)) + method.add_argument(Qmf::SchemaArgument.new("outList", Qmf::TYPE_LIST, :dir => Qmf::DIR_OUT)) + @parent_class.add_method(method) + + method = Qmf::SchemaMethod.new("set_short_string", :desc => "Set the short string value in the object") + method.add_argument(Qmf::SchemaArgument.new("value", Qmf::TYPE_SSTR, :dir => Qmf::DIR_IN_OUT)) + @parent_class.add_method(method) + + method = Qmf::SchemaMethod.new("set_long_string", :desc => "Set the long string value in the object") + method.add_argument(Qmf::SchemaArgument.new("value", Qmf::TYPE_LSTR, :dir => Qmf::DIR_IN_OUT)) + @parent_class.add_method(method) + + method = Qmf::SchemaMethod.new("create_child", :desc => "Create a new child object") + method.add_argument(Qmf::SchemaArgument.new("child_name", Qmf::TYPE_LSTR, :dir => Qmf::DIR_IN)) + method.add_argument(Qmf::SchemaArgument.new("child_ref", Qmf::TYPE_REF, :dir => Qmf::DIR_OUT)) + @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") + @child_class.add_property(Qmf::SchemaProperty.new("name", Qmf::TYPE_SSTR, :index => true)) + + @event_class = Qmf::SchemaEventClass.new("org.apache.qpid.qmf", "test_event", Qmf::SEV_INFORM) + @event_class.add_argument(Qmf::SchemaArgument.new("uint32val", Qmf::TYPE_UINT32)) + @event_class.add_argument(Qmf::SchemaArgument.new("strval", Qmf::TYPE_LSTR)) + @event_class.add_argument(Qmf::SchemaArgument.new("mapval", Qmf::TYPE_MAP)) + @event_class.add_argument(Qmf::SchemaArgument.new("listval", Qmf::TYPE_LIST)) + end + + def register(agent) + agent.register_class(@parent_class) + agent.register_class(@child_class) + agent.register_class(@event_class) + end +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 if query.object_id}" + if query.class_name == 'parent' + @agent.query_response(context, @parent) + elsif query.object_id == @parent_oid + @agent.query_response(context, @parent) + end + @agent.query_complete(context) + end + + def method_call(context, name, object_id, args, userId) +# puts "Method: user=#{userId} context=#{context} method=#{name} object_num=#{object_id if object_id} args=#{args}" + + retCode = 0 + retText = "OK" + + if name == "echo" + @agent.method_response(context, 0, "OK", args) + + elsif name == "test_map_list" + # build the output map from the input map, accessing each key, + # value to ensure they are encoded/decoded + outMap = {} + args['inMap'].each do |k,v| + outMap[k] = v + end + + # same deal for the output list + outList = [] + args['inList'].each do |v| + outList << v + end + + args['outMap'] = outMap + args['outList'] = outList + + elsif name == "set_numerics" + + if args['test'] == "big" + @parent.uint64val = 0x9494949449494949 + @parent.uint32val = 0xa5a55a5a + @parent.uint16val = 0xb66b + @parent.uint8val = 0xc7 + + @parent.int64val = 1000000000000000000 + @parent.int32val = 1000000000 + @parent.int16val = 10000 + @parent.int8val = 100 + + event = Qmf::QmfEvent.new(@model.event_class) + event.uint32val = @parent.uint32val + event.strval = "Unused" + event.mapval = @parent.mapval + event.listval = @parent.listval + @agent.raise_event(event) + + elsif args['test'] == "small" + @parent.uint64val = 4 + @parent.uint32val = 5 + @parent.uint16val = 6 + @parent.uint8val = 7 + + @parent.int64val = 8 + @parent.int32val = 9 + @parent.int16val = 10 + @parent.int8val = 11 + + event = Qmf::QmfEvent.new(@model.event_class) + event.uint32val = @parent.uint32val + event.strval = "Unused" + @agent.raise_event(event) + + elsif args['test'] == "negative" + @parent.uint64val = 0 + @parent.uint32val = 0 + @parent.uint16val = 0 + @parent.uint8val = 0 + + @parent.int64val = -10000000000 + @parent.int32val = -100000 + @parent.int16val = -1000 + @parent.int8val = -100 + + event = Qmf::QmfEvent.new(@model.event_class) + event.uint32val = @parent.uint32val + event.strval = "Unused" + @agent.raise_event(event) + + else + retCode = 1 + retText = "Invalid argument value for test" + end + + elsif name == "set_short_string" + @parent.sstrval = args['value'] + + event = Qmf::QmfEvent.new(@model.event_class) + event.uint32val = 0 + event.strval = @parent.sstrval + @agent.raise_event(event) + + elsif name == "set_long_string" + @parent.lstrval = args['value'] + + event = Qmf::QmfEvent.new(@model.event_class) + event.uint32val = 0 + event.strval = @parent.lstrval + @agent.raise_event(event) + + elsif name == "create_child" + oid = @agent.alloc_object_id(2) + args['child_ref'] = oid + @child = Qmf::AgentObject.new(@model.child_class) + @child.name = args.by_key("child_name") + @child.set_object_id(oid) + + elsif name == "probe_userid" + args['userid'] = userId + + else + retCode = 1 + retText = "Unimplemented Method: #{name}" + end + + @agent.method_response(context, retCode, retText, args) + 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) + @agent = Qmf::Agent.new(self, "agent_test_label") + + @model = Model.new + @model.register(@agent) + + @agent.set_connection(@connection) + + @parent = Qmf::AgentObject.new(@model.parent_class) + @parent.name = "Parent One" + @parent.state = "OPERATIONAL" + + @parent.uint64val = 0 + @parent.uint32val = 0 + @parent.uint16val = 0 + @parent.uint8val = 0 + + @parent.int64val = 0 + @parent.int32val = 0 + @parent.int16val = 0 + @parent.int8val = 0 + + # a list containing a list that contains a map (so there!) + @parent.listval = ['a', 1, 'b', -2, + ['c', true, 3.1415, + {"hi" => 10, "lo" => 5, "neg" => -3}]] + + # a default map + @parent.mapval = {'aLong' => 9999999999, + 'aInt' => 54321, + 'aSigned' => -666, + 'aString' => "A String", + 'aFloat'=> 3.1415, + 'aMap' => {"first" => 1, "second" => 2}, + 'aList' => ['x', -1, 'y', 2]} + + @parent_oid = @agent.alloc_object_id(1) + @parent.set_object_id(@parent_oid) + + sleep + end +end + +app = App.new +app.main + + 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..28ba47e1bb --- /dev/null +++ b/qpid/cpp/bindings/qmf/tests/python_agent.py @@ -0,0 +1,326 @@ +#!/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_property(qmf.SchemaProperty("sstrval", qmf.TYPE_SSTR)) + self.parent_class.add_property(qmf.SchemaProperty("lstrval", qmf.TYPE_LSTR)) + + self.parent_class.add_property(qmf.SchemaProperty("mapval", qmf.TYPE_MAP)) + self.parent_class.add_property(qmf.SchemaProperty("listval", qmf.TYPE_LIST)) + + + 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("test_map_list", {"desc":"A method call that accepts map and list arguments."}) + _method.add_argument(qmf.SchemaArgument("inMap", qmf.TYPE_MAP, {"dir":qmf.DIR_IN})) + _method.add_argument(qmf.SchemaArgument("inList", qmf.TYPE_LIST, {"dir":qmf.DIR_IN})) + _method.add_argument(qmf.SchemaArgument("outMap", qmf.TYPE_MAP, {"dir":qmf.DIR_OUT})) + _method.add_argument(qmf.SchemaArgument("outList", qmf.TYPE_LIST, {"dir":qmf.DIR_OUT})) + self.parent_class.add_method(_method) + + _method = qmf.SchemaMethod("set_short_string", {"desc":"Set the short string value in the object"}) + _method.add_argument(qmf.SchemaArgument("value", qmf.TYPE_SSTR, {"dir":qmf.DIR_IN_OUT})) + self.parent_class.add_method(_method) + + _method = qmf.SchemaMethod("set_long_string", {"desc":"Set the long string value in the object"}) + _method.add_argument(qmf.SchemaArgument("value", qmf.TYPE_LSTR, {"dir":qmf.DIR_IN_OUT})) + 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})) + + self.event_class = qmf.SchemaEventClass("org.apache.qpid.qmf", "test_event", qmf.SEV_NOTICE) + self.event_class.add_argument(qmf.SchemaArgument("uint32val", qmf.TYPE_UINT32)) + self.event_class.add_argument(qmf.SchemaArgument("strval", qmf.TYPE_LSTR)) + self.event_class.add_argument(qmf.SchemaArgument("mapval", qmf.TYPE_MAP)) + self.event_class.add_argument(qmf.SchemaArgument("listval", qmf.TYPE_LIST)) + + def register(self, agent): + agent.register_class(self.parent_class) + agent.register_class(self.child_class) + agent.register_class(self.event_class) + + + +class App(qmf.AgentHandler): + ''' + Object that handles events received by the Agent. + ''' + def get_query(self, context, query, userId): + ''' + Respond to a Query request from a console. + ''' + #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): + ''' + Invoke a method call requested by the console. + ''' + #print "Method: name=%s user=%s context=%d object_id=%s args=%s" % (name, userId, context, object_id, args) + if name == "echo": + self._agent.method_response(context, 0, "OK", args) + + elif name == "test_map_list": + # build the output map from the input map, accessing each key, + # value to ensure they are encoded/decoded + outMap = {} + for key,value in args['inMap'].items(): + outMap[key] = value + + # same deal for the output list + outList = [] + for value in args['inList']: + outList.append(value) + + args['outMap'] = outMap + args['outList'] = outList + self._agent.method_response(context, 0, "OK", args) + + elif name == "set_numerics": + _retCode = 0 + _retText = "OK" + + if args['test'] == "big": + # + # note the alternate forms for setting object attributes: + # + self._parent.set_attr("uint64val", 0x9494949449494949) + self._parent.uint32val = 0xa5a55a5a + self._parent.set_attr("uint16val", 0xb66b) + self._parent["uint8val"] = 0xc7 + + self._parent.int64val = 1000000000000000000 + self._parent.set_attr("int32val", 1000000000) + self._parent["int16val"] = 10000 + self._parent.set_attr("int8val", 100) + + event = qmf.QmfEvent(self._model.event_class) + event.uint32val = self._parent.get_attr("uint32val") + event.strval = "Unused" + event.mapval = self._parent.get_attr("mapval") + event.listval = self._parent["listval"] + + self._agent.raise_event(event) + + ## Test the __getattr__ implementation: + ## @todo: remove once python_client implements this + ## form of property access + assert self._parent["uint8val"] == 0xc7 + assert self._parent.uint64val == 0x9494949449494949 + + # note the alternative argument access syntax: + 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) + + event = qmf.QmfEvent(self._model.event_class) + event.uint32val = self._parent.uint32val + event.strval = "Unused" + self._agent.raise_event(event) + + 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) + + event = qmf.QmfEvent(self._model.event_class) + event.uint32val = self._parent.uint32val + event.strval = "Unused" + self._agent.raise_event(event) + + else: + _retCode = 1 + _retText = "Invalid argument value for test" + + self._agent.method_response(context, _retCode, _retText, args) + + elif name == "set_short_string": + self._parent.set_attr('sstrval', args['value']) + event = qmf.QmfEvent(self._model.event_class) + event.uint32val = 0 + event.strval = self._parent.sstrval + self._agent.raise_event(event) + + self._agent.method_response(context, 0, "OK", args) + + elif name == "set_long_string": + self._parent.set_attr('lstrval', args['value']) + event = qmf.QmfEvent(self._model.event_class) + event.uint32val = 0 + event.strval = self._parent.lstrval + self._agent.raise_event(event) + + self._agent.method_response(context, 0, "OK", args) + + elif name == "create_child": + # + # Instantiate an object based on the Child Schema Class + # + _oid = self._agent.alloc_object_id(2) + args['child_ref'] = _oid + self._child = qmf.AgentObject(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): + ''' + Agent application's main processing loop. + ''' + # Connect to the broker + self._settings = qmf.ConnectionSettings() + self._settings.sendUserId = True + if len(sys.argv) > 1: + self._settings.host = str(sys.argv[1]) + if len(sys.argv) > 2: + self._settings.port = int(sys.argv[2]) + self._connection = qmf.Connection(self._settings) + + # Instantiate an Agent to serve me queries and method calls + self._agent = qmf.Agent(self, "agent_test_label") + + # Dynamically define the parent and child schemas, then + # register them with the agent + self._model = Model() + self._model.register(self._agent) + + # Tell the agent about our connection to the broker + self._agent.set_connection(self._connection) + + # Instantiate and populate an instance of the Parent + # Schema Object + self._parent = qmf.AgentObject(self._model.parent_class) + + ## @todo how do we force a test failure? + # verify the properties() and statistics() object methods: + assert len(self._parent.properties()) == 14 + assert len(self._parent.statistics()) == 1 + + 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) + + # a list containing a list that contains a map (so there!) + self._parent.set_attr("listval", ['a', 1, 'b', -2, + ['c', True, 3.1415, + {"hi": 10, "lo": 5, "neg": -3}]]) + # a default map + self._parent.set_attr("mapval", {'aLong' : long(9999999999), + 'aInt' : int(54321), + 'aSigned' : -666, + 'aString' : "A String", + 'aFloat' : 3.1415, + 'aMap' : {'first' : 1, + 'second': 2}, + 'aList' : ['x', -1, 'y', 2]}) + + + + self._parent_oid = self._agent.alloc_object_id(1) + self._parent.set_object_id(self._parent_oid) + + # Now wait for events arriving on the connection + # to the broker... + while True: + time.sleep(1000) + + + +app = App() +app.main() + diff --git a/qpid/cpp/bindings/qmf/tests/python_console.py b/qpid/cpp/bindings/qmf/tests/python_console.py new file mode 100755 index 0000000000..1cef824fb5 --- /dev/null +++ b/qpid/cpp/bindings/qmf/tests/python_console.py @@ -0,0 +1,311 @@ +#!/usr/bin/env python +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import sys +from qpid.testlib import TestBase010 +from qpid.datatypes import Message +from qpid.queue import Empty +from time import sleep +import qmf.console + +class QmfInteropTests(TestBase010): + + def test_A_agent_presence(self): + self.startQmf(); + qmf = self.qmf + + agents = [] + count = 0 + while len(agents) == 0: + agents = qmf.getObjects(_class="agent") + sleep(1) + count += 1 + if count > 10: + self.fail("Timed out waiting for remote agent") + + def test_B_basic_method_invocation(self): + self.startQmf(); + qmf = self.qmf + + parents = qmf.getObjects(_class="parent") + self.assertEqual(len(parents), 1) + parent = parents[0] + for seq in range(10): + result = parent.echo(seq, _timeout=5) + self.assertEqual(result.status, 0) + self.assertEqual(result.text, "OK") + self.assertEqual(result.sequence, seq) + + result = parent.set_numerics("bogus") + self.assertEqual(result.status, 1) + self.assertEqual(result.text, "Invalid argument value for test") + + def test_C_basic_types_numeric_big(self): + self.startQmf(); + qmf = self.qmf + + parents = qmf.getObjects(_class="parent") + self.assertEqual(len(parents), 1) + parent = parents[0] + + result = parent.set_numerics("big") + self.assertEqual(result.status, 0) + self.assertEqual(result.text, "OK") + + parent.update() + + self.assertEqual(parent.uint64val, 0x9494949449494949) + self.assertEqual(parent.uint32val, 0xA5A55A5A) + self.assertEqual(parent.uint16val, 0xB66B) + self.assertEqual(parent.uint8val, 0xC7) + + self.assertEqual(parent.int64val, 1000000000000000000) + self.assertEqual(parent.int32val, 1000000000) + self.assertEqual(parent.int16val, 10000) + self.assertEqual(parent.int8val, 100) + + def test_C_basic_types_numeric_small(self): + self.startQmf(); + qmf = self.qmf + + parents = qmf.getObjects(_class="parent") + self.assertEqual(len(parents), 1) + parent = parents[0] + + result = parent.set_numerics("small") + self.assertEqual(result.status, 0) + self.assertEqual(result.text, "OK") + + parent.update() + + self.assertEqual(parent.uint64val, 4) + self.assertEqual(parent.uint32val, 5) + self.assertEqual(parent.uint16val, 6) + self.assertEqual(parent.uint8val, 7) + + self.assertEqual(parent.int64val, 8) + self.assertEqual(parent.int32val, 9) + self.assertEqual(parent.int16val, 10) + self.assertEqual(parent.int8val, 11) + + def test_C_basic_types_numeric_negative(self): + self.startQmf(); + qmf = self.qmf + + parents = qmf.getObjects(_class="parent") + self.assertEqual(len(parents), 1) + parent = parents[0] + + result = parent.set_numerics("negative") + self.assertEqual(result.status, 0) + self.assertEqual(result.text, "OK") + + parent.update() + + self.assertEqual(parent.uint64val, 0) + self.assertEqual(parent.uint32val, 0) + self.assertEqual(parent.uint16val, 0) + self.assertEqual(parent.uint8val, 0) + + self.assertEqual(parent.int64val, -10000000000) + self.assertEqual(parent.int32val, -100000) + self.assertEqual(parent.int16val, -1000) + self.assertEqual(parent.int8val, -100) + + def disabled_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 test_D_get_by_object_id(self): + self.startQmf() + qmf = self.qmf + + parents = qmf.getObjects(_class="parent") + self.assertEqual(len(parents), 1) + parent = parents[0] + + newList = qmf.getObjects(_objectId=parent.getObjectId()) + self.assertEqual(len(newList), 1) + + def test_E_filter_by_object_id(self): + self.startQmf() + qmf = self.qmf + + list = qmf.getObjects(_class="exchange", name="qpid.management") + self.assertEqual(len(list), 1, "No Management Exchange") + mgmt_exchange = list[0] + + bindings = qmf.getObjects(_class="binding", exchangeRef=mgmt_exchange.getObjectId()) + if len(bindings) == 0: + self.fail("No bindings found on management exchange") + + for binding in bindings: + self.assertEqual(binding.exchangeRef, mgmt_exchange.getObjectId()) + + def test_F_events(self): + class Handler(qmf.console.Console): + def __init__(self): + self.queue = [] + + def event(self, broker, event): + if event.getClassKey().getClassName() == "test_event": + self.queue.append(event) + + handler = Handler() + self.startQmf(handler) + + parents = self.qmf.getObjects(_class="parent") + self.assertEqual(len(parents), 1) + parent = parents[0] + + parent.set_numerics("big") + parent.set_numerics("small") + parent.set_numerics("negative") + parent.set_short_string("TEST") + parent.set_long_string("LONG_TEST") + parent.probe_userid() + + queue = handler.queue + self.assertEqual(len(queue), 5) + self.assertEqual(queue[0].arguments["uint32val"], 0xA5A55A5A) + self.assertEqual(queue[0].arguments["strval"], "Unused") + + # verify map and list event content. + # see agent for structure of listval and mapval + listval = queue[0].arguments["listval"] + self.assertTrue(isinstance(listval, list)) + self.assertEqual(len(listval), 5) + self.assertTrue(isinstance(listval[4], list)) + self.assertEqual(len(listval[4]), 4) + self.assertTrue(isinstance(listval[4][3], dict)) + self.assertEqual(listval[4][3]["hi"], 10) + self.assertEqual(listval[4][3]["lo"], 5) + self.assertEqual(listval[4][3]["neg"], -3) + + mapval = queue[0].arguments["mapval"] + self.assertTrue(isinstance(mapval, dict)) + self.assertEqual(len(mapval), 7) + self.assertEqual(mapval['aLong'], 9999999999) + self.assertEqual(mapval['aInt'], 54321) + self.assertEqual(mapval['aSigned'], -666) + self.assertEqual(mapval['aString'], "A String"), + self.assertEqual(mapval['aFloat'], 3.1415), + self.assertTrue(isinstance(mapval['aMap'], dict)) + self.assertEqual(len(mapval['aMap']), 2) + self.assertEqual(mapval['aMap']['second'], 2) + self.assertTrue(isinstance(mapval['aList'], list)) + self.assertEqual(len(mapval['aList']), 4) + self.assertEqual(mapval['aList'][1], -1) + + self.assertEqual(queue[1].arguments["uint32val"], 5) + self.assertEqual(queue[1].arguments["strval"], "Unused") + self.assertEqual(queue[2].arguments["uint32val"], 0) + self.assertEqual(queue[2].arguments["strval"], "Unused") + self.assertEqual(queue[3].arguments["uint32val"], 0) + self.assertEqual(queue[3].arguments["strval"], "TEST") + self.assertEqual(queue[4].arguments["uint32val"], 0) + self.assertEqual(queue[4].arguments["strval"], "LONG_TEST") + + + + def test_G_basic_map_list_data(self): + self.startQmf(); + qmf = self.qmf + + parents = qmf.getObjects(_class="parent") + self.assertEqual(len(parents), 1) + parent = parents[0] + + # see agent for structure of listval + + self.assertTrue(isinstance(parent.listval, list)) + self.assertEqual(len(parent.listval), 5) + self.assertTrue(isinstance(parent.listval[4], list)) + self.assertEqual(len(parent.listval[4]), 4) + self.assertTrue(isinstance(parent.listval[4][3], dict)) + self.assertEqual(parent.listval[4][3]["hi"], 10) + self.assertEqual(parent.listval[4][3]["lo"], 5) + self.assertEqual(parent.listval[4][3]["neg"], -3) + + # see agent for structure of mapval + + self.assertTrue(isinstance(parent.mapval, dict)) + self.assertEqual(len(parent.mapval), 7) + self.assertEqual(parent.mapval['aLong'], 9999999999) + self.assertEqual(parent.mapval['aInt'], 54321) + self.assertEqual(parent.mapval['aSigned'], -666) + self.assertEqual(parent.mapval['aString'], "A String"), + self.assertEqual(parent.mapval['aFloat'], 3.1415), + self.assertTrue(isinstance(parent.mapval['aMap'], dict)) + self.assertEqual(len(parent.mapval['aMap']), 2) + self.assertEqual(parent.mapval['aMap']['second'], 2) + self.assertTrue(isinstance(parent.mapval['aList'], list)) + self.assertEqual(len(parent.mapval['aList']), 4) + self.assertEqual(parent.mapval['aList'][1], -1) + + def test_H_map_list_method_call(self): + self.startQmf(); + qmf = self.qmf + + parents = qmf.getObjects(_class="parent") + self.assertEqual(len(parents), 1) + parent = parents[0] + + inMap = {'aLong' : long(9999999999), + 'aInt' : int(54321), + 'aSigned' : -666, + 'aString' : "A String", + 'aFloat' : 3.1415, + 'aList' : ['x', -1, 'y', 2], + 'abool' : False} + inList = ['aString', long(1), -1, 2.7182, {'aMap': -8}, True] + + result = parent.test_map_list(inMap, inList) + self.assertEqual(result.status, 0) + self.assertEqual(result.text, "OK") + + # verify returned values + self.assertEqual(len(inMap), len(result.outArgs['outMap'])) + for key,value in result.outArgs['outMap'].items(): + self.assertEqual(inMap[key], value) + + self.assertEqual(len(inList), len(result.outArgs['outList'])) + for idx in range(len(inList)): + self.assertEqual(inList[idx], result.outArgs['outList'][idx]) + + + def getProperty(self, msg, name): + for h in msg.headers: + if hasattr(h, name): return getattr(h, name) + return None + + def getAppHeader(self, msg, name): + headers = self.getProperty(msg, "application_headers") + if headers: + return headers[name] + return None 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..31670312d6 --- /dev/null +++ b/qpid/cpp/bindings/qmf/tests/ruby_console.rb @@ -0,0 +1,174 @@ +#!/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 agent_added(agent) + puts "AgentAdded: label=#{agent.label} key=#{agent.key}" + end + + def agent_deleted(agent) + puts "AgentDeleted: #{agent.label}" + end + + def new_package(package) + puts "NewPackage: #{package}" + end + + def new_class(class_key) + puts "NewClass: #{class_key}" + end + + def object_update(object, hasProps, hasStats) + puts "ObjectUpdate: #{object.object_class.class_name} props=#{hasProps} stats=#{hasStats}" + puts " agent-key=#{object.object_id.agent_key}" + puts " package=#{object.object_class.package_name}" + end + + def event_received(event); end + + def agent_heartbeat(agent, timestamp) + puts "AgentHeartbeat: #{agent.label} time=#{timestamp/1000000000}" + end + + def method_response(resp); end + def broker_info(broker); end + + + def dump_schema + packages = @qmfc.packages + puts "----- Packages -----" + packages.each do |p| + puts p + puts " ----- Object Classes -----" + classes = @qmfc.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.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.host = ARGV[0] if ARGV.size > 0 + @settings.port = ARGV[1].to_i if ARGV.size > 1 + @connection = Qmf::Connection.new(@settings) + @qmfc = Qmf::Console.new(self) + + @broker = @qmfc.add_connection(@connection) + @broker.wait_for_stable + + ##dump_schema + + agents = @qmfc.agents() + puts "---- Agents ----" + agents.each do |a| + puts " => #{a.label}" + end + puts "----" + + for idx in 0...20 + blist = @qmfc.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}" + puts " properties : #{b.properties}" + puts " statistics : #{b.statistics}" + + for rep in 0...1 + puts " Pinging..." + ret = b.echo(45, 'text string') + puts " status=#{ret.status} text=#{ret.exception.asString} seq=#{ret.args.sequence} body=#{ret.args.body}" + end + end + puts "----" + + elist = @qmfc.objects(:package => "org.apache.qpid.broker", :class => "exchange", 'durable' => true) + puts "---- Durable Exchanges ----" + elist.each do |e| + puts "Exchange: #{e.name}" + end + puts "----" + + qlist = @qmfc.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/ruby_console_test.rb b/qpid/cpp/bindings/qmf/tests/ruby_console_test.rb new file mode 100755 index 0000000000..972d5977b8 --- /dev/null +++ b/qpid/cpp/bindings/qmf/tests/ruby_console_test.rb @@ -0,0 +1,397 @@ +#!/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 'test_base' + +class ConsoleTest < ConsoleTestBase + + def test_A_agent_presence + assert(@connection.connected?, "Connection not connected") + + agents = [] + count = 0 + while agents.size == 0 + agents = @qmfc.objects(Qmf::Query.new(:class => "agent")) + sleep(1) + count += 1 + fail("Timed out waiting for remote agent") if count > 10 + end + + agentList = @qmfc.agents + assert_equal(agentList.size, 2, "Number of agents reported by Console") + end + + def test_A_connection_settings + begin + @settings.bogusAttribute = 25 + fail("Connection settings accepted bogus attribute") + rescue + end + end + + def test_B_basic_method_invocation + parent = @qmfc.object(:class => "parent") + assert(parent, "Number of 'parent' objects") + for seq in 0...10 + result = parent.echo(seq) + assert_equal(result.status, 0, "Method Response Status") + assert_equal(result.text, "OK", "Method Response Text") + assert_equal(result.args.sequence, seq, "Echo Response Sequence") + end + + result = parent.set_numerics("bogus") + assert_equal(result.status, 1) + assert_equal(result.text, "Invalid argument value for test") + end + + def test_C_basic_types_numeric_big + parent = @qmfc.object(:class =>"parent") + assert(parent, "Number of parent objects") + + result = parent.set_numerics("big") + assert_equal(result.status, 0, "Method Response Status") + assert_equal(result.text, "OK", "Method Response Text") + + parent.update + + assert_equal(parent.uint64val, 0x9494949449494949) + assert_equal(parent.uint32val, 0xA5A55A5A) + assert_equal(parent.uint16val, 0xB66B) + assert_equal(parent.uint8val, 0xC7) + + assert_equal(parent.int64val, 1000000000000000000) + assert_equal(parent.int32val, 1000000000) + assert_equal(parent.int16val, 10000) + assert_equal(parent.int8val, 100) + end + + def test_C_basic_types_numeric_small + parent = @qmfc.object(:class =>"parent") + assert(parent, "Number of parent objects") + + result = parent.set_numerics("small") + assert_equal(result.status, 0, "Method Response Status") + assert_equal(result.text, "OK", "Method Response Text") + + parent.update + + assert_equal(parent.uint64val, 4) + assert_equal(parent.uint32val, 5) + assert_equal(parent.uint16val, 6) + assert_equal(parent.uint8val, 7) + + assert_equal(parent.int64val, 8) + assert_equal(parent.int32val, 9) + assert_equal(parent.int16val, 10) + assert_equal(parent.int8val, 11) + end + + def test_C_basic_types_numeric_negative + parent = @qmfc.object(:class =>"parent") + assert(parent, "Number of parent objects") + + result = parent.set_numerics("negative") + assert_equal(result.status, 0, "Method Response Status") + assert_equal(result.text, "OK", "Method Response Text") + + parent.update + + assert_equal(parent.uint64val, 0) + assert_equal(parent.uint32val, 0) + assert_equal(parent.uint16val, 0) + assert_equal(parent.uint8val, 0) + + assert_equal(parent.int64val, -10000000000) + assert_equal(parent.int32val, -100000) + assert_equal(parent.int16val, -1000) + assert_equal(parent.int8val, -100) + end + + def test_C_basic_types_string_short + parent = @qmfc.object(:class =>"parent") + assert(parent, "Number of parent objects") + + strings = [] + strings << "" + strings << "A" + strings << "BC" + strings << "DEF" + strings << "GHIJKLMNOPQRSTUVWXYZ" + big = "a" + for i in 0...254 + big << "X" + end + strings << big + + strings.each do |str| + result = parent.set_short_string(str) + assert_equal(result.status, 0, "Method Response Status") + compare = str + compare = compare[0..254] if compare.size > 255 + assert_equal(result.args.value, compare, "Value returned by method") + parent.update + assert_equal(parent.sstrval, compare, "Value stored in the object") + end + end + + def test_C_basic_types_string_long + parent = @qmfc.object(:class =>"parent") + assert(parent, "Number of parent objects") + + strings = [] + strings << "" + strings << "A" + strings << "BC" + strings << "DEF" + strings << "GHIJKLMNOPQRSTUVWXYZ" + big = "a" + for i in 0...270 + big << "X" + end + strings << big + + strings.each do |str| + result = parent.set_long_string(str) + assert_equal(result.status, 0, "Method Response Status") + assert_equal(result.args.value, str, "Value returned by method") + parent.update + assert_equal(parent.lstrval, str, "Value stored in the object") + end + end + + def test_D_userid_for_method + parent = @qmfc.object(:class => "parent") + assert(parent, "Number of parent objects") + + result = parent.probe_userid + assert_equal(result.status, 0, "Method Response Status") + assert_equal(result.args.userid, "anonymous") + end + + def test_D_get_by_object_id + parent = @qmfc.object(:class => "parent") + assert(parent, "Number of parent objects") + + list = @qmfc.objects(:object_id => parent.object_id) + assert_equal(list.size, 1) + + bad_oid = Qmf::ObjectId.new + list = @qmfc.objects(:object_id => bad_oid) + assert_equal(list.size, 0) + + # TODO: test a bad_oid that has an agent-bank that is not associated with an attached agent. + + end + + def test_D_get_with_agent + agents = @qmfc.agents + agents.each do |agent| + if agent.label == "agent_test_label" + parent = @qmfc.object(:class => "parent", :agent => agent) + assert(parent, "Number of parent objects") + return + end + end + + fail("Didn't find a non-broker agent") + end + + def test_E_filter_by_object_id + mgmt_exchange = @qmfc.object(:class => "exchange", 'name' => "qpid.management") + assert(mgmt_exchange, "No Management Exchange") + + bindings = @qmfc.objects(:class => "binding", 'exchangeRef' => mgmt_exchange.object_id) + if bindings.size == 0 + fail("No bindings found on management exchange") + end + + bindings.each do |binding| + assert_equal(binding.exchangeRef, mgmt_exchange.object_id) + end + end + + + def test_F_events + + @event_list.clear + @store_events = :true + + parent = @qmfc.object(:class =>"parent") + assert(parent, "Number of parent objects") + + parent.set_numerics("big") + parent.set_numerics("small") + parent.set_numerics("negative") + parent.set_short_string("TEST") + parent.set_long_string("LONG_TEST") + parent.probe_userid() + + @store_events = :false + + assert_equal(@event_list.length, 5) + + assert_equal(@event_list[0].get_attr("uint32val"), 0xA5A55A5A) + assert_equal(@event_list[0].get_attr("strval"), "Unused") + + # verify map and list event content. + # see agent for structure of listval and mapval + + listval = @event_list[0].listval + assert(listval.class == Array) + assert_equal(listval.length, 5) + assert(listval[4].class == Array) + assert_equal(listval[4].length, 4) + assert(listval[4][3].class == Hash) + assert_equal(listval[4][3]["hi"], 10) + assert_equal(listval[4][3]["lo"], 5) + assert_equal(listval[4][3]["neg"], -3) + + mapval = @event_list[0].mapval + assert(mapval.class == Hash) + assert_equal(mapval.length, 7) + assert_equal(mapval['aLong'], 9999999999) + assert_equal(mapval['aInt'], 54321) + assert_equal(mapval['aSigned'], -666) + assert_equal(mapval['aString'], "A String") + assert_equal(mapval['aFloat'], 3.1415) + assert(mapval['aMap'].class == Hash) + assert_equal(mapval['aMap'].length, 2) + assert_equal(mapval['aMap']['second'], 2) + assert(mapval['aList'].class == Array) + assert_equal(mapval['aList'].length, 4) + assert_equal(mapval['aList'][1], -1) + + assert_equal(@event_list[1]["uint32val"], 5) + assert_equal(@event_list[1].get_attr("strval"), "Unused") + assert_equal(@event_list[2].get_attr("uint32val"), 0) + assert_equal(@event_list[2].get_attr("strval"), "Unused") + assert_equal(@event_list[3].get_attr("uint32val"), 0) + assert_equal(@event_list[3].get_attr("strval"), "TEST") + assert_equal(@event_list[4].get_attr("uint32val"), 0) + assert_equal(@event_list[4].get_attr("strval"), "LONG_TEST") + + @event_list.clear + + end + + def test_G_basic_map_list_data + parent = @qmfc.object(:class => "parent") + assert(parent, "Number of 'parent' objects") + + # see agent for structure of listval + + assert(parent.listval.class == Array) + assert_equal(parent.listval.length, 5) + assert(parent.listval[4].class == Array) + assert_equal(parent.listval[4].length, 4) + assert(parent.listval[4][3].class == Hash) + assert_equal(parent.listval[4][3]["hi"], 10) + assert_equal(parent.listval[4][3]["lo"], 5) + assert_equal(parent.listval[4][3]["neg"], -3) + + # see agent for structure of mapval + + assert(parent.mapval.class == Hash) + assert_equal(parent.mapval.length, 7) + assert_equal(parent.mapval['aLong'], 9999999999) + assert_equal(parent.mapval['aInt'], 54321) + assert_equal(parent.mapval['aSigned'], -666) + assert_equal(parent.mapval['aString'], "A String") + assert_equal(parent.mapval['aFloat'], 3.1415) + assert(parent.mapval['aMap'].class == Hash) + assert_equal(parent.mapval['aMap'].length, 2) + assert_equal(parent.mapval['aMap']['second'], 2) + assert(parent.mapval['aList'].class == Array) + assert_equal(parent.mapval['aList'].length, 4) + assert_equal(parent.mapval['aList'][1], -1) + end + + def test_H_map_list_method_call + parent = @qmfc.object(:class => "parent") + assert(parent, "Number of 'parent' objects") + + inMap = {'aLong' => 9999999999, + 'aInt' => 54321, + 'aSigned' => -666, + 'aString' => "A String", + 'aFloat' => 3.1415, + 'aList' => ['x', -1, 'y', 2], + 'abool' => false} + + inList = ['aString', 1, -1, 2.7182, {'aMap'=> -8}, true] + + result = parent.test_map_list(inMap, inList) + assert_equal(result.status, 0) + assert_equal(result.text, "OK") + + # verify returned values + assert_equal(inMap.length, result.args['outMap'].length) + result.args['outMap'].each do |k,v| + assert_equal(inMap[k], v) + end + + assert_equal(inList.length, result.args['outList'].length) + for idx in 0...inList.length + assert_equal(inList[idx], result.args['outList'][idx]) + end + end + + def test_H_map_list_method_call_big + parent = @qmfc.object(:class => "parent") + assert(parent, "Number of 'parent' objects") + + big_string = "" + segment = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + for idx in 1...1500 + big_string = big_string + segment + end + + inMap = {'aLong' => 9999999999, + 'aInt' => 54321, + 'aSigned' => -666, + 'aString' => big_string, + 'another' => big_string, + 'aFloat' => 3.1415, + 'aList' => ['x', -1, 'y', 2], + 'abool' => false} + + inList = ['aString', 1, -1, 2.7182, {'aMap'=> -8}, true] + + result = parent.test_map_list(inMap, inList) + assert_equal(result.status, 0) + assert_equal(result.text, "OK") + + # verify returned values + assert_equal(inMap.length, result.args['outMap'].length) + result.args['outMap'].each do |k,v| + assert_equal(inMap[k], v) + end + + assert_equal(inList.length, result.args['outList'].length) + for idx in 0...inList.length + assert_equal(inList[idx], result.args['outList'][idx]) + end + end + +end + +app = ConsoleTest.new + diff --git a/qpid/cpp/bindings/qmf/tests/run_interop_tests b/qpid/cpp/bindings/qmf/tests/run_interop_tests new file mode 100755 index 0000000000..83e7f2593b --- /dev/null +++ b/qpid/cpp/bindings/qmf/tests/run_interop_tests @@ -0,0 +1,135 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Run the qmf interoperability tests. +MY_DIR=`dirname \`which $0\`` +QPID_DIR=${MY_DIR}/../../../.. +BUILD_DIR=../../.. +PYTHON_DIR=${QPID_DIR}/python +QMF_DIR=${QPID_DIR}/extras/qmf +QMF_DIR_PY=${QMF_DIR}/src/py +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() { + ${BROKER_DIR}/qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no > _qpidd.port + BROKER_PORT=`cat _qpidd.port` +} + +stop_broker() { + ${BROKER_DIR}/qpidd -q --port $BROKER_PORT + echo "Broker stopped" +} + +start_ruby_agent() { + ruby -I${MY_DIR}/../ruby -I${RUBY_LIB_DIR} ${MY_DIR}/agent_ruby.rb localhost $BROKER_PORT & + AGENT_PID=$! +} + +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}:${QMF_DIR_PY}:${MY_DIR} + export PYTHONPATH + + 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=$? + if test x$RETCODE != x0; then + echo "FAIL qmf interop tests (Ruby Agent)"; + TESTS_FAILED=1 + fi + + echo " Ruby Agent (external storage) vs. Ruby Console" + ruby -I${MY_DIR} -I${MY_DIR}/../ruby -I${RUBY_LIB_DIR} ${MY_DIR}/ruby_console_test.rb localhost $BROKER_PORT $@ + RETCODE=$? + stop_ruby_agent + if test x$RETCODE != x0; then + echo "FAIL qmf interop tests (Ruby Console/Ruby Agent)"; + TESTS_FAILED=1 + fi + + if test -d ${PYTHON_LIB_DIR} ; then + echo " Python Agent (external storage) vs. Ruby Console" + start_python_agent + ruby -I${MY_DIR} -I${MY_DIR}/../ruby -I${RUBY_LIB_DIR} ${MY_DIR}/ruby_console_test.rb localhost $BROKER_PORT $@ + RETCODE=$? + stop_python_agent + if test x$RETCODE != x0; then + echo "FAIL qmf interop tests (Ruby Console/Python Agent)"; + TESTS_FAILED=1 + fi + 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$TESTS_FAILED != x0; then + echo "TEST FAILED!" + exit 1 + fi +fi diff --git a/qpid/cpp/bindings/qmf/tests/test_base.rb b/qpid/cpp/bindings/qmf/tests/test_base.rb new file mode 100644 index 0000000000..7d4609097c --- /dev/null +++ b/qpid/cpp/bindings/qmf/tests/test_base.rb @@ -0,0 +1,82 @@ +#!/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 ConsoleTestBase < Qmf::ConsoleHandler + def initialize + sleep(2) + @settings = Qmf::ConnectionSettings.new + @settings.host = ARGV[0] if ARGV.size > 0 + @settings.port = ARGV[1].to_i if ARGV.size > 1 + @connection = Qmf::Connection.new(@settings) + @qmfc = Qmf::Console.new(self) + + @broker = @qmfc.add_connection(@connection) + @broker.wait_for_stable + + @store_events = :false + @event_list = [] + + tests = [] + methods.each do |m| + name = m.to_s + tests << name if name[0..4] == "test_" + end + + failures = 0 + + tests.sort.each do |t| + begin + print "#{t}..." + $stdout.flush + send(t) + puts " Pass" + rescue + puts " Fail: #{$!}" + failures += 1 + end + end + + @qmfc.del_connection(@broker) + exit(1) if failures > 0 + end + + def assert_equal(left, right, in_text=nil) + text = " (#{in_text})" if in_text + raise "Assertion failed: #{left} != #{right}#{text}" unless left == right + end + + def assert(condition, in_text=nil) + text = " (#{in_text})" if in_text + raise "Assertion failed: #{condition} #{text}" unless condition + end + + def fail(text) + raise text + end + + def event_received(event) + @event_list << event if @store_events + end + +end |