summaryrefslogtreecommitdiff
path: root/qpid/cpp/bindings/qmf
diff options
context:
space:
mode:
authorRajith Muditha Attapattu <rajith@apache.org>2011-05-27 15:44:23 +0000
committerRajith Muditha Attapattu <rajith@apache.org>2011-05-27 15:44:23 +0000
commit66765100f4257159622cefe57bed50125a5ad017 (patch)
treea88ee23bb194eb91f0ebb2d9b23ff423e3ea8e37 /qpid/cpp/bindings/qmf
parent1aeaa7b16e5ce54f10c901d75c4d40f9f88b9db6 (diff)
parent88b98b2f4152ef59a671fad55a0d08338b6b78ca (diff)
downloadqpid-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.am33
-rw-r--r--qpid/cpp/bindings/qmf/python/Makefile.am51
-rw-r--r--qpid/cpp/bindings/qmf/python/python.i143
-rw-r--r--qpid/cpp/bindings/qmf/python/qmf.py1680
-rw-r--r--qpid/cpp/bindings/qmf/qmfengine.i59
-rw-r--r--qpid/cpp/bindings/qmf/ruby/Makefile.am45
-rw-r--r--qpid/cpp/bindings/qmf/ruby/qmf.rb1522
-rw-r--r--qpid/cpp/bindings/qmf/ruby/ruby.i106
-rw-r--r--qpid/cpp/bindings/qmf/tests/Makefile.am27
-rwxr-xr-xqpid/cpp/bindings/qmf/tests/agent_ruby.rb279
-rw-r--r--qpid/cpp/bindings/qmf/tests/python_agent.py326
-rwxr-xr-xqpid/cpp/bindings/qmf/tests/python_console.py311
-rwxr-xr-xqpid/cpp/bindings/qmf/tests/ruby_console.rb174
-rwxr-xr-xqpid/cpp/bindings/qmf/tests/ruby_console_test.rb397
-rwxr-xr-xqpid/cpp/bindings/qmf/tests/run_interop_tests135
-rw-r--r--qpid/cpp/bindings/qmf/tests/test_base.rb82
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