summaryrefslogtreecommitdiff
path: root/qpid/cpp/bindings/qmf2
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/cpp/bindings/qmf2')
-rw-r--r--qpid/cpp/bindings/qmf2/Makefile.am33
-rw-r--r--qpid/cpp/bindings/qmf2/examples/cpp/Makefile.am33
-rw-r--r--qpid/cpp/bindings/qmf2/examples/cpp/agent.cpp250
-rw-r--r--qpid/cpp/bindings/qmf2/examples/cpp/list_agents.cpp73
-rw-r--r--qpid/cpp/bindings/qmf2/examples/cpp/print_events.cpp64
-rwxr-xr-xqpid/cpp/bindings/qmf2/examples/python/agent.py196
-rw-r--r--qpid/cpp/bindings/qmf2/examples/python/find_agents.py57
-rw-r--r--qpid/cpp/bindings/qmf2/examples/ruby/agent_external.rb84
-rw-r--r--qpid/cpp/bindings/qmf2/examples/ruby/agent_internal.rb77
-rw-r--r--qpid/cpp/bindings/qmf2/examples/ruby/find_agents.rb63
-rw-r--r--qpid/cpp/bindings/qmf2/python/Makefile.am49
-rw-r--r--qpid/cpp/bindings/qmf2/python/python.i41
-rw-r--r--qpid/cpp/bindings/qmf2/python/qmf2.py933
-rw-r--r--qpid/cpp/bindings/qmf2/qmf2.i66
-rw-r--r--qpid/cpp/bindings/qmf2/ruby/Makefile.am44
-rw-r--r--qpid/cpp/bindings/qmf2/ruby/qmf2.rb855
-rw-r--r--qpid/cpp/bindings/qmf2/ruby/ruby.i35
17 files changed, 2953 insertions, 0 deletions
diff --git a/qpid/cpp/bindings/qmf2/Makefile.am b/qpid/cpp/bindings/qmf2/Makefile.am
new file mode 100644
index 0000000000..52b1bbd457
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/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 = qmf2.i
+SUBDIRS = examples/cpp
+
+if HAVE_RUBY_DEVEL
+SUBDIRS += ruby
+endif
+
+if HAVE_PYTHON_DEVEL
+SUBDIRS += python
+endif
+
+endif
diff --git a/qpid/cpp/bindings/qmf2/examples/cpp/Makefile.am b/qpid/cpp/bindings/qmf2/examples/cpp/Makefile.am
new file mode 100644
index 0000000000..84207d43c4
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/examples/cpp/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.
+#
+
+INCLUDE = -I$(top_srcdir)/include
+
+AM_CPPFLAGS = $(INCLUDE)
+
+noinst_PROGRAMS=agent list_agents print_events
+
+agent_SOURCES=agent.cpp
+agent_LDADD=$(top_builddir)/src/libqmf2.la
+
+list_agents_SOURCES=list_agents.cpp
+list_agents_LDADD=$(top_builddir)/src/libqmf2.la
+
+print_events_SOURCES=print_events.cpp
+print_events_LDADD=$(top_builddir)/src/libqmf2.la
diff --git a/qpid/cpp/bindings/qmf2/examples/cpp/agent.cpp b/qpid/cpp/bindings/qmf2/examples/cpp/agent.cpp
new file mode 100644
index 0000000000..00554539eb
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/examples/cpp/agent.cpp
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Duration.h>
+#include <qmf/AgentSession.h>
+#include <qmf/AgentEvent.h>
+#include <qmf/Schema.h>
+#include <qmf/SchemaProperty.h>
+#include <qmf/SchemaMethod.h>
+#include <qmf/Data.h>
+#include <qmf/DataAddr.h>
+#include <qpid/types/Variant.h>
+#include <string>
+#include <iostream>
+
+using namespace std;
+using namespace qmf;
+using qpid::types::Variant;
+using qpid::messaging::Duration;
+
+class ExampleAgent {
+public:
+ ExampleAgent(const string& url);
+ ~ExampleAgent();
+
+ void setupSchema();
+ void populateData();
+ void run();
+private:
+ qpid::messaging::Connection connection;
+ AgentSession session;
+ Schema sch_exception;
+ Schema sch_control;
+ Schema sch_child;
+ Schema sch_event;
+ Data control;
+ DataAddr controlAddr;
+
+ bool method(AgentEvent& event);
+};
+
+
+ExampleAgent::ExampleAgent(const string& url)
+{
+ //
+ // Create and open a messaging connection to a broker.
+ //
+ connection = qpid::messaging::Connection(url, "{reconnect:True}");
+ connection.open();
+
+ //
+ // Create, configure, and open a QMFv2 agent session using the connection.
+ //
+ session = AgentSession(connection, "{interval:30}");
+ session.setVendor("profitron.com");
+ session.setProduct("gizmo");
+ session.setAttribute("attr1", 2000);
+ session.open();
+}
+
+ExampleAgent::~ExampleAgent()
+{
+ //
+ // Clean up the QMF session and the AMQP connection.
+ //
+ session.close();
+ connection.close();
+}
+
+void ExampleAgent::setupSchema()
+{
+ //
+ // Create and register schema for this agent.
+ //
+ string package("com.profitron.gizmo");
+
+ //
+ // Declare a schema for a structured exception that can be used in failed
+ // method invocations.
+ //
+ sch_exception = Schema(SCHEMA_TYPE_DATA, package, "exception");
+ sch_exception.addProperty(SchemaProperty("whatHappened", SCHEMA_DATA_STRING));
+ sch_exception.addProperty(SchemaProperty("howBad", SCHEMA_DATA_INT));
+ sch_exception.addProperty(SchemaProperty("details", SCHEMA_DATA_MAP));
+
+ //
+ // Declare a control object to test methods against.
+ //
+ sch_control = Schema(SCHEMA_TYPE_DATA, package, "control");
+ sch_control.addProperty(SchemaProperty("state", SCHEMA_DATA_STRING));
+ sch_control.addProperty(SchemaProperty("methodCount", SCHEMA_DATA_INT));
+
+ SchemaMethod stopMethod("stop", "{desc:'Stop Agent'}");
+ stopMethod.addArgument(SchemaProperty("message", SCHEMA_DATA_STRING));
+ sch_control.addMethod(stopMethod);
+
+ SchemaMethod echoMethod("echo", "{desc:'Echo Arguments'}");
+ echoMethod.addArgument(SchemaProperty("sequence", SCHEMA_DATA_INT, "{dir:INOUT}"));
+ echoMethod.addArgument(SchemaProperty("map", SCHEMA_DATA_MAP, "{dir:INOUT}"));
+ sch_control.addMethod(echoMethod);
+
+ SchemaMethod eventMethod("event", "{desc:'Raise an Event'}");
+ eventMethod.addArgument(SchemaProperty("text", SCHEMA_DATA_STRING, "{dir:IN}"));
+ eventMethod.addArgument(SchemaProperty("severity", SCHEMA_DATA_INT, "{dir:IN}"));
+ sch_control.addMethod(eventMethod);
+
+ SchemaMethod failMethod("fail", "{desc:'Expected to Fail'}");
+ failMethod.addArgument(SchemaProperty("useString", SCHEMA_DATA_BOOL, "{dir:IN}"));
+ failMethod.addArgument(SchemaProperty("stringVal", SCHEMA_DATA_STRING, "{dir:IN}"));
+ failMethod.addArgument(SchemaProperty("details", SCHEMA_DATA_MAP, "{dir:IN}"));
+ sch_control.addMethod(failMethod);
+
+ SchemaMethod createMethod("create_child", "{desc:'Create Child Object'}");
+ createMethod.addArgument(SchemaProperty("name", SCHEMA_DATA_STRING, "{dir:IN}"));
+ createMethod.addArgument(SchemaProperty("childAddr", SCHEMA_DATA_MAP, "{dir:OUT}"));
+ sch_control.addMethod(createMethod);
+
+ //
+ // Declare the child class
+ //
+ sch_child = Schema(SCHEMA_TYPE_DATA, package, "child");
+ sch_child.addProperty(SchemaProperty("name", SCHEMA_DATA_STRING));
+
+ //
+ // Declare the event class
+ //
+ sch_event = Schema(SCHEMA_TYPE_EVENT, package, "event");
+ sch_event.addProperty(SchemaProperty("text", SCHEMA_DATA_STRING));
+
+ //
+ // Register our schemata with the agent session.
+ //
+ session.registerSchema(sch_exception);
+ session.registerSchema(sch_control);
+ session.registerSchema(sch_child);
+ session.registerSchema(sch_event);
+}
+
+void ExampleAgent::populateData()
+{
+ //
+ // Create a control object and give it to the agent session to manage.
+ //
+ control = Data(sch_control);
+ control.setProperty("state", "OPERATIONAL");
+ control.setProperty("methodCount", 0);
+ controlAddr = session.addData(control, "singleton");
+}
+
+void ExampleAgent::run()
+{
+ AgentEvent event;
+ bool running(true);
+
+ while (running) {
+ bool valid(session.nextEvent(event, Duration::SECOND));
+ if (valid && running) {
+ switch (event.getType()) {
+ case AGENT_METHOD:
+ running = method(event);
+ break;
+ }
+ }
+ }
+}
+
+bool ExampleAgent::method(AgentEvent& event)
+{
+ const string& name(event.getMethodName());
+ control.setProperty("methodCount", control.getProperty("methodCount").asUint32() + 1);
+
+ try {
+ if (controlAddr == event.getDataAddr()) {
+ if (name == "stop") {
+ cout << "Stopping: message=" << event.getArguments()["message"] << endl;
+ session.methodSuccess(event);
+ return false;
+ }
+
+ if (name == "echo") {
+ event.addReturnArgument("sequence", event.getArguments()["sequence"]);
+ event.addReturnArgument("map", event.getArguments()["map"]);
+ session.methodSuccess(event);
+ return true;
+ }
+
+ if (name == "event") {
+ Data ev(sch_event);
+ ev.setProperty("text", event.getArguments()["text"]);
+ session.raiseEvent(ev, event.getArguments()["severity"]);
+ session.methodSuccess(event);
+ return true;
+ }
+
+ if (name == "fail") {
+ if (event.getArguments()["useString"])
+ session.raiseException(event, event.getArguments()["stringVal"]);
+ else {
+ Data ex(sch_exception);
+ ex.setProperty("whatHappened", "It Failed");
+ ex.setProperty("howBad", 75);
+ ex.setProperty("details", event.getArguments()["details"]);
+ session.raiseException(event, ex);
+ }
+ }
+
+ if (name == "create_child") {
+ const string& name(event.getArguments()["name"]);
+ Data child(sch_child);
+ child.setProperty("name", name);
+ DataAddr addr(session.addData(child, name));
+ event.addReturnArgument("childAddr", addr.asMap());
+ session.methodSuccess(event);
+ }
+ }
+ } catch (const exception& e) {
+ //
+ // Pass the exception on to the caller.
+ //
+ session.raiseException(event, e.what());
+ }
+
+ return true;
+}
+
+int main()
+{
+ ExampleAgent agent("localhost");
+ agent.setupSchema();
+ agent.populateData();
+ agent.run();
+}
+
diff --git a/qpid/cpp/bindings/qmf2/examples/cpp/list_agents.cpp b/qpid/cpp/bindings/qmf2/examples/cpp/list_agents.cpp
new file mode 100644
index 0000000000..327da9661f
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/examples/cpp/list_agents.cpp
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Duration.h>
+#include <qmf/ConsoleSession.h>
+#include <qmf/ConsoleEvent.h>
+#include <qmf/Agent.h>
+#include <qpid/types/Variant.h>
+#include <string>
+#include <iostream>
+
+using namespace std;
+using namespace qmf;
+using qpid::types::Variant;
+using qpid::messaging::Duration;
+
+int main(int argc, char** argv)
+{
+ string url("localhost");
+ string connectionOptions;
+ string sessionOptions;
+
+ if (argc > 1)
+ url = argv[1];
+ if (argc > 2)
+ connectionOptions = argv[2];
+ if (argc > 3)
+ sessionOptions = argv[3];
+
+ qpid::messaging::Connection connection(url, connectionOptions);
+ connection.open();
+
+ ConsoleSession session(connection, sessionOptions);
+ session.open();
+
+ session.setAgentFilter("");
+
+ while (true) {
+ ConsoleEvent event;
+ if (session.nextEvent(event)) {
+ if (event.getType() == CONSOLE_AGENT_ADD) {
+ string extra;
+ if (event.getAgent().getName() == session.getConnectedBrokerAgent().getName())
+ extra = " [Connected Broker]";
+ cout << "Agent Added: " << event.getAgent().getName() << extra << endl;
+ }
+ if (event.getType() == CONSOLE_AGENT_DEL) {
+ if (event.getAgentDelReason() == AGENT_DEL_AGED)
+ cout << "Agent Aged: " << event.getAgent().getName() << endl;
+ else
+ cout << "Agent Filtered: " << event.getAgent().getName() << endl;
+ }
+ }
+ }
+}
+
diff --git a/qpid/cpp/bindings/qmf2/examples/cpp/print_events.cpp b/qpid/cpp/bindings/qmf2/examples/cpp/print_events.cpp
new file mode 100644
index 0000000000..9883a19962
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/examples/cpp/print_events.cpp
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Duration.h>
+#include <qmf/ConsoleSession.h>
+#include <qmf/ConsoleEvent.h>
+#include <qmf/Data.h>
+#include <qpid/types/Variant.h>
+#include <string>
+#include <iostream>
+
+using namespace std;
+using namespace qmf;
+using qpid::types::Variant;
+using qpid::messaging::Duration;
+
+int main(int argc, char** argv)
+{
+ string url("localhost");
+ string connectionOptions;
+ string sessionOptions;
+
+ if (argc > 1)
+ url = argv[1];
+ if (argc > 2)
+ connectionOptions = argv[2];
+ if (argc > 3)
+ sessionOptions = argv[3];
+
+ qpid::messaging::Connection connection(url, connectionOptions);
+ connection.open();
+
+ ConsoleSession session(connection, sessionOptions);
+ session.open();
+
+ while (true) {
+ ConsoleEvent event;
+ if (session.nextEvent(event)) {
+ if (event.getType() == CONSOLE_EVENT) {
+ const Data& data(event.getData(0));
+ cout << "Event: timestamp=" << event.getTimestamp() << " severity=" <<
+ event.getSeverity() << " content=" << data.getProperties() << endl;
+ }
+ }
+ }
+}
+
diff --git a/qpid/cpp/bindings/qmf2/examples/python/agent.py b/qpid/cpp/bindings/qmf2/examples/python/agent.py
new file mode 100755
index 0000000000..b24890f531
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/examples/python/agent.py
@@ -0,0 +1,196 @@
+#!/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 cqpid
+from qmf2 import *
+
+
+class ExampleAgent(AgentHandler):
+ """
+ This example agent is implemented as a single class that inherits AgentHandler.
+ It does not use a separate thread since once set up, it is driven strictly by
+ incoming method calls.
+ """
+
+ def __init__(self, url):
+ ##
+ ## Create and open a messaging connection to a broker.
+ ##
+ self.connection = cqpid.Connection(url, "{reconnect:True}")
+ self.session = None
+ self.connection.open()
+
+ ##
+ ## Create, configure, and open a QMFv2 agent session using the connection.
+ ##
+ self.session = AgentSession(self.connection, "{interval:30}")
+ self.session.setVendor('profitron.com')
+ self.session.setProduct('blastinator')
+ self.session.setAttribute('attr1', 1000)
+ self.session.open()
+
+ ##
+ ## Initialize the parent class.
+ ##
+ AgentHandler.__init__(self, self.session)
+
+
+ def shutdown(self):
+ """
+ Clean up the session and connection.
+ """
+ if self.session:
+ self.session.close()
+ self.connection.close()
+
+
+ def method(self, handle, methodName, args, subtypes, addr, userId):
+ """
+ Handle incoming method calls.
+ """
+ if addr == self.controlAddr:
+ self.control.methodCount += 1
+
+ try:
+ if methodName == "stop":
+ self.session.methodSuccess(handle)
+ self.cancel()
+
+ elif methodName == "echo":
+ handle.addReturnArgument("sequence", args["sequence"])
+ handle.addReturnArgument("map", args["map"])
+ self.session.methodSuccess(handle)
+
+ elif methodName == "event":
+ ev = Data(self.sch_event)
+ ev.text = args['text']
+ self.session.raiseEvent(ev, args['severity'])
+ self.session.methodSuccess(handle)
+
+ elif methodName == "fail":
+ if args['useString']:
+ self.session.raiseException(handle, args['stringVal'])
+ else:
+ ex = Data(self.sch_exception)
+ ex.whatHappened = "It Failed"
+ ex.howBad = 75
+ ex.details = args['details']
+ self.session.raiseException(handle, ex)
+
+ elif methodName == "create_child":
+ name = args['name']
+ child = Data(self.sch_child)
+ child.name = name
+ addr = self.session.addData(child, name)
+ handle.addReturnArgument("childAddr", addr.asMap())
+ self.session.methodSuccess(handle)
+ except BaseException, e:
+ self.session.raiseException(handle, "%r" % e)
+
+
+ def setupSchema(self):
+ """
+ Create and register the schema for this agent.
+ """
+ package = "com.profitron.bntor"
+
+ ##
+ ## Declare a schema for a structured exception that can be used in failed
+ ## method invocations.
+ ##
+ self.sch_exception = Schema(SCHEMA_TYPE_DATA, package, "exception")
+ self.sch_exception.addProperty(SchemaProperty("whatHappened", SCHEMA_DATA_STRING))
+ self.sch_exception.addProperty(SchemaProperty("howBad", SCHEMA_DATA_INT))
+ self.sch_exception.addProperty(SchemaProperty("details", SCHEMA_DATA_MAP))
+
+ ##
+ ## Declare a control object to test methods against.
+ ##
+ self.sch_control = Schema(SCHEMA_TYPE_DATA, package, "control")
+ self.sch_control.addProperty(SchemaProperty("state", SCHEMA_DATA_STRING))
+ self.sch_control.addProperty(SchemaProperty("methodCount", SCHEMA_DATA_INT))
+
+ stopMethod = SchemaMethod("stop", desc="Stop Agent")
+ stopMethod.addArgument(SchemaProperty("message", SCHEMA_DATA_STRING, direction=DIR_IN))
+ self.sch_control.addMethod(stopMethod)
+
+ echoMethod = SchemaMethod("echo", desc="Echo Arguments")
+ echoMethod.addArgument(SchemaProperty("sequence", SCHEMA_DATA_INT, direction=DIR_IN_OUT))
+ echoMethod.addArgument(SchemaProperty("map", SCHEMA_DATA_MAP, direction=DIR_IN_OUT))
+ self.sch_control.addMethod(echoMethod)
+
+ eventMethod = SchemaMethod("event", desc="Raise an Event")
+ eventMethod.addArgument(SchemaProperty("text", SCHEMA_DATA_STRING, direction=DIR_IN))
+ eventMethod.addArgument(SchemaProperty("severity", SCHEMA_DATA_INT, direction=DIR_IN))
+ self.sch_control.addMethod(eventMethod)
+
+ failMethod = SchemaMethod("fail", desc="Expected to Fail")
+ failMethod.addArgument(SchemaProperty("useString", SCHEMA_DATA_BOOL, direction=DIR_IN))
+ failMethod.addArgument(SchemaProperty("stringVal", SCHEMA_DATA_STRING, direction=DIR_IN))
+ failMethod.addArgument(SchemaProperty("details", SCHEMA_DATA_MAP, direction=DIR_IN))
+ self.sch_control.addMethod(failMethod)
+
+ createMethod = SchemaMethod("create_child", desc="Create Child Object")
+ createMethod.addArgument(SchemaProperty("name", SCHEMA_DATA_STRING, direction=DIR_IN))
+ createMethod.addArgument(SchemaProperty("childAddr", SCHEMA_DATA_MAP, direction=DIR_OUT))
+ self.sch_control.addMethod(createMethod)
+
+ ##
+ ## Declare a child object
+ ##
+ self.sch_child = Schema(SCHEMA_TYPE_DATA, package, "child")
+ self.sch_child.addProperty(SchemaProperty("name", SCHEMA_DATA_STRING))
+
+ ##
+ ## Declare the event class
+ ##
+ self.sch_event = Schema(SCHEMA_TYPE_EVENT, package, "event")
+ self.sch_event.addProperty(SchemaProperty("text", SCHEMA_DATA_STRING))
+
+ ##
+ ## Register our schemata with the agent session.
+ ##
+ self.session.registerSchema(self.sch_exception)
+ self.session.registerSchema(self.sch_control)
+ self.session.registerSchema(self.sch_child)
+ self.session.registerSchema(self.sch_event)
+
+
+ def populateData(self):
+ """
+ Create a control object and give it to the agent session to manage.
+ """
+ self.control = Data(self.sch_control)
+ self.control.state = "OPERATIONAL"
+ self.control.methodCount = 0
+ self.controlAddr = self.session.addData(self.control, "singleton")
+
+
+try:
+ agent = ExampleAgent("localhost")
+ agent.setupSchema()
+ agent.populateData()
+ agent.run() # Use agent.start() to launch the agent in a separate thread
+ agent.shutdown()
+except Exception, e:
+ print "Exception Caught:", e
+
+
diff --git a/qpid/cpp/bindings/qmf2/examples/python/find_agents.py b/qpid/cpp/bindings/qmf2/examples/python/find_agents.py
new file mode 100644
index 0000000000..5fd71b3f1c
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/examples/python/find_agents.py
@@ -0,0 +1,57 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import cqpid
+import qmf2
+
+class FindAgents(qmf2.ConsoleHandler):
+
+ def __init__(self, session):
+ qmf2.ConsoleHandler.__init__(self, session)
+
+ def agentAdded(self, agent):
+ print "Agent Added: %r" % agent
+
+ def agentDeleted(self, agent, reason):
+ print "Agent Deleted: %r reason: %s" % (agent, reason)
+
+ def agentRestarted(self, agent):
+ print "Agent Restarted: %r" % agent
+
+ def agentSchemaUpdated(self, agent):
+ print "Agent Schema Updated: %r" % agent
+
+ def eventRaised(self, agent, data, timestamp, severity):
+ print "Event: data=%r time=%d sev=%d" % (data.getProperties(), timestamp, severity)
+
+
+
+url = "localhost"
+options = ""
+
+connection = cqpid.Connection(url, options)
+connection.open()
+
+session = qmf2.ConsoleSession(connection)
+session.open()
+session.setAgentFilter("[]")
+
+main = FindAgents(session)
+main.run()
+
diff --git a/qpid/cpp/bindings/qmf2/examples/ruby/agent_external.rb b/qpid/cpp/bindings/qmf2/examples/ruby/agent_external.rb
new file mode 100644
index 0000000000..75171931ed
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/examples/ruby/agent_external.rb
@@ -0,0 +1,84 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'cqpid'
+require 'qmf2'
+
+class MyAgent < Qmf2::AgentHandler
+
+ def initialize(session, data)
+ super(session)
+ @data = data
+ end
+
+ def authorize_query(query, user_id)
+ puts "Authorizing #{user_id}"
+ return true
+ end
+
+ def get_query(context, query, user_id)
+ puts "Get Query"
+ context.response(@data)
+ context.complete
+ end
+
+ def method_call(context, method_name, data_addr, args, user_id)
+ puts "Method: #{method_name}"
+ context._success
+ end
+
+end
+
+
+class Program
+
+ def initialize(url)
+ @url = url
+ @sess_options = "{allow-queries:False, external:True}"
+ end
+
+ def setup_schema(agent)
+ @cls_control = Qmf2::Schema.new(Qmf2::SCHEMA_TYPE_DATA, "org.package", "control")
+ @cls_control.add_property(Qmf2::SchemaProperty.new("state", Qmf2::SCHEMA_DATA_STRING))
+ agent.register_schema(@cls_control)
+ end
+
+ def run
+ connection = Cqpid::Connection.new(@url)
+ connection.open
+
+ session = Qmf2::AgentSession.new(connection, @sess_options)
+ session.set_vendor("package.org")
+ session.set_product("external_agent")
+ setup_schema(session)
+ session.open
+
+ @control = Qmf2::Data.new(@cls_control)
+ @control.state = "OPERATIONAL-EXTERNAL"
+ @control.set_addr(Qmf2::DataAddr.new("singleton"))
+
+ main = MyAgent.new(session, @control)
+ main.run
+ end
+end
+
+prog = Program.new("localhost")
+prog.run
+
+
diff --git a/qpid/cpp/bindings/qmf2/examples/ruby/agent_internal.rb b/qpid/cpp/bindings/qmf2/examples/ruby/agent_internal.rb
new file mode 100644
index 0000000000..fc49a885f7
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/examples/ruby/agent_internal.rb
@@ -0,0 +1,77 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'cqpid'
+require 'qmf2'
+
+class MyAgent < Qmf2::AgentHandler
+
+ def initialize(session)
+ super(session)
+ end
+
+ def authorize_query(query, user_id)
+ puts "Authorizing #{user_id}"
+ return true
+ end
+
+ def method_call(context, method_name, data_addr, args, user_id)
+ puts "Method: #{method_name}"
+ context._success
+ end
+
+end
+
+
+class Program
+
+ def initialize(url)
+ @url = url
+ @sess_options = "{allow-queries:False}"
+ end
+
+ def setup_schema(agent)
+ @cls_control = Qmf2::Schema.new(Qmf2::SCHEMA_TYPE_DATA, "org.package", "control")
+ @cls_control.add_property(Qmf2::SchemaProperty.new("state", Qmf2::SCHEMA_DATA_STRING))
+ agent.register_schema(@cls_control)
+ end
+
+ def run
+ connection = Cqpid::Connection.new(@url)
+ connection.open
+
+ session = Qmf2::AgentSession.new(connection, @sess_options)
+ session.set_vendor("package.org")
+ session.set_product("internal_agent")
+ setup_schema(session)
+ session.open
+
+ control = Qmf2::Data.new(@cls_control)
+ control.state = "OPERATIONAL"
+ session.add_data(control)
+
+ main = MyAgent.new(session)
+ main.run
+ end
+end
+
+prog = Program.new("localhost")
+prog.run
+
+
diff --git a/qpid/cpp/bindings/qmf2/examples/ruby/find_agents.rb b/qpid/cpp/bindings/qmf2/examples/ruby/find_agents.rb
new file mode 100644
index 0000000000..41de7e5abe
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/examples/ruby/find_agents.rb
@@ -0,0 +1,63 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'cqpid'
+require 'qmf2'
+
+class FindAgents < Qmf2::ConsoleHandler
+
+ def initialize(session)
+ super(session)
+ end
+
+ def agent_added(agent)
+ puts "Agent Added: #{agent.name}"
+ end
+
+ def agent_deleted(agent, reason)
+ puts "Agent Deleted: #{agent.to_s} reason: #{reason}"
+ end
+
+ def agent_restarted(agent)
+ puts "Agent Restarted: #{agent.to_s} epoch: #{agent.epoch}"
+ end
+
+ def agent_schema_updated(agent)
+ puts "Agent with new Schemata: #{agent.to_s}"
+ end
+
+ def event_raised(agent, data, timestamp, severity)
+ puts "Event Raised time=#{timestamp} sev=#{severity} data=#{data.properties}"
+ end
+end
+
+
+url = "localhost"
+options = ""
+
+connection = Cqpid::Connection.new(url, options)
+connection.open
+
+session = Qmf2::ConsoleSession.new(connection)
+session.open
+session.set_agent_filter("[]")
+
+main = FindAgents.new(session)
+main.run
+
diff --git a/qpid/cpp/bindings/qmf2/python/Makefile.am b/qpid/cpp/bindings/qmf2/python/Makefile.am
new file mode 100644
index 0000000000..3dc04e832f
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/python/Makefile.am
@@ -0,0 +1,49 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+if HAVE_PYTHON_DEVEL
+
+INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src -I$(top_builddir)/src $(QMF_INCLUDES)
+
+generated_file_list = \
+ cqmf2.cpp \
+ cqmf2.py
+
+EXTRA_DIST = python.i
+BUILT_SOURCES = $(generated_file_list)
+SWIG_FLAGS = -w362,401
+
+$(generated_file_list): $(srcdir)/python.i $(srcdir)/../qmf2.i $(srcdir)/../../swig_python_typemaps.i
+ $(SWIG) -c++ -python $(SWIG_FLAGS) $(INCLUDES) $(QPID_CXXFLAGS) -I/usr/include -o cqmf2.cpp $(srcdir)/python.i
+
+pylibdir = $(PYTHON_LIB)
+
+lib_LTLIBRARIES = _cqmf2.la
+cqpiddir = $(pyexecdir)
+cqpid_PYTHON = qmf2.py cqmf2.py
+
+_cqmf2_la_LDFLAGS = -avoid-version -module -shared
+_cqmf2_la_LIBADD = $(PYTHON_LIBS) -L$(top_builddir)/src/.libs $(top_builddir)/src/libqmf2.la
+_cqmf2_la_CXXFLAGS = $(INCLUDES) -I$(srcdir)/qmf -I$(PYTHON_INC) -fno-strict-aliasing
+nodist__cqmf2_la_SOURCES = cqmf2.cpp
+
+CLEANFILES = $(generated_file_list)
+
+endif # HAVE_PYTHON_DEVEL
+
diff --git a/qpid/cpp/bindings/qmf2/python/python.i b/qpid/cpp/bindings/qmf2/python/python.i
new file mode 100644
index 0000000000..02dd1632b0
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/python/python.i
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+%module cqmf2
+%include "std_string.i"
+%include "../../swig_python_typemaps.i"
+
+/* Define the general-purpose exception handling */
+%exception {
+ std::string error;
+ Py_BEGIN_ALLOW_THREADS;
+ try {
+ $action
+ } catch (qpid::types::Exception& ex) {
+ error = ex.what();
+ }
+ Py_END_ALLOW_THREADS;
+ if (!error.empty()) {
+ PyErr_SetString(PyExc_RuntimeError, error.c_str());
+ return NULL;
+ }
+}
+
+%include "../qmf2.i"
+
diff --git a/qpid/cpp/bindings/qmf2/python/qmf2.py b/qpid/cpp/bindings/qmf2/python/qmf2.py
new file mode 100644
index 0000000000..9f2d8556f4
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/python/qmf2.py
@@ -0,0 +1,933 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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 cqmf2
+import cqpid
+from threading import Thread
+import time
+
+#===================================================================================================
+# CONSTANTS
+#===================================================================================================
+SCHEMA_TYPE_DATA = cqmf2.SCHEMA_TYPE_DATA
+SCHEMA_TYPE_EVENT = cqmf2.SCHEMA_TYPE_EVENT
+
+SCHEMA_DATA_VOID = cqmf2.SCHEMA_DATA_VOID
+SCHEMA_DATA_BOOL = cqmf2.SCHEMA_DATA_BOOL
+SCHEMA_DATA_INT = cqmf2.SCHEMA_DATA_INT
+SCHEMA_DATA_FLOAT = cqmf2.SCHEMA_DATA_FLOAT
+SCHEMA_DATA_STRING = cqmf2.SCHEMA_DATA_STRING
+SCHEMA_DATA_MAP = cqmf2.SCHEMA_DATA_MAP
+SCHEMA_DATA_LIST = cqmf2.SCHEMA_DATA_LIST
+SCHEMA_DATA_UUID = cqmf2.SCHEMA_DATA_UUID
+
+ACCESS_READ_CREATE = cqmf2.ACCESS_READ_CREATE
+ACCESS_READ_WRITE = cqmf2.ACCESS_READ_WRITE
+ACCESS_READ_ONLY = cqmf2.ACCESS_READ_ONLY
+
+DIR_IN = cqmf2.DIR_IN
+DIR_OUT = cqmf2.DIR_OUT
+DIR_IN_OUT = cqmf2.DIR_IN_OUT
+
+SEV_EMERG = cqmf2.SEV_EMERG
+SEV_ALERT = cqmf2.SEV_ALERT
+SEV_CRIT = cqmf2.SEV_CRIT
+SEV_ERROR = cqmf2.SEV_ERROR
+SEV_WARN = cqmf2.SEV_WARN
+SEV_NOTICE = cqmf2.SEV_NOTICE
+SEV_INFORM = cqmf2.SEV_INFORM
+SEV_DEBUG = cqmf2.SEV_DEBUG
+
+QUERY_OBJECT = cqmf2.QUERY_OBJECT
+QUERY_OBJECT_ID = cqmf2.QUERY_OBJECT_ID
+QUERY_SCHEMA = cqmf2.QUERY_SCHEMA
+QUERY_SCHEMA_ID = cqmf2.QUERY_SCHEMA_ID
+
+
+#===================================================================================================
+# EXCEPTIONS
+#===================================================================================================
+class QmfAgentException(Exception):
+ """
+ This exception class represents an exception that was raised by a remote agent and propagated
+ to a console via QMFv2.
+ """
+ def __init__(self, data):
+ self.value = data
+
+ def __str__(self):
+ return "From Remote Agent: %r" % self.value.getProperties()
+
+
+#===================================================================================================
+# AGENT HANDLER
+#===================================================================================================
+class AgentHandler(Thread):
+ """
+ Agent applications can create a subclass of AgentHandler to handle asynchronous events (like
+ incoming method calls) that occur on the agent session. AgentHandler contains a thread on which
+ the handler callbacks are invoked.
+
+ There are two ways to operate the handler: Cause it to start its own thread by calling
+ start() and later stop it by calling cancel(); and directly calling run() to operate it on the
+ main thread.
+
+ Example Usage:
+
+ class MyAgentHandler(qmf2.AgentHandler):
+ def __init__(self, agentSession):
+ qmf2.AgentHandler.__init__(self, agentSession)
+ def method(self, handle, methodName, args, subtypes, addr, userId):
+ ...method handling code goes here...
+ For success, add output arguments:
+ handle.addReturnArgument("argname", argvalue)
+ ...
+ self.agent.methodSuccess(handle)
+ For failure, raise an exception:
+ self.agent.raiseException(handle, "error text")
+ Or, if you have created a schema for a structured exception:
+ ex = qmf2.Data(exceptionSchema)
+ ex.whatHappened = "it failed"
+ ex.howBad = 84
+ ex.detailMap = {}
+ ...
+ self.agent.raiseException(handle, ex)
+ """
+
+ def __init__(self, agentSession):
+ Thread.__init__(self)
+ self.__agent = agentSession
+ self.__running = True
+
+ def cancel(self):
+ """
+ Stop the handler thread.
+ """
+ self.__running = None
+
+ def run(self):
+ event = cqmf2.AgentEvent()
+ while self.__running:
+ valid = self.__agent._impl.nextEvent(event, cqpid.Duration.SECOND)
+ if valid and self.__running:
+ if event.getType() == cqmf2.AGENT_METHOD:
+ self.method(event, event.getMethodName(), event.getArguments(), event.getArgumentSubtypes(),
+ DataAddr(event.getDataAddr()), event.getUserId())
+
+ def method(self, handle, methodName, args, subtypes, addr, userId):
+ """
+ Override this method to create your own method handler.
+ """
+ pass
+
+
+#===================================================================================================
+# CONSOLE HANDLER
+#===================================================================================================
+class ConsoleHandler(Thread):
+
+ def __init__(self, consoleSession):
+ Thread.__init__(self)
+ self.__session = consoleSession
+ self.__running = True
+
+ def cancel(self):
+ """
+ Stop the handler thread.
+ """
+ self.__running = None
+
+ def run(self):
+ event = cqmf2.ConsoleEvent()
+ while self.__running:
+ valid = self.__session._impl.nextEvent(event, cqpid.Duration.SECOND)
+ if valid and self.__running:
+ if event.getType() == cqmf2.CONSOLE_AGENT_ADD:
+ self.agentAdded(Agent(event.getAgent()))
+
+ elif event.getType() == cqmf2.CONSOLE_AGENT_DEL:
+ reason = 'filter'
+ if event.getAgentDelReason() == cqmf2.AGENT_DEL_AGED:
+ reason = 'aged'
+ self.agentDeleted(Agent(event.getAgent()), reason)
+
+ elif event.getType() == cqmf2.CONSOLE_AGENT_RESTART:
+ self.agentRestarted(Agent(event.getAgent()))
+
+ elif event.getType() == cqmf2.CONSOLE_AGENT_SCHEMA_UPDATE:
+ self.agentSchemaUpdated(Agent(event.getAgent()))
+
+ elif event.getType() == cqmf2.CONSOLE_EVENT:
+ self.eventRaised(Agent(event.getAgent()), Data(event.getData(0)), event.getTimestamp(), event.getSeverity())
+
+ ##
+ ## The following methods are intended to be overridden in a sub-class. They are
+ ## handlers for events that occur on QMF consoles.
+ ##
+
+ #
+ # A new agent, whose attributes match the console's agent filter, has been discovered.
+ #
+ def agentAdded(self, agent):
+ pass
+
+ #
+ # A known agent has been removed from the agent list. There are two possible reasons
+ # for agent deletion:
+ #
+ # 1) 'aged' - The agent hasn't been heard from for the maximum age interval and is
+ # presumed dead.
+ # 2) 'filter' - The agent no longer matches the console's agent-filter and has been
+ # effectively removed from the agent list. Such occurrences are likely
+ # to be seen immediately after setting the filter to a new value.
+ #
+ def agentDeleted(self, agent, reason):
+ pass
+
+ #
+ # An agent-restart was detected. This occurs when the epoch number advertised by the
+ # agent changes. It indicates that the agent in question was shut-down/crashed and
+ # restarted.
+ #
+ def agentRestarted(self, agent):
+ pass
+
+ #
+ # The agent has registered new schema information which can now be queried, if desired.
+ #
+ def agentSchemaUpdated(self, agent):
+ pass
+
+ #
+ # An agent raised an event. The 'data' argument is a Data object that contains the
+ # content of the event.
+ #
+ def eventRaised(self, agent, data, timestamp, severity):
+ pass
+
+
+#===================================================================================================
+# CONSOLE SESSION
+#===================================================================================================
+class ConsoleSession(object):
+ """
+ """
+
+ def __init__(self, connection, options=""):
+ """
+ ## The options string is of the form "{key:value,key:value}". The following keys are supported:
+ ##
+ ## domain:NAME - QMF Domain to join [default: "default"]
+ ## max-agent-age:N - Maximum time, in minutes, that we will tolerate not hearing from
+ ## an agent before deleting it [default: 5]
+ ## listen-on-direct:{True,False} - If True: Listen on legacy direct-exchange address for backward compatibility [default]
+ ## If False: Listen only on the routable direct address
+ ## strict-security:{True,False} - If True: Cooperate with the broker to enforce strict access control to the network
+ ## - If False: Operate more flexibly with regard to use of messaging facilities [default]
+ ##
+ """
+ self._impl = cqmf2.ConsoleSession(connection, options)
+
+ def setDomain(self, domain):
+ """
+ """
+ self._impl.setDomain(domain)
+
+ def setAgentFilter(self, filt):
+ """
+ """
+ self._impl.setAgentFilter(filt)
+
+ def open(self):
+ """
+ """
+ self._impl.open()
+
+ def close(self):
+ """
+ """
+ self._impl.close()
+
+ def getAgents(self):
+ """
+ """
+ result = []
+ count = self._impl.getAgentCount()
+ for i in range(count):
+ result.append(Agent(self._impl.getAgent(i)))
+ return result
+
+ def getConnectedBrokerAgent(self):
+ """
+ """
+ return Agent(self._impl.getConnectedBrokerAgent())
+
+ ## TODO: Async methods
+
+#===================================================================================================
+# AGENT SESSION
+#===================================================================================================
+class AgentSession(object):
+ """
+ """
+
+ def __init__(self, connection, options=""):
+ """
+ ## The options string is of the form "{key:value,key:value}". The following keys are supported:
+ ##
+ ## interval:N - Heartbeat interval in seconds [default: 60]
+ ## external:{True,False} - Use external data storage (queries and subscriptions are pass-through) [default: False]
+ ## allow-queries:{True,False} - If True: automatically allow all queries [default]
+ ## If False: generate an AUTH_QUERY event to allow per-query authorization
+ ## allow-methods:{True,False} - If True: automatically allow all methods [default]
+ ## If False: generate an AUTH_METHOD event to allow per-method authorization
+ ## max-subscriptions:N - Maximum number of concurrent subscription queries permitted [default: 64]
+ ## min-sub-interval:N - Minimum publish interval (in milliseconds) permitted for a subscription [default: 3000]
+ ## sub-lifetime:N - Lifetime (in seconds with no keepalive) for a subscription [default: 300]
+ ## public-events:{True,False} - If True: QMF events are sent to the topic exchange [default]
+ ## If False: QMF events are only sent to authorized subscribers
+ ## listen-on-direct:{True,False} - If True: Listen on legacy direct-exchange address for backward compatibility [default]
+ ## If False: Listen only on the routable direct address
+ ## strict-security:{True,False} - If True: Cooperate with the broker to enforce strict access control to the network
+ ## - If False: Operate more flexibly with regard to use of messaging facilities [default]
+ ##
+ """
+ self._impl = cqmf2.AgentSession(connection, options)
+
+ def setDomain(self, domain):
+ """
+ """
+ self._impl.setDomain(domain)
+
+ def setVendor(self, val):
+ """
+ """
+ self._impl.setVendor(val)
+
+ def setProduct(self, val):
+ """
+ """
+ self._impl.setProduct(val)
+
+ def setInstance(self, val):
+ """
+ """
+ self._impl.setInstance(val)
+
+ def setAttribute(self, key, val):
+ """
+ """
+ self._impl.setAttribute(key, val)
+
+ def open(self):
+ """
+ """
+ self._impl.open()
+
+ def close(self):
+ """
+ """
+ self._impl.close()
+
+ def registerSchema(self, schema):
+ """
+ """
+ self._impl.registerSchema(schema._impl)
+
+ def addData(self, data, name="", persistent=False):
+ """
+ """
+ return DataAddr(self._impl.addData(data._impl, name, persistent))
+
+ def delData(self, addr):
+ """
+ """
+ self._impl.delData(addr._impl)
+
+ def methodSuccess(self, handle):
+ """
+ """
+ self._impl.methodSuccess(handle)
+
+ def raiseException(self, handle, data):
+ """
+ """
+ if data.__class__ == Data:
+ self._impl.raiseException(handle, data._impl)
+ else:
+ self._impl.raiseException(handle, data)
+
+ def raiseEvent(self, data, severity=None):
+ """
+ """
+ if not severity:
+ self._impl.raiseEvent(data._impl)
+ else:
+ if (severity.__class__ != int and severity.__class__ != long) or severity < 0 or severity > 7:
+ raise Exception("Severity must be an int between 0..7")
+ self._impl.raiseEvent(data._impl, severity);
+
+
+#===================================================================================================
+# AGENT PROXY
+#===================================================================================================
+class Agent(object):
+ """
+ """
+
+ def __init__(self, impl):
+ self._impl = impl
+
+ def __repr__(self):
+ return self.getName()
+
+ def getName(self):
+ """
+ """
+ return self._impl.getName()
+
+ def getEpoch(self):
+ """
+ """
+ return self._impl.getEpoch()
+
+ def getVendor(self):
+ """
+ """
+ return self._impl.getVendor()
+
+ def getProduct(self):
+ """
+ """
+ return self._impl.getProduct()
+
+ def getInstance(self):
+ """
+ """
+ return self._impl.getInstance()
+
+ def getAttributes(self):
+ """
+ """
+ return self._impl.getAttributes()
+
+ def query(self, q, timeout=30):
+ """
+ """
+ if q.__class__ == Query:
+ q_arg = q._impl
+ else:
+ q_arg = q
+ dur = cqpid.Duration(cqpid.Duration.SECOND.getMilliseconds() * timeout)
+ result = self._impl.query(q_arg, dur)
+ if result.getType() == cqmf2.CONSOLE_EXCEPTION:
+ raise Exception(Data(result.getData(0)))
+ if result.getType() != cqmf2.CONSOLE_QUERY_RESPONSE:
+ raise Exception("Protocol error, expected CONSOLE_QUERY_RESPONSE, got %d" % result.getType())
+ dataList = []
+ count = result.getDataCount()
+ for i in range(count):
+ dataList.append(Data(result.getData(i)))
+ return dataList
+
+ def loadSchemaInfo(self, timeout=30):
+ """
+ """
+ dur = cqpid.Duration(cqpid.Duration.SECOND.getMilliseconds() * timeout)
+ self._impl.querySchema(dur)
+
+ def getPackages(self):
+ """
+ """
+ result = []
+ count = self._impl.getPackageCount()
+ for i in range(count):
+ result.append(self._impl.getPackage(i))
+ return result
+
+ def getSchemaIds(self, package):
+ """
+ """
+ result = []
+ count = self._impl.getSchemaIdCount(package)
+ for i in range(count):
+ result.append(SchemaId(self._impl.getSchemaId(package, i)))
+ return result
+
+ def getSchema(self, schemaId, timeout=30):
+ """
+ """
+ dur = cqpid.Duration(cqpid.Duration.SECOND.getMilliseconds() * timeout)
+ return Schema(self._impl.getSchema(schemaId._impl, dur))
+
+ ## TODO: Async query
+ ## TODO: Agent method
+
+#===================================================================================================
+# QUERY
+#===================================================================================================
+class Query(object):
+ """
+ """
+
+ def __init__(self, arg1, arg2=None, arg3=None, *kwargs):
+ """
+ """
+ if arg1.__class__ == DataAddr:
+ self._impl = cqmf2.Query(arg1._impl)
+
+ def getAddr(self):
+ """
+ """
+ return DataAddr(self._impl.getDataAddr())
+
+ def getSchemaId(self):
+ """
+ """
+ return SchemaId(self._impl.getSchemaId())
+
+ def getPredicate(self):
+ """
+ """
+ return self._impl.getPredicate()
+
+ def matches(self, data):
+ """
+ """
+ m = data
+ if data.__class__ == Data:
+ m = data.getProperties()
+ return self._impl.matchesPredicate(m)
+
+#===================================================================================================
+# DATA
+#===================================================================================================
+class Data(object):
+ """
+ """
+
+ def __init__(self, arg=None):
+ """
+ """
+ if arg == None:
+ self._impl = cqmf2.Data()
+ elif arg.__class__ == cqmf2.Data:
+ self._impl = arg
+ elif arg.__class__ == Schema:
+ self._impl = cqmf2.Data(arg._impl)
+ else:
+ raise Exception("Unsupported initializer for Data")
+ self._schema = None
+
+ def getSchemaId(self):
+ """
+ """
+ if self._impl.hasSchema():
+ return SchemaId(self._impl.getSchemaId())
+ return None
+
+ def getAddr(self):
+ """
+ """
+ if self._impl.hasAddr():
+ return DataAddr(self._impl.getAddr())
+ return None
+
+ def getAgent(self):
+ """
+ """
+ return Agent(self._impl.getAgent())
+
+ def update(self, timeout=5):
+ dur = cqpid.Duration(cqpid.Duration.SECOND.getMilliseconds() * timeout)
+ agent = self._impl.getAgent()
+ query = cqmf2.Query(self._impl.getAddr())
+ result = agent.query(query, dur)
+ if result.getType() != cqmf2.CONSOLE_QUERY_RESPONSE:
+ raise "Update query failed"
+ if result.getDataCount == 0:
+ raise "Object no longer exists on agent"
+ self._impl = cqmf2.Data(result.getData(0))
+
+ def getProperties(self):
+ """
+ """
+ return self._impl.getProperties();
+
+ def _getSchema(self):
+ if not self._schema:
+ if not self._impl.hasSchema():
+ raise Exception("Data object has no schema")
+ self._schema = Schema(self._impl.getAgent().getSchema(self._impl.getSchemaId()))
+
+ def _invoke(self, name, args, kwargs):
+ ##
+ ## Get local copies of the agent and the address of the data object
+ ##
+ agent = self._impl.getAgent()
+ addr = self._impl.getAddr()
+
+ ##
+ ## Set up the timeout duration for the method call. Set the default and override
+ ## it if the _timeout keyword arg was supplied.
+ ##
+ timeout = 30
+ if '_timeout' in kwargs:
+ timeout = kwargs['_timeout']
+ dur = cqpid.Duration(cqpid.Duration.SECOND.getMilliseconds() * timeout)
+
+ ##
+ ## Get the list of arguments from the schema, isolate those that are IN or IN_OUT,
+ ## validate that we have the right number of arguments supplied, and marshall them
+ ## into a map for transmission.
+ ##
+ arglist = []
+ methods = self._schema.getMethods()
+ for m in methods:
+ if m.getName() == name:
+ arglist = m.getArguments()
+ break
+ argmap = {}
+ count = 0
+ for a in arglist:
+ if a.getDirection() == DIR_IN or a.getDirection() == DIR_IN_OUT:
+ count += 1
+ if count != len(args):
+ raise Exception("Wrong number of arguments: expected %d, got %d" % (count, len(args)))
+ i = 0
+ for a in arglist:
+ if a.getDirection() == DIR_IN or a.getDirection() == DIR_IN_OUT:
+ argmap[a.getName()] = args[i]
+ i += 1
+
+ ##
+ ## Invoke the method through the agent proxy.
+ ##
+ result = agent.callMethod(name, argmap, addr, dur)
+
+ ##
+ ## If the agent sent an exception, raise it in a QmfAgentException.
+ ##
+ if result.getType() == cqmf2.CONSOLE_EXCEPTION:
+ exdata = result.getData(0)
+ raise QmfAgentException(exdata)
+
+ ##
+ ## If a successful method response was received, collect the output arguments into a map
+ ## and return them to the caller.
+ ##
+ if result.getType() != cqmf2.CONSOLE_METHOD_RESPONSE:
+ raise Exception("Protocol error: Unexpected event type in method-response: %d" % result.getType())
+ return result.getArguments()
+
+ def __getattr__(self, name):
+ ##
+ ## If we have a schema and an address, check to see if this name is the name of a method.
+ ##
+ if self._impl.hasSchema() and self._impl.hasAddr() and self._impl.hasAgent():
+ ##
+ ## Get the schema for the data object. Note that this call will block if the remote agent
+ ## needs to be queried for the schema (i.e. the schema is not in the local cache).
+ ##
+ self._getSchema()
+ methods = self._schema.getMethods()
+
+ ##
+ ## If the name matches a method in the schema, return a closure to be invoked.
+ ##
+ for method in methods:
+ if name == method.getName():
+ return lambda *args, **kwargs : self._invoke(name, args, kwargs)
+
+ ##
+ ## This is not a method call, return the property matching the name.
+ ##
+ return self._impl.getProperty(name)
+
+ def __setattr__(self, name, value):
+ if name[0] == '_':
+ super.__setattr__(self, name, value)
+ return
+ self._impl.setProperty(name, value)
+
+#===================================================================================================
+# DATA ADDRESS
+#===================================================================================================
+class DataAddr(object):
+ """
+ """
+
+ def __init__(self, arg, agentName=""):
+ if arg.__class__ == dict:
+ self._impl = cqmf2.DataAddr(arg)
+ elif arg.__class__ == cqmf2.DataAddr:
+ self._impl = arg
+ else:
+ self._impl = cqmf2.DataAddr(arg, agentName)
+
+ def __repr__(self):
+ return "%s:%s" % (self.getAgentName(), self.getName())
+
+ def __eq__(self, other):
+ return self.getAgentName() == other.getAgentName() and \
+ self.getName() == other.getName() and \
+ self.getAgentEpoch() == other.getAgentEpoch()
+
+ def asMap(self):
+ """
+ """
+ return self._impl.asMap()
+
+ def getAgentName(self):
+ """
+ """
+ return self._impl.getAgentName()
+
+ def getName(self):
+ """
+ """
+ return self._impl.getName()
+
+ def getAgentEpoch(self):
+ """
+ """
+ return self._impl.getAgentEpoch()
+
+#===================================================================================================
+# SCHEMA ID
+#===================================================================================================
+class SchemaId(object):
+ """
+ """
+
+ def __init__(self, impl):
+ self._impl = impl
+
+ def __repr__(self):
+ return "%s:%s" % (self.getPackageName(), self.getName())
+
+ def getType(self):
+ """
+ """
+ return self._impl.getType()
+
+ def getPackageName(self):
+ """
+ """
+ return self._impl.getPackageName()
+
+ def getName(self):
+ """
+ """
+ return self._impl.getName()
+
+ def getHash(self):
+ """
+ """
+ return self._impl.getHash()
+
+#===================================================================================================
+# SCHEMA
+#===================================================================================================
+class Schema(object):
+ """
+ """
+
+ def __init__(self, stype, packageName=None, className=None, desc=None, sev=None):
+ if stype.__class__ == cqmf2.Schema:
+ self._impl = stype
+ else:
+ self._impl = cqmf2.Schema(stype, packageName, className)
+ if desc:
+ self._impl.setDesc(desc)
+ if sev:
+ self._impl.setDefaultSeverity(sev)
+
+ def __repr__(self):
+ return "QmfSchema:%r" % SchemaId(self._impl.getSchemaId())
+
+ def finalize(self):
+ """
+ """
+ self._impl.finalize()
+
+ def getSchemaId(self):
+ """
+ """
+ return SchemaId(self._impl.getSchemaId())
+
+ def getDesc(self):
+ """
+ """
+ return self._impl.getDesc()
+
+ def getSev(self):
+ """
+ """
+ return self._impl.getDefaultSeverity()
+
+ def addProperty(self, prop):
+ """
+ """
+ self._impl.addProperty(prop._impl)
+
+ def addMethod(self, meth):
+ """
+ """
+ self._impl.addMethod(meth._impl)
+
+ def getProperties(self):
+ """
+ """
+ props = []
+ count = self._impl.getPropertyCount()
+ for i in range(count):
+ props.append(SchemaProperty(self._impl.getProperty(i)))
+ return props
+
+ def getMethods(self):
+ """
+ """
+ meths = []
+ count = self._impl.getMethodCount()
+ for i in range(count):
+ meths.append(SchemaMethod(self._impl.getMethod(i)))
+ return meths
+
+#===================================================================================================
+# SCHEMA PROPERTY
+#===================================================================================================
+class SchemaProperty(object):
+ """
+ """
+
+ def __init__(self, name, dtype=None, **kwargs):
+ """
+ """
+ if name.__class__ == cqmf2.SchemaProperty:
+ self._impl = name
+ else:
+ self._impl = cqmf2.SchemaProperty(name, dtype)
+ if 'access' in kwargs:
+ self._impl.setAccess(kwargs['access'])
+ if 'index' in kwargs:
+ self._impl.setIndex(kwargs['index'])
+ if 'optional' in kwargs:
+ self._impl.setOptional(kwargs['optional'])
+ if 'unit' in kwargs:
+ self._impl.setUnit(kwargs['unit'])
+ if 'desc' in kwargs:
+ self._impl.setDesc(kwargs['desc'])
+ if 'subtype' in kwargs:
+ self._impl.setSubtype(kwargs['subtype'])
+ if 'direction' in kwargs:
+ self._impl.setDirection(kwargs['direction'])
+
+ def __repr__(self):
+ return self._impl.getName()
+
+ def getName(self):
+ """
+ """
+ return self._impl.getName()
+
+ def getType(self):
+ """
+ """
+ return self._impl.getType()
+
+ def getAccess(self):
+ """
+ """
+ return self._impl.getAccess()
+
+ def isIndex(self):
+ """
+ """
+ return self._impl.isIndex()
+
+ def isOptional(self):
+ """
+ """
+ return self._impl.isOptional()
+
+ def getUnit(self):
+ """
+ """
+ return self._impl.getUnit()
+
+ def getDesc(self):
+ """
+ """
+ return self._impl.getDesc()
+
+ def getSubtype(self):
+ """
+ """
+ return self._impl.getSubtype()
+
+ def getDirection(self):
+ """
+ """
+ return self._impl.getDirection()
+
+#===================================================================================================
+# SCHEMA METHOD
+#===================================================================================================
+class SchemaMethod(object):
+ """
+ """
+
+ def __init__(self, name, **kwargs):
+ """
+ """
+ if name.__class__ == cqmf2.SchemaMethod:
+ self._impl = name
+ else:
+ self._impl = cqmf2.SchemaMethod(name)
+ if 'desc' in kwargs:
+ self._impl.setDesc(kwargs['desc'])
+
+ def __repr__(self):
+ return "%s()" % self._impl.getName()
+
+ def getName(self):
+ """
+ """
+ return self._impl.getName()
+
+ def getDesc(self):
+ """
+ """
+ return self._impl.getDesc()
+
+ def addArgument(self, arg):
+ """
+ """
+ self._impl.addArgument(arg._impl)
+
+ def getArguments(self):
+ """
+ """
+ result = []
+ count = self._impl.getArgumentCount()
+ for i in range(count):
+ result.append(SchemaProperty(self._impl.getArgument(i)))
+ return result
+
diff --git a/qpid/cpp/bindings/qmf2/qmf2.i b/qpid/cpp/bindings/qmf2/qmf2.i
new file mode 100644
index 0000000000..0f573fe3e6
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/qmf2.i
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+%{
+
+#include <qmf/exceptions.h>
+#include <qmf/AgentEvent.h>
+#include <qmf/Agent.h>
+#include <qmf/AgentSession.h>
+#include <qmf/ConsoleEvent.h>
+#include <qmf/ConsoleSession.h>
+#include <qmf/DataAddr.h>
+#include <qmf/Data.h>
+#include <qmf/Query.h>
+#include <qmf/Schema.h>
+#include <qmf/SchemaId.h>
+#include <qmf/SchemaMethod.h>
+#include <qmf/SchemaProperty.h>
+#include <qmf/SchemaTypes.h>
+#include <qmf/Subscription.h>
+
+%}
+
+%include <qpid/ImportExport.h>
+%include <qpid/messaging/ImportExport.h>
+%include <qpid/messaging/Duration.h>
+
+%include <qmf/ImportExport.h>
+%include <qmf/exceptions.h>
+%include <qmf/AgentEvent.h>
+%include <qmf/Agent.h>
+%include <qmf/AgentSession.h>
+%include <qmf/ConsoleEvent.h>
+%include <qmf/ConsoleSession.h>
+%include <qmf/DataAddr.h>
+%include <qmf/Data.h>
+%include <qmf/Query.h>
+%include <qmf/Schema.h>
+%include <qmf/SchemaId.h>
+%include <qmf/SchemaMethod.h>
+%include <qmf/SchemaProperty.h>
+%include <qmf/SchemaTypes.h>
+%include <qmf/Subscription.h>
+
+%{
+
+using namespace qmf;
+
+%};
+
diff --git a/qpid/cpp/bindings/qmf2/ruby/Makefile.am b/qpid/cpp/bindings/qmf2/ruby/Makefile.am
new file mode 100644
index 0000000000..97bbc6f385
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/ruby/Makefile.am
@@ -0,0 +1,44 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+if HAVE_RUBY_DEVEL
+
+INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src -I$(top_builddir)/src $(QMF_INCLUDES)
+
+EXTRA_DIST = ruby.i
+BUILT_SOURCES = cqmf2.cpp
+SWIG_FLAGS = -w362,401
+
+rubylibdir = $(RUBY_LIB)
+
+cqmf2.cpp: $(srcdir)/ruby.i $(srcdir)/../qmf2.i $(srcdir)/../../swig_ruby_typemaps.i
+ $(SWIG) -ruby -c++ $(SWIG_FLAGS) $(INCLUDES) $(QPID_CXXFLAGS) -I/usr/include -o cqmf2.cpp $(srcdir)/ruby.i
+
+rubylibarchdir = $(RUBY_LIB_ARCH)
+rubylibarch_LTLIBRARIES = cqmf2.la
+dist_rubylib_DATA = qmf2.rb
+
+cqmf2_la_LDFLAGS = -avoid-version -module -shared -shrext ".$(RUBY_DLEXT)"
+cqmf2_la_LIBADD = $(RUBY_LIBS) -L$(top_builddir)/src/.libs -lqmf2 $(top_builddir)/src/libqmf2.la
+cqmf2_la_CXXFLAGS = $(INCLUDES) -I$(RUBY_INC) -I$(RUBY_INC_ARCH) -fno-strict-aliasing
+nodist_cqmf2_la_SOURCES = cqmf2.cpp
+
+CLEANFILES = cqmf2.cpp
+
+endif # HAVE_RUBY_DEVEL
diff --git a/qpid/cpp/bindings/qmf2/ruby/qmf2.rb b/qpid/cpp/bindings/qmf2/ruby/qmf2.rb
new file mode 100644
index 0000000000..c14ecba4e1
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/ruby/qmf2.rb
@@ -0,0 +1,855 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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 'cqmf2'
+require 'cqpid'
+require 'thread'
+require 'socket'
+require 'monitor'
+
+module Qmf2
+
+ Cqmf2.constants.each do |c|
+ if c.index('AGENT_') == 0 or c.index('CONSOLE_') == 0 or
+ c.index('SCHEMA_') == 0 or c.index('SEV_') == 0 or c.index('QUERY') == 0
+ const_set(c, Cqmf2.const_get(c))
+ end
+ end
+
+ SCHEMA_TYPE_DATA = Cqmf2::SCHEMA_TYPE_DATA()
+ SCHEMA_TYPE_EVENT = Cqmf2::SCHEMA_TYPE_EVENT()
+
+ SCHEMA_DATA_VOID = Cqmf2::SCHEMA_DATA_VOID()
+ SCHEMA_DATA_BOOL = Cqmf2::SCHEMA_DATA_BOOL()
+ SCHEMA_DATA_INT = Cqmf2::SCHEMA_DATA_INT()
+ SCHEMA_DATA_FLOAT = Cqmf2::SCHEMA_DATA_FLOAT()
+ SCHEMA_DATA_STRING = Cqmf2::SCHEMA_DATA_STRING()
+ SCHEMA_DATA_MAP = Cqmf2::SCHEMA_DATA_MAP()
+ SCHEMA_DATA_LIST = Cqmf2::SCHEMA_DATA_LIST()
+ SCHEMA_DATA_UUID = Cqmf2::SCHEMA_DATA_UUID()
+
+ ACCESS_READ_CREATE = Cqmf2::ACCESS_READ_CREATE()
+ ACCESS_READ_WRITE = Cqmf2::ACCESS_READ_WRITE()
+ ACCESS_READ_ONLY = Cqmf2::ACCESS_READ_ONLY()
+
+ DIR_IN = Cqmf2::DIR_IN()
+ DIR_OUT = Cqmf2::DIR_OUT()
+ DIR_IN_OUT = Cqmf2::DIR_IN_OUT()
+
+ SEV_EMERG = Cqmf2::SEV_EMERG()
+ SEV_ALERT = Cqmf2::SEV_ALERT()
+ SEV_CRIT = Cqmf2::SEV_CRIT()
+ SEV_ERROR = Cqmf2::SEV_ERROR()
+ SEV_WARN = Cqmf2::SEV_WARN()
+ SEV_NOTICE = Cqmf2::SEV_NOTICE()
+ SEV_INFORM = Cqmf2::SEV_INFORM()
+ SEV_DEBUG = Cqmf2::SEV_DEBUG()
+
+ ##==============================================================================
+ ## EXCEPTIONS
+ ##==============================================================================
+
+ class QmfAgentException < RuntimeError
+ attr :data
+
+ def initialize(data)
+ @data = data
+ end
+
+ def to_s
+ "QmfAgentException: #{@data}"
+ end
+ end
+
+ ##==============================================================================
+ ## AGENT HANDLER
+ ##==============================================================================
+
+ class AgentHandler
+
+ def initialize(session)
+ @_session = session
+ @_running = false
+ @_thread = nil
+ end
+
+ ##
+ ## Call the "start" method to run the handler on a new thread.
+ ##
+ def start
+ @_thread = Thread.new do
+ run
+ end
+ end
+
+ ##
+ ## Request that the running thread complete and exit.
+ ##
+ def cancel
+ @_running = false
+ @_thread.join if @_thread
+ @_thread = nil
+ end
+
+ ##
+ ## Call the "run" method only if you want the handler to run on your own thread.
+ ##
+ def run
+ @_running = true
+ event = Cqmf2::AgentEvent.new
+ while @_running do
+ valid = @_session.impl.nextEvent(event, Cqpid::Duration.SECOND)
+ if valid and @_running
+ case event.getType
+ when Cqmf2::AGENT_AUTH_QUERY
+ yes = authorize_query(Query.new(event.getQuery()), event.getUserId())
+ if yes == true
+ @_session.impl.authAccept(event)
+ else
+ @_session.impl.authReject(event)
+ end
+
+ when Cqmf2::AGENT_QUERY
+ context = QueryContext.new(@_session, event)
+ get_query(context, Query.new(event.getQuery()), event.getUserId())
+
+ when Cqmf2::AGENT_METHOD
+ context = MethodContext.new(@_session, event)
+ begin
+ method_call(context, event.getMethodName(), event.getDataAddr(), event.getArguments(), event.getUserId())
+ rescue Exception => ex
+ @_session.impl.raiseException(event, "#{ex}")
+ end
+
+ end
+ end
+ end
+ end
+
+
+ ##
+ ## The following methods are intended to be overridden in a sub-class. They are
+ ## handlers for events that occur on QMF consoles.
+ ##
+
+ #
+ # This method will only be invoked if the "allow-queries" option is enabled on the
+ # agent session. When invoked, it provides the query and the authenticated user-id
+ # of the querying client.
+ #
+ # This method must return true if the query is permitted, false otherwise.
+ #
+ def authorize_query(query, user_id); end
+
+ #
+ # This method will only be invoked if the "external" option is "True" on the agent
+ # session. When invoked, the method should begin the process of responding to a data
+ # query. The authenticated user-id of the requestor is provided for informational
+ # purposes. The 'context' variable is used to provide the results back to the requestor.
+ #
+ # For each matching Data object, call context.response(data). When the query is complete,
+ # call context.complete(). After completing the query, you should not use 'context' any
+ # longer.
+ #
+ # Note: It is not necessary to process the query synchronously. If desired, this method
+ # may store the context for asynchronous processing or pass it to another thread for
+ # processing. There is no restriction on the number of contexts that may be in-flight
+ # concurrently.
+ #
+ def get_query(context, query, user_id); end
+
+ #
+ # This method is invoked when a console calls a QMF method on the agent. Supplied are
+ # a context for the response, the method name, the data address of the data object being
+ # called, the input arguments (a dictionary), and the caller's authenticated user-id.
+ #
+ # A method call can end one of two ways: Successful completion, in which the output
+ # arguments (if any) are supplied; and Exceptional completion if there is an error.
+ #
+ # Successful Completion:
+ # For each output argument, assign the value directly to context (context.arg1 = "value")
+ # Once arguments are assigned, call context._success().
+ #
+ # Exceptional Completion:
+ # Method 1: Call context._exception(data) where 'data' is a string or a Data object.
+ # Method 2: Raise an exception (raise "Error Text") synchronously in the method body.
+ #
+ # Note: Like get_query, method_call may process methods synchronously or asynchronously.
+ # This method may store the context for later asynchronous processing. There is no
+ # restriction on the number of contexts that may be in-flight concurrently.
+ #
+ # However, "Method 2" for Exceptional Completion can only be done synchronously.
+ #
+ def method_call(context, method_name, data_addr, args, user_id); end
+ end
+
+ class QueryContext
+ def initialize(agent, context)
+ @agent = agent
+ @context = context
+ end
+
+ def response(data)
+ @agent.impl.response(@context, data.impl)
+ end
+
+ def complete
+ @agent.impl.complete(@context)
+ end
+ end
+
+ class MethodContext
+ def initialize(agent, context)
+ @agent = agent
+ @context = context
+ end
+
+ def _success
+ @agent.impl.methodSuccess(@context)
+ end
+
+ def _exception(ex)
+ if ex.class == Data
+ @agent.impl.raiseException(@context, ex.impl)
+ else
+ @agent.impl.raiseException(@context, ex)
+ end
+ end
+
+ def method_missing(name_in, *args)
+ name = name_in.to_s
+ if name[name.length - 1] == 61
+ name = name[0..name.length - 2]
+ @context.impl.addReturnArgument(name, args[0])
+ else
+ super.method_missing(name_in, args)
+ end
+ end
+ end
+
+ ##==============================================================================
+ ## CONSOLE HANDLER
+ ##==============================================================================
+
+ class ConsoleHandler
+
+ def initialize(session)
+ @_session = session
+ @_running = false
+ @_thread = nil
+ end
+
+ ##
+ ## Call the "start" method to run the handler on a new thread.
+ ##
+ def start
+ @_thread = Thread.new do
+ run
+ end
+ end
+
+ ##
+ ## Request that the running thread complete and exit.
+ ##
+ def cancel
+ @_running = false
+ @_thread.join if @_thread
+ @_thread = nil
+ end
+
+ ##
+ ## Call the "run" method only if you want the handler to run on your own thread.
+ ##
+ def run
+ @_running = true
+ event = Cqmf2::ConsoleEvent.new
+ while @_running do
+ valid = @_session.impl.nextEvent(event, Cqpid::Duration.SECOND)
+ if valid and @_running
+ case event.getType
+ when Cqmf2::CONSOLE_AGENT_ADD
+ agent_added(Agent.new(event.getAgent))
+
+ when Cqmf2::CONSOLE_AGENT_DEL
+ reason = :filter
+ reason = :aged if event.getAgentDelReason == Cqmf2::AGENT_DEL_AGED
+ agent_deleted(Agent.new(event.getAgent), reason)
+
+ when Cqmf2::CONSOLE_AGENT_RESTART
+ agent_restarted(Agent.new(event.getAgent))
+
+ when Cqmf2::CONSOLE_AGENT_SCHEMA_UPDATE
+ agent_schema_updated(Agent.new(event.getAgent))
+
+ when Cqmf2::CONSOLE_EVENT
+ event_raised(Agent.new(event.getAgent), Data.new(event.getData(0)), event.getTimestamp, event.getSeverity)
+
+ end
+ end
+ end
+ end
+
+
+ ##
+ ## The following methods are intended to be overridden in a sub-class. They are
+ ## handlers for events that occur on QMF consoles.
+ ##
+
+ #
+ # A new agent, whose attributes match the console's agent filter, has been discovered.
+ #
+ def agent_added(agent); end
+
+ #
+ # A known agent has been removed from the agent list. There are two possible reasons
+ # for agent deletion:
+ #
+ # 1) :aged - The agent hasn't been heard from for the maximum age interval and is
+ # presumed dead.
+ # 2) :filter - The agent no longer matches the console's agent-filter and has been
+ # effectively removed from the agent list. Such occurrences are likely
+ # to be seen immediately after setting the filter to a new value.
+ #
+ def agent_deleted(agent, reason); end
+
+ #
+ # An agent-restart was detected. This occurs when the epoch number advertised by the
+ # agent changes. It indicates that the agent in question was shut-down/crashed and
+ # restarted.
+ #
+ def agent_restarted(agent); end
+
+ #
+ # The agent has registered new schema information which can now be queried, if desired.
+ #
+ def agent_schema_updated(agent); end
+
+ #
+ # An agent raised an event. The 'data' argument is a Data object that contains the
+ # content of the event.
+ #
+ def event_raised(agent, data, timestamp, severity); end
+ end
+
+ ##==============================================================================
+ ## CONSOLE SESSION
+ ##==============================================================================
+
+ class ConsoleSession
+ attr_reader :impl
+
+ ## The options string is of the form "{key:value,key:value}". The following keys are supported:
+ ##
+ ## domain:NAME - QMF Domain to join [default: "default"]
+ ## max-agent-age:N - Maximum time, in minutes, that we will tolerate not hearing from
+ ## an agent before deleting it [default: 5]
+ ## listen-on-direct:{True,False} - If True: Listen on legacy direct-exchange address for backward compatibility [default]
+ ## If False: Listen only on the routable direct address
+ ## strict-security:{True,False} - If True: Cooperate with the broker to enforce strict access control to the network
+ ## - If False: Operate more flexibly with regard to use of messaging facilities [default]
+ ##
+ def initialize(connection, options="")
+ @impl = Cqmf2::ConsoleSession.new(connection, options)
+ end
+
+ def set_domain(domain) @impl.setDomain(domain) end
+ def set_agent_filter(filter) @impl.setAgentFilter(filter) end
+
+ def open() @impl.open end
+ def close() @impl.close end
+
+ def agents
+ result = []
+ count = @impl.getAgentCount
+ for i in 0...count
+ result << Agent.new(@impl.getAgent(i))
+ end
+ return result
+ end
+
+ def connected_broker_agent
+ Agent.new(@impl.getConnectedBrokerAgent)
+ end
+ end
+
+ ##==============================================================================
+ ## AGENT SESSION
+ ##==============================================================================
+
+ class AgentSession
+ attr_reader :impl
+
+ ## The options string is of the form "{key:value,key:value}". The following keys are supported:
+ ##
+ ## interval:N - Heartbeat interval in seconds [default: 60]
+ ## external:{True,False} - Use external data storage (queries and subscriptions are pass-through) [default: False]
+ ## allow-queries:{True,False} - If True: automatically allow all queries [default]
+ ## If False: generate an AUTH_QUERY event to allow per-query authorization
+ ## allow-methods:{True,False} - If True: automatically allow all methods [default]
+ ## If False: generate an AUTH_METHOD event to allow per-method authorization
+ ## max-subscriptions:N - Maximum number of concurrent subscription queries permitted [default: 64]
+ ## min-sub-interval:N - Minimum publish interval (in milliseconds) permitted for a subscription [default: 3000]
+ ## sub-lifetime:N - Lifetime (in seconds with no keepalive) for a subscription [default: 300]
+ ## public-events:{True,False} - If True: QMF events are sent to the topic exchange [default]
+ ## If False: QMF events are only sent to authorized subscribers
+ ## listen-on-direct:{True,False} - If True: Listen on legacy direct-exchange address for backward compatibility [default]
+ ## If False: Listen only on the routable direct address
+ ## strict-security:{True,False} - If True: Cooperate with the broker to enforce strict access control to the network
+ ## - If False: Operate more flexibly with regard to use of messaging facilities [default]
+ ##
+ def initialize(connection, options="")
+ @impl = Cqmf2::AgentSession.new(connection, options)
+ end
+
+ def set_domain(val) @impl.setDomain(val) end
+ def set_vendor(val) @impl.setVendor(val) end
+ def set_product(val) @impl.setProduct(val) end
+ def set_instance(val) @impl.setInstance(val) end
+ def set_attribute(key, val) @impl.setAttribute(key, val) end
+ def open() @impl.open end
+ def close() @impl.close end
+ def register_schema(cls) @impl.registerSchema(cls.impl) end
+
+ def add_data(data, name="", persistent=false)
+ DataAddr.new(@impl.addData(data.impl, name, persistent))
+ end
+
+ def del_data(addr)
+ @impl.del_data(addr.impl)
+ end
+
+ def raise_event(data, severity=nil)
+ if !severity
+ @impl.raiseEvent(data.impl)
+ else
+ @impl.raiseEvent(data.impl, severity)
+ end
+ end
+ end
+
+ ##==============================================================================
+ ## AGENT PROXY
+ ##==============================================================================
+
+ class Agent
+ attr_reader :impl
+
+ def initialize(impl)
+ @impl = impl
+ end
+
+ def name() @impl.getName end
+ def epoch() @impl.getEpoch end
+ def vendor() @impl.getVendor end
+ def product() @impl.getProduct end
+ def instance() @impl.getInstance end
+ def attributes() @impl.getAttributes end
+
+ def to_s
+ "#{vendor}:#{product}:#{instance}"
+ end
+
+ def query(q, timeout=30)
+ if q.class == Query
+ q_arg = q.impl
+ else
+ q_arg = q
+ end
+ dur = Cqpid::Duration.new(Cqpid::Duration.SECOND.getMilliseconds * timeout)
+ result = @impl.query(q_arg, dur)
+ raise QmfAgentException.new(Data.new(result.getData(0))) if result.getType == Cqmf2::CONSOLE_EXCEPTION
+ raise "Protocol error, expected CONSOLE_QUERY_RESPONSE, got #{result.getType}" if result.getType != Cqmf2::CONSOLE_QUERY_RESPONSE
+ data_list = []
+ count = result.getDataCount
+ for i in 0...count
+ data_list << Data.new(result.getData(i))
+ end
+ return data_list
+ end
+
+ def load_schema_info(timeout=30)
+ dur = Cqpid::Duration.new(Cqpid::Duration.SECOND.getMilliseconds * timeout)
+ @impl.querySchema(dur)
+ end
+
+ def packages
+ result = []
+ count = @impl.getPackageCount
+ for i in 0...count
+ result << @impl.getPackage(i)
+ end
+ return result
+ end
+
+ def schema_ids(package)
+ result = []
+ count = @impl.getSchemaIdCount(package)
+ for i in 0...count
+ result << SchemaId.new(@impl.getSchemaId(package, i))
+ end
+ return result
+ end
+
+ def schema(sid, timeout=30)
+ dur = Cqpid::Duration.new(Cqpid::Duration.SECOND.getMilliseconds * timeout)
+ Schema.new(@impl.getSchema(sid.impl, dur))
+ end
+ end
+
+ ##==============================================================================
+ ## QUERY
+ ##==============================================================================
+
+ class Query
+ attr_reader :impl
+ def initialize(arg1, arg2=nil, arg3=nil)
+ if arg1.class == Qmf2::DataAddr
+ @impl = Cqmf2::Query.new(arg1.impl)
+ end
+ end
+
+ def addr() DataAddr.new(@impl.getDataAddr()) end
+ def schema_id() SchemaId.new(@impl.getSchemaId()) end
+ def predicate() @impl.getPredicate() end
+
+ def matches?(data)
+ map = data
+ map = data.properties if data.class == Data
+ @impl.matchesPredicate(map)
+ end
+ end
+
+ ##==============================================================================
+ ## DATA
+ ##==============================================================================
+
+ class Data
+ attr_reader :impl
+
+ def initialize(arg=nil)
+ @schema = nil
+ if arg == nil
+ @impl = Cqmf2::Data.new
+ elsif arg.class == Cqmf2::Data
+ @impl = arg
+ elsif arg.class == Schema
+ @impl = Cqmf2::Data.new(arg.impl)
+ @schema = arg
+ else
+ raise "Unsupported initializer for Data"
+ end
+ end
+
+ def to_s
+ "#{@impl.getProperties}"
+ end
+
+ def schema_id
+ if @impl.hasSchema
+ return SchemaId.new(@impl.getSchemaId)
+ end
+ return nil
+ end
+
+ def set_addr(addr)
+ @impl.setAddr(addr.impl)
+ end
+
+ def addr
+ if @impl.hasAddr
+ return DataAddr.new(@impl.getAddr)
+ end
+ return nil
+ end
+
+ def agent
+ return Agent.new(@impl.getAgent)
+ end
+
+ def update(timeout=5)
+ dur = Cqpid::Duration.new(Cqpid::Duration.SECOND.getMilliseconds * timeout)
+ agent = @impl.getAgent
+ query = Cqmf2::Query.new(@impl.getAddr)
+ result = agent.query(query, dur)
+ raise "Update query failed" if result.getType != Cqmf2::CONSOLE_QUERY_RESPONSE
+ raise "Object no longer exists on agent" if result.getDataCount == 0
+ @impl = Cqmf2::Data.new(result.getData(0))
+ return nil
+ end
+
+ def properties
+ return @impl.getProperties
+ end
+
+ def get_attr(name)
+ @impl.getProperty(name)
+ end
+
+ def set_attr(name, v)
+ @impl.setProperty(name, v)
+ end
+
+ def [](name)
+ get_attr(name)
+ end
+
+ def []=(name, value)
+ set_attr(name, value)
+ end
+
+ def _get_schema
+ unless @schema
+ raise "Data object has no schema" unless @impl.hasSchema
+ @schema = Schema.new(@impl.getAgent.getSchema(@impl.getSchemaId))
+ end
+ 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
+
+ #
+ # We'll be needing the schema to determine how to proceed. Get the schema.
+ # Note that this call may block if the remote agent needs to be queried
+ # for the schema (i.e. the schema isn't in the local cache).
+ #
+ _get_schema
+
+ #
+ # If the name matches a property name, set or return the value of the property.
+ #
+ @schema.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
+
+ #
+ # 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.
+ #
+ @schema.methods.each do |method|
+ if method.name == name
+ raise "Sets not permitted on methods" if attr_set
+ result = @impl.getAgent.callMethod(name, _marshall(method, args), @impl.getAddr)
+ if result.getType == Cqmf2::CONSOLE_EXCEPTION
+ raise QmfAgentException, result.getData(0)
+ end
+ return result.getArguments
+ 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
+
+ #
+ # Convert a Ruby array of arguments (positional) into a Value object of type "map".
+ #
+ private
+ def _marshall(schema, args)
+ count = 0
+ schema.arguments.each do |arg|
+ if arg.direction == DIR_IN || arg.direction == DIR_IN_OUT
+ count += 1
+ end
+ end
+ raise "Wrong number of arguments: expecter #{count}, got #{arge.length}" if count != args.length
+ map = {}
+ count = 0
+ schema.arguments.each do |arg|
+ if arg.direction == DIR_IN || arg.direction == DIR_IN_OUT
+ map[arg.name] = args[count]
+ count += 1
+ end
+ end
+ return map
+ end
+ end
+
+ ##==============================================================================
+ ## DATA ADDRESS
+ ##==============================================================================
+
+ class DataAddr
+ attr_reader :impl
+
+ def initialize(arg, agentName="")
+ if arg.class == Hash
+ @impl = Cqmf2::DataAddr.new(arg)
+ elsif arg.class == Cqmf2::DataAddr
+ @impl = arg
+ else
+ @impl = Cqmf2::DataAddr.new(arg, agentName)
+ end
+ end
+
+ def ==(other)
+ return @impl == other.impl
+ end
+
+ def as_map() @impl.asMap end
+ def agent_name() @impl.getAgentName end
+ def name() @impl.getName end
+ def agent_epoch() @impl.getAgentEpoch end
+ end
+
+ ##==============================================================================
+ ## SCHEMA ID
+ ##==============================================================================
+
+ class SchemaId
+ attr_reader :impl
+ def initialize(impl)
+ @impl = impl
+ end
+
+ def type() @impl.getType end
+ def package_name() @impl.getPackageName end
+ def class_name() @impl.getName end
+ def hash() @impl.getHash end
+ end
+
+ ##==============================================================================
+ ## SCHEMA
+ ##==============================================================================
+
+ class Schema
+ attr_reader :impl
+ def initialize(arg, packageName="", className="", kwargs={})
+ if arg.class == Cqmf2::Schema
+ @impl = arg
+ else
+ @impl = Cqmf2::Schema.new(arg, packageName, className)
+ @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc)
+ @impl.setDefaultSeverity(kwargs[:sev]) if kwargs.include?(:sev)
+ end
+ end
+
+ def finalize() @impl.finalize end
+ def schema_id() SchemaId.new(@impl.getSchemaId) end
+ def desc() @impl.getDesc end
+ def sev() @impl.getDefaultSeverity end
+ def add_property(prop) @impl.addProperty(prop.impl) end
+ def add_method(meth) @impl.addMethod(meth.impl) end
+
+ def properties
+ result = []
+ count = @impl.getPropertyCount
+ for i in 0...count
+ result << SchemaProperty.new(@impl.getProperty(i))
+ end
+ return result
+ end
+
+ def methods
+ result = []
+ count = @impl.getMethodCount
+ for i in 0...count
+ result << SchemaMethod.new(@impl.getMethod(i))
+ end
+ return result
+ end
+ end
+
+ ##==============================================================================
+ ## SCHEMA PROPERTY
+ ##==============================================================================
+
+ class SchemaProperty
+ attr_reader :impl
+
+ def initialize(arg, dtype=nil, kwargs={})
+ if arg.class == Cqmf2::SchemaProperty
+ @impl = arg
+ else
+ @impl = Cqmf2::SchemaProperty.new(arg, dtype)
+ @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)
+ @impl.setSubtype(kwargs[:subtype]) if kwargs.include?(:subtype)
+ @impl.setDirection(kwargs[:direction]) if kwargs.include?(:direction)
+ end
+ end
+
+ def name() @impl.getName end
+ def access() @impl.getAccess end
+ def index?() @impl.isIndex end
+ def optional?() @impl.isOptional end
+ def unit() @impl.getUnit end
+ def desc() @impl.getDesc end
+ def subtype() @impl.getSubtype end
+ def direction() @impl.getDirection end
+
+ def to_s
+ name
+ end
+ end
+
+ ##==============================================================================
+ ## SCHEMA METHOD
+ ##==============================================================================
+
+ class SchemaMethod
+ attr_reader :impl
+
+ def initialize(arg, kwargs={})
+ if arg.class == Cqmf2::SchemaMethod
+ @impl = arg
+ else
+ @impl = Cqmf2::SchemaMethod.new(arg)
+ @impl.setDesc(kwargs[:desc]) if kwargs.include?(:desc)
+ end
+ end
+
+ def name() @impl.getName end
+ def desc() @impl.getDesc end
+ def add_argument(arg) @impl.addArgument(arg.impl) end
+
+ def arguments
+ result = []
+ count = @impl.getArgumentCount
+ for i in 0...count
+ result << SchemaProperty.new(@impl.getArgument(i))
+ end
+ return result
+ end
+
+ def to_s
+ name
+ end
+ end
+end
+
+
diff --git a/qpid/cpp/bindings/qmf2/ruby/ruby.i b/qpid/cpp/bindings/qmf2/ruby/ruby.i
new file mode 100644
index 0000000000..1070c65a44
--- /dev/null
+++ b/qpid/cpp/bindings/qmf2/ruby/ruby.i
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+%module cqmf2
+%include "std_string.i"
+%include "../../swig_ruby_typemaps.i"
+
+/* Define the general-purpose exception handling */
+%exception {
+ try {
+ $action
+ }
+ catch (qpid::types::Exception& mex) {
+ static VALUE qmferror = rb_define_class("QmfError", rb_eStandardError);
+ rb_raise(qmferror, mex.what());
+ }
+}
+
+%include "../qmf2.i"