sharkcz / rpms / qpid-cpp

Forked from rpms/qpid-cpp 6 years ago
Clone
Blob Blame History Raw
Index: cpp/src/qmf/Agent.cpp
===================================================================
--- cpp/src/qmf/Agent.cpp	(revision 1056407)
+++ cpp/src/qmf/Agent.cpp	(working copy)
@@ -27,6 +27,7 @@
 #include "qmf/Query.h"
 #include "qmf/SchemaImpl.h"
 #include "qmf/agentCapability.h"
+#include "qmf/constants.h"
 #include "qpid/messaging/Sender.h"
 #include "qpid/messaging/AddressParser.h"
 #include "qpid/management/Buffer.h"
@@ -70,8 +71,8 @@
 
 
 AgentImpl::AgentImpl(const std::string& n, uint32_t e, ConsoleSessionImpl& s) :
-    name(n), epoch(e), session(s), touched(true), untouchedCount(0), capability(0),
-    nextCorrelator(1), schemaCache(s.schemaCache)
+    name(n), directSubject(n), epoch(e), session(s), touched(true), untouchedCount(0), capability(0),
+    sender(session.directSender), nextCorrelator(1), schemaCache(s.schemaCache)
 {
 }
 
@@ -82,6 +83,11 @@
         try {
             capability = v.asUint32();
         } catch (std::exception&) {}
+    if (k == "_direct_subject")
+        try {
+            directSubject = v.asString();
+            sender = session.topicSender;
+        } catch (std::exception&) {}
 }
 
 const Variant& AgentImpl::getAttribute(const string& k) const
@@ -113,7 +119,9 @@
                 context->cond.wait(context->lock,
                                    qpid::sys::AbsTime(qpid::sys::now(),
                                                       qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC)));
-            if (context->response.isValid() && context->response.isFinal())
+            if (context->response.isValid() &&
+                ((context->response.getType() == CONSOLE_QUERY_RESPONSE && context->response.isFinal()) ||
+                 (context->response.getType() == CONSOLE_EXCEPTION)))
                 result = context->response;
             else {
                 auto_ptr<ConsoleEventImpl> impl(new ConsoleEventImpl(CONSOLE_EXCEPTION));
@@ -331,7 +339,7 @@
     uint32_t correlator;
     boost::shared_ptr<SyncContext> context;
 
-    QPID_LOG(trace, "RCVD MethodResponse map=" << response);
+    QPID_LOG(trace, "RCVD MethodResponse cid=" << cid << " map=" << response);
 
     aIter = response.find("_arguments");
     if (aIter != response.end())
@@ -369,9 +377,43 @@
 }
 
 
-void AgentImpl::handleDataIndication(const Variant::List&, const Message&)
+void AgentImpl::handleDataIndication(const Variant::List& list, const Message& msg)
 {
-    // TODO
+    Variant::Map::const_iterator aIter;
+    const Variant::Map& props(msg.getProperties());
+    boost::shared_ptr<SyncContext> context;
+
+    aIter = props.find("qmf.content");
+    if (aIter == props.end())
+        return;
+
+    string content_type(aIter->second.asString());
+    if (content_type != "_event")
+        return;
+
+    for (Variant::List::const_iterator lIter = list.begin(); lIter != list.end(); lIter++) {
+        const Variant::Map& eventMap(lIter->asMap());
+        Data data(new DataImpl(eventMap, this));
+        int severity(SEV_NOTICE);
+        uint64_t timestamp(0);
+
+        aIter = eventMap.find("_severity");
+        if (aIter != eventMap.end())
+            severity = int(aIter->second.asInt8());
+
+        aIter = eventMap.find("_timestamp");
+        if (aIter != eventMap.end())
+            timestamp = aIter->second.asUint64();
+
+        auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_EVENT));
+        eventImpl->setAgent(this);
+        eventImpl->addData(data);
+        eventImpl->setSeverity(severity);
+        eventImpl->setTimestamp(timestamp);
+        if (data.hasSchema())
+            learnSchemaId(data.getSchemaId());
+        session.enqueueEvent(eventImpl.release());
+    }
 }
 
 
@@ -507,17 +549,21 @@
     Variant::Map map;
     Variant::Map& headers(msg.getProperties());
 
-    headers["method"] = "request";
-    headers["qmf.opcode"] = "_query_request";
-    headers["x-amqp-0-10.app-id"] = "qmf2";
+    headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_REQUEST;
+    headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_QUERY_REQUEST;
+    headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
 
     msg.setReplyTo(session.replyAddress);
     msg.setCorrelationId(boost::lexical_cast<string>(correlator));
-    msg.setSubject(name);
+    msg.setSubject(directSubject);
+    string userId(session.connection.getAuthenticatedUsername());
+    if (!userId.empty())
+        msg.setUserId(userId);
     encode(QueryImplAccess::get(query).asMap(), msg);
-    session.directSender.send(msg);
-
-    QPID_LOG(trace, "SENT QueryRequest to=" << name);
+    if (sender.isValid()) {
+        sender.send(msg);
+        QPID_LOG(trace, "SENT QueryRequest to=" << sender.getName() << "/" << directSubject << " cid=" << correlator);
+    }
 }
 
 
@@ -527,9 +573,9 @@
     Variant::Map map;
     Variant::Map& headers(msg.getProperties());
 
-    headers["method"] = "request";
-    headers["qmf.opcode"] = "_method_request";
-    headers["x-amqp-0-10.app-id"] = "qmf2";
+    headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_REQUEST;
+    headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_METHOD_REQUEST;
+    headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
 
     map["_method_name"] = method;
     map["_object_id"] = addr.asMap();
@@ -537,11 +583,15 @@
 
     msg.setReplyTo(session.replyAddress);
     msg.setCorrelationId(boost::lexical_cast<string>(correlator));
-    msg.setSubject(name);
+    msg.setSubject(directSubject);
+    string userId(session.connection.getAuthenticatedUsername());
+    if (!userId.empty())
+        msg.setUserId(userId);
     encode(map, msg);
-    session.directSender.send(msg);
-
-    QPID_LOG(trace, "SENT MethodRequest method=" << method << " to=" << name);
+    if (sender.isValid()) {
+        sender.send(msg);
+        QPID_LOG(trace, "SENT MethodRequest method=" << method << " to=" << sender.getName() << "/" << directSubject << " content=" << map << " cid=" << correlator);
+    }
 }
 
 void AgentImpl::sendSchemaRequest(const SchemaId& id)
@@ -577,10 +627,14 @@
     Message msg;
     msg.setReplyTo(session.replyAddress);
     msg.setContent(content);
-    msg.setSubject(name);
-    session.directSender.send(msg);
-
-    QPID_LOG(trace, "SENT V1SchemaRequest to=" << name);
+    msg.setSubject(directSubject);
+    string userId(session.connection.getAuthenticatedUsername());
+    if (!userId.empty())
+        msg.setUserId(userId);
+    if (sender.isValid()) {
+        sender.send(msg);
+        QPID_LOG(trace, "SENT V1SchemaRequest to=" << sender.getName() << "/" << directSubject);
+    }
 }
 
 
Index: cpp/src/qmf/Schema.cpp
===================================================================
--- cpp/src/qmf/Schema.cpp	(revision 1056407)
+++ cpp/src/qmf/Schema.cpp	(working copy)
@@ -192,6 +192,55 @@
 }
 
 
+bool SchemaImpl::isValidProperty(const std::string& k, const Variant& v) const
+{
+    for (list<SchemaProperty>::const_iterator iter = properties.begin(); iter != properties.end(); iter++)
+        if (iter->getName() == k)
+            return (isCompatibleType(iter->getType(), v.getType()));
+    return false;
+}
+
+
+bool SchemaImpl::isValidMethodInArg(const std::string& m, const std::string& k, const Variant& v) const
+{
+    for (list<SchemaMethod>::const_iterator mIter = methods.begin(); mIter != methods.end(); mIter++) {
+        if (mIter->getName() == m) {
+            uint32_t count(mIter->getArgumentCount());
+            for (uint32_t i = 0; i < count; i++) {
+                const SchemaProperty prop(mIter->getArgument(i));
+                if (prop.getName() == k) {
+                    if (prop.getDirection() == DIR_IN || prop.getDirection() == DIR_IN_OUT)
+                        return (isCompatibleType(prop.getType(), v.getType()));
+                    else
+                        return false;
+                }
+            }
+        }
+    }
+    return false;
+}
+
+
+bool SchemaImpl::isValidMethodOutArg(const std::string& m, const std::string& k, const Variant& v) const
+{
+    for (list<SchemaMethod>::const_iterator mIter = methods.begin(); mIter != methods.end(); mIter++) {
+        if (mIter->getName() == m) {
+            uint32_t count(mIter->getArgumentCount());
+            for (uint32_t i = 0; i < count; i++) {
+                const SchemaProperty prop(mIter->getArgument(i));
+                if (prop.getName() == k) {
+                    if (prop.getDirection() == DIR_OUT || prop.getDirection() == DIR_IN_OUT)
+                        return (isCompatibleType(prop.getType(), v.getType()));
+                    else
+                        return false;
+                }
+            }
+        }
+    }
+    return false;
+}
+
+
 void SchemaImpl::finalize()
 {
     Hash hash;
@@ -246,6 +295,57 @@
 }
 
 
+bool SchemaImpl::isCompatibleType(int qmfType, qpid::types::VariantType qpidType) const
+{
+    bool typeValid(false);
+
+    switch (qpidType) {
+    case qpid::types::VAR_VOID:
+        if (qmfType == SCHEMA_DATA_VOID)
+            typeValid = true;
+        break;
+    case qpid::types::VAR_BOOL:
+        if (qmfType == SCHEMA_DATA_BOOL)
+            typeValid = true;
+        break;
+    case qpid::types::VAR_UINT8:
+    case qpid::types::VAR_UINT16:
+    case qpid::types::VAR_UINT32:
+    case qpid::types::VAR_UINT64:
+    case qpid::types::VAR_INT8:
+    case qpid::types::VAR_INT16:
+    case qpid::types::VAR_INT32:
+    case qpid::types::VAR_INT64:
+        if (qmfType == SCHEMA_DATA_INT)
+            typeValid = true;
+        break;
+    case qpid::types::VAR_FLOAT:
+    case qpid::types::VAR_DOUBLE:
+        if (qmfType == SCHEMA_DATA_FLOAT)
+            typeValid = true;
+        break;
+    case qpid::types::VAR_STRING:
+        if (qmfType == SCHEMA_DATA_STRING)
+            typeValid = true;
+        break;
+    case qpid::types::VAR_MAP:
+        if (qmfType == SCHEMA_DATA_MAP)
+            typeValid = true;
+        break;
+    case qpid::types::VAR_LIST:
+        if (qmfType == SCHEMA_DATA_LIST)
+            typeValid = true;
+        break;
+    case qpid::types::VAR_UUID:
+        if (qmfType == SCHEMA_DATA_UUID)
+            typeValid = true;
+        break;
+    }
+
+    return typeValid;
+}
+
+
 SchemaImpl& SchemaImplAccess::get(Schema& item)
 {
     return *item.impl;
Index: cpp/src/qmf/DataImpl.h
===================================================================
--- cpp/src/qmf/DataImpl.h	(revision 1056407)
+++ cpp/src/qmf/DataImpl.h	(working copy)
@@ -24,8 +24,10 @@
 #include "qpid/RefCounted.h"
 #include "qmf/Data.h"
 #include "qmf/SchemaId.h"
+#include "qmf/Schema.h"
 #include "qmf/DataAddr.h"
 #include "qmf/Agent.h"
+#include "qmf/AgentSubscription.h"
 #include "qpid/types/Variant.h"
 
 namespace qmf {
@@ -37,18 +39,21 @@
         DataImpl(const qpid::types::Variant::Map&, const Agent&);
         qpid::types::Variant::Map asMap() const;
         DataImpl() {}
+        void addSubscription(boost::shared_ptr<AgentSubscription>);
+        void delSubscription(uint64_t);
+        qpid::types::Variant::Map publishSubscription(uint64_t);
+        const Schema& getSchema() const { return schema; }
 
         //
         // Methods from API handle
         //
-        DataImpl(const SchemaId& s) : schemaId(s) {}
-        void setSchema(const SchemaId& s) { schemaId = s; }
+        DataImpl(const Schema& s) : schema(s) {}
         void setAddr(const DataAddr& a) { dataAddr = a; }
-        void setProperty(const std::string& k, const qpid::types::Variant& v) { properties[k] = v; }
+        void setProperty(const std::string& k, const qpid::types::Variant& v);
         void overwriteProperties(const qpid::types::Variant::Map& m);
-        bool hasSchema() const { return schemaId.isValid(); }
+        bool hasSchema() const { return schemaId.isValid() || schema.isValid(); }
         bool hasAddr() const { return dataAddr.isValid(); }
-        const SchemaId& getSchemaId() const { return schemaId; }
+        const SchemaId& getSchemaId() const { if (schema.isValid()) return schema.getSchemaId(); else return schemaId; }
         const DataAddr& getAddr() const { return dataAddr; }
         const qpid::types::Variant& getProperty(const std::string& k) const;
         const qpid::types::Variant::Map& getProperties() const { return properties; }
@@ -56,7 +61,14 @@
         const Agent& getAgent() const { return agent; }
 
     private:
+        struct Subscr {
+            boost::shared_ptr<AgentSubscription> subscription;
+            qpid::types::Variant::Map deltas;
+        };
+        std::map<uint64_t, boost::shared_ptr<Subscr> > subscriptions;
+
         SchemaId schemaId;
+        Schema   schema;
         DataAddr dataAddr;
         qpid::types::Variant::Map properties;
         Agent agent;
Index: cpp/src/qmf/AgentSubscription.cpp
===================================================================
--- cpp/src/qmf/AgentSubscription.cpp	(revision 0)
+++ cpp/src/qmf/AgentSubscription.cpp	(revision 0)
@@ -0,0 +1,51 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qmf/AgentSubscription.h"
+
+using namespace qmf;
+
+AgentSubscription::AgentSubscription(uint64_t _id, uint64_t _interval, uint64_t _life, 
+                                     const std::string& _replyTo, const std::string& _cid, Query _query) :
+    id(_id), interval(_interval), lifetime(_life), timeSincePublish(0), timeSinceKeepalive(0),
+    replyTo(_replyTo), cid(_cid), query(_query)
+{
+}
+
+
+AgentSubscription::~AgentSubscription()
+{
+}
+
+
+bool AgentSubscription::tick(uint64_t seconds)
+{
+    timeSinceKeepalive += seconds;
+    if (timeSinceKeepalive >= lifetime)
+        return false;
+
+    timeSincePublish += seconds;
+    if (timeSincePublish >= interval) {
+    }
+
+    return true;
+}
+
Index: cpp/src/qmf/DataAddr.cpp
===================================================================
--- cpp/src/qmf/DataAddr.cpp	(revision 1056407)
+++ cpp/src/qmf/DataAddr.cpp	(working copy)
@@ -38,6 +38,7 @@
 bool DataAddr::operator==(const DataAddr& o) { return *impl == *o.impl; }
 bool DataAddr::operator<(const DataAddr& o) { return *impl < *o.impl; }
 
+DataAddr::DataAddr(const qpid::types::Variant::Map& m) { PI::ctor(*this, new DataAddrImpl(m)); }
 DataAddr::DataAddr(const string& n, const string& a, uint32_t e) { PI::ctor(*this, new DataAddrImpl(n, a, e)); }
 const string& DataAddr::getName() const { return impl->getName(); }
 const string& DataAddr::getAgentName() const { return impl->getAgentName(); }
Index: cpp/src/qmf/SubscriptionImpl.h
===================================================================
--- cpp/src/qmf/SubscriptionImpl.h	(revision 0)
+++ cpp/src/qmf/SubscriptionImpl.h	(revision 0)
@@ -0,0 +1,57 @@
+#ifndef _QMF_SUBSCRIPTION_IMPL_H_
+#define _QMF_SUBSCRIPTION_IMPL_H_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qmf/Subscription.h"
+
+namespace qmf {
+    class SubscriptionImpl : public virtual qpid::RefCounted {
+    public:
+        //
+        // Public impl-only methods
+        //
+        SubscriptionImpl(int p) : placeholder(p) {}
+        ~SubscriptionImpl();
+
+        //
+        // Methods from API handle
+        //
+        void cancel();
+        bool isActive() const;
+        void lock();
+        void unlock();
+        uint32_t getDataCount() const;
+        Data getData(uint32_t) const;
+
+    private:
+        int placeholder;
+    };
+
+    struct SubscriptionImplAccess
+    {
+        static SubscriptionImpl& get(Subscription&);
+        static const SubscriptionImpl& get(const Subscription&);
+    };
+}
+
+#endif
Index: cpp/src/qmf/SchemaId.cpp
===================================================================
--- cpp/src/qmf/SchemaId.cpp	(revision 1056407)
+++ cpp/src/qmf/SchemaId.cpp	(working copy)
@@ -78,7 +78,8 @@
         result["_type"] = "_data";
     else
         result["_type"] = "_event";
-    result["_hash"] = hash;
+    if (!hash.isNull())
+        result["_hash"] = hash;
     return result;
 }
 
Index: cpp/src/qmf/constants.h
===================================================================
--- cpp/src/qmf/constants.h	(revision 0)
+++ cpp/src/qmf/constants.h	(revision 0)
@@ -0,0 +1,83 @@
+#ifndef QMF_CONSTANTS_H
+#define QMF_CONSTANTS_H
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <string>
+
+namespace qmf {
+
+    struct protocol {
+        /**
+         * Header key strings
+         */
+        static const std::string HEADER_KEY_APP_ID;
+        static const std::string HEADER_KEY_METHOD;
+        static const std::string HEADER_KEY_OPCODE;
+        static const std::string HEADER_KEY_AGENT;
+        static const std::string HEADER_KEY_CONTENT;
+        static const std::string HEADER_KEY_PARTIAL;
+
+        /**
+         * Header values per-key
+         */
+        static const std::string HEADER_APP_ID_QMF;
+
+        static const std::string HEADER_METHOD_REQUEST;
+        static const std::string HEADER_METHOD_RESPONSE;
+        static const std::string HEADER_METHOD_INDICATION;
+
+        static const std::string HEADER_OPCODE_EXCEPTION;
+        static const std::string HEADER_OPCODE_AGENT_LOCATE_REQUEST;
+        static const std::string HEADER_OPCODE_AGENT_LOCATE_RESPONSE;
+        static const std::string HEADER_OPCODE_AGENT_HEARTBEAT_INDICATION;
+        static const std::string HEADER_OPCODE_QUERY_REQUEST;
+        static const std::string HEADER_OPCODE_QUERY_RESPONSE;
+        static const std::string HEADER_OPCODE_SUBSCRIBE_REQUEST;
+        static const std::string HEADER_OPCODE_SUBSCRIBE_RESPONSE;
+        static const std::string HEADER_OPCODE_SUBSCRIBE_CANCEL_INDICATION;
+        static const std::string HEADER_OPCODE_SUBSCRIBE_REFRESH_INDICATION;
+        static const std::string HEADER_OPCODE_DATA_INDICATION;
+        static const std::string HEADER_OPCODE_METHOD_REQUEST;
+        static const std::string HEADER_OPCODE_METHOD_RESPONSE;
+
+        static const std::string HEADER_CONTENT_SCHEMA_ID;
+        static const std::string HEADER_CONTENT_SCHEMA_CLASS;
+        static const std::string HEADER_CONTENT_OBJECT_ID;
+        static const std::string HEADER_CONTENT_DATA;
+        static const std::string HEADER_CONTENT_EVENT;
+        static const std::string HEADER_CONTENT_QUERY;
+
+        /**
+         * Keywords for Agent attributes
+         */
+        static const std::string AGENT_ATTR_VENDOR;
+        static const std::string AGENT_ATTR_PRODUCT;
+        static const std::string AGENT_ATTR_INSTANCE;
+        static const std::string AGENT_ATTR_NAME;
+        static const std::string AGENT_ATTR_TIMESTAMP;
+        static const std::string AGENT_ATTR_HEARTBEAT_INTERVAL;
+        static const std::string AGENT_ATTR_EPOCH;
+        static const std::string AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP;
+    };
+}
+
+#endif
Index: cpp/src/qmf/AgentImpl.h
===================================================================
--- cpp/src/qmf/AgentImpl.h	(revision 1056407)
+++ cpp/src/qmf/AgentImpl.h	(working copy)
@@ -29,6 +29,7 @@
 #include "qmf/SchemaCache.h"
 #include "qpid/messaging/Session.h"
 #include "qpid/messaging/Message.h"
+#include "qpid/messaging/Sender.h"
 #include "qpid/sys/Mutex.h"
 #include "qpid/sys/Condition.h"
 #include <boost/shared_ptr.hpp>
@@ -90,11 +91,13 @@
 
         mutable qpid::sys::Mutex lock;
         std::string name;
+        std::string directSubject;
         uint32_t epoch;
         ConsoleSessionImpl& session;
         bool touched;
         uint32_t untouchedCount;
         uint32_t capability;
+        qpid::messaging::Sender sender;
         qpid::types::Variant::Map attributes;
         uint32_t nextCorrelator;
         std::map<uint32_t, boost::shared_ptr<SyncContext> > contextMap;
Index: cpp/src/qmf/SchemaImpl.h
===================================================================
--- cpp/src/qmf/SchemaImpl.h	(revision 1056407)
+++ cpp/src/qmf/SchemaImpl.h	(working copy)
@@ -46,6 +46,9 @@
         qpid::types::Variant::Map asMap() const;
         SchemaImpl(qpid::management::Buffer& v1Buffer);
         std::string asV1Content(uint32_t sequence) const;
+        bool isValidProperty(const std::string& k, const qpid::types::Variant& v) const;
+        bool isValidMethodInArg(const std::string& m, const std::string& k, const qpid::types::Variant& v) const;
+        bool isValidMethodOutArg(const std::string& m, const std::string& k, const qpid::types::Variant& v) const;
 
         //
         // Methods from API handle
@@ -79,6 +82,7 @@
 
         void checkFinal() const;
         void checkNotFinal() const;
+        bool isCompatibleType(int qmfType, qpid::types::VariantType qpidType) const;
     };
 
     struct SchemaImplAccess
Index: cpp/src/qmf/DataAddrImpl.h
===================================================================
--- cpp/src/qmf/DataAddrImpl.h	(revision 1056407)
+++ cpp/src/qmf/DataAddrImpl.h	(working copy)
@@ -32,7 +32,6 @@
         //
         // Impl-only methods
         //
-        DataAddrImpl(const qpid::types::Variant::Map&);
         void setName(const std::string& n) { name = n; }
         void setAgent(const std::string& n, uint32_t e=0) { agentName = n; agentEpoch = e; }
 
@@ -41,6 +40,7 @@
         //
         bool operator==(const DataAddrImpl&);
         bool operator<(const DataAddrImpl&);
+        DataAddrImpl(const qpid::types::Variant::Map&);
         DataAddrImpl(const std::string& _name, const std::string& _agentName, uint32_t _agentEpoch=0) :
             agentName(_agentName), name(_name), agentEpoch(_agentEpoch) {}
         const std::string& getName() const { return name; }
Index: cpp/src/qmf/AgentEvent.cpp
===================================================================
--- cpp/src/qmf/AgentEvent.cpp	(revision 1056407)
+++ cpp/src/qmf/AgentEvent.cpp	(working copy)
@@ -21,6 +21,7 @@
 
 #include "qmf/AgentEventImpl.h"
 #include "qmf/PrivateImplRef.h"
+#include "qmf/SchemaImpl.h"
 
 using namespace std;
 using namespace qmf;
@@ -64,6 +65,8 @@
 
 void AgentEventImpl::addReturnArgument(const string& key, const Variant& val, const string& subtype)
 {
+    if (schema.isValid() && !SchemaImplAccess::get(schema).isValidMethodOutArg(methodName, key, val))
+        throw QmfException("Output argument is unknown or the type is incompatible");
     outArguments[key] = val;
     if (!subtype.empty())
         outArgumentSubtypes[key] = subtype;
Index: cpp/src/qmf/SchemaMethod.cpp
===================================================================
--- cpp/src/qmf/SchemaMethod.cpp	(revision 1056407)
+++ cpp/src/qmf/SchemaMethod.cpp	(working copy)
@@ -163,7 +163,7 @@
     Variant::Map map;
 
     map["name"] = name;
-    map["argCount"] = arguments.size();
+    map["argCount"] = (uint64_t) arguments.size();
     if (!desc.empty())
         map["desc"] = desc;
 
Index: cpp/src/qmf/AgentSession.cpp
===================================================================
--- cpp/src/qmf/AgentSession.cpp	(revision 1056407)
+++ cpp/src/qmf/AgentSession.cpp	(working copy)
@@ -30,6 +30,7 @@
 #include "qmf/DataImpl.h"
 #include "qmf/QueryImpl.h"
 #include "qmf/agentCapability.h"
+#include "qmf/constants.h"
 #include "qpid/sys/Mutex.h"
 #include "qpid/sys/Condition.h"
 #include "qpid/sys/Thread.h"
@@ -84,6 +85,7 @@
         void complete(AgentEvent& e);
         void methodSuccess(AgentEvent& e);
         void raiseEvent(const Data& d);
+        void raiseEvent(const Data& d, int s);
 
     private:
         typedef map<DataAddr, Data, DataAddrCompare> DataIndex;
@@ -111,6 +113,12 @@
         bool externalStorage;
         bool autoAllowQueries;
         bool autoAllowMethods;
+        uint32_t maxSubscriptions;
+        uint32_t minSubInterval;
+        uint32_t subLifetime;
+        bool publicEvents;
+        bool listenOnDirect;
+        bool strictSecurity;
         uint64_t schemaUpdateTime;
         string directBase;
         string topicBase;
@@ -129,6 +137,7 @@
         void handleV1SchemaRequest(qpid::management::Buffer&, uint32_t, const Message&);
         void dispatch(Message);
         void sendHeartbeat();
+        void send(Message, const Address&);
         void flushResponses(AgentEvent&, bool);
         void periodicProcessing(uint64_t);
         void run();
@@ -163,6 +172,7 @@
 void AgentSession::complete(AgentEvent& e) { impl->complete(e); }
 void AgentSession::methodSuccess(AgentEvent& e) { impl->methodSuccess(e); }
 void AgentSession::raiseEvent(const Data& d) { impl->raiseEvent(d); }
+void AgentSession::raiseEvent(const Data& d, int s) { impl->raiseEvent(d, s); }
 
 //========================================================================================
 // Impl Method Bodies
@@ -172,10 +182,12 @@
     connection(c), domain("default"), opened(false), thread(0), threadCanceled(false),
     bootSequence(1), interval(60), lastHeartbeat(0), lastVisit(0), forceHeartbeat(false),
     externalStorage(false), autoAllowQueries(true), autoAllowMethods(true),
+    maxSubscriptions(64), minSubInterval(3000), subLifetime(300), publicEvents(true),
+    listenOnDirect(true), strictSecurity(false),
     schemaUpdateTime(uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now())))
 {
     //
-    // Set Capability Level to 1
+    // Set Agent Capability Level
     //
     attributes["qmf.agent_capability"] = AGENT_CAPABILITY_0_8;
 
@@ -208,6 +220,30 @@
         iter = optMap.find("allow-methods");
         if (iter != optMap.end())
             autoAllowMethods = iter->second.asBool();
+
+        iter = optMap.find("max-subscriptions");
+        if (iter != optMap.end())
+            maxSubscriptions = iter->second.asUint32();
+
+        iter = optMap.find("min-sub-interval");
+        if (iter != optMap.end())
+            minSubInterval = iter->second.asUint32();
+
+        iter = optMap.find("sub-lifetime");
+        if (iter != optMap.end())
+            subLifetime = iter->second.asUint32();
+
+        iter = optMap.find("public-events");
+        if (iter != optMap.end())
+            publicEvents = iter->second.asBool();
+
+        iter = optMap.find("listen-on-direct");
+        if (iter != optMap.end())
+            listenOnDirect = iter->second.asBool();
+
+        iter = optMap.find("strict-security");
+        if (iter != optMap.end())
+            strictSecurity = iter->second.asBool();
     }
 }
 
@@ -225,6 +261,8 @@
         throw QmfException("The session is already open");
 
     const string addrArgs(";{create:never,node:{type:topic}}");
+    const string routableAddr("direct-agent.route." + qpid::types::Uuid(true).str());
+    attributes["_direct_subject"] = routableAddr;
 
     // Establish messaging addresses
     setAgentName();
@@ -233,13 +271,20 @@
 
     // Create AMQP session, receivers, and senders
     session = connection.createSession();
-    Receiver directRx = session.createReceiver(directBase + "/" + agentName + addrArgs);
+    Receiver directRx;
+    Receiver routableDirectRx = session.createReceiver(topicBase + "/" + routableAddr + addrArgs);
     Receiver topicRx = session.createReceiver(topicBase + "/console.#" + addrArgs);
 
-    directRx.setCapacity(64);
+    if (listenOnDirect && !strictSecurity) {
+        directRx = session.createReceiver(directBase + "/" + agentName + addrArgs);
+        directRx.setCapacity(64);
+    }
+
+    routableDirectRx.setCapacity(64);
     topicRx.setCapacity(64);
 
-    directSender = session.createSender(directBase + addrArgs);
+    if (!strictSecurity)
+        directSender = session.createSender(directBase + addrArgs);
     topicSender = session.createSender(topicBase + addrArgs);
 
     // Start the receiver thread
@@ -421,20 +466,18 @@
     Variant::Map map;
     Variant::Map& headers(msg.getProperties());
 
-    headers["method"] = "response";
-    headers["qmf.opcode"] = "_exception";
-    headers["qmf.content"] = "_data";
-    headers["qmf.agent"] = agentName;
-    headers["x-amqp-0-10.app-id"] = "qmf2";
+    headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_RESPONSE;
+    headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_EXCEPTION;
+    headers[protocol::HEADER_KEY_CONTENT] = protocol::HEADER_CONTENT_DATA;
+    headers[protocol::HEADER_KEY_AGENT] = agentName;
+    headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
 
     AgentEventImpl& eventImpl(AgentEventImplAccess::get(event));
     const DataImpl& dataImpl(DataImplAccess::get(data));
 
     msg.setCorrelationId(eventImpl.getCorrelationId());
     encode(dataImpl.asMap(), msg);
-    Sender sender(session.createSender(eventImpl.getReplyTo()));
-    sender.send(msg);
-    sender.close();
+    send(msg, eventImpl.getReplyTo());
 
     QPID_LOG(trace, "SENT Exception to=" << eventImpl.getReplyTo());
 }
@@ -461,10 +504,10 @@
     Variant::Map map;
     Variant::Map& headers(msg.getProperties());
 
-    headers["method"] = "response";
-    headers["qmf.opcode"] = "_method_response";
-    headers["qmf.agent"] = agentName;
-    headers["x-amqp-0-10.app-id"] = "qmf2";
+    headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_RESPONSE;
+    headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_METHOD_RESPONSE;
+    headers[protocol::HEADER_KEY_AGENT] = agentName;
+    headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
 
     AgentEventImpl& eventImpl(AgentEventImplAccess::get(event));
 
@@ -477,9 +520,7 @@
 
     msg.setCorrelationId(eventImpl.getCorrelationId());
     encode(map, msg);
-    Sender sender(session.createSender(eventImpl.getReplyTo()));
-    sender.send(msg);
-    sender.close();
+    send(msg, eventImpl.getReplyTo());
 
     QPID_LOG(trace, "SENT MethodResponse to=" << eventImpl.getReplyTo());
 }
@@ -487,24 +528,50 @@
 
 void AgentSessionImpl::raiseEvent(const Data& data)
 {
+    int severity(SEV_NOTICE);
+    if (data.hasSchema()) {
+        const Schema& schema(DataImplAccess::get(data).getSchema());
+        if (schema.isValid())
+            severity = schema.getDefaultSeverity();
+    }
+
+    raiseEvent(data, severity);
+}
+
+
+void AgentSessionImpl::raiseEvent(const Data& data, int severity)
+{
     Message msg;
     Variant::Map map;
     Variant::Map& headers(msg.getProperties());
+    string subject("agent.ind.event");
 
-    // TODO: add severity.package.class to key
-    //       or modify to send only to subscriptions with matching queries
+    if (data.hasSchema()) {
+        const SchemaId& schemaId(data.getSchemaId());
+        if (schemaId.getType() != SCHEMA_TYPE_EVENT)
+            throw QmfException("Cannot call raiseEvent on data that is not an Event");
+        subject = subject + "." + schemaId.getPackageName() + "." + schemaId.getName();
+    }
 
-    headers["method"] = "indication";
-    headers["qmf.opcode"] = "_data_indication";
-    headers["qmf.content"] = "_event";
-    headers["qmf.agent"] = agentName;
-    headers["x-amqp-0-10.app-id"] = "qmf2";
-    msg.setSubject("agent.ind.event");
+    if (severity < SEV_EMERG || severity > SEV_DEBUG)
+        throw QmfException("Invalid severity value");
 
-    encode(DataImplAccess::get(data).asMap(), msg);
+    headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_INDICATION;
+    headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_DATA_INDICATION;
+    headers[protocol::HEADER_KEY_CONTENT] = protocol::HEADER_CONTENT_EVENT;
+    headers[protocol::HEADER_KEY_AGENT] = agentName;
+    headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
+    msg.setSubject(subject);
+
+    Variant::List list;
+    Variant::Map dataAsMap(DataImplAccess::get(data).asMap());
+    dataAsMap["_severity"] = severity;
+    dataAsMap["_timestamp"] = uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()));
+    list.push_back(dataAsMap);
+    encode(list, msg);
     topicSender.send(msg);
 
-    QPID_LOG(trace, "SENT EventIndication to=agent.ind.event");
+    QPID_LOG(trace, "SENT EventIndication to=" << topicSender.getName() << "/" << subject);
 }
 
 
@@ -558,7 +625,7 @@
 
 void AgentSessionImpl::handleLocateRequest(const Variant::List& predicate, const Message& msg)
 {
-    QPID_LOG(trace, "RCVD AgentLocateRequest");
+    QPID_LOG(trace, "RCVD AgentLocateRequest from=" << msg.getReplyTo());
 
     if (!predicate.empty()) {
         Query agentQuery(QUERY_OBJECT);
@@ -573,28 +640,26 @@
     Variant::Map map;
     Variant::Map& headers(reply.getProperties());
 
-    headers["method"] = "indication";
-    headers["qmf.opcode"] = "_agent_locate_response";
-    headers["qmf.agent"] = agentName;
-    headers["x-amqp-0-10.app-id"] = "qmf2";
+    headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_INDICATION;
+    headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_AGENT_LOCATE_RESPONSE;
+    headers[protocol::HEADER_KEY_AGENT] = agentName;
+    headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
 
     map["_values"] = attributes;
-    map["_values"].asMap()["timestamp"] = uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()));
-    map["_values"].asMap()["heartbeat_interval"] = interval;
-    map["_values"].asMap()["epoch"] = bootSequence;
-    map["_values"].asMap()["schemaUpdated"] = schemaUpdateTime;
+    map["_values"].asMap()[protocol::AGENT_ATTR_TIMESTAMP] = uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()));
+    map["_values"].asMap()[protocol::AGENT_ATTR_HEARTBEAT_INTERVAL] = interval;
+    map["_values"].asMap()[protocol::AGENT_ATTR_EPOCH] = bootSequence;
+    map["_values"].asMap()[protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP] = schemaUpdateTime;
 
     encode(map, reply);
-    Sender sender = session.createSender(msg.getReplyTo());
-    sender.send(reply);
+    send(reply, msg.getReplyTo());
     QPID_LOG(trace, "SENT AgentLocateResponse to=" << msg.getReplyTo());
-    sender.close();
 }
 
 
 void AgentSessionImpl::handleMethodRequest(const Variant::Map& content, const Message& msg)
 {
-    QPID_LOG(trace, "RCVD MethodRequest map=" << content << " from=" << msg.getReplyTo());
+    QPID_LOG(trace, "RCVD MethodRequest map=" << content << " from=" << msg.getReplyTo() << " cid=" << msg.getCorrelationId());
 
     //
     // Construct an AgentEvent to be sent to the application.
@@ -614,10 +679,6 @@
     }
     eventImpl->setMethodName(iter->second.asString());
 
-    iter = content.find("_object_id");
-    if (iter != content.end())
-        eventImpl->setDataAddr(DataAddr(new DataAddrImpl(iter->second.asMap())));
-
     iter = content.find("_arguments");
     if (iter != content.end())
         eventImpl->setArguments(iter->second.asMap());
@@ -626,13 +687,39 @@
     if (iter != content.end())
         eventImpl->setArgumentSubtypes(iter->second.asMap());
 
+    iter = content.find("_object_id");
+    if (iter != content.end()) {
+        DataAddr addr(new DataAddrImpl(iter->second.asMap()));
+        eventImpl->setDataAddr(addr);
+        DataIndex::const_iterator iter(globalIndex.find(addr));
+        if (iter == globalIndex.end()) {
+            AgentEvent event(eventImpl.release());
+            raiseException(event, "No data object found with the specified address");
+            return;
+        }
+
+        const Schema& schema(DataImplAccess::get(iter->second).getSchema());
+        if (schema.isValid()) {
+            eventImpl->setSchema(schema);
+            for (Variant::Map::const_iterator aIter = eventImpl->getArguments().begin();
+                 aIter != eventImpl->getArguments().end(); aIter++) {
+                const Schema& schema(DataImplAccess::get(iter->second).getSchema());
+                if (!SchemaImplAccess::get(schema).isValidMethodInArg(eventImpl->getMethodName(), aIter->first, aIter->second)) {
+                    AgentEvent event(eventImpl.release());
+                    raiseException(event, "Invalid argument: " + aIter->first);
+                    return;
+                }
+            }
+        }
+    }
+
     enqueueEvent(AgentEvent(eventImpl.release()));
 }
 
 
 void AgentSessionImpl::handleQueryRequest(const Variant::Map& content, const Message& msg)
 {
-    QPID_LOG(trace, "RCVD QueryRequest query=" << content << " from=" << msg.getReplyTo());
+    QPID_LOG(trace, "RCVD QueryRequest query=" << content << " from=" << msg.getReplyTo() << " cid=" << msg.getCorrelationId());
 
     //
     // Construct an AgentEvent to be sent to the application or directly handled by the agent.
@@ -668,19 +755,19 @@
     Variant::Map map;
     Variant::Map& headers(msg.getProperties());
 
-    headers["method"] = "response";
-    headers["qmf.opcode"] = "_query_response";
-    headers["qmf.agent"] = agentName;
-    headers["x-amqp-0-10.app-id"] = "qmf2";
+    headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_RESPONSE;
+    headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_QUERY_RESPONSE;
+    headers[protocol::HEADER_KEY_AGENT] = agentName;
+    headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
 
     {
         qpid::sys::Mutex::ScopedLock l(lock);
         if (query.getTarget() == QUERY_SCHEMA_ID) {
-            headers["qmf.content"] = "_schema_id";
+            headers[protocol::HEADER_KEY_CONTENT] = "_schema_id";
             for (iter = schemata.begin(); iter != schemata.end(); iter++)
                 content.push_back(SchemaIdImplAccess::get(iter->first).asMap());
         } else if (query.getSchemaId().isValid()) {
-            headers["qmf.content"] = "_schema";
+            headers[protocol::HEADER_KEY_CONTENT] = "_schema";
             iter = schemata.find(query.getSchemaId());
             if (iter != schemata.end())
                 content.push_back(SchemaImplAccess::get(iter->second).asMap());
@@ -698,9 +785,7 @@
 
     msg.setCorrelationId(eventImpl.getCorrelationId());
     encode(content, msg);
-    Sender sender(session.createSender(eventImpl.getReplyTo()));
-    sender.send(msg);
-    sender.close();
+    send(msg, eventImpl.getReplyTo());
 
     QPID_LOG(trace, "SENT QueryResponse(Schema) to=" << eventImpl.getReplyTo());
 }
@@ -744,13 +829,11 @@
     Message reply;
     Variant::Map& headers(reply.getProperties());
 
-    headers["qmf.agent"] = agentName;
+    headers[protocol::HEADER_KEY_AGENT] = agentName;
     reply.setContent(replyContent);
 
-    Sender sender = session.createSender(msg.getReplyTo());
-    sender.send(reply);
+    send(reply, msg.getReplyTo());
     QPID_LOG(trace, "SENT QMFv1 SchemaResponse to=" << msg.getReplyTo());
-    sender.close();
 }
 
 
@@ -759,12 +842,23 @@
     const Variant::Map& properties(msg.getProperties());
     Variant::Map::const_iterator iter;
 
-    iter = properties.find("x-amqp-0-10.app-id");
-    if (iter != properties.end() && iter->second.asString() == "qmf2") {
+    //
+    // If strict-security is enabled, make sure that reply-to address complies with the
+    // strict-security addressing pattern (i.e. start with 'qmf.<domain>.topic/direct-console.').
+    //
+    if (strictSecurity && msg.getReplyTo()) {
+        if (msg.getReplyTo().getName() != topicBase || msg.getReplyTo().getSubject().find("direct-console.") != 0) {
+            QPID_LOG(warning, "Reply-to violates strict-security policy: " << msg.getReplyTo().str());
+            return;
+        }
+    }
+
+    iter = properties.find(protocol::HEADER_KEY_APP_ID);
+    if (iter != properties.end() && iter->second.asString() == protocol::HEADER_APP_ID_QMF) {
         //
         // Dispatch a QMFv2 formatted message
         //
-        iter = properties.find("qmf.opcode");
+        iter = properties.find(protocol::HEADER_KEY_OPCODE);
         if (iter == properties.end()) {
             QPID_LOG(trace, "Message received with no 'qmf.opcode' header");
             return;
@@ -776,7 +870,7 @@
             Variant::List content;
             decode(msg, content);
 
-            if (opcode == "_agent_locate_request") handleLocateRequest(content, msg);
+            if (opcode == protocol::HEADER_OPCODE_AGENT_LOCATE_REQUEST) handleLocateRequest(content, msg);
             else {
                 QPID_LOG(trace, "Unexpected QMFv2 opcode with 'amqp/list' content: " << opcode);
             }
@@ -785,8 +879,8 @@
             Variant::Map content;
             decode(msg, content);
 
-            if      (opcode == "_method_request") handleMethodRequest(content, msg);
-            else if (opcode == "_query_request")  handleQueryRequest(content, msg);
+            if      (opcode == protocol::HEADER_OPCODE_METHOD_REQUEST) handleMethodRequest(content, msg);
+            else if (opcode == protocol::HEADER_OPCODE_QUERY_REQUEST)  handleQueryRequest(content, msg);
             else {
                 QPID_LOG(trace, "Unexpected QMFv2 opcode with 'amqp/map' content: " << opcode);
             }
@@ -835,17 +929,17 @@
         }
     }
 
-    headers["method"] = "indication";
-    headers["qmf.opcode"] = "_agent_heartbeat_indication";
-    headers["qmf.agent"] = agentName;
-    headers["x-amqp-0-10.app-id"] = "qmf2";
+    headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_INDICATION;
+    headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_AGENT_HEARTBEAT_INDICATION;
+    headers[protocol::HEADER_KEY_AGENT] = agentName;
+    headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
     msg.setSubject(address.str());
 
     map["_values"] = attributes;
-    map["_values"].asMap()["timestamp"] = uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()));
-    map["_values"].asMap()["heartbeat_interval"] = interval;
-    map["_values"].asMap()["epoch"] = bootSequence;
-    map["_values"].asMap()["schemaUpdated"] = schemaUpdateTime;
+    map["_values"].asMap()[protocol::AGENT_ATTR_TIMESTAMP] = uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()));
+    map["_values"].asMap()[protocol::AGENT_ATTR_HEARTBEAT_INTERVAL] = interval;
+    map["_values"].asMap()[protocol::AGENT_ATTR_EPOCH] = bootSequence;
+    map["_values"].asMap()[protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP] = schemaUpdateTime;
 
     encode(map, msg);
     topicSender.send(msg);
@@ -853,19 +947,41 @@
 }
 
 
+void AgentSessionImpl::send(Message msg, const Address& to)
+{
+    Sender sender;
+
+    if (strictSecurity && to.getName() != topicBase) {
+        QPID_LOG(warning, "Address violates strict-security policy: " << to);
+        return;
+    }
+
+    if (to.getName() == directBase) {
+        msg.setSubject(to.getSubject());
+        sender = directSender;
+    } else if (to.getName() == topicBase) {
+        msg.setSubject(to.getSubject());
+        sender = topicSender;
+    } else
+        sender = session.createSender(to);
+
+    sender.send(msg);
+}
+
+
 void AgentSessionImpl::flushResponses(AgentEvent& event, bool final)
 {
     Message msg;
     Variant::Map map;
     Variant::Map& headers(msg.getProperties());
 
-    headers["method"] = "response";
-    headers["qmf.opcode"] = "_query_response";
-    headers["qmf.content"] = "_data";
-    headers["qmf.agent"] = agentName;
-    headers["x-amqp-0-10.app-id"] = "qmf2";
+    headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_RESPONSE;
+    headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_QUERY_RESPONSE;
+    headers[protocol::HEADER_KEY_CONTENT] = protocol::HEADER_CONTENT_DATA;
+    headers[protocol::HEADER_KEY_AGENT] = agentName;
+    headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
     if (!final)
-        headers["partial"] = Variant();
+        headers[protocol::HEADER_KEY_PARTIAL] = Variant();
 
     Variant::List body;
     AgentEventImpl& eventImpl(AgentEventImplAccess::get(event));
@@ -878,9 +994,7 @@
 
     msg.setCorrelationId(eventImpl.getCorrelationId());
     encode(body, msg);
-    Sender sender(session.createSender(eventImpl.getReplyTo()));
-    sender.send(msg);
-    sender.close();
+    send(msg, eventImpl.getReplyTo());
 
     QPID_LOG(trace, "SENT QueryResponse to=" << eventImpl.getReplyTo());
 }
@@ -894,6 +1008,7 @@
     //
     if (seconds == lastVisit)
         return;
+    //uint64_t thisInterval(seconds - lastVisit);
     lastVisit = seconds;
 
     //
Index: cpp/src/qmf/ConsoleEvent.cpp
===================================================================
--- cpp/src/qmf/ConsoleEvent.cpp	(revision 1056407)
+++ cpp/src/qmf/ConsoleEvent.cpp	(working copy)
@@ -44,6 +44,8 @@
 Data ConsoleEvent::getData(uint32_t i) const { return impl->getData(i); }
 bool ConsoleEvent::isFinal() const { return impl->isFinal(); }
 const Variant::Map& ConsoleEvent::getArguments() const { return impl->getArguments(); }
+int ConsoleEvent::getSeverity() const { return impl->getSeverity(); }
+uint64_t ConsoleEvent::getTimestamp() const { return impl->getTimestamp(); }
 
 
 SchemaId ConsoleEventImpl::getSchemaId(uint32_t i) const
Index: cpp/src/qmf/Query.cpp
===================================================================
--- cpp/src/qmf/Query.cpp	(revision 1056407)
+++ cpp/src/qmf/Query.cpp	(working copy)
@@ -50,7 +50,7 @@
 bool Query::matchesPredicate(const qpid::types::Variant::Map& map) const { return impl->matchesPredicate(map); }
 
 
-QueryImpl::QueryImpl(const Variant::Map& map)
+QueryImpl::QueryImpl(const Variant::Map& map) : predicateCompiled(false)
 {
     Variant::Map::const_iterator iter;
     
Index: cpp/src/qmf/ConsoleSession.cpp
===================================================================
--- cpp/src/qmf/ConsoleSession.cpp	(revision 1056407)
+++ cpp/src/qmf/ConsoleSession.cpp	(working copy)
@@ -25,14 +25,20 @@
 #include "qmf/SchemaId.h"
 #include "qmf/SchemaImpl.h"
 #include "qmf/ConsoleEventImpl.h"
+#include "qmf/constants.h"
 #include "qpid/log/Statement.h"
 #include "qpid/messaging/AddressParser.h"
 #include "qpid/messaging/Sender.h"
 #include "qpid/messaging/Receiver.h"
 
 using namespace std;
-using namespace qpid::messaging;
 using namespace qmf;
+using qpid::messaging::Address;
+using qpid::messaging::Connection;
+using qpid::messaging::Receiver;
+using qpid::messaging::Sender;
+using qpid::messaging::Duration;
+using qpid::messaging::Message;
 using qpid::types::Variant;
 
 typedef qmf::PrivateImplRef<ConsoleSession> PI;
@@ -51,15 +57,17 @@
 uint32_t ConsoleSession::getAgentCount() const { return impl->getAgentCount(); }
 Agent ConsoleSession::getAgent(uint32_t i) const { return impl->getAgent(i); }
 Agent ConsoleSession::getConnectedBrokerAgent() const { return impl->getConnectedBrokerAgent(); }
+Subscription ConsoleSession::subscribe(const Query& q, const string& f, const string& o) { return impl->subscribe(q, f, o); }
+Subscription ConsoleSession::subscribe(const string& q, const string& f, const string& o) { return impl->subscribe(q, f, o); }
 
 //========================================================================================
 // Impl Method Bodies
 //========================================================================================
 
 ConsoleSessionImpl::ConsoleSessionImpl(Connection& c, const string& options) :
-    connection(c), domain("default"), maxAgentAgeMinutes(5), opened(false),
-    thread(0), threadCanceled(false),
-    lastVisit(0), lastAgePass(0), connectedBrokerInAgentList(false), schemaCache(new SchemaCache())
+    connection(c), domain("default"), maxAgentAgeMinutes(5),
+    opened(false), thread(0), threadCanceled(false), lastVisit(0), lastAgePass(0),
+    connectedBrokerInAgentList(false), schemaCache(new SchemaCache())
 {
     if (!options.empty()) {
         qpid::messaging::AddressParser parser(options);
@@ -75,6 +83,14 @@
         iter = optMap.find("max-agent-age");
         if (iter != optMap.end())
             maxAgentAgeMinutes = iter->second.asUint32();
+
+        iter = optMap.find("listen-on-direct");
+        if (iter != optMap.end())
+            listenOnDirect = iter->second.asBool();
+
+        iter = optMap.find("strict-security");
+        if (iter != optMap.end())
+            strictSecurity = iter->second.asBool();
     }
 }
 
@@ -110,9 +126,17 @@
             enqueueEventLH(eventImpl.release());
         }
 
-        if (!connectedBrokerInAgentList && agentQuery.matchesPredicate(connectedBrokerAgent.getAttributes())) {
+        if (!connectedBrokerInAgentList && connectedBrokerAgent.isValid() &&
+            agentQuery.matchesPredicate(connectedBrokerAgent.getAttributes())) {
             agents[connectedBrokerAgent.getName()] = connectedBrokerAgent;
             connectedBrokerInAgentList = true;
+
+            //
+            // Enqueue a notification of the new agent.
+            //
+            auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_ADD));
+            eventImpl->setAgent(connectedBrokerAgent);
+            enqueueEventLH(ConsoleEvent(eventImpl.release()));
         }
     }
 
@@ -133,24 +157,26 @@
     directBase = "qmf." + domain + ".direct";
     topicBase = "qmf." + domain + ".topic";
 
-    string myKey("qmf-console-" + qpid::types::Uuid(true).str());
+    string myKey("direct-console." + qpid::types::Uuid(true).str());
 
-    replyAddress = Address(directBase + "/" + myKey + ";{node:{type:topic}}");
+    replyAddress = Address(topicBase + "/" + myKey + ";{node:{type:topic}}");
 
     // Create AMQP session, receivers, and senders
     session = connection.createSession();
     Receiver directRx = session.createReceiver(replyAddress);
     Receiver topicRx = session.createReceiver(topicBase + "/agent.#"); // TODO: be more discriminating
-    Receiver legacyRx = session.createReceiver("amq.direct/" + myKey + ";{node:{type:topic}}");
+    if (!strictSecurity) {
+        Receiver legacyRx = session.createReceiver("amq.direct/" + myKey + ";{node:{type:topic}}");
+        legacyRx.setCapacity(64);
+        directSender = session.createSender(directBase + ";{create:never,node:{type:topic}}");
+        directSender.setCapacity(128);
+    }
 
     directRx.setCapacity(64);
     topicRx.setCapacity(128);
-    legacyRx.setCapacity(64);
 
-    directSender = session.createSender(directBase + ";{create:never,node:{type:topic}}");
     topicSender = session.createSender(topicBase + ";{create:never,node:{type:topic}}");
 
-    directSender.setCapacity(64);
     topicSender.setCapacity(128);
 
     // Start the receiver thread
@@ -219,6 +245,18 @@
 }
 
 
+Subscription ConsoleSessionImpl::subscribe(const Query&, const string&, const string&)
+{
+    return Subscription();
+}
+
+
+Subscription ConsoleSessionImpl::subscribe(const string&, const string&, const string&)
+{
+    return Subscription();
+}
+
+
 void ConsoleSessionImpl::enqueueEvent(const ConsoleEvent& event)
 {
     qpid::sys::Mutex::ScopedLock l(lock);
@@ -241,17 +279,17 @@
     Variant::Map::const_iterator iter;
     Variant::Map::const_iterator oiter;
 
-    oiter = properties.find("qmf.opcode");
-    iter = properties.find("x-amqp-0-10.app-id");
+    oiter = properties.find(protocol::HEADER_KEY_OPCODE);
+    iter = properties.find(protocol::HEADER_KEY_APP_ID);
     if (iter == properties.end())
         iter = properties.find("app_id");
-    if (iter != properties.end() && iter->second.asString() == "qmf2" && oiter != properties.end()) {
+    if (iter != properties.end() && iter->second.asString() == protocol::HEADER_APP_ID_QMF && oiter != properties.end()) {
         //
         // Dispatch a QMFv2 formatted message
         //
         const string& opcode = oiter->second.asString();
 
-        iter = properties.find("qmf.agent");
+        iter = properties.find(protocol::HEADER_KEY_AGENT);
         if (iter == properties.end()) {
             QPID_LOG(trace, "Message received with no 'qmf.agent' header");
             return;
@@ -269,7 +307,7 @@
         }
 
         if (msg.getContentType() == "amqp/map" &&
-            (opcode == "_agent_heartbeat_indication" || opcode == "_agent_locate_response")) {
+            (opcode == protocol::HEADER_OPCODE_AGENT_HEARTBEAT_INDICATION || opcode == protocol::HEADER_OPCODE_AGENT_LOCATE_RESPONSE)) {
             //
             // This is the one case where it's ok (necessary actually) to receive a QMFv2
             // message from an unknown agent (how else are they going to get known?)
@@ -289,8 +327,8 @@
             Variant::Map content;
             decode(msg, content);
 
-            if      (opcode == "_exception")       agentImpl.handleException(content, msg);
-            else if (opcode == "_method_response") agentImpl.handleMethodResponse(content, msg);
+            if      (opcode == protocol::HEADER_OPCODE_EXCEPTION)       agentImpl.handleException(content, msg);
+            else if (opcode == protocol::HEADER_OPCODE_METHOD_RESPONSE) agentImpl.handleMethodResponse(content, msg);
             else
                 QPID_LOG(error, "Received a map-formatted QMFv2 message with opcode=" << opcode);
 
@@ -301,8 +339,8 @@
             Variant::List content;
             decode(msg, content);
 
-            if      (opcode == "_query_response")  agentImpl.handleQueryResponse(content, msg);
-            else if (opcode == "_data_indication") agentImpl.handleDataIndication(content, msg);
+            if      (opcode == protocol::HEADER_OPCODE_QUERY_RESPONSE)  agentImpl.handleQueryResponse(content, msg);
+            else if (opcode == protocol::HEADER_OPCODE_DATA_INDICATION) agentImpl.handleDataIndication(content, msg);
             else
                 QPID_LOG(error, "Received a list-formatted QMFv2 message with opcode=" << opcode);
 
@@ -336,15 +374,17 @@
     Message msg;
     Variant::Map& headers(msg.getProperties());
 
-    headers["method"] = "request";
-    headers["qmf.opcode"] = "_agent_locate_request";
-    headers["x-amqp-0-10.app-id"] = "qmf2";
+    headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_REQUEST;
+    headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_AGENT_LOCATE_REQUEST;
+    headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
 
     msg.setReplyTo(replyAddress);
     msg.setCorrelationId("broker-locate");
     msg.setSubject("broker");
 
-    directSender.send(msg);
+    Sender sender = session.createSender(directBase + ";{create:never,node:{type:topic}}");
+    sender.send(msg);
+    sender.close();
 
     QPID_LOG(trace, "SENT AgentLocate to broker");
 }
@@ -354,19 +394,20 @@
 {
     Message msg;
     Variant::Map& headers(msg.getProperties());
+    static const string subject("console.request.agent_locate");
 
-    headers["method"] = "request";
-    headers["qmf.opcode"] = "_agent_locate_request";
-    headers["x-amqp-0-10.app-id"] = "qmf2";
+    headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_REQUEST;
+    headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_AGENT_LOCATE_REQUEST;
+    headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
 
     msg.setReplyTo(replyAddress);
     msg.setCorrelationId("agent-locate");
-    msg.setSubject("console.request.agent_locate");
+    msg.setSubject(subject);
     encode(agentQuery.getPredicate(), msg);
 
     topicSender.send(msg);
 
-    QPID_LOG(trace, "SENT AgentLocate to topic");
+    QPID_LOG(trace, "SENT AgentLocate to=" << topicSender.getName() << "/" << subject);
 }
 
 
@@ -382,17 +423,28 @@
         return;
     Variant::Map attrs(iter->second.asMap());
 
-    iter = attrs.find("epoch");
+    iter = attrs.find(protocol::AGENT_ATTR_EPOCH);
     if (iter != attrs.end())
         epoch = iter->second.asUint32();
 
     if (cid == "broker-locate") {
         qpid::sys::Mutex::ScopedLock l(lock);
-        agent = Agent(new AgentImpl(agentName, epoch, *this));
+        auto_ptr<AgentImpl> impl(new AgentImpl(agentName, epoch, *this));
+        for (iter = attrs.begin(); iter != attrs.end(); iter++)
+            if (iter->first != protocol::AGENT_ATTR_EPOCH)
+                impl->setAttribute(iter->first, iter->second);
+        agent = Agent(impl.release());
         connectedBrokerAgent = agent;
         if (!agentQuery || agentQuery.matchesPredicate(attrs)) {
             connectedBrokerInAgentList = true;
             agents[agentName] = agent;
+
+            //
+            // Enqueue a notification of the new agent.
+            //
+            auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_ADD));
+            eventImpl->setAgent(agent);
+            enqueueEventLH(ConsoleEvent(eventImpl.release()));
         }
         return;
     }
@@ -415,7 +467,7 @@
             //
             auto_ptr<AgentImpl> impl(new AgentImpl(agentName, epoch, *this));
             for (iter = attrs.begin(); iter != attrs.end(); iter++)
-                if (iter->first != "epoch")
+                if (iter->first != protocol::AGENT_ATTR_EPOCH)
                     impl->setAttribute(iter->first, iter->second);
             agent = Agent(impl.release());
             agents[agentName] = agent;
@@ -430,6 +482,7 @@
             //
             // This is a refresh of an agent we are already tracking.
             //
+            bool detectedRestart(false);
             agent = aIter->second;
             AgentImpl& impl(AgentImplAccess::get(agent));
             impl.touch();
@@ -442,19 +495,23 @@
                 auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_RESTART));
                 eventImpl->setAgent(agent);
                 enqueueEventLH(ConsoleEvent(eventImpl.release()));
+                detectedRestart = true;
             }
 
-            iter = attrs.find("schemaUpdated");
+            iter = attrs.find(protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP);
             if (iter != attrs.end()) {
                 uint64_t ts(iter->second.asUint64());
-                if (ts > impl.getAttribute("schemaUpdated").asUint64()) {
+                if (ts > impl.getAttribute(protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP).asUint64()) {
                     //
                     // The agent has added new schema entries since we last heard from it.
-                    // Enqueue a notification.
+                    // Update the attribute and, if this doesn't accompany a restart, enqueue a notification.
                     //
-                    auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_SCHEMA_UPDATE));
-                    eventImpl->setAgent(agent);
-                    enqueueEventLH(ConsoleEvent(eventImpl.release()));
+                    if (!detectedRestart) {
+                        auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_SCHEMA_UPDATE));
+                        eventImpl->setAgent(agent);
+                        enqueueEventLH(ConsoleEvent(eventImpl.release()));
+                    }
+                    impl.setAttribute(protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP, iter->second);
                 }
             }
         }
Index: cpp/src/qmf/AgentSubscription.h
===================================================================
--- cpp/src/qmf/AgentSubscription.h	(revision 0)
+++ cpp/src/qmf/AgentSubscription.h	(revision 0)
@@ -0,0 +1,52 @@
+#ifndef _QMF_AGENT_SUBSCRIPTION_H_
+#define _QMF_AGENT_SUBSCRIPTION_H_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/types/Variant.h"
+#include "qmf/Query.h"
+#include "qmf/Data.h"
+#include <boost/shared_ptr.hpp>
+
+namespace qmf {
+    class AgentSubscription {
+    public:
+        AgentSubscription(uint64_t _id, uint64_t _interval, uint64_t _life, 
+                          const std::string& _replyTo, const std::string& _cid, Query _query);
+        ~AgentSubscription();
+        bool tick(uint64_t seconds);
+        void keepalive() { timeSinceKeepalive = 0; }
+
+    private:
+        uint64_t id;
+        uint64_t interval;
+        uint64_t lifetime;
+        uint64_t timeSincePublish;
+        uint64_t timeSinceKeepalive;
+        const std::string replyTo;
+        const std::string cid;
+        Query query;
+    };
+
+}
+
+#endif
Index: cpp/src/qmf/Data.cpp
===================================================================
--- cpp/src/qmf/Data.cpp	(revision 1056407)
+++ cpp/src/qmf/Data.cpp	(working copy)
@@ -21,8 +21,10 @@
 
 #include "qmf/DataImpl.h"
 #include "qmf/DataAddrImpl.h"
+#include "qmf/SchemaImpl.h"
 #include "qmf/SchemaIdImpl.h"
 #include "qmf/PrivateImplRef.h"
+#include "qmf/SchemaProperty.h"
 
 using namespace std;
 using namespace qmf;
@@ -35,8 +37,7 @@
 Data::~Data() { PI::dtor(*this); }
 Data& Data::operator=(const Data& s) { return PI::assign(*this, s); }
 
-Data::Data(const SchemaId& s) { PI::ctor(*this, new DataImpl(s)); }
-void Data::setSchema(const SchemaId& s) { impl->setSchema(s); }
+Data::Data(const Schema& s) { PI::ctor(*this, new DataImpl(s)); }
 void Data::setAddr(const DataAddr& a) { impl->setAddr(a); }
 void Data::setProperty(const string& k, const qpid::types::Variant& v) { impl->setProperty(k, v); }
 void Data::overwriteProperties(const qpid::types::Variant::Map& m) { impl->overwriteProperties(m); }
@@ -103,6 +104,20 @@
 }
 
 
+void DataImpl::setProperty(const std::string& k, const qpid::types::Variant& v)
+{
+    if (schema.isValid()) {
+        //
+        //  If we have a valid schema, make sure that the property is included in the 
+        //  schema and that the variant type is compatible with the schema type.
+        //
+        if (!SchemaImplAccess::get(schema).isValidProperty(k, v))
+            throw QmfException("Property '" + k + "' either not in the schema or value is of incompatible type");
+    }
+    properties[k] = v;
+}
+
+
 DataImpl& DataImplAccess::get(Data& item)
 {
     return *item.impl;
Index: cpp/src/qmf/constants.cpp
===================================================================
--- cpp/src/qmf/constants.cpp	(revision 0)
+++ cpp/src/qmf/constants.cpp	(revision 0)
@@ -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.
+ *
+ */
+
+#include "constants.h"
+
+using namespace std;
+using namespace qmf;
+
+/**
+ * Header key strings
+ */
+const string protocol::HEADER_KEY_APP_ID  = "x-amqp-0-10.app-id";
+const string protocol::HEADER_KEY_METHOD  = "method";
+const string protocol::HEADER_KEY_OPCODE  = "qmf.opcode";
+const string protocol::HEADER_KEY_AGENT   = "qmf.agent";
+const string protocol::HEADER_KEY_CONTENT = "qmf.content";
+const string protocol::HEADER_KEY_PARTIAL = "partial";
+
+/**
+ * Header values per-key
+ */
+const string protocol::HEADER_APP_ID_QMF = "qmf2";
+
+const string protocol::HEADER_METHOD_REQUEST    = "request";
+const string protocol::HEADER_METHOD_RESPONSE   = "response";
+const string protocol::HEADER_METHOD_INDICATION = "indication";
+
+const string protocol::HEADER_OPCODE_EXCEPTION                    = "_exception";
+const string protocol::HEADER_OPCODE_AGENT_LOCATE_REQUEST         = "_agent_locate_request";
+const string protocol::HEADER_OPCODE_AGENT_LOCATE_RESPONSE        = "_agent_locate_response";
+const string protocol::HEADER_OPCODE_AGENT_HEARTBEAT_INDICATION   = "_agent_heartbeat_indication";
+const string protocol::HEADER_OPCODE_QUERY_REQUEST                = "_query_request";
+const string protocol::HEADER_OPCODE_QUERY_RESPONSE               = "_query_response";
+const string protocol::HEADER_OPCODE_SUBSCRIBE_REQUEST            = "_subscribe_request";
+const string protocol::HEADER_OPCODE_SUBSCRIBE_RESPONSE           = "_subscribe_response";
+const string protocol::HEADER_OPCODE_SUBSCRIBE_CANCEL_INDICATION  = "_subscribe_cancel_indication";
+const string protocol::HEADER_OPCODE_SUBSCRIBE_REFRESH_INDICATION = "_subscribe_refresh_indication";
+const string protocol::HEADER_OPCODE_DATA_INDICATION              = "_data_indication";
+const string protocol::HEADER_OPCODE_METHOD_REQUEST               = "_method_request";
+const string protocol::HEADER_OPCODE_METHOD_RESPONSE              = "_method_response";
+
+const string protocol::HEADER_CONTENT_SCHEMA_ID    = "_schema_id";
+const string protocol::HEADER_CONTENT_SCHEMA_CLASS = "_schema_class";
+const string protocol::HEADER_CONTENT_OBJECT_ID    = "_object_id";
+const string protocol::HEADER_CONTENT_DATA         = "_data";
+const string protocol::HEADER_CONTENT_EVENT        = "_event";
+const string protocol::HEADER_CONTENT_QUERY        = "_query";
+
+/**
+ * Keywords for Agent attributes
+ */
+const string protocol::AGENT_ATTR_VENDOR                   = "_vendor";
+const string protocol::AGENT_ATTR_PRODUCT                  = "_product";
+const string protocol::AGENT_ATTR_INSTANCE                 = "_instance";
+const string protocol::AGENT_ATTR_NAME                     = "_name";
+const string protocol::AGENT_ATTR_TIMESTAMP                = "_timestamp";
+const string protocol::AGENT_ATTR_HEARTBEAT_INTERVAL       = "_heartbeat_interval";
+const string protocol::AGENT_ATTR_EPOCH                    = "_epoch";
+const string protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP = "_schema_updated";
Index: cpp/src/qmf/Subscription.cpp
===================================================================
--- cpp/src/qmf/Subscription.cpp	(revision 0)
+++ cpp/src/qmf/Subscription.cpp	(revision 0)
@@ -0,0 +1,88 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qmf/PrivateImplRef.h"
+#include "qmf/exceptions.h"
+#include "qmf/SubscriptionImpl.h"
+#include "qmf/DataImpl.h"
+
+using namespace std;
+using namespace qmf;
+using qpid::types::Variant;
+
+typedef PrivateImplRef<Subscription> PI;
+
+Subscription::Subscription(SubscriptionImpl* impl) { PI::ctor(*this, impl); }
+Subscription::Subscription(const Subscription& s) : qmf::Handle<SubscriptionImpl>() { PI::copy(*this, s); }
+Subscription::~Subscription() { PI::dtor(*this); }
+Subscription& Subscription::operator=(const Subscription& s) { return PI::assign(*this, s); }
+
+void Subscription::cancel() { impl->cancel(); }
+bool Subscription::isActive() const { return impl->isActive(); }
+void Subscription::lock() { impl->lock(); }
+void Subscription::unlock() { impl->unlock(); }
+uint32_t Subscription::getDataCount() const { return impl->getDataCount(); }
+Data Subscription::getData(uint32_t i) const { return impl->getData(i); }
+
+
+void SubscriptionImpl::cancel()
+{
+}
+
+
+bool SubscriptionImpl::isActive() const
+{
+    return false;
+}
+
+
+void SubscriptionImpl::lock()
+{
+}
+
+
+void SubscriptionImpl::unlock()
+{
+}
+
+
+uint32_t SubscriptionImpl::getDataCount() const
+{
+    return 0;
+}
+
+
+Data SubscriptionImpl::getData(uint32_t) const
+{
+    return Data();
+}
+
+
+SubscriptionImpl& SubscriptionImplAccess::get(Subscription& item)
+{
+    return *item.impl;
+}
+
+
+const SubscriptionImpl& SubscriptionImplAccess::get(const Subscription& item)
+{
+    return *item.impl;
+}
Index: cpp/src/qmf/AgentEventImpl.h
===================================================================
--- cpp/src/qmf/AgentEventImpl.h	(revision 1056407)
+++ cpp/src/qmf/AgentEventImpl.h	(working copy)
@@ -29,6 +29,7 @@
 #include "qmf/Query.h"
 #include "qmf/DataAddr.h"
 #include "qmf/Data.h"
+#include "qmf/Schema.h"
 #include <queue>
 
 namespace qmf {
@@ -45,6 +46,7 @@
         void setArguments(const qpid::types::Variant::Map& a) { arguments = a; }
         void setArgumentSubtypes(const qpid::types::Variant::Map& a) { argumentSubtypes = a; }
         void setReplyTo(const qpid::messaging::Address& r) { replyTo = r; }
+        void setSchema(const Schema& s) { schema = s; }
         const qpid::messaging::Address& getReplyTo() { return replyTo; }
         void setCorrelationId(const std::string& c) { correlationId = c; }
         const std::string& getCorrelationId() { return correlationId; }
@@ -73,6 +75,7 @@
         std::string correlationId;
         Query query;
         DataAddr dataAddr;
+        Schema schema;
         std::string methodName;
         qpid::types::Variant::Map arguments;
         qpid::types::Variant::Map argumentSubtypes;
Index: cpp/src/qmf/ConsoleEventImpl.h
===================================================================
--- cpp/src/qmf/ConsoleEventImpl.h	(revision 1056407)
+++ cpp/src/qmf/ConsoleEventImpl.h	(working copy)
@@ -42,6 +42,8 @@
         void addSchemaId(const SchemaId& s) { newSchemaIds.push_back(SchemaId(s)); }
         void setFinal() { final = true; }
         void setArguments(const qpid::types::Variant::Map& a) { arguments = a; }
+        void setSeverity(int s) { severity = s; }
+        void setTimestamp(uint64_t t) { timestamp = t; }
 
         //
         // Methods from API handle
@@ -56,6 +58,8 @@
         Data getData(uint32_t i) const;
         bool isFinal() const { return final; }
         const qpid::types::Variant::Map& getArguments() const { return arguments; }
+        int getSeverity() const { return severity; }
+        uint64_t getTimestamp() const { return timestamp; }
 
     private:
         const ConsoleEventCode eventType;
@@ -66,6 +70,8 @@
         std::list<Data> dataList;
         std::list<SchemaId> newSchemaIds;
         qpid::types::Variant::Map arguments;
+        int severity;
+        uint64_t timestamp;
     };
 
     struct ConsoleEventImplAccess
Index: cpp/src/qmf/ConsoleSessionImpl.h
===================================================================
--- cpp/src/qmf/ConsoleSessionImpl.h	(revision 1056407)
+++ cpp/src/qmf/ConsoleSessionImpl.h	(working copy)
@@ -61,6 +61,8 @@
         uint32_t getAgentCount() const;
         Agent getAgent(uint32_t i) const;
         Agent getConnectedBrokerAgent() const { return connectedBrokerAgent; }
+        Subscription subscribe(const Query&, const std::string& agentFilter, const std::string& options);
+        Subscription subscribe(const std::string&, const std::string& agentFilter, const std::string& options);
 
     protected:
         mutable qpid::sys::Mutex lock;
@@ -71,6 +73,8 @@
         qpid::messaging::Sender topicSender;
         std::string domain;
         uint32_t maxAgentAgeMinutes;
+        bool listenOnDirect;
+        bool strictSecurity;
         Query agentQuery;
         bool opened;
         std::queue<ConsoleEvent> eventQueue;
Index: cpp/src/qmf.mk
===================================================================
--- cpp/src/qmf.mk	(revision 1056407)
+++ cpp/src/qmf.mk	(working copy)
@@ -51,7 +51,8 @@
   ../include/qmf/SchemaId.h		\
   ../include/qmf/SchemaMethod.h		\
   ../include/qmf/SchemaProperty.h	\
-  ../include/qmf/SchemaTypes.h
+  ../include/qmf/SchemaTypes.h		\
+  ../include/qmf/Subscription.h
 
 
 #
@@ -91,10 +92,14 @@
   qmf/AgentEventImpl.h		\
   qmf/AgentImpl.h		\
   qmf/AgentSession.cpp		\
+  qmf/AgentSubscription.cpp	\
+  qmf/AgentSubscription.h	\
   qmf/ConsoleEvent.cpp		\
   qmf/ConsoleEventImpl.h	\
   qmf/ConsoleSession.cpp	\
   qmf/ConsoleSessionImpl.h	\
+  qmf/constants.cpp		\
+  qmf/constants.h		\
   qmf/DataAddr.cpp		\
   qmf/DataAddrImpl.h		\
   qmf/Data.cpp			\
@@ -116,7 +121,9 @@
   qmf/SchemaMethod.cpp		\
   qmf/SchemaMethodImpl.h	\
   qmf/SchemaProperty.cpp	\
-  qmf/SchemaPropertyImpl.h
+  qmf/SchemaPropertyImpl.h	\
+  qmf/Subscription.cpp		\
+  qmf/SubscriptionImpl.h
 
 libqmfengine_la_SOURCES =			\
   $(QMF_ENGINE_API)				\
Index: cpp/src/qpid/agent/ManagementAgentImpl.h
===================================================================
--- cpp/src/qpid/agent/ManagementAgentImpl.h	(revision 1056407)
+++ cpp/src/qpid/agent/ManagementAgentImpl.h	(working copy)
@@ -128,12 +128,14 @@
     };
 
     struct QueuedMethod {
-    QueuedMethod(const std::string& _cid, const std::string& _reply, const std::string& _body) :
-        cid(_cid), replyTo(_reply), body(_body) {}
+    QueuedMethod(const std::string& _cid, const std::string& _rte, const std::string& _rtk, const std::string& _body, const std::string& _uid) :
+        cid(_cid), replyToExchange(_rte), replyToKey(_rtk), body(_body), userId(_uid) {}
 
         std::string cid;
-        std::string replyTo;
+        std::string replyToExchange;
+        std::string replyToKey;
         std::string body;
+        std::string userId;
     };
 
     typedef std::deque<QueuedMethod*> MethodQueue;
@@ -277,16 +279,16 @@
                                                 uint8_t type=ManagementItem::CLASS_KIND_TABLE);
     bool checkHeader  (framing::Buffer& buf, uint8_t *opcode, uint32_t *seq);
     void sendHeartbeat();
-    void sendException(const std::string& replyToKey, const std::string& cid,
+    void sendException(const std::string& replyToExchange, const std::string& replyToKey, const std::string& cid,
                        const std::string& text, uint32_t code=1);
     void handlePackageRequest (qpid::framing::Buffer& inBuffer);
     void handleClassQuery     (qpid::framing::Buffer& inBuffer);
-    void handleSchemaRequest  (qpid::framing::Buffer& inBuffer, uint32_t sequence, const std::string& replyTo);
-    void invokeMethodRequest  (const std::string& body, const std::string& cid, const std::string& replyTo);
+    void handleSchemaRequest  (qpid::framing::Buffer& inBuffer, uint32_t sequence, const std::string& rte, const std::string& rtk);
+    void invokeMethodRequest  (const std::string& body, const std::string& cid, const std::string& rte, const std::string& rtk, const std::string& userId);
 
-    void handleGetQuery       (const std::string& body, const std::string& cid, const std::string& replyTo);
-    void handleLocateRequest  (const std::string& body, const std::string& sequence, const std::string& replyTo);
-    void handleMethodRequest  (const std::string& body, const std::string& sequence, const std::string& replyTo);
+    void handleGetQuery       (const std::string& body, const std::string& cid, const std::string& rte, const std::string& rtk);
+    void handleLocateRequest  (const std::string& body, const std::string& sequence, const std::string& rte, const std::string& rtk);
+    void handleMethodRequest  (const std::string& body, const std::string& sequence, const std::string& rte, const std::string& rtk, const std::string& userId);
     void handleConsoleAddedIndication();
     void getHeartbeatContent  (qpid::types::Variant::Map& map);
 };
Index: cpp/src/qpid/agent/ManagementAgentImpl.cpp
===================================================================
--- cpp/src/qpid/agent/ManagementAgentImpl.cpp	(revision 1056407)
+++ cpp/src/qpid/agent/ManagementAgentImpl.cpp	(working copy)
@@ -339,8 +339,10 @@
     headers["qmf.content"] = "_event";
     headers["qmf.agent"] = name_address;
 
-    MapCodec::encode(map_, content);
-    connThreadBody.sendBuffer(content, "", headers, topicExchange, key.str());
+    Variant::List list;
+    list.push_back(map_);
+    ListCodec::encode(list, content);
+    connThreadBody.sendBuffer(content, "", headers, topicExchange, key.str(), "amqp/list");
 }
 
 uint32_t ManagementAgentImpl::pollCallbacks(uint32_t callLimit)
@@ -360,7 +362,7 @@
         methodQueue.pop_front();
         {
             sys::Mutex::ScopedUnlock unlock(agentLock);
-            invokeMethodRequest(item->body, item->cid, item->replyTo);
+            invokeMethodRequest(item->body, item->cid, item->replyToExchange, item->replyToKey, item->userId);
             delete item;
         }
     }
@@ -495,7 +497,7 @@
     QPID_LOG(trace, "SENT AgentHeartbeat name=" << name_address);
 }
 
-void ManagementAgentImpl::sendException(const string& replyToKey, const string& cid,
+void ManagementAgentImpl::sendException(const string& rte, const string& rtk, const string& cid,
                                         const string& text, uint32_t code)
 {
     Variant::Map map;
@@ -512,12 +514,12 @@
     map["_values"] = values;
 
     MapCodec::encode(map, content);
-    connThreadBody.sendBuffer(content, cid, headers, directExchange, replyToKey);
+    connThreadBody.sendBuffer(content, cid, headers, rte, rtk);
 
     QPID_LOG(trace, "SENT Exception code=" << code <<" text=" << text);
 }
 
-void ManagementAgentImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, const string& replyTo)
+void ManagementAgentImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, const string& rte, const string& rtk)
 {
     sys::Mutex::ScopedLock lock(agentLock);
     string packageName;
@@ -544,7 +546,7 @@
             outBuffer.putRawData(body);
             outLen = MA_BUFFER_SIZE - outBuffer.available();
             outBuffer.reset();
-            connThreadBody.sendBuffer(outBuffer, outLen, "amq.direct", replyTo);
+            connThreadBody.sendBuffer(outBuffer, outLen, rte, rtk);
 
             QPID_LOG(trace, "SENT SchemaInd: package=" << packageName << " class=" << key.name);
         }
@@ -559,7 +561,7 @@
     QPID_LOG(trace, "RCVD ConsoleAddedInd");
 }
 
-void ManagementAgentImpl::invokeMethodRequest(const string& body, const string& cid, const string& replyTo)
+void ManagementAgentImpl::invokeMethodRequest(const string& body, const string& cid, const string& rte, const string& rtk, const string& userId)
 {
     string  methodName;
     bool    failed = false;
@@ -570,11 +572,9 @@
 
     MapCodec::decode(body, inMap);
 
-    outMap["_values"] = Variant::Map();
-
     if ((oid = inMap.find("_object_id")) == inMap.end() ||
         (mid = inMap.find("_method_name")) == inMap.end()) {
-        sendException(replyTo, cid, Manageable::StatusText(Manageable::STATUS_PARAMETER_INVALID),
+        sendException(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_PARAMETER_INVALID),
                       Manageable::STATUS_PARAMETER_INVALID);
         failed = true;
     } else {
@@ -593,6 +593,8 @@
                 inArgs = (mid->second).asMap();
             }
 
+            QPID_LOG(trace, "Invoking Method: name=" << methodName << " args=" << inArgs);
+
             boost::shared_ptr<ManagementObject> oPtr;
             {
                 sys::Mutex::ScopedLock lock(agentLock);
@@ -602,11 +604,11 @@
             }
 
             if (oPtr.get() == 0) {
-                sendException(replyTo, cid, Manageable::StatusText(Manageable::STATUS_UNKNOWN_OBJECT),
+                sendException(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_UNKNOWN_OBJECT),
                               Manageable::STATUS_UNKNOWN_OBJECT);
                 failed = true;
             } else {
-                oPtr->doMethod(methodName, inArgs, callMap);
+                oPtr->doMethod(methodName, inArgs, callMap, userId);
 
                 if (callMap["_status_code"].asUint32() == 0) {
                     outMap["_arguments"] = Variant::Map();
@@ -615,13 +617,13 @@
                         if (iter->first != "_status_code" && iter->first != "_status_text")
                             outMap["_arguments"].asMap()[iter->first] = iter->second;
                 } else {
-                    sendException(replyTo, cid, callMap["_status_text"], callMap["_status_code"]);
+                    sendException(rte, rtk, cid, callMap["_status_text"], callMap["_status_code"]);
                     failed = true;
                 }
             }
 
         } catch(types::InvalidConversion& e) {
-            sendException(replyTo, cid, e.what(), Manageable::STATUS_EXCEPTION);
+            sendException(rte, rtk, cid, e.what(), Manageable::STATUS_EXCEPTION);
             failed = true;
         }
     }
@@ -633,11 +635,11 @@
         headers["qmf.opcode"] = "_method_response";
         QPID_LOG(trace, "SENT MethodResponse map=" << outMap);
         MapCodec::encode(outMap, content);
-        connThreadBody.sendBuffer(content, cid, headers, directExchange, replyTo);
+        connThreadBody.sendBuffer(content, cid, headers, rte, rtk);
     }
 }
 
-void ManagementAgentImpl::handleGetQuery(const string& body, const string& cid, const string& replyTo)
+void ManagementAgentImpl::handleGetQuery(const string& body, const string& cid, const string& rte, const string& rtk)
 {
     moveNewObjectsLH();
 
@@ -664,12 +666,12 @@
      */
     i = inMap.find("_what");
     if (i == inMap.end()) {
-        sendException(replyTo, cid, "_what element missing in Query");
+        sendException(rte, rtk, cid, "_what element missing in Query");
         return;
     }
 
     if (i->second.getType() != qpid::types::VAR_STRING) {
-        sendException(replyTo, cid, "_what element is not a string");
+        sendException(rte, rtk, cid, "_what element is not a string");
         return;
     }
 
@@ -707,8 +709,8 @@
                 headers.erase("partial");
 
                 ListCodec::encode(list_, content);
-                connThreadBody.sendBuffer(content, cid, headers, directExchange, replyTo, "amqp/list");
-                QPID_LOG(trace, "SENT QueryResponse (query by object_id) to=" << replyTo);
+                connThreadBody.sendBuffer(content, cid, headers, rte, rtk, "amqp/list");
+                QPID_LOG(trace, "SENT QueryResponse (query by object_id) to=" << rte << "/" << rtk);
                 return;
             }
         } else { // match using schema_id, if supplied
@@ -769,8 +771,8 @@
                         if (++objCount >= maxV2ReplyObjs) {
                             objCount = 0;
                             ListCodec::encode(list_, content);
-                            connThreadBody.sendBuffer(content, cid, headers, directExchange, replyTo, "amqp/list");
-                            QPID_LOG(trace, "SENT QueryResponse (query by schema_id) to=" << replyTo);
+                            connThreadBody.sendBuffer(content, cid, headers, rte, rtk, "amqp/list");
+                            QPID_LOG(trace, "SENT QueryResponse (query by schema_id) to=" << rte << "/" << rtk);
                             content.clear();
                             list_.clear();
                         }
@@ -782,8 +784,8 @@
         // Send last "non-partial" message to indicate CommandComplete
         headers.erase("partial");
         ListCodec::encode(list_, content);
-        connThreadBody.sendBuffer(content, cid, headers, directExchange, replyTo, "amqp/list");
-        QPID_LOG(trace, "SENT QueryResponse (empty with no 'partial' indicator) to=" << replyTo);
+        connThreadBody.sendBuffer(content, cid, headers, rte, rtk, "amqp/list");
+        QPID_LOG(trace, "SENT QueryResponse (last message, no 'partial' indicator) to=" << rte << "/" << rtk);
 
     } else if (i->second.asString() == "SCHEMA_ID") {
         headers["qmf.content"] = "_schema_id";
@@ -804,16 +806,16 @@
 
         headers.erase("partial");
         ListCodec::encode(list_, content);
-        connThreadBody.sendBuffer(content, cid, headers, directExchange, replyTo, "amqp/list");
-        QPID_LOG(trace, "SENT QueryResponse (SchemaId) to=" << replyTo);
+        connThreadBody.sendBuffer(content, cid, headers, rte, rtk, "amqp/list");
+        QPID_LOG(trace, "SENT QueryResponse (SchemaId) to=" << rte << "/" << rtk);
 
     } else {
         // Unknown query target
-        sendException(replyTo, cid, "Query for _what => '" + i->second.asString() + "' not supported");
+        sendException(rte, rtk, cid, "Query for _what => '" + i->second.asString() + "' not supported");
     }
 }
 
-void ManagementAgentImpl::handleLocateRequest(const string&, const string& cid, const string& replyTo)
+void ManagementAgentImpl::handleLocateRequest(const string&, const string& cid, const string& rte, const string& rtk)
 {
     QPID_LOG(trace, "RCVD AgentLocateRequest");
 
@@ -827,9 +829,9 @@
 
     getHeartbeatContent(map);
     MapCodec::encode(map, content);
-    connThreadBody.sendBuffer(content, cid, headers, directExchange, replyTo);
+    connThreadBody.sendBuffer(content, cid, headers, rte, rtk);
 
-    QPID_LOG(trace, "SENT AgentLocateResponse replyTo=" << replyTo);
+    QPID_LOG(trace, "SENT AgentLocateResponse replyTo=" << rte << "/" << rtk);
 
     {
         sys::Mutex::ScopedLock lock(agentLock);
@@ -837,12 +839,12 @@
     }
 }
 
-void ManagementAgentImpl::handleMethodRequest(const string& body, const string& cid, const string& replyTo)
+void ManagementAgentImpl::handleMethodRequest(const string& body, const string& cid, const string& rte, const string& rtk, const string& userId)
 {
     if (extThread) {
         sys::Mutex::ScopedLock lock(agentLock);
 
-        methodQueue.push_back(new QueuedMethod(cid, replyTo, body));
+        methodQueue.push_back(new QueuedMethod(cid, rte, rtk, body, userId));
         if (pipeHandle != 0) {
             pipeHandle->write("X", 1);
         } else if (notifyable != 0) {
@@ -861,7 +863,7 @@
             inCallback = false;
         }
     } else {
-        invokeMethodRequest(body, cid, replyTo);
+        invokeMethodRequest(body, cid, rte, rtk, userId);
     }
 
     QPID_LOG(trace, "RCVD MethodRequest");
@@ -869,21 +871,27 @@
 
 void ManagementAgentImpl::received(Message& msg)
 {
+    string   replyToExchange;
     string   replyToKey;
     framing::MessageProperties mp = msg.getMessageProperties();
     if (mp.hasReplyTo()) {
         const framing::ReplyTo& rt = mp.getReplyTo();
+        replyToExchange = rt.getExchange();
         replyToKey = rt.getRoutingKey();
     }
 
+    string userId;
+    if (mp.hasUserId())
+        userId = mp.getUserId();
+
     if (mp.hasAppId() && mp.getAppId() == "qmf2")
     {
         string opcode = mp.getApplicationHeaders().getAsString("qmf.opcode");
         string cid = msg.getMessageProperties().getCorrelationId();
 
-        if      (opcode == "_agent_locate_request") handleLocateRequest(msg.getData(), cid, replyToKey);
-        else if (opcode == "_method_request")       handleMethodRequest(msg.getData(), cid, replyToKey);
-        else if (opcode == "_query_request")        handleGetQuery(msg.getData(), cid, replyToKey);
+        if      (opcode == "_agent_locate_request") handleLocateRequest(msg.getData(), cid, replyToExchange, replyToKey);
+        else if (opcode == "_method_request")       handleMethodRequest(msg.getData(), cid, replyToExchange, replyToKey, userId);
+        else if (opcode == "_query_request")        handleGetQuery(msg.getData(), cid, replyToExchange, replyToKey);
         else {
             QPID_LOG(warning, "Support for QMF V2 Opcode [" << opcode << "] TBD!!!");
         }
@@ -900,7 +908,7 @@
 
     if (checkHeader(inBuffer, &opcode, &sequence))
     {
-        if      (opcode == 'S') handleSchemaRequest(inBuffer, sequence, replyToKey);
+        if      (opcode == 'S') handleSchemaRequest(inBuffer, sequence, replyToExchange, replyToKey);
         else if (opcode == 'x') handleConsoleAddedIndication();
         else
             QPID_LOG(warning, "Ignoring old-format QMF Request! opcode=" << char(opcode));
@@ -1161,10 +1169,10 @@
 void ManagementAgentImpl::getHeartbeatContent(qpid::types::Variant::Map& map)
 {
     map["_values"] = attrMap;
-    map["_values"].asMap()["timestamp"] = uint64_t(Duration(EPOCH, now()));
-    map["_values"].asMap()["heartbeat_interval"] = interval;
-    map["_values"].asMap()["epoch"] = bootSequence;
-    map["_values"].asMap()["schema_timestamp"] = uint64_t(schemaTimestamp);
+    map["_values"].asMap()["_timestamp"] = uint64_t(Duration(EPOCH, now()));
+    map["_values"].asMap()["_heartbeat_interval"] = interval;
+    map["_values"].asMap()["_epoch"] = bootSequence;
+    map["_values"].asMap()["_schema_updated"] = uint64_t(schemaTimestamp);
 }
 
 void ManagementAgentImpl::ConnectionThread::run()
Index: cpp/src/qpid/management/ManagementAgent.h
===================================================================
--- cpp/src/qpid/management/ManagementAgent.h	(revision 1056407)
+++ cpp/src/qpid/management/ManagementAgent.h	(working copy)
@@ -306,7 +306,11 @@
     void sendBufferLH(framing::Buffer&             buf,
                       uint32_t                     length,
                       qpid::broker::Exchange::shared_ptr exchange,
-                      std::string                  routingKey);
+                      const std::string&           routingKey);
+    void sendBufferLH(framing::Buffer&             buf,
+                      uint32_t                     length,
+                      const std::string&           exchange,
+                      const std::string&           routingKey);
     void sendBufferLH(const std::string&     data,
                       const std::string&     cid,
                       const qpid::types::Variant::Map& headers,
@@ -314,6 +318,13 @@
                       qpid::broker::Exchange::shared_ptr exchange,
                       const std::string& routingKey,
                       uint64_t ttl_msec = 0);
+    void sendBufferLH(const std::string& data,
+                      const std::string& cid,
+                      const qpid::types::Variant::Map& headers,
+                      const std::string& content_type,
+                      const std::string& exchange,
+                      const std::string& routingKey,
+                      uint64_t ttl_msec = 0);
     void moveNewObjectsLH();
 
     bool authorizeAgentMessageLH(qpid::broker::Message& msg);
@@ -337,20 +348,20 @@
     void deleteOrphanedAgentsLH();
     void sendCommandCompleteLH(const std::string& replyToKey, uint32_t sequence,
                               uint32_t code = 0, const std::string& text = "OK");
-    void sendExceptionLH(const std::string& replyToKey, const std::string& cid, const std::string& text, uint32_t code=1, bool viaLocal=false);
+    void sendExceptionLH(const std::string& rte, const std::string& rtk, const std::string& cid, const std::string& text, uint32_t code=1, bool viaLocal=false);
     void handleBrokerRequestLH  (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
     void handlePackageQueryLH   (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
     void handlePackageIndLH     (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
     void handleClassQueryLH     (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
     void handleClassIndLH       (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
-    void handleSchemaRequestLH  (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
+    void handleSchemaRequestLH  (framing::Buffer& inBuffer, const std::string& replyToEx ,const std::string& replyToKey, uint32_t sequence);
     void handleSchemaResponseLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
     void handleAttachRequestLH  (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence, const qpid::broker::ConnectionToken* connToken);
     void handleGetQueryLH       (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
     void handleMethodRequestLH  (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence, const qpid::broker::ConnectionToken* connToken);
-    void handleGetQueryLH       (const std::string& body, const std::string& replyToKey, const std::string& cid, bool viaLocal);
-    void handleMethodRequestLH  (const std::string& body, const std::string& replyToKey, const std::string& cid, const qpid::broker::ConnectionToken* connToken, bool viaLocal);
-    void handleLocateRequestLH  (const std::string& body, const std::string &replyToKey, const std::string& cid);
+    void handleGetQueryLH       (const std::string& body, const std::string& replyToEx, const std::string& replyToKey, const std::string& cid, bool viaLocal);
+    void handleMethodRequestLH  (const std::string& body, const std::string& replyToEx, const std::string& replyToKey, const std::string& cid, const qpid::broker::ConnectionToken* connToken, bool viaLocal);
+    void handleLocateRequestLH  (const std::string& body, const std::string& replyToEx, const std::string& replyToKey, const std::string& cid);
 
 
     size_t validateSchema(framing::Buffer&, uint8_t kind);
Index: cpp/src/qpid/management/ManagementAgent.cpp
===================================================================
--- cpp/src/qpid/management/ManagementAgent.cpp	(revision 1056407)
+++ cpp/src/qpid/management/ManagementAgent.cpp	(working copy)
@@ -404,8 +404,10 @@
 
 
         string content;
-        MapCodec::encode(map_, content);
-        sendBufferLH(content, "", headers, "amqp/map", v2Topic, key.str());
+        Variant::List list_;
+        list_.push_back(map_);
+        ListCodec::encode(list_, content);
+        sendBufferLH(content, "", headers, "amqp/list", v2Topic, key.str());
         QPID_LOG(trace, "SEND raiseEvent (v2) class=" << event.getPackageName() << "." << event.getEventName());
     }
 
@@ -504,7 +506,7 @@
 void ManagementAgent::sendBufferLH(Buffer&  buf,
                                    uint32_t length,
                                    qpid::broker::Exchange::shared_ptr exchange,
-                                   string   routingKey)
+                                   const string& routingKey)
 {
     if (suppressed) {
         QPID_LOG(trace, "Suppressed management message to " << routingKey);
@@ -549,6 +551,17 @@
 }
 
 
+void ManagementAgent::sendBufferLH(Buffer&  buf,
+                                   uint32_t length,
+                                   const string& exchange,
+                                   const string& routingKey)
+{
+    qpid::broker::Exchange::shared_ptr ex(broker->getExchanges().get(exchange));
+    if (ex.get() != 0)
+        sendBufferLH(buf, length, ex, routingKey);
+}
+
+
 // NOTE WELL: assumes userLock is held by caller (LH)
 // NOTE EVEN WELLER: drops this lock when delivering the message!!!
 void ManagementAgent::sendBufferLH(const string& data,
@@ -612,6 +625,20 @@
 }
 
 
+void ManagementAgent::sendBufferLH(const string& data,
+                                   const string& cid,
+                                   const Variant::Map& headers,
+                                   const string& content_type,
+                                   const string& exchange,
+                                   const string& routingKey,
+                                   uint64_t ttl_msec)
+{
+    qpid::broker::Exchange::shared_ptr ex(broker->getExchanges().get(exchange));
+    if (ex.get() != 0)
+        sendBufferLH(data, cid, headers, content_type, ex, routingKey, ttl_msec);
+}
+
+
 void ManagementAgent::moveNewObjectsLH()
 {
     sys::Mutex::ScopedLock lock (addLock);
@@ -840,9 +867,9 @@
         headers["qmf.agent"] = name_address;
 
         map["_values"] = attrMap;
-        map["_values"].asMap()["timestamp"] = uint64_t(sys::Duration(sys::EPOCH, sys::now()));
-        map["_values"].asMap()["heartbeat_interval"] = interval;
-        map["_values"].asMap()["epoch"] = bootSequence;
+        map["_values"].asMap()["_timestamp"] = uint64_t(sys::Duration(sys::EPOCH, sys::now()));
+        map["_values"].asMap()["_heartbeat_interval"] = interval;
+        map["_values"].asMap()["_epoch"] = bootSequence;
 
         string content;
         MapCodec::encode(map, content);
@@ -944,7 +971,7 @@
              replyToKey << " seq=" << sequence);
 }
 
-void ManagementAgent::sendExceptionLH(const string& replyToKey, const string& cid,
+void ManagementAgent::sendExceptionLH(const string& rte, const string& rtk, const string& cid,
                                       const string& text, uint32_t code, bool viaLocal)
 {
     static const string addr_exchange("qmf.default.direct");
@@ -963,7 +990,7 @@
     map["_values"] = values;
 
     MapCodec::encode(map, content);
-    sendBufferLH(content, cid, headers, "amqp/map", v2Direct, replyToKey);
+    sendBufferLH(content, cid, headers, "amqp/map", rte, rtk);
 
     QPID_LOG(trace, "SENT Exception code=" << code <<" text=" << text);
 }
@@ -1083,8 +1110,8 @@
         return;
     }
 
+    string userId = ((const qpid::broker::ConnectionState*) connToken)->getUserId();
     if (acl != 0) {
-        string userId = ((const qpid::broker::ConnectionState*) connToken)->getUserId();
         map<acl::Property, string> params;
         params[acl::PROP_SCHEMAPACKAGE] = packageName;
         params[acl::PROP_SCHEMACLASS]   = className;
@@ -1115,7 +1142,7 @@
                 outBuffer.record();
                 sys::Mutex::ScopedUnlock u(userLock);
                 string outBuf;
-                iter->second->doMethod(methodName, inArgs, outBuf);
+                iter->second->doMethod(methodName, inArgs, outBuf, userId);
                 outBuffer.putRawData(outBuf);
             } catch(exception& e) {
                 outBuffer.restore();
@@ -1131,7 +1158,7 @@
 }
 
 
-void ManagementAgent::handleMethodRequestLH (const string& body, const string& replyTo,
+void ManagementAgent::handleMethodRequestLH (const string& body, const string& rte, const string& rtk,
                                              const string& cid, const ConnectionToken* connToken, bool viaLocal)
 {
     string   methodName;
@@ -1151,7 +1178,7 @@
 
     if ((oid = inMap.find("_object_id")) == inMap.end() ||
         (mid = inMap.find("_method_name")) == inMap.end()) {
-        sendExceptionLH(replyTo, cid, Manageable::StatusText(Manageable::STATUS_PARAMETER_INVALID),
+        sendExceptionLH(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_PARAMETER_INVALID),
                         Manageable::STATUS_PARAMETER_INVALID, viaLocal);
         return;
     }
@@ -1170,7 +1197,7 @@
             inArgs = (mid->second).asMap();
         }
     } catch(exception& e) {
-        sendExceptionLH(replyTo, cid, e.what(), Manageable::STATUS_EXCEPTION, viaLocal);
+        sendExceptionLH(rte, rtk, cid, e.what(), Manageable::STATUS_EXCEPTION, viaLocal);
         return;
     }
 
@@ -1179,7 +1206,7 @@
     if (iter == managementObjects.end() || iter->second->isDeleted()) {
         stringstream estr;
         estr << "No object found with ID=" << objId;
-        sendExceptionLH(replyTo, cid, estr.str(), 1, viaLocal);
+        sendExceptionLH(rte, rtk, cid, estr.str(), 1, viaLocal);
         return;
     }
 
@@ -1189,18 +1216,18 @@
 
     i = disallowed.find(make_pair(iter->second->getClassName(), methodName));
     if (i != disallowed.end()) {
-        sendExceptionLH(replyTo, cid, i->second, Manageable::STATUS_FORBIDDEN, viaLocal);
+        sendExceptionLH(rte, rtk, cid, i->second, Manageable::STATUS_FORBIDDEN, viaLocal);
         return;
     }
 
+    string userId = ((const qpid::broker::ConnectionState*) connToken)->getUserId();
     if (acl != 0) {
-        string userId = ((const qpid::broker::ConnectionState*) connToken)->getUserId();
         map<acl::Property, string> params;
         params[acl::PROP_SCHEMAPACKAGE] = iter->second->getPackageName();
         params[acl::PROP_SCHEMACLASS]   = iter->second->getClassName();
 
         if (!acl->authorise(userId, acl::ACT_ACCESS, acl::OBJ_METHOD, methodName, &params)) {
-            sendExceptionLH(replyTo, cid, Manageable::StatusText(Manageable::STATUS_FORBIDDEN),
+            sendExceptionLH(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_FORBIDDEN),
                             Manageable::STATUS_FORBIDDEN, viaLocal);
             return;
         }
@@ -1210,11 +1237,11 @@
 
     QPID_LOG(trace, "RECV MethodRequest (v2) class=" << iter->second->getPackageName()
              << ":" << iter->second->getClassName() << " method=" <<
-             methodName << " replyTo=" << replyTo << " objId=" << objId << " inArgs=" << inArgs);
+             methodName << " replyTo=" << rte << "/" << rtk << " objId=" << objId << " inArgs=" << inArgs);
 
     try {
         sys::Mutex::ScopedUnlock u(userLock);
-        iter->second->doMethod(methodName, inArgs, callMap);
+        iter->second->doMethod(methodName, inArgs, callMap, userId);
         errorCode = callMap["_status_code"].asUint32();
         if (errorCode == 0) {
             outMap["_arguments"] = Variant::Map();
@@ -1225,18 +1252,18 @@
         } else
             error = callMap["_status_text"].asString();
     } catch(exception& e) {
-        sendExceptionLH(replyTo, cid, e.what(), Manageable::STATUS_EXCEPTION, viaLocal);
+        sendExceptionLH(rte, rtk, cid, e.what(), Manageable::STATUS_EXCEPTION, viaLocal);
         return;
     }
 
     if (errorCode != 0) {
-        sendExceptionLH(replyTo, cid, error, errorCode, viaLocal);
+        sendExceptionLH(rte, rtk, cid, error, errorCode, viaLocal);
         return;
     }
 
     MapCodec::encode(outMap, content);
-    sendBufferLH(content, cid, headers, "amqp/map", v2Direct, replyTo);
-    QPID_LOG(trace, "SEND MethodResponse (v2) to=" << replyTo << " seq=" << cid << " map=" << outMap);
+    sendBufferLH(content, cid, headers, "amqp/map", rte, rtk);
+    QPID_LOG(trace, "SEND MethodResponse (v2) to=" << rte << "/" << rtk << " seq=" << cid << " map=" << outMap);
 }
 
 
@@ -1383,7 +1410,7 @@
         buf.putRawData(reinterpret_cast<uint8_t*>(&data[0]), data.size());
 }
 
-void ManagementAgent::handleSchemaRequestLH(Buffer& inBuffer, const string& replyToKey, uint32_t sequence)
+void ManagementAgent::handleSchemaRequestLH(Buffer& inBuffer, const string& rte, const string& rtk, uint32_t sequence)
 {
     string         packageName;
     SchemaClassKey key;
@@ -1392,7 +1419,7 @@
     key.decode(inBuffer);
 
     QPID_LOG(trace, "RECV SchemaRequest class=" << packageName << ":" << key.name << "(" << Uuid(key.hash) <<
-             "), replyTo=" << replyToKey << " seq=" << sequence);
+             "), replyTo=" << rte << "/" << rtk << " seq=" << sequence);
 
     PackageMap::iterator pIter = packages.find(packageName);
     if (pIter != packages.end()) {
@@ -1408,17 +1435,17 @@
                 classInfo.appendSchema(outBuffer);
                 outLen = MA_BUFFER_SIZE - outBuffer.available();
                 outBuffer.reset();
-                sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
-                QPID_LOG(trace, "SEND SchemaResponse to=" << replyToKey << " seq=" << sequence);
+                sendBufferLH(outBuffer, outLen, rte, rtk);
+                QPID_LOG(trace, "SEND SchemaResponse to=" << rte << "/" << rtk << " seq=" << sequence);
             }
             else
-                sendCommandCompleteLH(replyToKey, sequence, 1, "Schema not available");
+                sendCommandCompleteLH(rtk, sequence, 1, "Schema not available");
         }
         else
-            sendCommandCompleteLH(replyToKey, sequence, 1, "Class key not found");
+            sendCommandCompleteLH(rtk, sequence, 1, "Class key not found");
     }
     else
-        sendCommandCompleteLH(replyToKey, sequence, 1, "Package not found");
+        sendCommandCompleteLH(rtk, sequence, 1, "Package not found");
 }
 
 void ManagementAgent::handleSchemaResponseLH(Buffer& inBuffer, const string& /*replyToKey*/, uint32_t sequence)
@@ -1678,7 +1705,7 @@
 }
 
 
-void ManagementAgent::handleGetQueryLH(const string& body, const string& replyTo, const string& cid, bool viaLocal)
+void ManagementAgent::handleGetQueryLH(const string& body, const string& rte, const string& rtk, const string& cid, bool viaLocal)
 {
     moveNewObjectsLH();
 
@@ -1699,17 +1726,17 @@
      */
     i = inMap.find("_what");
     if (i == inMap.end()) {
-        sendExceptionLH(replyTo, cid, "_what element missing in Query");
+        sendExceptionLH(rte, rtk, cid, "_what element missing in Query");
         return;
     }
 
     if (i->second.getType() != qpid::types::VAR_STRING) {
-        sendExceptionLH(replyTo, cid, "_what element is not a string");
+        sendExceptionLH(rte, rtk, cid, "_what element is not a string");
         return;
     }
 
     if (i->second.asString() != "OBJECT") {
-        sendExceptionLH(replyTo, cid, "Query for _what => '" + i->second.asString() + "' not supported");
+        sendExceptionLH(rte, rtk, cid, "Query for _what => '" + i->second.asString() + "' not supported");
         return;
     }
 
@@ -1768,8 +1795,8 @@
             string content;
 
             ListCodec::encode(list_, content);
-            sendBufferLH(content, cid, headers, "amqp/list", v2Direct, replyTo);
-            QPID_LOG(trace, "SENT QueryResponse (query by object_id) to=" << replyTo);
+            sendBufferLH(content, cid, headers, "amqp/list", rte, rtk);
+            QPID_LOG(trace, "SENT QueryResponse (query by object_id) to=" << rte << "/" << rtk);
             return;
         }
     } else {
@@ -1821,27 +1848,26 @@
         string content;
         while (_list.size() > 1) {
             ListCodec::encode(_list.front().asList(), content);
-            sendBufferLH(content, cid, headers, "amqp/list", v2Direct, replyTo);
+            sendBufferLH(content, cid, headers, "amqp/list", rte, rtk);
             _list.pop_front();
-            QPID_LOG(trace, "SENT QueryResponse (partial, query by schema_id) to=" << replyTo << " size=" << content.length());
+            QPID_LOG(trace, "SENT QueryResponse (partial, query by schema_id) to=" << rte << "/" << rtk << " size=" << content.length());
         }
         headers.erase("partial");
         ListCodec::encode(_list.size() ? _list.front().asList() : Variant::List(), content);
-        sendBufferLH(content, cid, headers, "amqp/list", v2Direct, replyTo);
-        QPID_LOG(trace, "SENT QueryResponse (query by schema_id) to=" << replyTo << " size=" << content.length());
+        sendBufferLH(content, cid, headers, "amqp/list", rte, rtk);
+        QPID_LOG(trace, "SENT QueryResponse (query by schema_id) to=" << rte << "/" << rtk << " size=" << content.length());
         return;
     }
 
     // Unrecognized query - Send empty message to indicate CommandComplete
     string content;
     ListCodec::encode(Variant::List(), content);
-    sendBufferLH(content, cid, headers, "amqp/list", v2Direct, replyTo);
-    QPID_LOG(trace, "SENT QueryResponse (empty) to=" << replyTo);
+    sendBufferLH(content, cid, headers, "amqp/list", rte, rtk);
+    QPID_LOG(trace, "SENT QueryResponse (empty) to=" << rte << "/" << rtk);
 }
 
 
-void ManagementAgent::handleLocateRequestLH(const string&, const string& replyTo,
-                                            const string& cid)
+void ManagementAgent::handleLocateRequestLH(const string&, const string& rte, const string& rtk, const string& cid)
 {
     QPID_LOG(trace, "RCVD AgentLocateRequest");
 
@@ -1853,16 +1879,16 @@
     headers["qmf.agent"] = name_address;
 
     map["_values"] = attrMap;
-    map["_values"].asMap()["timestamp"] = uint64_t(sys::Duration(sys::EPOCH, sys::now()));
-    map["_values"].asMap()["heartbeat_interval"] = interval;
-    map["_values"].asMap()["epoch"] = bootSequence;
+    map["_values"].asMap()["_timestamp"] = uint64_t(sys::Duration(sys::EPOCH, sys::now()));
+    map["_values"].asMap()["_heartbeat_interval"] = interval;
+    map["_values"].asMap()["_epoch"] = bootSequence;
 
     string content;
     MapCodec::encode(map, content);
-    sendBufferLH(content, cid, headers, "amqp/map", v2Direct, replyTo);
+    sendBufferLH(content, cid, headers, "amqp/map", rte, rtk);
     clientWasAdded = true;
 
-    QPID_LOG(trace, "SENT AgentLocateResponse replyTo=" << replyTo);
+    QPID_LOG(trace, "SENT AgentLocateResponse replyTo=" << rte << "/" << rtk);
 }
 
 
@@ -1984,13 +2010,14 @@
             msg.getFrames().getHeaders()->get<framing::MessageProperties>();
         if (p && p->hasReplyTo()) {
             const framing::ReplyTo& rt = p->getReplyTo();
-            string replyToKey = rt.getRoutingKey();
+            string rte = rt.getExchange();
+            string rtk = rt.getRoutingKey();
             string cid;
             if (p && p->hasCorrelationId())
                 cid = p->getCorrelationId();
 
             if (mapMsg) {
-                sendExceptionLH(replyToKey, cid, Manageable::StatusText(Manageable::STATUS_FORBIDDEN),
+                sendExceptionLH(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_FORBIDDEN),
                                 Manageable::STATUS_FORBIDDEN, false);
             } else {
 
@@ -2002,7 +2029,7 @@
                 outBuffer.putMediumString(Manageable::StatusText(Manageable::STATUS_FORBIDDEN));
                 outLen = MA_BUFFER_SIZE - outBuffer.available();
                 outBuffer.reset();
-                sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+                sendBufferLH(outBuffer, outLen, rte, rtk);
             }
 
             QPID_LOG(trace, "SEND MethodResponse status=FORBIDDEN" << " seq=" << sequence);
@@ -2016,12 +2043,15 @@
 
 void ManagementAgent::dispatchAgentCommandLH(Message& msg, bool viaLocal)
 {
-    string   replyToKey;
+    string rte;
+    string rtk;
+
     const framing::MessageProperties* p =
         msg.getFrames().getHeaders()->get<framing::MessageProperties>();
     if (p && p->hasReplyTo()) {
         const framing::ReplyTo& rt = p->getReplyTo();
-        replyToKey = rt.getRoutingKey();
+        rte = rt.getExchange();
+        rtk = rt.getRoutingKey();
     }
     else
         return;
@@ -2053,11 +2083,11 @@
         }
 
         if (opcode == "_method_request")
-            return handleMethodRequestLH(body, replyToKey, cid, msg.getPublisher(), viaLocal);
+            return handleMethodRequestLH(body, rte, rtk, cid, msg.getPublisher(), viaLocal);
         else if (opcode == "_query_request")
-            return handleGetQueryLH(body, replyToKey, cid, viaLocal);
+            return handleGetQueryLH(body, rte, rtk, cid, viaLocal);
         else if (opcode == "_agent_locate_request")
-            return handleLocateRequestLH(body, replyToKey, cid);
+            return handleLocateRequestLH(body, rte, rtk, cid);
 
         QPID_LOG(warning, "Support for QMF Opcode [" << opcode << "] TBD!!!");
         return;
@@ -2070,16 +2100,16 @@
         if (!checkHeader(inBuffer, &opcode, &sequence))
             return;
 
-        if      (opcode == 'B') handleBrokerRequestLH  (inBuffer, replyToKey, sequence);
-        else if (opcode == 'P') handlePackageQueryLH   (inBuffer, replyToKey, sequence);
-        else if (opcode == 'p') handlePackageIndLH     (inBuffer, replyToKey, sequence);
-        else if (opcode == 'Q') handleClassQueryLH     (inBuffer, replyToKey, sequence);
-        else if (opcode == 'q') handleClassIndLH       (inBuffer, replyToKey, sequence);
-        else if (opcode == 'S') handleSchemaRequestLH  (inBuffer, replyToKey, sequence);
-        else if (opcode == 's') handleSchemaResponseLH (inBuffer, replyToKey, sequence);
-        else if (opcode == 'A') handleAttachRequestLH  (inBuffer, replyToKey, sequence, msg.getPublisher());
-        else if (opcode == 'G') handleGetQueryLH       (inBuffer, replyToKey, sequence);
-        else if (opcode == 'M') handleMethodRequestLH  (inBuffer, replyToKey, sequence, msg.getPublisher());
+        if      (opcode == 'B') handleBrokerRequestLH  (inBuffer, rtk, sequence);
+        else if (opcode == 'P') handlePackageQueryLH   (inBuffer, rtk, sequence);
+        else if (opcode == 'p') handlePackageIndLH     (inBuffer, rtk, sequence);
+        else if (opcode == 'Q') handleClassQueryLH     (inBuffer, rtk, sequence);
+        else if (opcode == 'q') handleClassIndLH       (inBuffer, rtk, sequence);
+        else if (opcode == 'S') handleSchemaRequestLH  (inBuffer, rte, rtk, sequence);
+        else if (opcode == 's') handleSchemaResponseLH (inBuffer, rtk, sequence);
+        else if (opcode == 'A') handleAttachRequestLH  (inBuffer, rtk, sequence, msg.getPublisher());
+        else if (opcode == 'G') handleGetQueryLH       (inBuffer, rtk, sequence);
+        else if (opcode == 'M') handleMethodRequestLH  (inBuffer, rtk, sequence, msg.getPublisher());
     }
 }
 
Index: cpp/src/qpid/management/Manageable.cpp
===================================================================
--- cpp/src/qpid/management/Manageable.cpp	(revision 1056407)
+++ cpp/src/qpid/management/Manageable.cpp	(working copy)
@@ -46,3 +46,8 @@
     return STATUS_UNKNOWN_METHOD;
 }
 
+bool Manageable::AuthorizeMethod(uint32_t, Args&, const std::string&)
+{
+    return true;
+}
+
Index: cpp/bindings/swig_python_typemaps.i
===================================================================
--- cpp/bindings/swig_python_typemaps.i	(revision 1056407)
+++ cpp/bindings/swig_python_typemaps.i	(working copy)
@@ -32,11 +32,11 @@
     void PyToList(PyObject*, qpid::types::Variant::List*);
 
     qpid::types::Variant PyToVariant(PyObject* value) {
+        if (PyBool_Check(value))   return qpid::types::Variant(bool(PyInt_AS_LONG(value) ? true : false));
         if (PyFloat_Check(value))  return qpid::types::Variant(PyFloat_AS_DOUBLE(value));
-        if (PyString_Check(value)) return qpid::types::Variant(std::string(PyString_AS_STRING(value)));
         if (PyInt_Check(value))    return qpid::types::Variant(int64_t(PyInt_AS_LONG(value)));
         if (PyLong_Check(value))   return qpid::types::Variant(int64_t(PyLong_AsLongLong(value)));
-        if (PyBool_Check(value))   return qpid::types::Variant(bool(PyInt_AS_LONG(value) ? true : false));
+        if (PyString_Check(value)) return qpid::types::Variant(std::string(PyString_AS_STRING(value)));
         if (PyDict_Check(value)) {
             qpid::types::Variant::Map map;
             PyToMap(value, &map);
@@ -271,19 +271,27 @@
     Py_INCREF($result);
 }
 
-%typemap(typecheck) qpid::types::Variant::Map& {
-    $1 = PyDict_Check($input) ? 1 : 0;
-}
-
 /*
  * Variant types: C++ --> Python
  */
+%typemap(out) qpid::types::Variant::Map {
+    $result = MapToPy(&$1);
+    if ($result)
+        Py_INCREF($result);
+}
+
 %typemap(out) qpid::types::Variant::Map& {
     $result = MapToPy($1);
     if ($result)
         Py_INCREF($result);
 }
 
+%typemap(out) qpid::types::Variant::List {
+    $result = ListToPy(&$1);
+    if ($result)
+        Py_INCREF($result);
+}
+
 %typemap(out) qpid::types::Variant::List& {
     $result = ListToPy($1);
     if ($result)
@@ -314,6 +322,16 @@
     PyToList($input, $1);
 }
 
+%typemap(in) const qpid::types::Variant::Map const & {
+    $1 = new qpid::types::Variant::Map();
+    PyToMap($input, $1);
+}
+
+%typemap(in) const qpid::types::Variant::List const & {
+    $1 = new qpid::types::Variant::List();
+    PyToList($input, $1);
+}
+
 %typemap(freearg) qpid::types::Variant& {
     delete $1;
 }
@@ -334,7 +352,7 @@
     $1 = PyDict_Check($input) ? 1 : 0;
 }
 
-%typemap(typecheck) qpid::types::Variant::List& {
+%typemap(typecheck)  qpid::types::Variant::List& {
     $1 = PyList_Check($input) ? 1 : 0;
 }
 
@@ -348,6 +366,24 @@
           PyBool_Check($input)) ? 1 : 0;
 }
 
+%typemap(typecheck) const qpid::types::Variant::Map const & {
+    $1 = PyDict_Check($input) ? 1 : 0;
+}
+
+%typemap(typecheck) const qpid::types::Variant::List const & {
+    $1 = PyList_Check($input) ? 1 : 0;
+}
+
+%typemap(typecheck) const qpid::types::Variant const & {
+    $1 = (PyFloat_Check($input)  ||
+          PyString_Check($input) ||
+          PyInt_Check($input)    ||
+          PyLong_Check($input)   ||
+          PyDict_Check($input)   ||
+          PyList_Check($input)   ||
+          PyBool_Check($input)) ? 1 : 0;
+}
+
 %typemap(typecheck) bool {
     $1 = PyBool_Check($input) ? 1 : 0;
 }
Index: cpp/bindings/swig_ruby_typemaps.i
===================================================================
--- cpp/bindings/swig_ruby_typemaps.i	(revision 1056407)
+++ cpp/bindings/swig_ruby_typemaps.i	(working copy)
@@ -237,10 +237,18 @@
 /*
  * Variant types: C++ --> Ruby
  */
+%typemap(out) qpid::types::Variant::Map {
+    $result = MapToRb(&$1);
+}
+
 %typemap(out) qpid::types::Variant::Map& {
     $result = MapToRb($1);
 }
 
+%typemap(out) qpid::types::Variant::List {
+    $result = ListToRb(&$1);
+}
+
 %typemap(out) qpid::types::Variant::List& {
     $result = ListToRb($1);
 }
@@ -267,6 +275,16 @@
     RbToList($input, $1);
 }
 
+%typemap(in) const qpid::types::Variant::Map const & {
+    $1 = new qpid::types::Variant::Map();
+    RbToMap($input, $1);
+}
+
+%typemap(in) const qpid::types::Variant::List const & {
+    $1 = new qpid::types::Variant::List();
+    RbToList($input, $1);
+}
+
 %typemap(freearg) qpid::types::Variant& {
     delete $1;
 }
@@ -300,6 +318,23 @@
           TYPE($input) == T_FALSE) ? 1 : 0;
 }
 
+%typemap(typecheck) qpid::types::Variant::Map const & {
+    $1 = (TYPE($input) == T_HASH) ? 1 : 0;
+}
+
+%typemap(typecheck) qpid::types::Variant::List const & {
+    $1 = (TYPE($input) == T_ARRAY) ? 1 : 0;
+}
+
+%typemap(typecheck) const qpid::types::Variant const & {
+    $1 = (TYPE($input) == T_FLOAT  ||
+          TYPE($input) == T_STRING ||
+          TYPE($input) == T_FIXNUM ||
+          TYPE($input) == T_BIGNUM ||
+          TYPE($input) == T_TRUE   ||
+          TYPE($input) == T_FALSE) ? 1 : 0;
+}
+
 %typemap(typecheck) bool {
     $1 = (TYPE($input) == T_TRUE ||
           TYPE($input) == T_FALSE) ? 1 : 0;
Index: cpp/bindings/qmf2/python/Makefile.am
===================================================================
--- cpp/bindings/qmf2/python/Makefile.am	(revision 1056407)
+++ cpp/bindings/qmf2/python/Makefile.am	(working copy)
@@ -27,13 +27,16 @@
 
 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 -Wall $(INCLUDES) $(QPID_CXXFLAGS) -I/usr/include -o cqmf2.cpp $(srcdir)/python.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 = $(pythondir)
+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
Index: cpp/bindings/qmf2/python/qmf2.py
===================================================================
--- cpp/bindings/qmf2/python/qmf2.py	(revision 1056407)
+++ cpp/bindings/qmf2/python/qmf2.py	(working copy)
@@ -137,7 +137,91 @@
     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
 #===================================================================================================
@@ -147,6 +231,16 @@
 
   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)
 
@@ -195,6 +289,24 @@
 
   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)
 
@@ -261,7 +373,15 @@
     else:
       self._impl.raiseException(handle, data)
 
-  ## TODO: async and external operations
+  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);
 
 
 #===================================================================================================
@@ -311,7 +431,7 @@
     """
     """
     if q.__class__ == Query:
-      q_arg = Query._impl
+      q_arg = q._impl
     else:
       q_arg = q
     dur = cqpid.Duration(cqpid.Duration.SECOND.getMilliseconds() * timeout)
@@ -366,11 +486,35 @@
   """
   """
 
-  def __init__(self, *kwargs):
+  def __init__(self, arg1, arg2=None, arg3=None, *kwargs):
     """
     """
-    pass
+    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
 #===================================================================================================
@@ -385,10 +529,8 @@
       self._impl = cqmf2.Data()
     elif arg.__class__ == cqmf2.Data:
       self._impl = arg
-    elif arg.__class__ == SchemaId:
+    elif arg.__class__ == Schema:
       self._impl = cqmf2.Data(arg._impl)
-    elif arg.__class__ == Schema:
-      self._impl = cqmf2.Data(arg.getSchemaId()._impl)
     else:
       raise Exception("Unsupported initializer for Data")
     self._schema = None
@@ -412,6 +554,17 @@
     """
     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):
     """
     """
@@ -444,6 +597,7 @@
     ## 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:
@@ -519,8 +673,13 @@
   """
   """
 
-  def __init__(self, impl):
-    self._impl = impl
+  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())
@@ -530,6 +689,11 @@
         self.getName() == other.getName() and \
         self.getAgentEpoch() == other.getAgentEpoch()
 
+  def asMap(self):
+    """
+    """
+    return self._impl.asMap()
+
   def getAgentName(self):
     """
     """
@@ -683,6 +847,11 @@
     """
     return self._impl.getName()
 
+  def getType(self):
+    """
+    """
+    return self._impl.getType()
+
   def getAccess(self):
     """
     """
Index: cpp/bindings/qmf2/ruby/qmf2.rb
===================================================================
--- cpp/bindings/qmf2/ruby/qmf2.rb	(revision 1056407)
+++ cpp/bindings/qmf2/ruby/qmf2.rb	(working copy)
@@ -78,21 +78,292 @@
   end
 
   ##==============================================================================
-  ## AGENT HANDLER TODO
+  ## AGENT HANDLER
   ##==============================================================================
 
   class AgentHandler
-    def get_query(context, query, userId); end
-    def method_call(context, name, object_id, args, userId); end
+
+    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
@@ -101,7 +372,7 @@
     def set_agent_filter(filter)  @impl.setAgentFilter(filter) end
 
     def open()   @impl.open  end
-    def close()  @imp.close  end
+    def close()  @impl.close  end
 
     def agents
       result = []
@@ -124,6 +395,24 @@
   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
@@ -134,10 +423,10 @@
     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()                 @imp.close                     end
+    def close()                 @impl.close                    end
     def register_schema(cls)    @impl.registerSchema(cls.impl) end
 
-    def add_data(data, name="", persistent=:false)
+    def add_data(data, name="", persistent=false)
       DataAddr.new(@impl.addData(data.impl, name, persistent))
     end
 
@@ -145,15 +434,11 @@
       @impl.del_data(addr.impl)
     end
 
-    def method_success(handle)
-      @impl.methodSuccess(handle)
-    end
-
-    def raise_exception(handle, data)
-      if data.class == Data
-        @impl.raiseException(handle, data.impl)
+    def raise_event(data, severity=nil)
+      if !severity
+        @impl.raiseEvent(data.impl)
       else
-        @impl.raiseException(handle, data)
+        @impl.raiseEvent(data.impl, severity)
       end
     end
   end
@@ -228,14 +513,26 @@
   end
 
   ##==============================================================================
-  ## QUERY TODO
+  ## QUERY
   ##==============================================================================
 
   class Query
     attr_reader :impl
-    def initialize
-      @impl = nil
+    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
 
   ##==============================================================================
@@ -246,18 +543,17 @@
     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 == SchemaId
-        @impl = Cqmf2::Data(arg.impl)
       elsif arg.class == Schema
-        @impl = Cqmf2::Data(arg.impl.getSchemaId)
+        @impl = Cqmf2::Data.new(arg.impl)
+        @schema = arg
       else
         raise "Unsupported initializer for Data"
       end
-      @schema = nil
     end
 
     def to_s
@@ -271,6 +567,10 @@
       return nil
     end
 
+    def set_addr(addr)
+      @impl.setAddr(addr.impl)
+    end
+
     def addr
       if @impl.hasAddr
         return DataAddr.new(@impl.getAddr)
@@ -282,6 +582,17 @@
       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
@@ -392,14 +703,21 @@
   class DataAddr
     attr_reader :impl
 
-    def initialize(impl)
-      @impl = 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
Index: cpp/bindings/qmf2/ruby/Makefile.am
===================================================================
--- cpp/bindings/qmf2/ruby/Makefile.am	(revision 1056407)
+++ cpp/bindings/qmf2/ruby/Makefile.am	(working copy)
@@ -23,14 +23,16 @@
 
 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++ -Wall $(INCLUDES) $(QPID_CXXFLAGS) -I/usr/include -o cqmf2.cpp $(srcdir)/ruby.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 -shrext ".$(RUBY_DLEXT)"
 cqmf2_la_LIBADD = $(RUBY_LIBS) -L$(top_builddir)/src/.libs -lqmf2 $(top_builddir)/src/libqmf2.la
Index: cpp/bindings/qmf2/qmf2.i
===================================================================
--- cpp/bindings/qmf2/qmf2.i	(revision 1056407)
+++ cpp/bindings/qmf2/qmf2.i	(working copy)
@@ -33,6 +33,7 @@
 #include <qmf/SchemaMethod.h>
 #include <qmf/SchemaProperty.h>
 #include <qmf/SchemaTypes.h>
+#include <qmf/Subscription.h>
 
 %}
 
@@ -54,6 +55,7 @@
 %include <qmf/SchemaMethod.h>
 %include <qmf/SchemaProperty.h>
 %include <qmf/SchemaTypes.h>
+%include <qmf/Subscription.h>
 
 %{
 
Index: cpp/bindings/qmf2/examples/python/agent.py
===================================================================
--- cpp/bindings/qmf2/examples/python/agent.py	(revision 1056407)
+++ cpp/bindings/qmf2/examples/python/agent.py	(working copy)
@@ -34,7 +34,8 @@
     ##
     ## Create and open a messaging connection to a broker.
     ##
-    self.connection = cqpid.Connection(url)
+    self.connection = cqpid.Connection(url, "{reconnect:True}")
+    self.session = None
     self.connection.open()
 
     ##
@@ -56,7 +57,8 @@
     """
     Clean up the session and connection.
     """
-    self.session.close()
+    if self.session:
+      self.session.close()
     self.connection.close()
 
 
@@ -67,26 +69,43 @@
     if addr == self.controlAddr:
       self.control.methodCount += 1
 
-      if methodName == "stop":
-        self.session.methodSuccess(handle)
-        self.cancel()
+      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 == "echo":
+          handle.addReturnArgument("sequence", args["sequence"])
+          handle.addReturnArgument("map", args["map"])
+          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 == "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.
@@ -118,17 +137,41 @@
     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):
@@ -141,10 +184,13 @@
     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
 
-agent = ExampleAgent("localhost")
-agent.setupSchema()
-agent.populateData()
-agent.run()  # Use agent.start() to launch the agent in a separate thread
-agent.shutdown()
 
Index: cpp/bindings/qmf2/examples/ruby/agent_external.rb
===================================================================
--- cpp/bindings/qmf2/examples/ruby/agent_external.rb	(revision 0)
+++ cpp/bindings/qmf2/examples/ruby/agent_external.rb	(revision 0)
@@ -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
+
+
Index: cpp/bindings/qmf2/examples/ruby/find_agents.rb
===================================================================
--- cpp/bindings/qmf2/examples/ruby/find_agents.rb	(revision 0)
+++ cpp/bindings/qmf2/examples/ruby/find_agents.rb	(revision 0)
@@ -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
+
Index: cpp/bindings/qmf2/examples/ruby/agent_internal.rb
===================================================================
--- cpp/bindings/qmf2/examples/ruby/agent_internal.rb	(revision 0)
+++ cpp/bindings/qmf2/examples/ruby/agent_internal.rb	(revision 0)
@@ -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
+
+
Index: cpp/bindings/qmf2/examples/cpp/agent.cpp
===================================================================
--- cpp/bindings/qmf2/examples/cpp/agent.cpp	(revision 1056407)
+++ cpp/bindings/qmf2/examples/cpp/agent.cpp	(working copy)
@@ -48,6 +48,8 @@
     AgentSession session;
     Schema sch_exception;
     Schema sch_control;
+    Schema sch_child;
+    Schema sch_event;
     Data control;
     DataAddr controlAddr;
 
@@ -60,7 +62,7 @@
     //
     // Create and open a messaging connection to a broker.
     //
-    connection = qpid::messaging::Connection(url);
+    connection = qpid::messaging::Connection(url, "{reconnect:True}");
     connection.open();
 
     //
@@ -114,17 +116,41 @@
     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()
@@ -132,7 +158,7 @@
     //
     // Create a control object and give it to the agent session to manage.
     //
-    control = Data(sch_control.getSchemaId());
+    control = Data(sch_control);
     control.setProperty("state", "OPERATIONAL");
     control.setProperty("methodCount", 0);
     controlAddr = session.addData(control, "singleton");
@@ -160,31 +186,55 @@
     const string& name(event.getMethodName());
     control.setProperty("methodCount", control.getProperty("methodCount").asUint32() + 1);
 
-    if (controlAddr == event.getDataAddr()) {
-        if (name == "stop") {
-            cout << "Stopping: message=" << event.getArguments()["message"] << endl;
-            session.methodSuccess(event);
-            return false;
-        }
+    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 == "echo") {
+                event.addReturnArgument("sequence", event.getArguments()["sequence"]);
+                event.addReturnArgument("map", event.getArguments()["map"]);
+                session.methodSuccess(event);
+                return true;
+            }
 
-        if (name == "fail") {
-            if (event.getArguments()["useString"])
-                session.raiseException(event, event.getArguments()["stringVal"]);
-            else {
-                Data ex(sch_exception.getSchemaId());
-                ex.setProperty("whatHappened", "It Failed");
-                ex.setProperty("howBad", 75);
-                ex.setProperty("details", event.getArguments()["details"]);
-                session.raiseException(event, ex);
+            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;
Index: cpp/bindings/qmf2/examples/cpp/print_events.cpp
===================================================================
--- cpp/bindings/qmf2/examples/cpp/print_events.cpp	(revision 0)
+++ cpp/bindings/qmf2/examples/cpp/print_events.cpp	(revision 0)
@@ -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;
+            }
+        }
+    }
+}
+
Index: cpp/bindings/qmf2/examples/cpp/Makefile.am
===================================================================
--- cpp/bindings/qmf2/examples/cpp/Makefile.am	(revision 1056407)
+++ cpp/bindings/qmf2/examples/cpp/Makefile.am	(working copy)
@@ -21,6 +21,13 @@
 
 AM_CPPFLAGS = $(INCLUDE)
 
-noinst_PROGRAMS=agent
+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
Index: cpp/bindings/qmf2/examples/cpp/list_agents.cpp
===================================================================
--- cpp/bindings/qmf2/examples/cpp/list_agents.cpp	(revision 0)
+++ cpp/bindings/qmf2/examples/cpp/list_agents.cpp	(revision 0)
@@ -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;
+            }
+        }
+    }
+}
+
Index: cpp/bootstrap
===================================================================
--- cpp/bootstrap	(revision 1056407)
+++ cpp/bootstrap	(working copy)
@@ -36,7 +36,7 @@
 	\$(mgen_cmd)
 EOF
 
-automake
+automake --add-missing
 autoconf
 
 # Optionally do the build as well.
Index: cpp/include/qmf/Agent.h
===================================================================
--- cpp/include/qmf/Agent.h	(revision 1056407)
+++ cpp/include/qmf/Agent.h	(working copy)
@@ -23,6 +23,7 @@
 
 #include <qmf/ImportExport.h>
 #include "qmf/Handle.h"
+//#include "qmf/Subscription.h"
 #include "qmf/exceptions.h"
 #include "qpid/messaging/Duration.h"
 #include "qpid/types/Variant.h"
@@ -61,6 +62,12 @@
         QMF_EXTERN uint32_t queryAsync(const Query&);
         QMF_EXTERN uint32_t queryAsync(const std::string&);
 
+        /**
+         * Create a subscription to this agent
+         */
+        //QMF_EXTERN Subscription subscribe(const Query&, const std::string& options = "");
+        //QMF_EXTERN Subscription subscribe(const std::string&, const std::string& options = "");
+
         QMF_EXTERN ConsoleEvent callMethod(const std::string&, const qpid::types::Variant::Map&, const DataAddr&,
                                            qpid::messaging::Duration timeout=qpid::messaging::Duration::MINUTE);
         QMF_EXTERN uint32_t callMethodAsync(const std::string&, const qpid::types::Variant::Map&, const DataAddr&);
Index: cpp/include/qmf/AgentSession.h
===================================================================
--- cpp/include/qmf/AgentSession.h	(revision 1056407)
+++ cpp/include/qmf/AgentSession.h	(working copy)
@@ -57,11 +57,20 @@
          * 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 are pass-through) [default: False]
+         *    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]
          */
         QMF_EXTERN AgentSession(qpid::messaging::Connection&, const std::string& options="");
 
@@ -143,7 +152,7 @@
          * authReject - Reject/forbid an authorization request.
          * raiseException - indicate failure of an operation (i.e. query or method call).
          * response - Provide data in response to a query (only for option:  external:True)
-         * complete - Indicate that the response to a query is complete (external:true only)
+         * complete - Indicate that the response to a query is complete (external:True only)
          * methodSuccess - Indicate the successful completion of a method call.
          */
         QMF_EXTERN void authAccept(AgentEvent&);
@@ -156,8 +165,14 @@
 
         /**
          * Raise an event to be sent into the QMF network.
+         *
+         * @param data - A data object that contains the event contents.
+         * @param severity - Explicit severity (from qmf/SchemaTypes.h).  If omitted, the severity is set to
+         *        the default severity for the data's schema.  If the data has no schema, the severity defaults
+         *        to SEV_NOTICE.
          */
-        QMF_EXTERN void raiseEvent(const Data&);
+        QMF_EXTERN void raiseEvent(const Data& data);
+        QMF_EXTERN void raiseEvent(const Data& data, int severity);
 
 #ifndef SWIG
     private:
Index: cpp/include/qmf/DataAddr.h
===================================================================
--- cpp/include/qmf/DataAddr.h	(revision 1056407)
+++ cpp/include/qmf/DataAddr.h	(working copy)
@@ -44,6 +44,7 @@
         QMF_EXTERN bool operator==(const DataAddr&);
         QMF_EXTERN bool operator<(const DataAddr&);
 
+        QMF_EXTERN DataAddr(const qpid::types::Variant::Map&);
         QMF_EXTERN DataAddr(const std::string& name, const std::string& agentName, uint32_t agentEpoch=0);
         QMF_EXTERN const std::string& getName() const;
         QMF_EXTERN const std::string& getAgentName() const;
Index: cpp/include/qmf/ConsoleEvent.h
===================================================================
--- cpp/include/qmf/ConsoleEvent.h	(revision 1056407)
+++ cpp/include/qmf/ConsoleEvent.h	(working copy)
@@ -46,8 +46,10 @@
     CONSOLE_QUERY_RESPONSE        = 7,
     CONSOLE_METHOD_RESPONSE       = 8,
     CONSOLE_EXCEPTION             = 9,
-    CONSOLE_SUBSCRIBE_UPDATE      = 10,
-    CONSOLE_THREAD_FAILED         = 11
+    CONSOLE_SUBSCRIBE_ADD         = 10,
+    CONSOLE_SUBSCRIBE_UPDATE      = 11,
+    CONSOLE_SUBSCRIBE_DEL         = 12,
+    CONSOLE_THREAD_FAILED         = 13
     };
 
     enum AgentDelReason {
@@ -72,6 +74,8 @@
         QMF_EXTERN Data getData(uint32_t) const;
         QMF_EXTERN bool isFinal() const;
         QMF_EXTERN const qpid::types::Variant::Map& getArguments() const;
+        QMF_EXTERN int getSeverity() const;
+        QMF_EXTERN uint64_t getTimestamp() const;
 
 #ifndef SWIG
     private:
Index: cpp/include/qmf/ConsoleSession.h
===================================================================
--- cpp/include/qmf/ConsoleSession.h	(revision 1056407)
+++ cpp/include/qmf/ConsoleSession.h	(working copy)
@@ -24,6 +24,7 @@
 #include <qmf/ImportExport.h>
 #include "qmf/Handle.h"
 #include "qmf/Agent.h"
+#include "qmf/Subscription.h"
 #include "qpid/messaging/Duration.h"
 #include "qpid/messaging/Connection.h"
 #include <string>
@@ -56,6 +57,10 @@
          *    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]
          */
         QMF_EXTERN ConsoleSession(qpid::messaging::Connection&, const std::string& options="");
         QMF_EXTERN void setDomain(const std::string&);
@@ -67,6 +72,16 @@
         QMF_EXTERN Agent getAgent(uint32_t) const;
         QMF_EXTERN Agent getConnectedBrokerAgent() const;
 
+        /**
+         * Create a subscription that involves a subset of the known agents.  The set of known agents is defined by
+         * the session's agent-filter (see setAgentFilter).  The agentFilter argument to the subscribe method is used
+         * to further refine the set of agents.  If agentFilter is the empty string (i.e. match-all) the subscription
+         * will involve all known agents.  If agentFilter is non-empty, it will be applied only to the set of known
+         * agents.  A subscription cannot be created that involves an agent not known by the session.
+         */
+        QMF_EXTERN Subscription subscribe(const Query&,       const std::string& agentFilter = "", const std::string& options = "");
+        QMF_EXTERN Subscription subscribe(const std::string&, const std::string& agentFilter = "", const std::string& options = "");
+
 #ifndef SWIG
     private:
         friend class qmf::PrivateImplRef<ConsoleSession>;
Index: cpp/include/qmf/Data.h
===================================================================
--- cpp/include/qmf/Data.h	(revision 1056407)
+++ cpp/include/qmf/Data.h	(working copy)
@@ -34,6 +34,7 @@
 #endif
 
     class DataImpl;
+    class Schema;
     class SchemaId;
     class DataAddr;
     class Agent;
@@ -45,8 +46,7 @@
         QMF_EXTERN Data& operator=(const Data&);
         QMF_EXTERN ~Data();
 
-        QMF_EXTERN Data(const SchemaId&);
-        QMF_EXTERN void setSchema(const SchemaId&);
+        QMF_EXTERN Data(const Schema&);
         QMF_EXTERN void setAddr(const DataAddr&);
         QMF_EXTERN void setProperty(const std::string&, const qpid::types::Variant&);
         QMF_EXTERN void overwriteProperties(const qpid::types::Variant::Map&);
Index: cpp/include/qmf/Subscription.h
===================================================================
--- cpp/include/qmf/Subscription.h	(revision 0)
+++ cpp/include/qmf/Subscription.h	(revision 0)
@@ -0,0 +1,82 @@
+#ifndef QMF_SUBSCRIPTION_H
+#define QMF_SUBSCRIPTION_H
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qmf/ImportExport.h>
+#include "qmf/Handle.h"
+#include "qpid/types/Variant.h"
+#include <string>
+
+namespace qmf {
+
+#ifndef SWIG
+    template <class> class PrivateImplRef;
+#endif
+
+    class SubscriptionImpl;
+    class Data;
+
+    class Subscription : public qmf::Handle<SubscriptionImpl> {
+    public:
+        QMF_EXTERN Subscription(SubscriptionImpl* impl = 0);
+        QMF_EXTERN Subscription(const Subscription&);
+        QMF_EXTERN Subscription& operator=(const Subscription&);
+        QMF_EXTERN ~Subscription();
+
+        /**
+         * Construction:  A subscription is created by calling ConsoleSession::subscribe.
+         */
+
+        /**
+         * Cancel subscriptions to all subscribed agents.  After this is called, the subscription
+         * shall be inactive.
+         */
+        QMF_EXTERN void cancel();
+
+        /**
+         * Check to see if this subscription is active.  It is active if it has a live subscription
+         * on at least one agent.  If it is not active, there is nothing that can be done to make it
+         * active, it can only be deleted.
+         */
+        QMF_EXTERN bool isActive() const;
+
+        /**
+         * lock and unlock should be used to bracket a traversal of the data set.  After lock is called,
+         * the subscription will not change its set of available data objects.  Between calls to getDataCount
+         * and getData, no data objects will be added or removed.  After unlock is called, the set of data
+         * will catch up to any activity that occurred while the lock was in effect.
+         */
+        QMF_EXTERN void lock();
+        QMF_EXTERN void unlock();
+        QMF_EXTERN uint32_t getDataCount() const;
+        QMF_EXTERN Data getData(uint32_t) const;
+
+#ifndef SWIG
+    private:
+        friend class qmf::PrivateImplRef<Subscription>;
+        friend class SubscriptionImplAccess;
+#endif
+    };
+
+}
+
+#endif
Index: cpp/include/qpid/management/Manageable.h
===================================================================
--- cpp/include/qpid/management/Manageable.h	(revision 1056407)
+++ cpp/include/qpid/management/Manageable.h	(working copy)
@@ -63,6 +63,11 @@
     //  method being called and must be down-cast to the appropriate sub class
     //  before use.
     virtual status_t ManagementMethod(uint32_t methodId, Args& args, std::string& text);
+
+    //  This optional method can be overridden to allow the agent application to
+    //  authorize method invocations.  Return true iff the authenticated user identified
+    //  in userId us authorized to execute the method.
+    virtual bool AuthorizeMethod(uint32_t methodId, Args& args, const std::string& userId);
 };
 
 inline Manageable::~Manageable(void) {}
Index: cpp/include/qpid/management/ManagementObject.h
===================================================================
--- cpp/include/qpid/management/ManagementObject.h	(revision 1056407)
+++ cpp/include/qpid/management/ManagementObject.h	(working copy)
@@ -175,7 +175,8 @@
     virtual void mapDecodeValues(const types::Variant::Map& map) = 0;
     virtual void doMethod(std::string&           methodName,
                           const types::Variant::Map& inMap,
-                          types::Variant::Map& outMap) = 0;
+                          types::Variant::Map& outMap,
+                          const std::string& userId) = 0;
     QPID_COMMON_EXTERN void writeTimestamps(types::Variant::Map& map) const;
     QPID_COMMON_EXTERN void readTimestamps(const types::Variant::Map& buf);
 
@@ -187,7 +188,7 @@
     virtual void readProperties(const std::string&) {}
     virtual void writeProperties(std::string&) const {}
     virtual void writeStatistics(std::string&, bool = false) {}
-    virtual void doMethod(std::string&, const std::string&, std::string&) {}
+    virtual void doMethod(std::string&, const std::string&, std::string&, const std::string&) {}
 
     QPID_COMMON_EXTERN virtual void setReference(ObjectId objectId);
 
Index: cpp/managementgen/Makefile.am
===================================================================
--- cpp/managementgen/Makefile.am	(revision 1056407)
+++ cpp/managementgen/Makefile.am	(working copy)
@@ -31,6 +31,8 @@
 	qmfgen/templates/Makefile.mk \
 	qmfgen/templates/Package.cpp \
 	qmfgen/templates/Package.h \
+	qmfgen/templates/V2Package.cpp \
+	qmfgen/templates/V2Package.h \
 	qmfgen/management-types.xml
 
 EXTRA_DIST = $(nobase_qmfpython_DATA) CMakeLists.txt
Index: cpp/managementgen/qmf-gen
===================================================================
--- cpp/managementgen/qmf-gen	(revision 1056407)
+++ cpp/managementgen/qmf-gen	(working copy)
@@ -47,12 +47,15 @@
                   help="Generate makefile for Qpid broker")
 parser.add_option("-b", "--broker-plugin", dest="brokerplugin", default=False, action="store_true",
                   help="Generate code for use in a qpid broker plugin")
+parser.add_option("-2", "--v2-style", dest="v2_style", default=False, action="store_true",
+                  help="Generate code for use with the QMFv2 Agent API")
 
 (opts, args) = parser.parse_args()
 
 typefile    = opts.typefile
 templatedir = opts.templatedir
 outdir      = opts.outputdir
+v2_style    = opts.v2_style
 gen         = Generator(outdir, templatedir)
 
 if len(args) == 0:
@@ -70,15 +73,20 @@
 for schemafile in args:
   package = SchemaPackage(typefile, schemafile, opts)
 
-  gen.setPackage      (package.packageName)
-  gen.makeClassFiles  ("Class.h",     package, vars=vargs)
-  gen.makeClassFiles  ("Class.cpp",   package, vars=vargs)
-  gen.makeMethodFiles ("Args.h",      package, vars=vargs)
-  gen.makeEventFiles  ("Event.h",     package, vars=vargs)
-  gen.makeEventFiles  ("Event.cpp",   package, vars=vargs)
-  gen.makePackageFile ("Package.h",   package, vars=vargs)
-  gen.makePackageFile ("Package.cpp", package, vars=vargs)
+  gen.setPackage(package.packageName)
 
+  if v2_style:
+    gen.makeV2PackageFile("V2Package.h",   package, vars=vargs)
+    gen.makeV2PackageFile("V2Package.cpp", package, vars=vargs)
+  else:
+    gen.makeClassFiles  ("Class.h",     package, vars=vargs)
+    gen.makeClassFiles  ("Class.cpp",   package, vars=vargs)
+    gen.makeMethodFiles ("Args.h",      package, vars=vargs)
+    gen.makeEventFiles  ("Event.h",     package, vars=vargs)
+    gen.makeEventFiles  ("Event.cpp",   package, vars=vargs)
+    gen.makePackageFile ("Package.h",   package, vars=vargs)
+    gen.makePackageFile ("Package.cpp", package, vars=vargs)
+
 if opts.makefile != None:
   args = {}
   args["qpidbroker"] = opts.qpidbroker
Index: cpp/managementgen/qmfgen/generate.py
===================================================================
--- cpp/managementgen/qmfgen/generate.py	(revision 1056407)
+++ cpp/managementgen/qmfgen/generate.py	(working copy)
@@ -346,6 +346,14 @@
     path = self.packagePath + "Package" + extension
     return path
 
+  def targetV2PackageFile (self, schema, templateFile):
+    dot = templateFile.find(".")
+    if dot == -1:
+      raise ValueError ("Invalid template file name %s" % templateFile)
+    extension = templateFile[dot:len (templateFile)]
+    path = self.packagePath + "QmfPackage" + extension
+    return path
+
   def targetClassFile (self, _class, templateFile):
     dot = templateFile.find(".")
     if dot == -1:
@@ -448,6 +456,17 @@
     stream = template.expand (schema)
     self.writeIfChanged (stream, target, force)
 
+  def makeV2PackageFile (self, templateFile, schema, force=False, vars=None):
+    """ Generate a QMFv2 package definition file """
+    template = Template (self.input + templateFile, self)
+    if vars:
+      for arg in vars:
+        self.setVariable(arg, vars[arg])
+    self.templateFiles.append (templateFile)
+    target = self.targetV2PackageFile (schema, templateFile)
+    stream = template.expand (schema)
+    self.writeIfChanged (stream, target, force)
+
   def makeSingleFile (self, templateFile, target, force=False, vars=None):
     """ Generate a single expanded template """
     dot = templateFile.find(".")
Index: cpp/managementgen/qmfgen/schema.py
===================================================================
--- cpp/managementgen/qmfgen/schema.py	(revision 1056407)
+++ cpp/managementgen/qmfgen/schema.py	(working copy)
@@ -1228,12 +1228,12 @@
           inArgCount = inArgCount + 1
 
     if methodCount == 0:
-      stream.write ("string&, const string&, string& outStr")
+      stream.write ("string&, const string&, string& outStr, const string&")
     else:
       if inArgCount == 0:
-        stream.write ("string& methodName, const string&, string& outStr")
+        stream.write ("string& methodName, const string&, string& outStr, const string& userId")
       else:
-        stream.write ("string& methodName, const string& inStr, string& outStr")
+        stream.write ("string& methodName, const string& inStr, string& outStr, const string& userId")
 
 
   def genDoMapMethodArgs (self, stream, variables):
@@ -1248,16 +1248,16 @@
     if methodCount == 0:
       stream.write ("string&," +
                     " const ::qpid::types::Variant::Map&," +
-                    " ::qpid::types::Variant::Map& outMap")
+                    " ::qpid::types::Variant::Map& outMap, const string&")
     else:
       if inArgCount == 0:
         stream.write ("string& methodName," +
                       " const ::qpid::types::Variant::Map&," +
-                      " ::qpid::types::Variant::Map& outMap")
+                      " ::qpid::types::Variant::Map& outMap, const string& userId")
       else:
         stream.write ("string& methodName," +
                       " const ::qpid::types::Variant::Map& inMap," +
-                      " ::qpid::types::Variant::Map& outMap")
+                      " ::qpid::types::Variant::Map& outMap, const string& userId")
 
   def genHiLoStatResets (self, stream, variables):
     for inst in self.statistics:
@@ -1367,8 +1367,13 @@
                                                    arg.dir.lower () + "_" +\
                                                    arg.name, "inBuf") + ";\n")
 
-      stream.write ("        status = coreObject->ManagementMethod (METHOD_" +\
+      stream.write ("        bool allow = coreObject->AuthorizeMethod(METHOD_" +\
+                    method.getName().upper() + ", ioArgs, userId);\n")
+      stream.write ("        if (allow)\n")
+      stream.write ("            status = coreObject->ManagementMethod (METHOD_" +\
                     method.getName().upper() + ", ioArgs, text);\n")
+      stream.write ("        else\n")
+      stream.write ("            status = Manageable::STATUS_FORBIDDEN;\n")
       stream.write ("        outBuf.putLong        (status);\n")
       stream.write ("        outBuf.putMediumString(::qpid::management::Manageable::StatusText (status, text));\n")
       for arg in method.args:
@@ -1402,8 +1407,13 @@
                                  arg.name,
                                  "inMap")
 
-      stream.write ("        status = coreObject->ManagementMethod (METHOD_" +\
+      stream.write ("        bool allow = coreObject->AuthorizeMethod(METHOD_" +\
+                    method.getName().upper() + ", ioArgs, userId);\n")
+      stream.write ("        if (allow)\n")
+      stream.write ("            status = coreObject->ManagementMethod (METHOD_" +\
                     method.getName().upper() + ", ioArgs, text);\n")
+      stream.write ("        else\n")
+      stream.write ("            status = Manageable::STATUS_FORBIDDEN;\n")
       stream.write ("        outMap[\"_status_code\"] = (uint32_t) status;\n")
       stream.write ("        outMap[\"_status_text\"] = ::qpid::management::Manageable::StatusText(status, text);\n")
       for arg in method.args:
@@ -1473,6 +1483,9 @@
     for method in self.methods:
       method.genSchemaMap(stream, variables)
 
+  def genName (self, stream, variables):
+    stream.write (self.name)
+
   def genNameCap (self, stream, variables):
     stream.write (capitalize(self.name))
 
@@ -1637,6 +1650,9 @@
     up = "_".join(self.packageName.split("."))
     stream.write (up.upper())
 
+  def genPackageName (self, stream, variables):
+    stream.write(self.packageName)
+
   def genNamePackageLower (self, stream, variables):
     stream.write (self.packageName.lower ())
 
@@ -1660,7 +1676,140 @@
       _event.genNameCap(stream, variables)
       stream.write("::registerSelf(agent);\n")
 
+  def genV2ClassMembers(self, stream, variables):
+      for _class in self.classes:
+          stream.write("    ::qmf::Schema data_%s;\n" % _class.name)
+      for _event in self.events:
+          stream.write("    ::qmf::Schema event_%s;\n" % _event.name)
 
+  def genV2ClassDefines(self, stream, variables):
+      for _class in self.classes:
+          stream.write("\n    //\n    // Data: %s\n    //\n" % _class.name)
+          stream.write("    data_%s = qmf::Schema(SCHEMA_TYPE_DATA, package, \"%s\");\n" % (_class.name, _class.name))
+
+          for prop in _class.properties:
+              typeName, subType = self.qmfv2Type(prop.type)
+              access = self.qmfv2Access(prop.access)
+              stream.write("    {\n")
+              stream.write("        qmf::SchemaProperty prop(\"%s\", %s);\n" % (prop.name, typeName))
+              if subType:
+                  stream.write("        prop.setSubtype(\"%s\");\n" % subType)
+              stream.write("        prop.setAccess(%s);\n" % access)
+              if prop.isIndex == 1:
+                  stream.write("        prop.setIndex(true);\n")
+              if prop.isOptional == 1:
+                  stream.write("        prop.setOptional(true);\n")
+              if prop.unit:
+                  stream.write("        prop.setUnit(\"%s\");\n" % prop.unit)
+              if prop.desc:
+                  stream.write("        prop.setDesc(\"%s\");\n" % prop.desc)
+              stream.write("        data_%s.addProperty(prop);\n" % _class.name)
+              stream.write("    }\n\n")
+
+          for stat in _class.statistics:
+              typeName, subType = self.qmfv2Type(stat.type)
+              stream.write("    {\n")
+              stream.write("        qmf::SchemaProperty prop(\"%s\", %s);\n" % (stat.name, typeName))
+              if subType:
+                  stream.write("        prop.setSubtype(\"%s\");\n" % subType)
+              if stat.unit:
+                  stream.write("        prop.setUnit(\"%s\");\n" % stat.unit)
+              if stat.desc:
+                  stream.write("        prop.setDesc(\"%s\");\n" % stat.desc)
+              stream.write("        data_%s.addProperty(prop);\n" % _class.name)
+              stream.write("    }\n\n")
+
+          for method in _class.methods:
+              stream.write("    {\n")
+              stream.write("        qmf::SchemaMethod method(\"%s\");\n" % method.name)
+              if method.desc:
+                  stream.write("        method.setDesc(\"%s\");\n" % method.desc)
+                  
+              for arg in method.args:
+                  typeName, subType = self.qmfv2Type(arg.type)
+                  stream.write("        {\n")
+                  stream.write("            qmf::SchemaProperty arg(\"%s\", %s);\n" % (arg.name, typeName))
+                  if subType:
+                      stream.write("            arg.setSubtype(\"%s\");\n" % subType)
+                  if stat.unit:
+                      stream.write("            arg.setUnit(\"%s\");\n" % arg.unit)
+                  if stat.desc:
+                      stream.write("            arg.setDesc(\"%s\");\n" % arg.desc)
+                  stream.write("            arg.setDirection(%s);\n" % self.qmfv2Dir(arg.dir))
+                  stream.write("            method.addArgument(arg);\n")
+                  stream.write("        }\n\n")
+                  
+              stream.write("        data_%s.addMethod(method);\n" % _class.name)
+              stream.write("    }\n\n")
+
+          stream.write("    session.registerSchema(data_%s);\n" % _class.name)
+
+      for _event in self.events:
+          stream.write("\n    //\n    // Event: %s\n    //\n" % _event.name)
+          stream.write("    event_%s = qmf::Schema(SCHEMA_TYPE_EVENT, package, \"%s\");\n" % (_event.name, _event.name))
+          stream.write("    event_%s.setDefaultSeverity(%s);\n" % (_event.name, self.qmfv2Severity(_event.sev)))
+          for prop in _event.args:
+              typeName, subType = self.qmfv2Type(prop.type)
+              stream.write("    {\n")
+              stream.write("        qmf::SchemaProperty prop(\"%s\", %s);\n" % (prop.name, typeName))
+              if subType:
+                  stream.write("        prop.setSubtype(\"%s\");\n" % subType)
+              if prop.unit:
+                  stream.write("        prop.setUnit(\"%s\");\n" % prop.unit)
+              if prop.desc:
+                  stream.write("        prop.setDesc(\"%s\");\n" % prop.desc)
+              stream.write("        event_%s.addProperty(prop);\n" % _event.name)
+              stream.write("    }\n\n")
+
+          stream.write("    session.registerSchema(event_%s);\n" % _event.name)
+              
+
+  def qmfv2Type(self, typecode):
+      base = typecode.type.base
+      if base == "REF"       : return ("qmf::SCHEMA_DATA_MAP", "reference")
+      if base == "U8"        : return ("qmf::SCHEMA_DATA_INT", None)
+      if base == "U16"       : return ("qmf::SCHEMA_DATA_INT", None)
+      if base == "U32"       : return ("qmf::SCHEMA_DATA_INT", None)
+      if base == "U64"       : return ("qmf::SCHEMA_DATA_INT", None)
+      if base == "S8"        : return ("qmf::SCHEMA_DATA_INT", None)
+      if base == "S16"       : return ("qmf::SCHEMA_DATA_INT", None)
+      if base == "S32"       : return ("qmf::SCHEMA_DATA_INT", None)
+      if base == "S64"       : return ("qmf::SCHEMA_DATA_INT", None)
+      if base == "BOOL"      : return ("qmf::SCHEMA_DATA_BOOL", None)
+      if base == "SSTR"      : return ("qmf::SCHEMA_DATA_STRING", None)
+      if base == "LSTR"      : return ("qmf::SCHEMA_DATA_STRING", None)
+      if base == "ABSTIME"   : return ("qmf::SCHEMA_DATA_INT", "abstime")
+      if base == "DELTATIME" : return ("qmf::SCHEMA_DATA_INT", "deltatime")
+      if base == "FLOAT"     : return ("qmf::SCHEMA_DATA_FLOAT", None)
+      if base == "DOUBLE"    : return ("qmf::SCHEMA_DATA_FLOAT", None)
+      if base == "UUID"      : return ("qmf::SCHEMA_DATA_UUID", None)
+      if base == "FTABLE"    : return ("qmf::SCHEMA_DATA_MAP", None)
+      if base == "LIST"      : return ("qmf::SCHEMA_DATA_LIST", None)
+      raise ValueError("Unknown base type %s" % base)
+
+  def qmfv2Access(self, code):
+      if code == "RC": return "qmf::ACCESS_READ_CREATE"
+      if code == "RO": return "qmf::ACCESS_READ_ONLY"
+      if code == "RW": return "qmf::ACCESS_READ_WRITE"
+      raise ValueError("Unknown access type %s" % code)
+
+  def qmfv2Dir(self, code):
+      if code == "I" : return "qmf::DIR_IN"
+      if code == "O" : return "qmf::DIR_OUT"
+      if code == "IO": return "qmf::DIR_IN_OUT"
+      raise ValueError("Unknown direction type %s" % code)
+
+  def qmfv2Severity(self, code):
+      if code == 0 : return "qmf::SEV_EMERG"
+      if code == 1 : return "qmf::SEV_ALERT"
+      if code == 2 : return "qmf::SEV_CRIT"
+      if code == 3 : return "qmf::SEV_ERROR"
+      if code == 4 : return "qmf::SEV_WARN"
+      if code == 5 : return "qmf::SEV_NOTICE"
+      if code == 6 : return "qmf::SEV_INFORM"
+      if code == 7 : return "qmf::SEV_DEBUG"
+      raise ValueError("Out of Range Severity %d" % code)
+
 #=====================================================================================
 # Utility Functions
 #=====================================================================================
Index: cpp/managementgen/qmfgen/templates/V2Package.cpp
===================================================================
--- cpp/managementgen/qmfgen/templates/V2Package.cpp	(revision 0)
+++ cpp/managementgen/qmfgen/templates/V2Package.cpp	(revision 0)
@@ -0,0 +1,37 @@
+/*MGEN:commentPrefix=//*/
+//
+// 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.
+//
+
+/*MGEN:Root.Disclaimer*/
+
+#include "QmfPackage.h"
+#include <qmf/Schema.h>
+#include <qmf/SchemaProperty.h>
+#include <qmf/SchemaMethod.h>
+#include <string>
+
+using namespace std;
+using namespace qmf::/*MGEN:Schema.Namespace*/;
+
+void PackageDefinition::configure(::qmf::AgentSession& session)
+{
+    string package("/*MGEN:Schema.PackageName*/");
+/*MGEN:Schema.V2ClassDefines*/
+}
+
Index: cpp/managementgen/qmfgen/templates/V2Package.h
===================================================================
--- cpp/managementgen/qmfgen/templates/V2Package.h	(revision 0)
+++ cpp/managementgen/qmfgen/templates/V2Package.h	(revision 0)
@@ -0,0 +1,46 @@
+/*MGEN:commentPrefix=//*/
+#ifndef _QMF_PACKAGE_/*MGEN:Schema.PackageNameUpper*/_
+#define _QMF_PACKAGE_/*MGEN:Schema.PackageNameUpper*/_
+
+//
+// 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.
+//
+
+/*MGEN:Root.Disclaimer*/
+
+#include <qmf/AgentSession.h>
+#include <qmf/Schema.h>
+#include <qmf/Data.h>
+#include <qmf/DataAddr.h>
+
+namespace qmf {
+/*MGEN:Class.OpenNamespaces*/
+
+class PackageDefinition
+{
+  public:
+    ~PackageDefinition() {}
+    void configure(::qmf::AgentSession& session);
+
+/*MGEN:Schema.V2ClassMembers*/
+};
+
+}/*MGEN:Class.CloseNamespaces*/
+            
+
+#endif  /*!_QMF_PACKAGE_/*MGEN:Schema.PackageNameUpper*/_*/
Index: cpp/managementgen/qmfgen/templates/Class.h
===================================================================
--- cpp/managementgen/qmfgen/templates/Class.h	(revision 1056407)
+++ cpp/managementgen/qmfgen/templates/Class.h	(working copy)
@@ -79,7 +79,8 @@
     void mapDecodeValues(const ::qpid::types::Variant::Map& map);
     void doMethod(std::string&           methodName,
                   const ::qpid::types::Variant::Map& inMap,
-                  ::qpid::types::Variant::Map& outMap);
+                  ::qpid::types::Variant::Map& outMap,
+                  const std::string& userId);
     std::string getKey() const;
 /*MGEN:IF(Root.GenQMFv1)*/
     uint32_t writePropertiesSize() const;
@@ -88,7 +89,8 @@
     void writeStatistics(std::string& buf, bool skipHeaders = false);
     void doMethod(std::string& methodName,
                   const std::string& inBuf,
-                  std::string& outBuf);
+                  std::string& outBuf,
+                  const std::string& userId);
 /*MGEN:ENDIF*/
 
     writeSchemaCall_t getWriteSchemaCall() { return writeSchema; }
Index: tools/src/py/qmf-tool
===================================================================
--- tools/src/py/qmf-tool	(revision 0)
+++ tools/src/py/qmf-tool	(revision 0)
@@ -0,0 +1,684 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import os
+import optparse
+import sys
+import socket
+from cmd         import Cmd
+from shlex       import split
+from threading   import Lock
+from time        import strftime, gmtime
+from qpid.disp   import Display
+import cqpid
+import qmf2
+
+class Mcli(Cmd):
+  """ Management Command Interpreter """
+
+  def __init__(self, dataObject, dispObject):
+    Cmd.__init__(self)
+    self.dataObject = dataObject
+    self.dispObject = dispObject
+    self.dataObject.setCli(self)
+    self.prompt = "qmf: "
+    
+  def emptyline(self):
+    pass
+
+  def setPromptMessage(self, p=None):
+    if p == None:
+      self.prompt = "qmf: "
+    else:
+      self.prompt = "qmf[%s]: " % p
+
+  def do_help(self, data):
+    print "Management Tool for QMF"
+    print
+    print "Agent Commands:"
+    print "    set filter <filter-string> - Filter the list of agents"
+    print "    show filter                - Show the agent filter currently in effect"
+    print "    list agents                - Print a list of the known Agents"
+    print "    show agent <item-number>   - Print detailed information about an Agent"
+    print "    set default <item-number>  - Set the default agent for operations"
+    print
+    print "Schema Commands:"
+    print "    list packages                            - Print a list of packages supported by the default agent"
+    print "    list classes [<package-name>]            - Print all classes supported byt the default agent"
+    print "    show class <class-name> [<package-name>] - Show details of a class"
+    print
+    print "Data Commands:"
+    print "    query <class-name> [<package-name>] [<predicate>] - Query for data from the agent"
+    print "    list                                              - List accumulated query results"
+    print "    clear                                             - Clear accumulated query results"
+    print "    show <id>                                         - Show details from a data object"
+    print "    call <id> <method> [<args>]                       - Call a method on a data object"
+    print
+    print "General Commands:"
+    print "    set time-format short           - Select short timestamp format (default)"
+    print "    set time-format long            - Select long timestamp format"
+    print "    quit or ^D                      - Exit the program"
+    print
+
+  def complete_set(self, text, line, begidx, endidx):
+    """ Command completion for the 'set' command """
+    tokens = split(line[:begidx])
+    if len(tokens) == 1:
+      return [i for i in ('filter ', 'default ', 'time-format ') if i.startswith(text)]
+    if len(tokens) == 2 and tokens[1] == 'time-format':
+      return [i for i in ('long', 'short') if i.startswith(text)]
+    return []
+
+  def do_set(self, data):
+    tokens = split(data)
+    try:
+      if tokens[0] == "time-format":
+        self.dispObject.do_setTimeFormat(tokens[1])
+      else:
+        self.dataObject.do_set(data)
+    except Exception, e:
+      print "Exception in set command:", e
+
+  def complete_list(self, text, line, begidx, endidx):
+    tokens = split(line[:begidx])
+    if len(tokens) == 1:
+      return [i for i in ('agents', 'packages', 'classes ') if i.startswith(text)]
+    return []
+
+  def do_list(self, data):
+    try:
+      self.dataObject.do_list(data)
+    except Exception, e:
+      print "Exception in list command:", e
+
+  def complete_show(self, text, line, begidx, endidx):
+    tokens = split(line[:begidx])
+    if len(tokens) == 1:
+      return [i for i in ('filter', 'agent ', 'class ') if i.startswith(text)]
+    return []
+
+  def do_show(self, data):
+    try:
+      self.dataObject.do_show(data)
+    except Exception, e:
+      print "Exception in show command:", e
+
+  def complete_query(self, text, line, begidx, endidx):
+    return []
+
+  def do_query(self, data):
+    try:
+      self.dataObject.do_query(data)
+    except Exception, e:
+      if e.message.__class__ == qmf2.Data:
+        e = e.message.getProperties()
+      print "Exception in query command:", e
+
+  def do_call(self, data):
+    try:
+      self.dataObject.do_call(data)
+    except Exception, e:
+      if e.message.__class__ == qmf2.Data:
+        e = e.message.getProperties()
+      print "Exception in call command:", e
+
+  def do_clear(self, data):
+    try:
+      self.dataObject.do_clear(data)
+    except Exception, e:
+      print "Exception in clear command:", e
+
+  def do_EOF(self, data):
+    print "quit"
+    try:
+      self.dataObject.do_exit()
+    except:
+      pass
+    return True
+
+  def do_quit(self, data):
+    try:
+      self.dataObject.do_exit()
+    except:
+      pass
+    return True
+
+  def postcmd(self, stop, line):
+    return stop
+
+  def postloop(self):
+    print "Exiting..."
+    self.dataObject.close()
+
+
+#======================================================================================================
+# QmfData
+#======================================================================================================
+class QmfData:
+  """
+  """
+  def __init__(self, disp, url):
+    self.disp = disp
+    self.url = url
+    self.agent_filter = '[]'
+    self.connection = cqpid.Connection(self.url)
+    self.connection.open()
+    self.session = qmf2.ConsoleSession(self.connection)
+    self.session.setAgentFilter(self.agent_filter)
+    self.session.open()
+    self.lock = Lock()
+    self.cli = None
+    self.agents = {}            # Map of number => agent object
+    self.deleted_agents = {}    # Map of number => agent object
+    self.agent_numbers = {}     # Map of agent name => number
+    self.next_number = 1
+    self.focus_agent = None
+    self.data_list = {}
+    self.next_data_index = 1
+
+  #=======================
+  # Methods to support CLI
+  #=======================
+  def setCli(self, cli):
+    self.cli = cli
+
+  def close(self):
+    try:
+      self.session.close()
+      self.connection.close()
+    except:
+      pass   # we're shutting down - ignore any errors
+
+  def do_list(self, data):
+    tokens = data.split()
+    if len(tokens) == 0:
+      self.listData()
+    elif tokens[0] == 'agents' or tokens[0] == 'agent':
+      self.listAgents()
+    elif tokens[0] == 'packages' or tokens[0] == 'package':
+      self.listPackages()
+    elif tokens[0] == 'classes' or tokens[0] == 'class':
+      self.listClasses(tokens[1:])
+
+  def do_set(self, data):
+    tokens = split(data)
+    if len(tokens) == 0:
+      print "What do you want to set?  type 'help' for more information."
+      return
+    if tokens[0] == 'filter':
+      if len(tokens) == 2:
+        self.setAgentFilter(tokens[1])
+    elif tokens[0] == 'default':
+      if len(tokens) == 2:
+        self.updateAgents()
+        number = int(tokens[1])
+        self.focus_agent = self.agents[number]
+        print "Default Agent: %s" % self.focus_agent.getName()
+
+  def do_show(self, data):
+    tokens = split(data)
+    if len(tokens) == 0:
+      print "What do you want to show?  Type 'help' for more information."
+      return
+
+    if tokens[0] == 'agent':
+      self.showAgent(tokens[1:])
+      return
+
+    if tokens[0] == 'filter':
+      print self.agent_filter
+      return
+
+    if tokens[0] == "default":
+      if not self.focus_agent:
+        self.updateAgents()
+      if self.focus_agent:
+        print "Default Agent: %s" % self.focus_agent.getName()
+      else:
+        print "Default Agent not set"
+      return
+
+    if tokens[0] == "class":
+      self.showClass(tokens[1:])
+      return
+
+    if tokens[0].isdigit():
+      self.showData(tokens[0])
+      return
+
+    print "What do you want to show?  Type 'help' for more information."
+    return
+
+  def do_query(self, data):
+    tokens = split(data)
+    if len(tokens) == 0:
+      print "Class name not specified."
+      return
+    cname = tokens[0]
+    pname = None
+    pred  = None
+    if len(tokens) >= 2:
+      if tokens[1][0] == '[':
+        pred = tokens[1]
+      else:
+        pname = tokens[1]
+        if len(tokens) >= 3:
+          pred = tokens[2]
+    query = "{class:'%s'" % cname
+    if pname:
+      query += ",package:'%s'" % pname
+    if pred:
+      query += ",where:%s" % pred
+    query += "}"
+    if not self.focus_agent:
+      self.updateAgents()
+    d_list = self.focus_agent.query(query)
+    local_data_list = {}
+    for d in d_list:
+      local_data_list[self.next_data_index] = d
+      self.next_data_index += 1
+    rows = []
+    for index,val in local_data_list.items():
+      rows.append((index, val.getAddr().getName()))
+      self.data_list[index] = val
+    self.disp.table("Data Objects Returned: %d:" % len(d_list), ("Number", "Data Address"), rows)
+
+  def do_call(self, data):
+    tokens = split(data)
+    if len(tokens) < 2:
+      print "Data ID and method-name not specified."
+      return
+    idx = int(tokens[0])
+    methodName = tokens[1]
+    args = []
+    for arg in tokens[2:]:
+      ##
+      ## If the argument is a map, list, boolean, integer, or floating (one decimal point),
+      ## run it through the Python evaluator so it is converted to the correct type.
+      ##
+      ## TODO: use a regex for this instead of this convoluted logic
+      if arg[0] == '{' or arg[0] == '[' or arg == "True" or arg == "False" or \
+            ((arg.count('.') < 2 and (arg.count('-') == 0 or \
+            (arg.count('-') == 1 and  arg[0] == '-')) and \
+            arg.replace('.','').replace('-','').isdigit())):
+        args.append(eval(arg))
+      else:
+        args.append(arg)
+
+    if not idx in self.data_list:
+      print "Unknown data index, run 'query' to get a list of data indices"
+      return
+
+    data = self.data_list[idx]
+    data._getSchema()
+    result = data._invoke(methodName, args, {})
+    rows = []
+    for k,v in result.items():
+      rows.append((k,v))
+    self.disp.table("Output Parameters:", ("Name", "Value"), rows)
+
+  def do_clear(self, data):
+    self.data_list = {}
+    self.next_data_index = 1
+    print "Accumulated query results cleared"
+
+  def do_exit(self):
+    pass
+
+  #====================
+  # Sub-Command Methods
+  #====================
+  def setAgentFilter(self, filt):
+    self.agent_filter = filt
+    self.session.setAgentFilter(filt)
+
+  def updateAgents(self):
+    agents = self.session.getAgents()
+    number_list = []
+    for agent in agents:
+      if agent.getName() not in self.agent_numbers:
+        number = self.next_number
+        number_list.append(number)
+        self.next_number += 1
+        self.agent_numbers[agent.getName()] = number
+        self.agents[number] = agent
+      else:
+        ## Track seen agents so we can clean out deleted ones
+        number = self.agent_numbers[agent.getName()]
+        number_list.append(number)
+        if number in self.deleted_agents:
+          self.agents[number] = self.deleted_agents.pop(number)
+    deleted = []
+    for number in self.agents:
+      if number not in number_list:
+        deleted.append(number)
+    for number in deleted:
+      self.deleted_agents[number] = self.agents.pop(number)
+    if not self.focus_agent:
+      self.focus_agent = self.session.getConnectedBrokerAgent()
+
+  def listAgents(self):
+    self.updateAgents()
+    rows = []
+    for number in self.agents:
+      agent = self.agents[number]
+      if self.focus_agent and agent.getName() == self.focus_agent.getName():
+        d = '*'
+      else:
+        d = ''
+      rows.append((d, number, agent.getVendor(), agent.getProduct(), agent.getInstance(), agent.getEpoch()))
+    self.disp.table("QMF Agents:", ("", "Id", "Vendor", "Product", "Instance", "Epoch"), rows)
+
+  def listPackages(self):
+    if not self.focus_agent:
+      raise "Default Agent not set - use 'set default'"
+    self.focus_agent.loadSchemaInfo()
+    packages = self.focus_agent.getPackages()
+    for p in packages:
+      print "    %s" % p
+
+  def getClasses(self, tokens):
+    if not self.focus_agent:
+      raise "Default Agent not set - use 'set default'"
+      return
+    self.focus_agent.loadSchemaInfo()
+    if len(tokens) == 1:
+      classes = self.focus_agent.getSchemaIds(tokens[0]);
+    else:
+      packages = self.focus_agent.getPackages()
+      classes = []
+      for p in packages:
+        classes.extend(self.focus_agent.getSchemaIds(p))
+    return classes
+
+  def listClasses(self, tokens):
+    classes = self.getClasses(tokens)
+    rows = []
+    for c in classes:
+      rows.append((c.getPackageName(), c.getName(), self.classTypeName(c.getType())))
+    self.disp.table("Classes:", ("Package", "Class", "Type"), rows)
+
+  def showClass(self, tokens):
+    if len(tokens) < 1:
+      return
+    classes = self.getClasses([])
+    c = tokens[0]
+    p = None
+    if len(tokens) == 2:
+      p = tokens[1]
+    schema = None
+    sid = None
+    for cls in classes:
+      if c == cls.getName():
+        if not p or p == cls.getPackageName():
+          schema = self.focus_agent.getSchema(cls)
+          sid = cls
+          break
+    if not sid:
+      return
+    print "Class: %s:%s (%s) - %s" % \
+        (sid.getPackageName(), sid.getName(), self.classTypeName(sid.getType()), schema.getDesc())
+    print "  hash: %r" % sid.getHash()
+    props = schema.getProperties()
+    methods = schema.getMethods()
+    rows = []
+    for prop in props:
+      name = prop.getName()
+      dtype = self.typeName(prop.getType())
+      if len(prop.getSubtype()) > 0:
+        dtype += "(%s)" % prop.getSubtype()
+      access = self.accessName(prop.getAccess())
+      idx = self.yes_blank(prop.isIndex())
+      opt = self.yes_blank(prop.isOptional())
+      unit = prop.getUnit()
+      desc = prop.getDesc()
+      rows.append((name, dtype, idx, access, opt, unit, desc))
+    self.disp.table("Properties:", ("Name", "Type", "Index", "Access", "Optional", "Unit", "Description"), rows)
+    if len(methods) > 0:
+      for meth in methods:
+        name = meth.getName()
+        desc = meth.getDesc()
+        if len(desc) > 0:
+          desc = " - " + desc
+        args = meth.getArguments()
+        rows = []
+        for prop in args:
+          aname = prop.getName()
+          dtype = self.typeName(prop.getType())
+          if len(prop.getSubtype()) > 0:
+            dtype += "(%s)" % prop.getSubtype()
+          unit = prop.getUnit()
+          adesc = prop.getDesc()
+          io = self.dirName(prop.getDirection())
+          rows.append((aname, dtype, io, unit, adesc))
+        print
+        print "   Method: %s%s" % (name, desc)
+        self.disp.table("Arguments:", ("Name", "Type", "Dir", "Unit", "Description"), rows)
+
+  def showAgent(self, tokens):
+    self.updateAgents()
+    for token in tokens:
+      number = int(token)
+      agent = self.agents[number]
+      print
+      print "    =================================================================================="
+      print "      Agent Id: %d" % number
+      print "    Agent Name: %s" % agent.getName()
+      print "         Epoch: %d" % agent.getEpoch()
+      print "    Attributes:"
+      attrs = agent.getAttributes()
+      keys = attrs.keys()
+      keys.sort()
+      pairs = []
+      for key in keys:
+        if key == '_timestamp' or key == '_schema_updated':
+          val = disp.timestamp(attrs[key])
+        else:
+          val = attrs[key]
+        pairs.append((key, val))
+      self.printAlignedPairs(pairs)
+      agent.loadSchemaInfo()
+      print "    Packages:"
+      packages = agent.getPackages()
+      for package in packages:
+        print "        %s" % package
+
+  def showData(self, idx):
+    num = int(idx)
+    if not num in self.data_list:
+      print "Data ID not known, run 'query' first to get data"
+      return
+    data = self.data_list[num]
+    props = data.getProperties()
+    rows = []
+    for k,v in props.items():
+      rows.append((k, v))
+    self.disp.table("Properties:", ("Name", "Value"), rows)
+
+  def listData(self):
+    if len(self.data_list) == 0:
+      print "No Query Results - Use the 'query' command"
+      return
+    rows = []
+    for index,val in self.data_list.items():
+      rows.append((index, val.getAgent().getName(), val.getAddr().getName()))
+    self.disp.table("Accumulated Query Results:", ('Number', 'Agent', 'Data Address'), rows)
+
+  def printAlignedPairs(self, rows, indent=8):
+    maxlen = 0
+    for first, second in rows:
+      if len(first) > maxlen:
+        maxlen = len(first)
+    maxlen += indent
+    for first, second in rows:
+      for i in range(maxlen - len(first)):
+        print "",
+      print "%s : %s" % (first, second)
+
+  def classTypeName(self, code):
+    if code == qmf2.SCHEMA_TYPE_DATA:   return "Data"
+    if code == qmf2.SCHEMA_TYPE_EVENT:  return "Event"
+    return "Unknown"
+
+  def typeName (self, typecode):
+    """ Convert type-codes to printable strings """
+    if   typecode == qmf2.SCHEMA_DATA_VOID:    return "void"
+    elif typecode == qmf2.SCHEMA_DATA_BOOL:    return "bool"
+    elif typecode == qmf2.SCHEMA_DATA_INT:     return "int"
+    elif typecode == qmf2.SCHEMA_DATA_FLOAT:   return "float"
+    elif typecode == qmf2.SCHEMA_DATA_STRING:  return "string"
+    elif typecode == qmf2.SCHEMA_DATA_MAP:     return "map"
+    elif typecode == qmf2.SCHEMA_DATA_LIST:    return "list"
+    elif typecode == qmf2.SCHEMA_DATA_UUID:    return "uuid"
+    else:
+      raise ValueError ("Invalid type code: %s" % str(typecode))
+
+  def valueByType(self, typecode, val):
+    if   typecode == 1:  return "%d" % val
+    elif typecode == 2:  return "%d" % val
+    elif typecode == 3:  return "%d" % val
+    elif typecode == 4:  return "%d" % val
+    elif typecode == 6:  return val
+    elif typecode == 7:  return val
+    elif typecode == 8:  return strftime("%c", gmtime(val / 1000000000))
+    elif typecode == 9:
+      if val < 0: val = 0
+      sec = val / 1000000000
+      min = sec / 60
+      hour = min / 60
+      day = hour / 24
+      result = ""
+      if day > 0:
+        result = "%dd " % day
+      if hour > 0 or result != "":
+        result += "%dh " % (hour % 24)
+      if min > 0 or result != "":
+        result += "%dm " % (min % 60)
+      result += "%ds" % (sec % 60)
+      return result
+
+    elif typecode == 10: return str(self.idRegistry.displayId(val))
+    elif typecode == 11:
+      if val:
+        return "True"
+      else:
+        return "False"
+
+    elif typecode == 12: return "%f" % val
+    elif typecode == 13: return "%f" % val
+    elif typecode == 14: return "%r" % val
+    elif typecode == 15: return "%r" % val
+    elif typecode == 16: return "%d" % val
+    elif typecode == 17: return "%d" % val
+    elif typecode == 18: return "%d" % val
+    elif typecode == 19: return "%d" % val
+    elif typecode == 20: return "%r" % val
+    elif typecode == 21: return "%r" % val
+    elif typecode == 22: return "%r" % val
+    else:
+      raise ValueError ("Invalid type code: %s" % str(typecode))
+
+  def accessName (self, code):
+    """ Convert element access codes to printable strings """
+    if   code == qmf2.ACCESS_READ_CREATE: return "ReadCreate"
+    elif code == qmf2.ACCESS_READ_WRITE: return "ReadWrite"
+    elif code == qmf2.ACCESS_READ_ONLY: return "ReadOnly"
+    else:
+      raise ValueError ("Invalid access code: %s" % str(code))
+
+  def dirName(self, io):
+    if   io == qmf2.DIR_IN:     return "in"
+    elif io == qmf2.DIR_OUT:    return "out"
+    elif io == qmf2.DIR_IN_OUT: return "in_out"
+    else:
+      raise ValueError("Invalid direction code: %r" % io)
+
+  def notNone (self, text):
+    if text == None:
+      return ""
+    else:
+      return text
+
+  def yes_blank(self, val):
+    if val:
+      return "Y"
+    return ""
+
+  def objectIndex(self, obj):
+    if obj._objectId.isV2:
+      return obj._objectId.getObject()
+    result = ""
+    first = True
+    props = obj.getProperties()
+    for prop in props:
+      if prop[0].index:
+        if not first:
+          result += "."
+        result += self.valueByType(prop[0].type, prop[1])
+        first = None
+    return result
+
+def Usage():
+  print "Usage:  qpid-tool [[<username>/<password>@]<target-host>[:<tcp-port>]]"
+  print
+
+#=========================================================
+# Main Program
+#=========================================================
+
+# Get host name and port if specified on the command line
+cargs = sys.argv[1:]
+_host = "localhost"
+
+if len(cargs) > 0:
+  _host = cargs[0]
+
+if _host[0] == '-':
+  Usage()
+  if _host != '-h' and _host != "--help":
+    print "qpid-tool: error: no such option:", _host
+  sys.exit(1)
+
+disp = Display()
+
+# Attempt to make a connection to the target broker
+try:
+  data = QmfData(disp, _host)
+except Exception, e:
+  if str(e).find("Exchange not found") != -1:
+    print "Management not enabled on broker:  Use '-m yes' option on broker startup."
+  else:
+    print "Failed: %s - %s" % (e.__class__.__name__, e)
+  sys.exit(1)
+
+# Instantiate the CLI interpreter and launch it.
+cli = Mcli(data, disp)
+print("Management Tool for QMF")
+try:
+  cli.cmdloop()
+except KeyboardInterrupt:
+  print
+  print "Exiting..."
+except Exception, e:
+  print "Failed: %s - %s" % (e.__class__.__name__, e)
+
+# alway attempt to cleanup broker resources
+data.close()

Property changes on: tools/src/py/qmf-tool
___________________________________________________________________
Added: svn:executable
   + *

Index: tools/setup.py
===================================================================
--- tools/setup.py	(revision 1056407)
+++ tools/setup.py	(working copy)
@@ -23,7 +23,8 @@
       version="0.8",
       author="Apache Qpid",
       author_email="dev@qpid.apache.org",
-      scripts=["src/py/qpid-cluster",
+      scripts=["src/py/qmf-tool",
+               "src/py/qpid-cluster",
                "src/py/qpid-cluster-store",
                "src/py/qpid-config",
                "src/py/qpid-printevents",
Index: extras/qmf/src/py/qmf/console.py
===================================================================
--- extras/qmf/src/py/qmf/console.py	(revision 1056413)
+++ extras/qmf/src/py/qmf/console.py	(working copy)
@@ -53,6 +53,10 @@
     """ Invoked when a connection is established to a broker """
     pass
 
+  def brokerConnectionFailed(self, broker):
+    """ Invoked when a connection to a broker fails """
+    pass
+
   def brokerDisconnected(self, broker):
     """ Invoked when the connection to a broker is lost """
     pass
@@ -376,7 +380,8 @@
           dp.routing_key = self.getV2RoutingKey()
           mp = self._broker.amqpSession.message_properties()
           mp.content_type = "amqp/map"
-          mp.user_id = self._broker.authUser
+          if self._broker.saslUser:
+            mp.user_id = self._broker.saslUser
           mp.correlation_id = str(seq)
           mp.app_id = "qmf2"
           mp.reply_to = self._broker.amqpSession.reply_to("qmf.default.direct", self._broker.v2_direct_queue)
@@ -1206,11 +1211,11 @@
     try:
       agentName = ah["qmf.agent"]
       values = content["_values"]
-      timestamp = values["timestamp"]
-      interval = values["heartbeat_interval"]
+      timestamp = values["_timestamp"]
+      interval = values["_heartbeat_interval"]
       epoch = 0
-      if 'epoch' in values:
-        epoch = values['epoch']
+      if '_epoch' in values:
+        epoch = values['_epoch']
     except Exception,e:
       return
 
@@ -1239,7 +1244,7 @@
       agent.touch()
     if self.rcvHeartbeats and self.console and agent:
       self._heartbeatCallback(agent, timestamp)
-    agent.update_schema_timestamp(values.get("schema_timestamp", 0))
+    agent.update_schema_timestamp(values.get("_schema_updated", 0))
 
 
   def _v2HandleAgentLocateRsp(self, broker, mp, ah, content):
@@ -1488,7 +1493,8 @@
           dp.routing_key = objectId.getV2RoutingKey()
           mp = broker.amqpSession.message_properties()
           mp.content_type = "amqp/map"
-          mp.user_id = broker.authUser
+          if broker.saslUser:
+            mp.user_id = broker.saslUser
           mp.correlation_id = str(seq)
           mp.app_id = "qmf2"
           mp.reply_to = broker.amqpSession.reply_to("qmf.default.direct", broker.v2_direct_queue)
@@ -2227,9 +2233,12 @@
     self.port = port
     self.mechanisms = authMechs
     self.ssl = ssl
+    if connTimeout is not None:
+	connTimeout = float(connTimeout)
     self.connTimeout = connTimeout
     self.authUser = authUser
     self.authPass = authPass
+    self.saslUser = None
     self.cv = Condition()
     self.seqToAgentMap = {}
     self.error = None
@@ -2403,6 +2412,11 @@
       self.conn.start()
       sock.settimeout(oldTimeout)
       self.conn.aborted = oldAborted
+      uid = self.conn.user_id
+      if uid.__class__ == tuple and len(uid) == 2:
+        self.saslUser = uid[1]
+      else:
+        self.saslUser = None
 
       # prevent topic queues from filling up (and causing the agents to
       # disconnect) by discarding the oldest queued messages when full.
@@ -2508,6 +2522,8 @@
     except Exception, e:
       self.error = "Exception during connection setup: %s - %s" % (e.__class__.__name__, e)
       self.conn_exc = e
+      if self.session.console:
+        self.session.console.brokerConnectionFailed(self)
       return False     # connection failed
 
   def _updateAgent(self, obj):
@@ -2571,7 +2587,7 @@
       for agent in to_notify:
         self.session._delAgentCallback(agent)
 
-  def _v2SendAgentLocate(self, predicate={}):
+  def _v2SendAgentLocate(self, predicate=[]):
     """
     Broadcast an agent-locate request to cause all agents in the domain to tell us who they are.
     """
@@ -2579,13 +2595,14 @@
     dp = self.amqpSession.delivery_properties()
     dp.routing_key = "console.request.agent_locate"
     mp = self.amqpSession.message_properties()
-    mp.content_type = "amqp/map"
-    mp.user_id = self.authUser
+    mp.content_type = "amqp/list"
+    if self.saslUser:
+      mp.user_id = self.saslUser
     mp.app_id = "qmf2"
     mp.reply_to = self.amqpSession.reply_to("qmf.default.direct", self.v2_direct_queue)
     mp.application_headers = {'qmf.opcode':'_agent_locate_request'}
     sendCodec = Codec()
-    sendCodec.write_map(predicate)
+    sendCodec.write_list(predicate)
     msg = Message(dp, mp, sendCodec.encoded)
     self._send(msg, "qmf.default.topic")
 
@@ -2622,7 +2639,8 @@
       dp.ttl = ttl
     mp = self.amqpSession.message_properties()
     mp.content_type = "x-application/qmf"
-    mp.user_id = self.authUser
+    if self.saslUser:
+      mp.user_id = self.saslUser
     mp.reply_to = self.amqpSession.reply_to("amq.direct", self.replyName)
     return Message(dp, mp, body)
 
@@ -2853,7 +2871,7 @@
             content = None
         else:
           content = None
-  
+
         if content != None:
           ##
           ## Directly handle agent heartbeats and agent locate responses as these are broker-scope (they are
@@ -3366,6 +3384,9 @@
     Handle a QMFv2 data indication from the agent.  Note: called from context
     of the Broker thread.
     """
+    if content.__class__ != list:
+      return
+
     if mp.correlation_id:
       try:
         self.lock.acquire()
@@ -3382,8 +3403,6 @@
     if "qmf.content" in ah:
       kind = ah["qmf.content"]
     if kind == "_data":
-      if content.__class__ != list:
-        return
       for omap in content:
         context.addV2QueryResult(omap)
       context.processV2Data()
@@ -3391,14 +3410,15 @@
         context.signal()
 
     elif kind == "_event":
-      event = Event(self, v2Map=content)
-      if event.classKey is None or event.schema:
-        # schema optional or present
-        context.doEvent(event)
-      else:
-        # schema not optional and not present
-        if context.addPendingEvent(event):
-          self._v2SendSchemaRequest(event.classKey)
+      for omap in content:
+        event = Event(self, v2Map=omap)
+        if event.classKey is None or event.schema:
+          # schema optional or present
+          context.doEvent(event)
+        else:
+          # schema not optional and not present
+          if context.addPendingEvent(event):
+            self._v2SendSchemaRequest(event.classKey)
 
     elif kind == "_schema_id":
       for sid in content:
@@ -3533,7 +3553,8 @@
     dp.routing_key = self.getV2RoutingKey()
     mp = self.broker.amqpSession.message_properties()
     mp.content_type = "amqp/map"
-    mp.user_id = self.broker.authUser
+    if self.broker.saslUser:
+      mp.user_id = self.broker.saslUser
     mp.correlation_id = str(sequence)
     mp.app_id = "qmf2"
     mp.reply_to = self.broker.amqpSession.reply_to("qmf.default.direct", self.broker.v2_direct_queue)
@@ -3976,6 +3997,9 @@
   def brokerConnected(self, broker):
     print "brokerConnected:", broker
 
+  def brokerConnectionFailed(self, broker):
+    print "brokerConnectionFailed:", broker
+
   def brokerDisconnected(self, broker):
     print "brokerDisconnected:", broker
 
Index: extras/qmf/src/py/qmf2/common.py
===================================================================
--- extras/qmf/src/py/qmf2/common.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/common.py	(working copy)
@@ -1,1738 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-import time
-from logging import getLogger
-from threading import Lock
-from threading import Condition
-try:
-    import hashlib
-    _md5Obj = hashlib.md5
-except ImportError:
-    import md5
-    _md5Obj = md5.new
-
-log = getLogger("qmf")
-log_query = getLogger("qmf.query")
-
-
-##
-## Constants
-##
-
-QMF_APP_ID="qmf2"
-
-
-class ContentType(object):
-    """ Values for the 'qmf.content' message header
-    """
-    schema_package = "_schema_package"
-    schema_id = "_schema_id"
-    schema_class = "_schema_class"
-    object_id = "_object_id"
-    data = "_data"
-    event = "_event"
-
-
-class OpCode(object):
-    """ Values for the 'qmf.opcode' message header.
-    """
-    noop = "_noop"
-
-    # codes sent by a console and processed by the agent
-    agent_locate_req = "_agent_locate_request"
-    subscribe_req = "_subscribe_request"
-    subscribe_cancel_ind = "_subscribe_cancel_indication"
-    subscribe_refresh_ind = "_subscribe_refresh_indication"
-    query_req = "_query_request"
-    method_req = "_method_request"
-
-
-    # codes sent by the agent to a console
-    agent_locate_rsp = "_agent_locate_response"
-    agent_heartbeat_ind = "_agent_heartbeat_indication"
-    query_rsp = "_query_response"
-    subscribe_rsp = "_subscribe_response"
-    data_ind = "_data_indication"
-    method_rsp = "_method_response"
-
-
-
-def timedelta_to_secs(td):
-    """
-    Convert a time delta to a time interval in seconds (float)
-    """
-    return td.days * 86400 + td.seconds + td.microseconds/1000000.0
-
-
-##==============================================================================
-## Async Event Model
-##==============================================================================
-
-
-class Notifier(object):
-    """
-    Virtual base class that defines a call back which alerts the application that
-    a QMF Console notification is pending.
-    """
-    def indication(self):
-        """
-        Called when one or more items are ready for the application to process.
-        This method may be called by an internal QMF library thread.  Its purpose is to
-        indicate that the application should process pending work items.
-        """
-        raise Exception("The indication method must be overridden by the application!")
-
-
-
-class WorkItem(object):
-    """
-    Describes an event that has arrived for the application to process.  The
-    Notifier is invoked when one or more of these WorkItems become available
-    for processing. 
-    """
-    # Enumeration of the types of WorkItems produced on the Console
-    AGENT_ADDED=1
-    AGENT_DELETED=2
-    NEW_PACKAGE=3
-    NEW_CLASS=4
-    OBJECT_UPDATE=5
-    EVENT_RECEIVED=7
-    AGENT_HEARTBEAT=8
-    QUERY_COMPLETE=9
-    METHOD_RESPONSE=10
-    SUBSCRIBE_RESPONSE=11
-    SUBSCRIBE_INDICATION=12
-    RESUBSCRIBE_RESPONSE=13
-    # Enumeration of the types of WorkItems produced on the Agent
-    METHOD_CALL=1000
-    QUERY=1001
-    SUBSCRIBE_REQUEST=1002
-    RESUBSCRIBE_REQUEST=1003
-    UNSUBSCRIBE_REQUEST=1004
-
-    def __init__(self, kind, handle, _params=None):
-        """
-        Used by the Console to create a work item.
-        
-        @type kind: int
-        @param kind: work item type
-        """
-        self._kind = kind
-        self._handle = handle
-        self._params = _params
-
-    def get_type(self):
-        return self._kind
-
-    def get_handle(self):
-        return self._handle
-
-    def get_params(self):
-        return self._params
-
-
-
-##==============================================================================
-## Addressing
-##==============================================================================
-
-class QmfAddress(object):
-    """
-    Address format: "qmf.<domain>.[topic|direct]/<subject>"
-    TBD
-    """
-
-    TYPE_DIRECT = "direct"
-    TYPE_TOPIC = "topic"
-
-    ADDRESS_FMT = "qmf.%s.%s/%s"
-    DEFAULT_DOMAIN = "default"
-
-    # Directly-addressed messages:
-    # agent's direct address: "qmf.<domain>.direct/<agent-name>
-    # console's direct address: "qmf.<domain>.direct/<console-name>
-
-    # Well-known Topic Addresses:
-    # "qmf.<domain>.topic/<subject>
-    # Where <subject> has the following format:
-    #   "console.ind#" - indications sent from consoles
-    #   "agent.ind#" - indications sent from agents
-    #
-    # The following "well known" subjects are defined:
-    #
-    # console.ind.locate[.<agent-name>] - agent discovery request
-    # agent.ind.heartbeat[.<agent-name>"] - agent heartbeats
-    # agent.ind.event[.<severity>.<agent-name>] - events
-    # agent.ind.schema[TBD] - schema updates
-    #
-    SUBJECT_AGENT_IND="agent.ind"
-    SUBJECT_AGENT_HEARTBEAT = "agent.ind.heartbeat"
-    SUBJECT_AGENT_EVENT="agent.ind.event"
-    SUBJECT_AGENT_SCHEMA="agent.ind.schema"
-
-    SUBJECT_CONSOLE_IND="console.ind"
-    SUBJECT_CONSOLE_LOCATE_AGENT="console.ind.locate"
-    
-
-
-    def __init__(self, subject, domain, type_):
-        if '/' in domain or '.' in domain:
-            raise Exception("domain string must not contain '/' or '.'"
-                            " characters.")
-
-        self._subject = subject
-        self._domain = domain
-        self._type = type_
-
-    def _direct(cls, subject, _domain=None):
-        if _domain is None:
-            _domain = QmfAddress.DEFAULT_DOMAIN
-        return cls(subject, _domain, type_=QmfAddress.TYPE_DIRECT)
-    direct = classmethod(_direct)
-
-    def _topic(cls, subject, _domain=None):
-        if _domain is None:
-            _domain = QmfAddress.DEFAULT_DOMAIN
-        return cls(subject, _domain, type_=QmfAddress.TYPE_TOPIC)
-    topic = classmethod(_topic)
-
-    def __from_string(cls, address):
-        node,subject = address.split('/',1)
-        qmf,domain,type_ = node.split('.',2)
-
-        if qmf != "qmf" or (type_ != QmfAddress.TYPE_DIRECT and 
-                            type_ != QmfAddress.TYPE_TOPIC):
-            raise ValueError("invalid QmfAddress format: %s" % address)
-
-        return cls(subject, domain, type_)
-    from_string = classmethod(__from_string)
-
-    def get_address(self):
-        """
-        Return the QMF address as a string, suitable for use with the AMQP
-        messaging API.
-        """
-        return str(self)
-
-    def get_node(self):
-        """
-        Return the 'node' portion of the address.
-        """
-        return self.get_address().split('/',1)[0]
-
-    def get_subject(self):
-        """
-        Return the 'subject' portion of the address.
-        """
-        return self.get_address().split('/',1)[1]
-
-    def get_domain(self):
-        return self._domain
-
-    def is_direct(self):
-        return self._type == self.TYPE_DIRECT
-
-    def __repr__(self):
-        return QmfAddress.ADDRESS_FMT % (self._domain, self._type, self._subject)
-
-
-
-
-class AgentName(object):
-    """
-    Uniquely identifies a management agent within the management domain.
-    """
-    _separator = ":"
-
-    def __init__(self, vendor, product, name, _str=None):
-        """
-        Note: this object must be immutable, as it is used to index into a dictionary
-        """
-        if _str is not None:
-            # construct from string representation
-            if _str.count(AgentName._separator) < 2:
-                raise TypeError("AgentName string format must be 'vendor.product.name'")
-            self._vendor, self._product, self._name = _str.split(AgentName._separator)
-        else:
-            self._vendor = vendor
-            self._product = product
-            self._name = name
-
-
-    def _from_str(cls, str_):
-        return cls(None, None, None, str_=str_)
-    from_str = classmethod(_from_str)
-
-    def vendor(self):
-        return self._vendor
-
-    def product(self):
-        return self._product
-
-    def name(self):
-        return self._name
-
-    def __cmp__(self, other):
-        if not isinstance(other, AgentName) :
-            raise TypeError("Invalid types for compare")
-            # return 1
-        me = str(self)
-        them = str(other)
-
-        if me < them:
-            return -1
-        if me > them:
-            return 1
-        return 0
-
-    def __hash__(self):
-        return (self._vendor, self._product, self._name).__hash__()
-
-    def __repr__(self):
-        return self._vendor + AgentName._separator + \
-            self._product + AgentName._separator + \
-            self._name
-
-
-
-##==============================================================================
-## DATA MODEL
-##==============================================================================
-
-
-class _mapEncoder(object):
-    """ 
-    virtual base class for all objects that support being converted to a map
-    """
-
-    def map_encode(self):
-        raise Exception("The map_encode method my be overridden.")
-
-
-class QmfData(_mapEncoder):
-    """
-    Base class representing management data.
-
-    Map format:
-    map["_values"] = map of unordered "name"=<value> pairs (optional)
-    map["_subtype"] = map of unordered "name"="subtype string" pairs (optional)
-    map["_tag"] = application-specific tag for this instance (optional)
-    """
-    KEY_VALUES = "_values"
-    KEY_SUBTYPES = "_subtypes"
-    KEY_TAG="_tag"
-    KEY_OBJECT_ID = "_object_id"
-    KEY_SCHEMA_ID = "_schema_id"
-    KEY_UPDATE_TS = "_update_ts"
-    KEY_CREATE_TS = "_create_ts"
-    KEY_DELETE_TS = "_delete_ts"
-
-    def __init__(self,
-                 _values={}, _subtypes={}, _tag=None,
-                 _object_id=None, _schema_id=None,
-                 _ctime = 0, _utime = 0, _dtime = 0,
-                 _map=None, _const=False):
-        """
-        @type _values: dict
-        @param _values: dictionary of initial name=value pairs for object's
-        named data. 
-        @type _subtypes: dict
-        @param _subtype: dictionary of subtype strings for each of the object's
-        named data. 
-        @type _desc: string
-        @param _desc: Human-readable description of this data object.
-        @type _const: boolean
-        @param _const: if true, this object cannot be modified
-        """
-        if _map is not None:
-            # construct from map
-            _tag = _map.get(self.KEY_TAG, _tag)
-            _values = _map.get(self.KEY_VALUES, _values)
-            _subtypes = _map.get(self.KEY_SUBTYPES, _subtypes)
-            _object_id = _map.get(self.KEY_OBJECT_ID, _object_id)
-            sid = _map.get(self.KEY_SCHEMA_ID)
-            if sid:
-                _schema_id = SchemaClassId.from_map(sid)
-            _ctime = long(_map.get(self.KEY_CREATE_TS, _ctime))
-            _utime = long(_map.get(self.KEY_UPDATE_TS, _utime))
-            _dtime = long(_map.get(self.KEY_DELETE_TS, _dtime))
-
-        self._values = _values.copy()
-        self._subtypes = _subtypes.copy()
-        self._tag = _tag
-        self._ctime = _ctime
-        self._utime = _utime
-        self._dtime = _dtime
-        self._const = _const
-        self._schema_id = _schema_id
-        self._object_id = str(_object_id)
-
-
-    def __create(cls, values, _subtypes={}, _tag=None, _object_id=None,
-                _schema_id=None, _const=False):
-        # timestamp in millisec since epoch UTC
-        ctime = long(time.time() * 1000)
-        return cls(_values=values, _subtypes=_subtypes, _tag=_tag,
-                   _ctime=ctime, _utime=ctime,
-                   _object_id=_object_id, _schema_id=_schema_id, _const=_const)
-    create = classmethod(__create)
-
-    def __from_map(cls, map_, _const=False):
-        return cls(_map=map_, _const=_const)
-    from_map = classmethod(__from_map)
-
-    def is_managed(self):
-        return self._object_id is not None
-
-    def is_described(self):
-        return self._schema_id is not None
-
-    def get_tag(self):
-        return self._tag
-
-    def get_value(self, name):
-        """
-        Will throw an AttributeError exception if the named value does not exist.
-        """
-        # meta-properties first:
-        if name == SchemaClassId.KEY_PACKAGE:
-            if self._schema_id:
-                return self._schema_id.get_package_name()
-            return None
-        if name == SchemaClassId.KEY_CLASS:
-            if self._schema_id:
-                return self._schema_id.get_class_name()
-            return None
-        if name == SchemaClassId.KEY_TYPE:
-            if self._schema_id:
-                return self._schema_id.get_type()
-            return None
-        if name == SchemaClassId.KEY_HASH:
-            if self._schema_id:
-                return self._schema_id.get_hash_string()
-            return None
-        if name == self.KEY_SCHEMA_ID:
-            return self._schema_id
-        if name == self.KEY_OBJECT_ID:
-            return self._object_id
-        if name == self.KEY_TAG:
-            return self._tag
-        if name == self.KEY_UPDATE_TS:
-            return self._utime
-        if name == self.KEY_CREATE_TS:
-            return self._ctime
-        if name == self.KEY_DELETE_TS:
-            return self._dtime
-
-        try:
-            return self._values[name]
-        except KeyError:
-            raise AttributeError("no value named '%s' in this object" % name)
-
-    def has_value(self, name):
-
-        if name in [SchemaClassId.KEY_PACKAGE, SchemaClassId.KEY_CLASS,
-                    SchemaClassId.KEY_TYPE, SchemaClassId.KEY_HASH,
-                    self.KEY_SCHEMA_ID]:
-            return self._schema_id is not None
-        if name in [self.KEY_UPDATE_TS, self.KEY_CREATE_TS,
-                    self.KEY_DELETE_TS]:
-            return True
-        if name == self.KEY_OBJECT_ID:
-            return self._object_id is not None
-        if name == self.KEY_TAG:
-            return self._tag is not None
-
-        return name in self._values
-
-    def set_value(self, _name, _value, _subType=None):
-        if self._const:
-            raise Exception("cannot modify constant data object")
-        self._values[_name] = _value
-        if _subType:
-            self._subtypes[_name] = _subType
-        return _value
-
-    def get_subtype(self, _name):
-        return self._subtypes.get(_name)
-
-    def get_schema_class_id(self): 
-        """
-        @rtype: class SchemaClassId
-        @returns: the identifier of the Schema that describes the structure of the data.
-        """
-        return self._schema_id
-
-    def get_object_id(self): 
-        """
-        Get the instance's identification string.
-        @rtype: str
-        @returns: the identification string, or None if not assigned and id. 
-        """
-        return self._object_id
-
-    def map_encode(self):
-        _map = {}
-        if self._tag:
-            _map[self.KEY_TAG] = self._tag
-
-        # data in the _values map may require recursive map_encode()
-        vmap = {}
-        for name,val in self._values.iteritems():
-            if isinstance(val, _mapEncoder):
-                vmap[name] = val.map_encode()
-            else:
-                # otherwise, just toss in the native type...
-                vmap[name] = val
-
-        _map[self.KEY_VALUES] = vmap
-        # subtypes are never complex, so safe to just copy
-        _map[self.KEY_SUBTYPES] = self._subtypes.copy()
-        if self._object_id:
-            _map[self.KEY_OBJECT_ID] = self._object_id
-        if self._schema_id:
-            _map[self.KEY_SCHEMA_ID] = self._schema_id.map_encode()
-        return _map
-
-    def __repr__(self):
-        return "QmfData=<<" + str(self.map_encode()) + ">>"
-        
-
-    def __setattr__(self, _name, _value):
-        # ignore private data members
-        if _name[0] == '_':
-            return super(QmfData, self).__setattr__(_name, _value)
-        if _name in self._values:
-            return self.set_value(_name, _value)
-        return super(QmfData, self).__setattr__(_name, _value)
-
-    def __getattr__(self, _name):
-        if _name != "_values" and _name in self._values: 
-            return self._values[_name]
-        raise AttributeError("no value named '%s' in this object" % _name)
-
-    def __getitem__(self, _name):
-        return self.__getattr__(_name)
-
-    def __setitem__(self, _name, _value):
-        return self.__setattr__(_name, _value)
-
-
-
-class QmfEvent(QmfData):
-    """
-    A QMF Event is a type of described data that is not managed. Events are 
-    notifications that are sent by Agents. An event notifies a Console of a 
-    change in some aspect of the system under managment.
-    """
-    KEY_TIMESTAMP = "_timestamp"
-    KEY_SEVERITY = "_severity"
-
-    SEV_EMERG = "emerg"
-    SEV_ALERT = "alert"
-    SEV_CRIT = "crit"
-    SEV_ERR = "err"
-    SEV_WARNING = "warning"
-    SEV_NOTICE = "notice"
-    SEV_INFO = "info"
-    SEV_DEBUG = "debug"
-
-    def __init__(self, _timestamp=None, _sev=SEV_NOTICE, _values={},
-                 _subtypes={}, _tag=None,
-                 _map=None,
-                 _schema_id=None, _const=True):
-        """
-        @type _map: dict
-        @param _map: if not None, construct instance from map representation. 
-        @type _timestamp: int
-        @param _timestamp: moment in time when event occurred, expressed
-               as milliseconds since Midnight, Jan 1, 1970 UTC.
-        @type _agentId: class AgentId
-        @param _agentId: Identifies agent issuing this event.
-        @type _schema: class Schema
-        @param _schema:
-        @type _schemaId: class SchemaClassId (event)
-        @param _schemaId: identi
-        """
-
-        if _map is not None:
-            # construct from map
-            super(QmfEvent, self).__init__(_map=_map, _const=_const,
-                                           _object_id="_event")
-            _timestamp = _map.get(self.KEY_TIMESTAMP, _timestamp)
-            _sev = _map.get(self.KEY_SEVERITY, _sev)
-        else:
-            super(QmfEvent, self).__init__(_object_id="_event",
-                                           _values=_values,
-                                           _subtypes=_subtypes, _tag=_tag,
-                                           _schema_id=_schema_id,
-                                           _const=_const)
-        if _timestamp is None:
-            raise TypeError("QmfEvent: a valid timestamp is required.")
-
-        try:
-            self._timestamp = long(_timestamp)
-        except:
-            raise TypeError("QmfEvent: a numeric timestamp is required.")
-
-        self._severity = _sev
-
-    def _create(cls, timestamp, severity, values,
-                _subtypes={}, _tag=None, _schema_id=None, _const=False):
-        return cls(_timestamp=timestamp, _sev=severity, _values=values,
-                _subtypes=_subtypes, _tag=_tag, _schema_id=_schema_id, _const=_const)
-    create = classmethod(_create)
-
-    def _from_map(cls, map_, _const=False):
-        return cls(_map=map_, _const=_const)
-    from_map = classmethod(_from_map)
-
-    def get_timestamp(self): 
-        return self._timestamp
-
-    def get_severity(self):
-        return self._severity
-
-    def map_encode(self):
-        _map = super(QmfEvent, self).map_encode()
-        _map[self.KEY_TIMESTAMP] = self._timestamp
-        _map[self.KEY_SEVERITY] = self._severity
-        return _map
-
-
-
-##==============================================================================
-## QUERY
-##==============================================================================
-
-
-
-class QmfQuery(_mapEncoder):
-
-    KEY_TARGET="what"
-    KEY_PREDICATE="where"
-    KEY_ID="id"
-
-    ### Query Types
-    ID=1
-    PREDICATE=2
-
-    #### Query Targets ####
-    TARGET_PACKAGES="schema_package"
-    # (returns just package names)
-    # allowed predicate key(s):
-    #
-    # SchemaClassId.KEY_PACKAGE
-
-    TARGET_SCHEMA_ID="schema_id"
-    TARGET_SCHEMA="schema"
-    # allowed id: value:
-    # SchemaClassId
-    #
-    # allowed predicate key(s):
-    # SchemaClassId.KEY_PACKAGE
-    # SchemaClassId.KEY_CLASS
-    # SchemaClassId.KEY_TYPE
-    # SchemaClassId.KEY_HASH
-    # SchemaClass.KEY_SCHEMA_ID
-    # name of property (exist test only)
-    # name of method (exist test only)
-
-    TARGET_AGENT="agent"
-    # allowed id: value:
-    # string name of agent
-    # allowed predicate keys(s):
-    # 
-    KEY_AGENT_NAME="_name"
-
-    TARGET_OBJECT_ID="object_id"
-    TARGET_OBJECT="object"
-    # If object is described by a schema, the value of the target map must
-    # include a "_schema_id": {map encoded schema id} value.
-    #
-    # allowed id: value:
-    # object_id string
-    #
-    # allowed predicate keys(s):
-    #
-    # QmfData.KEY_OBJECT_ID
-    # QmfData.KEY_UPDATE_TS
-    # QmfData.KEY_CREATE_TS
-    # QmfData.KEY_DELETE_TS
-    # <name of data value>
-
-    # supported predicate operators
-
-    # evaluation operators
-    QUOTE="quote"
-    UNQUOTE="unquote"
-    # boolean operators
-    EQ="eq"
-    NE="ne"
-    LT="lt"
-    LE="le"
-    GT="gt"
-    GE="ge"
-    RE_MATCH="re_match"
-    EXISTS="exists"
-    TRUE="true"
-    FALSE="false"
-    # logic operators
-    AND="and"
-    OR="or"
-    NOT="not"
-
-    _valid_targets = [TARGET_PACKAGES, TARGET_OBJECT_ID, TARGET_SCHEMA, TARGET_SCHEMA_ID, 
-                      TARGET_OBJECT, TARGET_AGENT]
-    _valid_bool_ops = [EQ, NE, LT, GT, LE, GE, EXISTS, RE_MATCH, TRUE, FALSE]
-    _valid_logic_ops = [AND, OR, NOT]
-    _valid_eval_ops = [QUOTE, UNQUOTE]
-
-    def __init__(self, _target=None, _target_params=None, _predicate=None,
-                 _id=None, _map=None):
-        """
-        """
-        if _map is not None:
-            target_map = _map.get(self.KEY_TARGET)
-            if not target_map:
-                raise TypeError("QmfQuery requires a target map")
-
-            _target = None
-            for key in target_map.iterkeys():
-                if key in self._valid_targets:
-                    _target = key
-                    break
-            if _target is None:
-                raise TypeError("Invalid QmfQuery target: '%s'" %
-                                str(target_map))
-
-            # convert target params from map format
-            _target_params = target_map.get(_target)
-            if _target_params:
-                if not isinstance(_target_params, type({})):
-                    raise TypeError("target params must be a map: '%s'" %
-                                    str(_target_params))
-                t_params = {}
-                for name,value in _target_params.iteritems():
-                    if name == QmfData.KEY_SCHEMA_ID:
-                        t_params[name] = SchemaClassId.from_map(value)
-                    else:
-                        t_params[name] = value
-                _target_params = t_params
-
-            _id = _map.get(self.KEY_ID)
-            if _id is not None:
-                # Convert identifier to native type if necessary
-                if _target == self.TARGET_SCHEMA:
-                    _id = SchemaClassId.from_map(_id)
-            else: 
-                _predicate = _map.get(self.KEY_PREDICATE, _predicate)
-
-        self._target = _target
-        if not self._target:
-            raise TypeError("QmfQuery requires a target value")
-        self._target_params = _target_params
-        self._predicate = _predicate
-        self._id = _id
-
-    # constructors
-    def _create_wildcard(cls, target, _target_params=None):
-        return cls(_target=target, _target_params=_target_params)
-    create_wildcard = classmethod(_create_wildcard)
-
-    def _create_wildcard_object_id(cls, schema_id):
-        """
-        Create a wildcard to match all object_ids for a given schema.
-        """
-        if not isinstance(schema_id, SchemaClassId):
-            raise TypeError("class SchemaClassId expected")
-        params = {QmfData.KEY_SCHEMA_ID: schema_id}
-        return cls(_target=QmfQuery.TARGET_OBJECT_ID,
-                   _target_params=params)
-    create_wildcard_object_id = classmethod(_create_wildcard_object_id)
-
-    def _create_wildcard_object(cls, schema_id):
-        """
-        Create a wildcard to match all objects for a given schema.
-        """
-        if not isinstance(schema_id, SchemaClassId):
-            raise TypeError("class SchemaClassId expected")
-        params = {QmfData.KEY_SCHEMA_ID: schema_id}
-        return cls(_target=QmfQuery.TARGET_OBJECT,
-                   _target_params=params)
-    create_wildcard_object = classmethod(_create_wildcard_object)
-
-    def _create_predicate(cls, target, predicate, _target_params=None): 
-        return cls(_target=target, _target_params=_target_params,
-                   _predicate=predicate)
-    create_predicate = classmethod(_create_predicate)
-
-    def _create_id(cls, target, ident, _target_params=None): 
-        return cls(_target=target, _target_params=_target_params, _id=ident)
-    create_id = classmethod(_create_id)
-
-    def _create_id_object(cls, object_id, _schema_id=None):
-        """
-        Create a ID Query for an object (schema optional).
-        """
-        if _schema_id is not None:
-            if not isinstance(_schema_id, SchemaClassId):
-                raise TypeError("class SchemaClassId expected")
-            params = {QmfData.KEY_SCHEMA_ID: _schema_id}
-        else:
-            params = None
-        return cls(_target=QmfQuery.TARGET_OBJECT,
-                   _id=object_id,
-                   _target_params=params)
-    create_id_object = classmethod(_create_id_object)
-
-    def _create_id_object_id(cls, object_id, _schema_id=None):
-        """
-        Create a ID Query for object_ids (schema optional).
-        """
-        if _schema_id is not None:
-            if not isinstance(_schema_id, SchemaClassId):
-                raise TypeError("class SchemaClassId expected")
-            params = {QmfData.KEY_SCHEMA_ID: _schema_id}
-        else:
-            params = None
-        return cls(_target=QmfQuery.TARGET_OBJECT_ID,
-                   _id=object_id,
-                   _target_params=params)
-    create_id_object_id = classmethod(_create_id_object_id)
-
-    def _from_map(cls, map_):
-        return cls(_map=map_)
-    from_map = classmethod(_from_map)
-    # end constructors
-
-    def get_target(self):
-        return self._target
-
-    def get_target_param(self):
-        return self._target_params
-
-    def get_selector(self):
-        if self._id:
-            return QmfQuery.ID
-        else:
-            return QmfQuery.PREDICATE
-
-    def get_id(self):
-        return self._id
-
-    def get_predicate(self):
-        """
-        """
-        return self._predicate
-
-    def evaluate(self, qmfData):
-        """
-        """
-        if self._id:
-            if self._target == self.TARGET_SCHEMA:
-                return (qmfData.has_value(qmfData.KEY_SCHEMA_ID) and
-                        qmfData.get_value(qmfData.KEY_SCHEMA_ID) == self._id)
-            elif self._target == self.TARGET_OBJECT:
-                return (qmfData.has_value(qmfData.KEY_OBJECT_ID) and
-                        qmfData.get_value(qmfData.KEY_OBJECT_ID) == self._id)
-            elif self._target == self.TARGET_AGENT:
-                return (qmfData.has_value(self.KEY_AGENT_NAME) and
-                        qmfData.get_value(self.KEY_AGENT_NAME) == self._id)
-
-            raise Exception("Unsupported query target '%s'" % str(self._target))
-
-        if self._predicate:
-            return self._eval_pred(self._predicate, qmfData)
-        # no predicate and no id - always match
-        return True
-
-    def map_encode(self):
-        t_params = {}
-        if self._target_params:
-            for name,value in self._target_params.iteritems():
-                if isinstance(value, _mapEncoder):
-                    t_params[name] = value.map_encode()
-                else:
-                    t_params[name] = value
-        if t_params:
-            _map = {self.KEY_TARGET: {self._target: t_params}}
-        else:
-            _map = {self.KEY_TARGET: {self._target: None}}
-
-        if self._id is not None:
-            if isinstance(self._id, _mapEncoder):
-                _map[self.KEY_ID] = self._id.map_encode()
-            else:
-                _map[self.KEY_ID] = self._id
-        elif self._predicate is not None:
-            _map[self.KEY_PREDICATE] = self._predicate
-        return _map
-
-    def _eval_pred(self, pred, qmfData):
-        """
-        Evaluate the predicate expression against a QmfData object.
-        """
-        if not isinstance(qmfData, QmfData):
-            raise TypeError("Query expects to evaluate QmfData types.")
-
-        if not isinstance(pred, type([])):
-            log.warning("Invalid type for predicate expression: '%s'" % str(pred))
-            return False
-
-        # empty predicate - match all???
-        if len(pred) == 0:
-            return True
-
-        oper = pred[0]
-        if oper == QmfQuery.TRUE:
-            log_query.debug("query evaluate TRUE")
-            return True
-
-        if oper == QmfQuery.FALSE:
-            log_query.debug("query evaluate FALSE")
-            return False
-
-        if oper == QmfQuery.AND:
-            log_query.debug("query evaluate AND: '%s'" % str(pred))
-            for exp in pred[1:]:
-                if not self._eval_pred(exp, qmfData):
-                    log_query.debug("---> False")
-                    return False
-            log_query.debug("---> True")
-            return True
-
-        if oper == QmfQuery.OR:
-            log_query.debug("query evaluate OR: [%s]" % str(pred))
-            for exp in pred[1:]:
-                if self._eval_pred(exp, qmfData):
-                    log_query.debug("---> True")
-                    return True
-            log_query.debug("---> False")
-            return False
-
-        if oper == QmfQuery.NOT:
-            log_query.debug("query evaluate NOT: [%s]" % str(pred))
-            for exp in pred[1:]:
-                if self._eval_pred(exp, qmfData):
-                    log_query.debug("---> False")
-                    return False
-            log_query.debug("---> True")
-            return True
-
-        if oper == QmfQuery.EXISTS:
-            if len(pred) != 2:
-                log.warning("Malformed query: 'exists' operator"
-                            " - bad arguments '%s'" % str(pred))
-                return False
-            ### Q: Should we assume "quote", or should it be explicit?
-            ### "foo" or ["quote" "foo"] 
-            ### my guess is "explicit"
-            log_query.debug("query evaluate EXISTS: [%s]" % str(pred))
-            try:
-                arg = self._fetch_pred_arg(pred[1], qmfData)
-            except AttributeError:
-                log.warning("query parameter not found: '%s'" % str(pred))
-                return False
-            v = qmfData.has_value(arg)
-            log_query.debug("---> %s" % str(v))
-            return v
-
-        # binary operators
-        if oper in [QmfQuery.EQ, QmfQuery.NE, QmfQuery.LT,
-                    QmfQuery.LE, QmfQuery.GT, QmfQuery.GE,
-                    QmfQuery.RE_MATCH]:
-            if len(pred) != 3:
-                log.warning("Malformed query: '%s' operator"
-                            " - requires 2 arguments '%s'" %
-                            (oper, str(pred)))
-                return False
-            # @todo: support regular expression match
-            log_query.debug("query evaluate binary op: [%s]" % str(pred))
-            try:
-                arg1 = self._fetch_pred_arg(pred[1], qmfData)
-                arg2 = self._fetch_pred_arg(pred[2], qmfData)
-            except AttributeError:
-                log.warning("query parameter not found: '%s'" % str(pred))
-                return False
-            log_query.debug("query evaluate %s: %s, %s" % (oper, str(arg1), str(arg2)))
-            v = False
-            try:
-                if oper == QmfQuery.EQ: v = arg1 == arg2
-                elif oper == QmfQuery.NE: v = arg1 != arg2
-                elif oper == QmfQuery.LT: v = arg1 < arg2
-                elif oper == QmfQuery.LE: v = arg1 <= arg2
-                elif oper == QmfQuery.GT: v = arg1 > arg2
-                elif oper == QmfQuery.GE: v = arg1 >= arg2
-            except TypeError:
-                log.warning("query comparison failed: '%s'" %  str(pred))
-            log_query.debug("---> %s" % str(v))
-            return v
-
-        log.warning("Unrecognized query operator: [%s]" % str(pred[0]))
-        return False
-
-    def _fetch_pred_arg(self, arg, qmfData):
-        """
-        Determine the value of a predicate argument by evaluating quoted
-        arguments.
-        """
-        if isinstance(arg, basestring):
-            return qmfData.get_value(arg)
-        if isinstance(arg, type([])) and len(arg) == 2:
-            if arg[0] == QmfQuery.QUOTE:
-                return arg[1]
-            if arg[0] == QmfQuery.UNQUOTE:
-                return qmfData.get_value(arg[1])
-        return arg
-
-    def __repr__(self):
-        return "QmfQuery=<<" + str(self.map_encode()) + ">>"
-
-
-
-
-
-##==============================================================================
-## SCHEMA
-##==============================================================================
-
-
-# Argument typecodes, access, and direction qualifiers
-
-class qmfTypes(object):
-    TYPE_UINT8      = 1
-    TYPE_UINT16     = 2
-    TYPE_UINT32     = 3
-    TYPE_UINT64     = 4
-
-    TYPE_SSTR       = 6
-    TYPE_LSTR       = 7
-
-    TYPE_ABSTIME    = 8
-    TYPE_DELTATIME  = 9
-
-    TYPE_REF        = 10
-
-    TYPE_BOOL       = 11
-
-    TYPE_FLOAT      = 12
-    TYPE_DOUBLE     = 13
-
-    TYPE_UUID       = 14
-
-    TYPE_MAP        = 15
-
-    TYPE_INT8       = 16
-    TYPE_INT16      = 17
-    TYPE_INT32      = 18
-    TYPE_INT64      = 19
-
-    TYPE_OBJECT     = 20
-
-    TYPE_LIST       = 21
-
-    TYPE_ARRAY      = 22
-
-# New subtypes:
-# integer (for time, duration, signed/unsigned)
-# double (float)
-# bool
-# string
-# map (ref, qmfdata)
-# list
-# uuid
-
-
-class qmfAccess(object):
-    READ_CREATE = 1 
-    READ_WRITE = 2
-    READ_ONLY = 3
-
-
-class qmfDirection(object):
-    DIR_IN = 1
-    DIR_OUT = 2
-    DIR_IN_OUT = 3
-
-
-
-def _to_bool( param ):
-    """
-    Helper routine to convert human-readable representations of
-    boolean values to python bool types.
-    """
-    _false_strings = ["off", "no", "false", "0", "none"]
-    _true_strings =  ["on",  "yes", "true", "1"]
-    if type(param) == str:
-        lparam = param.lower()
-        if lparam in _false_strings:
-            return False
-        if lparam in _true_strings:
-            return True
-        raise TypeError("unrecognized boolean string: '%s'" % param )
-    else:
-        return bool(param)
-
-
-
-class SchemaClassId(_mapEncoder):
-    """ 
-    Unique identifier for an instance of a SchemaClass.
-
-    Map format:
-    map["package_name"] = str, name of associated package
-    map["class_name"] = str, name of associated class
-    map["type"] = str, "data"|"event", default: "data"
-    optional:
-    map["hash_str"] = str, hash value in standard format or None 
-    if hash is unknown.
-    """
-    KEY_PACKAGE="_package_name"
-    KEY_CLASS="_class_name"
-    KEY_TYPE="_type"
-    KEY_HASH="_hash_str"
-
-    TYPE_DATA = "_data"
-    TYPE_EVENT = "_event"
-
-    _valid_types=[TYPE_DATA, TYPE_EVENT]
-    _schemaHashStrFormat = "%08x-%08x-%08x-%08x"
-    _schemaHashStrDefault = "00000000-00000000-00000000-00000000"
-
-    def __init__(self, pname=None, cname=None, stype=TYPE_DATA, hstr=None,
-                 _map=None): 
-        """
-        @type pname: str
-        @param pname: the name of the class's package
-        @type cname: str
-        @param cname: name of the class
-        @type stype: str
-        @param stype: schema type [data | event]
-        @type hstr: str
-        @param hstr: the hash value in '%08x-%08x-%08x-%08x' format
-        """
-        if _map is not None:
-            # construct from map
-            pname = _map.get(self.KEY_PACKAGE, pname)
-            cname = _map.get(self.KEY_CLASS, cname)
-            stype = _map.get(self.KEY_TYPE, stype)
-            hstr = _map.get(self.KEY_HASH, hstr)
-
-        self._pname = pname
-        self._cname = cname
-        if stype not in SchemaClassId._valid_types:
-            raise TypeError("Invalid SchemaClassId type: '%s'" % stype)
-        self._type = stype
-        self._hstr = hstr
-        if self._hstr:
-            try:
-                # sanity check the format of the hash string
-                hexValues = hstr.split("-")
-                h0 = int(hexValues[0], 16)
-                h1 = int(hexValues[1], 16)
-                h2 = int(hexValues[2], 16)
-                h3 = int(hexValues[3], 16)
-            except:
-                raise Exception("Invalid SchemaClassId format: bad hash string: '%s':"
-                                % hstr)
-    # constructor
-    def _create(cls, pname, cname, stype=TYPE_DATA, hstr=None):
-        return cls(pname=pname, cname=cname, stype=stype, hstr=hstr)
-    create = classmethod(_create)
-
-    # map constructor 
-    def _from_map(cls, map_):
-        return cls(_map=map_)
-    from_map = classmethod(_from_map)
-
-    def get_package_name(self): 
-        """ 
-        Access the package name in the SchemaClassId.
-
-        @rtype: str
-        """
-        return self._pname
-
-
-    def get_class_name(self): 
-        """ 
-        Access the class name in the SchemaClassId
-
-        @rtype: str
-        """
-        return self._cname
-
-
-    def get_hash_string(self): 
-        """ 
-        Access the schema's hash as a string value
-
-        @rtype: str
-        """
-        return self._hstr
-
-
-    def get_type(self):
-        """ 
-        Returns the type code associated with this Schema
-
-        @rtype: str
-        """
-        return self._type
-
-    def map_encode(self):
-        _map = {}
-        _map[self.KEY_PACKAGE] = self._pname
-        _map[self.KEY_CLASS] = self._cname
-        _map[self.KEY_TYPE] = self._type
-        if self._hstr: _map[self.KEY_HASH] = self._hstr
-        return _map
-
-    def __repr__(self):
-        hstr = self.get_hash_string()
-        if not hstr:
-            hstr = SchemaClassId._schemaHashStrDefault
-        return self._pname + ":" + self._cname + ":" + self._type +  "(" + hstr  + ")"
-
-
-    def __cmp__(self, other):
-        if isinstance(other, dict):
-            other = SchemaClassId.from_map(other)
-        if not isinstance(other, SchemaClassId):
-            raise TypeError("Invalid types for compare")
-            # return 1
-        me = str(self)
-        them = str(other)
-        if me < them:
-            return -1
-        if me > them:
-            return 1
-        return 0
-
-
-    def __hash__(self):
-        return (self._pname, self._cname, self._hstr).__hash__()
-
-
-
-class SchemaProperty(_mapEncoder):
-    """
-    Describes the structure of a Property data object.
-    Map format:
-    map["amqp_type"] = int, AMQP type code indicating property's data type
-    
-    optional:
-    map["access"] = str, access allowed to this property, default "RO"
-    map["index"] = bool, True if this property is an index value, default False
-    map["optional"] = bool, True if this property is optional, default False
-    map["unit"] = str, describes units used
-    map["min"] = int, minimum allowed value
-    map["max"] = int, maximun allowed value
-    map["maxlen"] = int, if string type, this is the maximum length in bytes
-    required to represent the longest instance of this string.
-    map["desc"] = str, human-readable description of this argument
-    map["reference"] = str, ???
-    map["parent_ref"] = bool, true if this property references an object  in
-    which this object is in a child-parent relationship. Default False
-    map["continuous"] = bool, true if the value potentially changes too fast to
-    be directly monitorable.  Example: fast changing statistic or random
-    number. Subscriptions to objects containing continuous data will publish
-    only on an interval basis, rather than every time the data changes. Default
-    False.
-    """
-    __hash__ = None
-    _access_strings = ["RO","RW","RC"]
-    _dir_strings = ["I", "O", "IO"]
-    def __init__(self, _type_code=None, _map=None, kwargs={}):
-        if _map is not None:
-            # construct from map
-            _type_code = _map.get("amqp_type", _type_code)
-            kwargs = _map
-            if not _type_code:
-                raise TypeError("SchemaProperty: amqp_type is a mandatory"
-                                " parameter")
-
-        self._type = _type_code
-        self._access  = "RO"
-        self._isIndex   = False
-        self._isOptional = False
-        self._unit    = None
-        self._min     = None
-        self._max     = None
-        self._maxlen  = None
-        self._desc    = None
-        self._reference = None
-        self._isParentRef  = False
-        self._dir = None
-        self._default = None
-        self._is_continuous = False
-
-        for key, value in kwargs.items():
-            if key == "access":
-                value = str(value).upper()
-                if value not in self._access_strings:
-                    raise TypeError("invalid value for access parameter: '%s':" % value )
-                self._access = value
-            elif key == "index"   : self._isIndex = _to_bool(value)
-            elif key == "optional": self._isOptional = _to_bool(value)
-            elif key == "unit"    : self._unit    = value
-            elif key == "min"     : self._min     = value
-            elif key == "max"     : self._max     = value
-            elif key == "maxlen"  : self._maxlen  = value
-            elif key == "desc"    : self._desc    = value
-            elif key == "reference" : self._reference = value
-            elif key == "parent_ref"   : self._isParentRef = _to_bool(value)
-            elif key == "parent_ref"   : self._isParentRef = _to_bool(value)
-            elif key == "continuous"   : self._is_continuous = _to_bool(value)
-            elif key == "dir":
-                value = str(value).upper()
-                if value not in self._dir_strings:
-                    raise TypeError("invalid value for direction parameter: '%s'" % value)
-                self._dir = value
-            elif key == "default" : self._default = value
-
-    # constructor
-    def _create(cls, type_code, **kwargs):
-        return cls(_type_code=type_code, kwargs=kwargs)
-    create = classmethod(_create)
-
-    # map constructor
-    def _from_map(cls, map_):
-        return cls(_map=map_)
-    from_map = classmethod(_from_map)
-
-    def get_type(self): return self._type
-
-    def get_access(self): return self._access
-
-    def is_optional(self): return self._isOptional
-
-    def is_index(self): return self._isIndex
-
-    def get_unit(self): return self._unit
-
-    def get_min(self): return self._min
-
-    def get_max(self): return self._max
-
-    def get_max_len(self): return self._maxlen
-
-    def get_desc(self): return self._desc
-
-    def get_reference(self): return self._reference
-
-    def is_parent_ref(self): return self._isParentRef
-
-    def get_direction(self): return self._dir
-
-    def get_default(self): return self._default
-
-    def is_continuous(self): return self._is_continuous
-
-    def map_encode(self):
-        """
-        Return the map encoding of this schema.
-        """
-        _map = {}
-        _map["amqp_type"] = self._type
-        _map["access"] = self._access
-        _map["index"] = self._isIndex
-        _map["optional"] = self._isOptional
-        if self._unit: _map["unit"] = self._unit
-        if self._min:  _map["min"] = self._min
-        if self._max:  _map["max"] = self._max
-        if self._maxlen: _map["maxlen"] = self._maxlen
-        if self._desc: _map["desc"] = self._desc
-        if self._reference: _map["reference"] = self._reference
-        _map["parent_ref"] = self._isParentRef
-        if self._dir: _map["dir"] = self._dir
-        if self._default: _map["default"] = self._default
-        if self._is_continuous: _map["continuous"] = self._is_continuous
-        return _map
-
-    def __repr__(self): 
-        return "SchemaProperty=<<" + str(self.map_encode()) + ">>"
-
-    def _update_hash(self, hasher):
-        """
-        Update the given hash object with a hash computed over this schema.
-        """
-        hasher.update(str(self._type))
-        hasher.update(str(self._isIndex))
-        hasher.update(str(self._isOptional))
-        hasher.update(str(self._is_continuous))
-        if self._access: hasher.update(self._access)
-        if self._unit: hasher.update(self._unit)
-        if self._desc: hasher.update(self._desc)
-        if self._dir: hasher.update(self._dir)
-        if self._default: hasher.update(self._default)
-
-
-class SchemaMethod(_mapEncoder):
-    """ 
-    The SchemaMethod class describes the method's structure, and contains a
-    SchemaProperty class for each argument declared by the method.
-
-    Map format:
-    map["arguments"] = map of "name"=<SchemaProperty> pairs.
-    map["desc"] = str, description of the method
-    """
-    KEY_NAME="_name"
-    KEY_ARGUMENTS="_arguments"
-    KEY_DESC="_desc"
-    KEY_ERROR="_error"
-    def __init__(self, _args={}, _desc=None, _map=None):
-        """
-        Construct a SchemaMethod.
-
-        @type args: map of "name"=<SchemaProperty> objects
-        @param args: describes the arguments accepted by the method
-        @type _desc: str
-        @param _desc: Human-readable description of the schema
-        """
-        if _map is not None:
-            _desc = _map.get(self.KEY_DESC)
-            margs = _map.get(self.KEY_ARGUMENTS)
-            if margs:
-                # margs are in map format - covert to SchemaProperty
-                tmp_args = {}
-                for name,val in margs.iteritems():
-                    tmp_args[name] = SchemaProperty.from_map(val)
-                _args=tmp_args
-
-        self._arguments = _args.copy()
-        self._desc = _desc
-
-    # map constructor
-    def _from_map(cls, map_):
-        return cls(_map=map_)
-    from_map = classmethod(_from_map)
-
-    def get_desc(self): return self._desc
-
-    def get_arg_count(self): return len(self._arguments)
-
-    def get_arguments(self): return self._arguments.copy()
-
-    def get_argument(self, name): return self._arguments.get(name)
-
-    def add_argument(self, name, schema):
-        """
-        Add an argument to the list of arguments passed to this method.  
-        Used by an agent for dynamically creating method schema.
-
-        @type name: string
-        @param name: name of new argument
-        @type schema: SchemaProperty
-        @param schema: SchemaProperty to add to this method
-        """
-        if not isinstance(schema, SchemaProperty):
-            raise TypeError("argument must be a SchemaProperty class")
-        # "Input" argument, by default
-        if schema._dir is None:
-            schema._dir = "I"
-        self._arguments[name] = schema
-
-    def map_encode(self):
-        """
-        Return the map encoding of this schema.
-        """
-        _map = {}
-        _args = {}
-        for name,val in self._arguments.iteritems():
-            _args[name] = val.map_encode()
-        _map[self.KEY_ARGUMENTS] = _args
-        if self._desc: _map[self.KEY_DESC] = self._desc
-        return _map
-
-    def __repr__(self):
-        result = "SchemaMethod=<<args=("
-        first = True
-        for name,arg in self._arguments.iteritems():
-            if first:
-                first = False
-            else:
-                result += ", "
-            result += name
-        result += ")>>"
-        return result
-
-    def _update_hash(self, hasher):
-        """
-        Update the given hash object with a hash computed over this schema.
-        """
-        for name,val in self._arguments.iteritems():
-            hasher.update(name)
-            val._update_hash(hasher)
-        if self._desc: hasher.update(self._desc)
-
-
-
-class SchemaClass(QmfData):
-    """
-    Base class for Data and Event Schema classes.
-
-    Map format:
-    map(QmfData), plus:
-    map["_schema_id"] = map representation of a SchemaClassId instance
-    map["_primary_key_names"] = order list of primary key names
-    """
-    KEY_PRIMARY_KEY_NAMES="_primary_key_names"
-    KEY_DESC = "_desc"
-
-    SUBTYPE_PROPERTY="qmfProperty"
-    SUBTYPE_METHOD="qmfMethod"
-
-    def __init__(self, _classId=None, _desc=None, _map=None):
-        """
-        Schema Class constructor.
-
-        @type classId: class SchemaClassId
-        @param classId: Identifier for this SchemaClass
-        @type _desc: str
-        @param _desc: Human-readable description of the schema
-        """
-        if _map is not None:
-            super(SchemaClass, self).__init__(_map=_map)
-
-            # decode each value based on its type
-            for name,value in self._values.iteritems():
-                if self._subtypes.get(name) == self.SUBTYPE_METHOD:
-                    self._values[name] = SchemaMethod.from_map(value)
-                else:
-                    self._values[name] = SchemaProperty.from_map(value)
-            cid = _map.get(self.KEY_SCHEMA_ID)
-            if cid:
-                _classId = SchemaClassId.from_map(cid)
-            self._object_id_names = _map.get(self.KEY_PRIMARY_KEY_NAMES,[])
-            _desc = _map.get(self.KEY_DESC)
-        else:
-            if _classId is None:
-                raise Exception("A class identifier must be supplied.")
-            super(SchemaClass, self).__init__(_object_id=str(_classId))
-            self._object_id_names = []
-
-        self._classId = _classId
-        self._desc = _desc
-
-    def get_class_id(self): 
-        if not self._classId.get_hash_string():
-            self.generate_hash()
-        return self._classId
-
-    def get_desc(self): return self._desc
-
-    def generate_hash(self): 
-        """
-        generate an md5 hash over the body of the schema,
-        and return a string representation of the hash
-        in format "%08x-%08x-%08x-%08x"
-        """
-        md5Hash = _md5Obj()
-        md5Hash.update(self._classId.get_package_name())
-        md5Hash.update(self._classId.get_class_name())
-        md5Hash.update(self._classId.get_type())
-        for name,x in self._values.iteritems():
-            md5Hash.update(name)
-            x._update_hash( md5Hash )
-        for name,value in self._subtypes.iteritems():
-            md5Hash.update(name)
-            md5Hash.update(value)
-        idx = 0
-        for name in self._object_id_names:
-            md5Hash.update(str(idx) + name)
-            idx += 1
-        hstr = md5Hash.hexdigest()[0:8] + "-" +\
-            md5Hash.hexdigest()[8:16] + "-" +\
-            md5Hash.hexdigest()[16:24] + "-" +\
-            md5Hash.hexdigest()[24:32]
-        # update classId with new hash value
-        self._classId._hstr = hstr
-        return hstr
-
-
-    def get_property_count(self): 
-        count = 0
-        for value in self._subtypes.itervalues():
-            if value == self.SUBTYPE_PROPERTY:
-                count += 1
-        return count
-
-    def get_properties(self): 
-        props = {}
-        for name,value in self._subtypes.iteritems():
-            if value == self.SUBTYPE_PROPERTY:
-                props[name] = self._values.get(name)
-        return props
-
-    def get_property(self, name):
-        if self._subtypes.get(name) == self.SUBTYPE_PROPERTY:
-            return self._values.get(name)
-        return None
-
-    def add_property(self, name, prop):
-        self.set_value(name, prop, self.SUBTYPE_PROPERTY)
-        # need to re-generate schema hash
-        self._classId._hstr = None
-
-    def get_value(self, name):
-        # check for meta-properties first
-        if name == SchemaClassId.KEY_PACKAGE:
-            return self._classId.get_package_name()
-        if name == SchemaClassId.KEY_CLASS:
-            return self._classId.get_class_name()
-        if name == SchemaClassId.KEY_TYPE:
-            return self._classId.get_type()
-        if name == SchemaClassId.KEY_HASH:
-            return self.get_class_id().get_hash_string()
-        if name == self.KEY_SCHEMA_ID:
-            return self.get_class_id()
-        if name == self.KEY_PRIMARY_KEY_NAMES:
-            return self._object_id_names[:]
-        return super(SchemaClass, self).get_value(name)
-
-    def has_value(self, name):
-        if name in [SchemaClassId.KEY_PACKAGE, SchemaClassId.KEY_CLASS, SchemaClassId.KEY_TYPE,
-                    SchemaClassId.KEY_HASH, self.KEY_SCHEMA_ID, self.KEY_PRIMARY_KEY_NAMES]:
-            return True
-        super(SchemaClass, self).has_value(name)
-
-    def map_encode(self):
-        """
-        Return the map encoding of this schema.
-        """
-        _map = super(SchemaClass,self).map_encode()
-        _map[self.KEY_SCHEMA_ID] = self.get_class_id().map_encode()
-        if self._object_id_names:
-            _map[self.KEY_PRIMARY_KEY_NAMES] = self._object_id_names[:]
-        if self._desc:
-            _map[self.KEY_DESC] = self._desc
-        return _map
-
-    def __repr__(self):
-        return str(self.get_class_id())
-
-
-
-class SchemaObjectClass(SchemaClass):
-    """
-    A schema class that describes a data object.  The data object is composed 
-    of zero or more properties and methods.  An instance of the SchemaObjectClass
-    can be identified using a key generated by concantenating the values of
-    all properties named in the primary key list.
-    
-    Map format:
-    map(SchemaClass)
-    """
-    def __init__(self, _classId=None, _desc=None,
-                 _props={}, _methods={}, _object_id_names=[],
-                 _map=None):
-        """
-        @type pname: str
-        @param pname: name of package this schema belongs to
-        @type cname: str
-        @param cname: class name for this schema
-        @type desc: str
-        @param desc: Human-readable description of the schema
-        @type _hash: str
-        @param _methods: hash computed on the body of this schema, if known
-        @type _props: map of 'name':<SchemaProperty> objects
-        @param _props: all properties provided by this schema
-        @type _pkey: list of strings
-        @param _pkey: names of each property to be used for constructing the primary key
-        @type _methods: map of 'name':<SchemaMethod> objects
-        @param _methods: all methods provided by this schema
-        """
-        if _map is not None:
-            super(SchemaObjectClass,self).__init__(_map=_map)
-        else:
-            super(SchemaObjectClass, self).__init__(_classId=_classId, _desc=_desc)
-            self._object_id_names = _object_id_names
-            for name,value in _props.iteritems():
-                self.set_value(name, value, self.SUBTYPE_PROPERTY)
-            for name,value in _methods.iteritems():
-                self.set_value(name, value, self.SUBTYPE_METHOD)
-
-        if self._classId.get_type() != SchemaClassId.TYPE_DATA:
-            raise TypeError("Invalid ClassId type for data schema: %s" % self._classId)
-
-    # map constructor
-    def __from_map(cls, map_):
-        return cls(_map=map_)
-    from_map = classmethod(__from_map)
-
-    def get_id_names(self): 
-        return self._object_id_names[:]
-
-    def get_method_count(self):
-        count = 0
-        for value in self._subtypes.itervalues():
-            if value == self.SUBTYPE_METHOD:
-                count += 1
-        return count
-
-    def get_methods(self):
-        meths = {}
-        for name,value in self._subtypes.iteritems():
-            if value == self.SUBTYPE_METHOD:
-                meths[name] = self._values.get(name)
-        return meths
-
-    def get_method(self, name):
-        if self._subtypes.get(name) == self.SUBTYPE_METHOD:
-            return self._values.get(name)
-        return None
-
-    def add_method(self, name, method): 
-        self.set_value(name, method, self.SUBTYPE_METHOD)
-        # need to re-generate schema hash
-        self._classId._hstr = None
-
-
-
-
-class SchemaEventClass(SchemaClass):
-    """
-    A schema class that describes an event.  The event is composed
-    of zero or more properties.
-    
-    Map format:
-    map["schema_id"] = map, SchemaClassId map for this object.
-    map["desc"] = string description of this schema
-    map["properties"] = map of "name":SchemaProperty values.
-    """
-    def __init__(self, _classId=None, _desc=None, _props={},
-                 _map=None):
-        if _map is not None:
-            super(SchemaEventClass,self).__init__(_map=_map)
-        else:
-            super(SchemaEventClass, self).__init__(_classId=_classId,
-                                                   _desc=_desc)
-            for name,value in _props.iteritems():
-                self.set_value(name, value, self.SUBTYPE_PROPERTY)
-
-        if self._classId.get_type() != SchemaClassId.TYPE_EVENT:
-            raise TypeError("Invalid ClassId type for event schema: %s" %
-                            self._classId)
-
-    # map constructor
-    def __from_map(cls, map_):
-        return cls(_map=map_)
-    from_map = classmethod(__from_map)
-
Index: extras/qmf/src/py/qmf2/tests/multi_response.py
===================================================================
--- extras/qmf/src/py/qmf2/tests/multi_response.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/tests/multi_response.py	(working copy)
@@ -1,280 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-import unittest
-import logging
-from threading import Thread, Event
-
-import qpid.messaging
-from qmf2.common import (Notifier, SchemaObjectClass, SchemaClassId,
-                         SchemaProperty, qmfTypes, SchemaMethod, QmfQuery,
-                         QmfData) 
-import qmf2.console
-from qmf2.agent import(QmfAgentData, Agent)
-
-# note: objects, schema per agent must each be > max objs
-_SCHEMAS_PER_AGENT=7
-_OBJS_PER_AGENT=19
-_MAX_OBJS_PER_MSG=3
-
-
-class _testNotifier(Notifier):
-    def __init__(self):
-        self._event = Event()
-
-    def indication(self):
-        # note: called by qmf daemon thread
-        self._event.set()
-
-    def wait_for_work(self, timeout):
-        # note: called by application thread to wait
-        # for qmf to generate work
-        self._event.wait(timeout)
-        timed_out = self._event.isSet() == False
-        if not timed_out:
-            self._event.clear()
-            return True
-        return False
-
-
-class _agentApp(Thread):
-    def __init__(self, name, broker_url, heartbeat):
-        Thread.__init__(self)
-        self.schema_count = _SCHEMAS_PER_AGENT
-        self.obj_count = _OBJS_PER_AGENT
-        self.notifier = _testNotifier()
-        self.broker_url = broker_url
-        self.agent = Agent(name,
-                           _notifier=self.notifier,
-                           heartbeat_interval=heartbeat,
-                           max_msg_size=_MAX_OBJS_PER_MSG)
-
-        # Dynamically construct a management database
-        for i in range(self.schema_count):
-            _schema = SchemaObjectClass( _classId=SchemaClassId("MyPackage",
-                                                                "MyClass-" + str(i)),
-                                         _desc="A test data schema",
-                                         _object_id_names=["index1", "index2"] )
-            # add properties
-            _schema.add_property( "index1", SchemaProperty(qmfTypes.TYPE_UINT8))
-            _schema.add_property( "index2", SchemaProperty(qmfTypes.TYPE_LSTR))
-
-            # these two properties are statistics
-            _schema.add_property( "query_count", SchemaProperty(qmfTypes.TYPE_UINT32))
-            _schema.add_property( "method_call_count", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-            # These two properties can be set via the method call
-            _schema.add_property( "set_string", SchemaProperty(qmfTypes.TYPE_LSTR))
-            _schema.add_property( "set_int", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-            # add method
-            _meth = SchemaMethod( _desc="Method to set string and int in object." )
-            _meth.add_argument( "arg_int", SchemaProperty(qmfTypes.TYPE_UINT32) )
-            _meth.add_argument( "arg_str", SchemaProperty(qmfTypes.TYPE_LSTR) )
-            _schema.add_method( "set_meth", _meth )
-
-            # Add schema to Agent
-
-            self.agent.register_object_class(_schema)
-
-            # instantiate managed data objects matching the schema
-
-            for j in range(self.obj_count):
-
-                self.agent.add_object( QmfAgentData( self.agent, _schema=_schema,
-                                                     _values={"index1":j,
-                                                              "index2": "name-" + str(j),
-                                                              "set_string": "UNSET",
-                                                              "set_int": 0,
-                                                              "query_count": 0,
-                                                              "method_call_count": 0} ))
-
-        self.running = False
-        self.ready = Event()
-
-    def start_app(self):
-        self.running = True
-        self.start()
-        self.ready.wait(10)
-        if not self.ready.is_set():
-            raise Exception("Agent failed to connect to broker.")
-
-    def stop_app(self):
-        self.running = False
-        # wake main thread
-        self.notifier.indication() # hmmm... collide with daemon???
-        self.join(10)
-        if self.isAlive():
-            raise Exception("AGENT DID NOT TERMINATE AS EXPECTED!!!")
-
-    def run(self):
-        # broker_url = "user/passwd@hostname:port"
-        self.conn = qpid.messaging.Connection(self.broker_url)
-        self.conn.open()
-        self.agent.set_connection(self.conn)
-        self.ready.set()
-
-        while self.running:
-            self.notifier.wait_for_work(None)
-            wi = self.agent.get_next_workitem(timeout=0)
-            while wi is not None:
-                logging.error("UNEXPECTED AGENT WORKITEM RECEIVED=%s" % wi.get_type())
-                self.agent.release_workitem(wi)
-                wi = self.agent.get_next_workitem(timeout=0)
-
-        if self.conn:
-            self.agent.remove_connection(10)
-        self.agent.destroy(10)
-
-
-
-
-class BaseTest(unittest.TestCase):
-    def configure(self, config):
-        self.agent_count = 2
-        self.config = config
-        self.broker = config.broker
-        self.defines = self.config.defines
-
-    def setUp(self):
-        # one second agent indication interval
-        self.agent_heartbeat = 1
-        self.agents = []
-        for a in range(self.agent_count):
-            agent = _agentApp("agent-" + str(a), 
-                              self.broker, 
-                              self.agent_heartbeat)
-            agent.start_app()
-            self.agents.append(agent)
-
-    def tearDown(self):
-        for agent in self.agents:
-            if agent is not None:
-                agent.stop_app()
-
-    def test_all_schema_id(self):
-        # create console
-        # find agents
-        # synchronous query for all schemas_ids
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for agent_app in self.agents:
-            agent = self.console.find_agent(agent_app.agent.get_name(), timeout=3)
-            self.assertTrue(agent and agent.get_name() == agent_app.agent.get_name())
-
-            # get a list of all schema_ids
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_SCHEMA_ID)
-            sid_list = self.console.do_query(agent, query)
-            self.assertTrue(sid_list and len(sid_list) == _SCHEMAS_PER_AGENT)
-            for sid in sid_list:
-                self.assertTrue(isinstance(sid, SchemaClassId))
-                self.assertTrue(sid.get_package_name() == "MyPackage")
-                self.assertTrue(sid.get_class_name().split('-')[0] == "MyClass")
-
-        self.console.destroy(10)
-
-
-    def test_all_schema(self):
-        # create console
-        # find agents
-        # synchronous query for all schemas
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for agent_app in self.agents:
-            agent = self.console.find_agent(agent_app.agent.get_name(), timeout=3)
-            self.assertTrue(agent and agent.get_name() == agent_app.agent.get_name())
-
-            # get a list of all schema_ids
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_SCHEMA)
-            schema_list = self.console.do_query(agent, query)
-            self.assertTrue(schema_list and 
-                            len(schema_list) == _SCHEMAS_PER_AGENT) 
-            for schema in schema_list:
-                self.assertTrue(isinstance(schema, SchemaObjectClass))
-
-        self.console.destroy(10)
-
-
-    def test_all_object_id(self):
-        # create console
-        # find agents
-        # synchronous query for all object_ids by schema_id
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for agent_app in self.agents:
-            agent = self.console.find_agent(agent_app.agent.get_name(), timeout=3)
-            self.assertTrue(agent and agent.get_name() == agent_app.agent.get_name())
-
-            # get a list of all schema_ids
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_SCHEMA_ID)
-            sid_list = self.console.do_query(agent, query)
-            self.assertTrue(sid_list and len(sid_list) == _SCHEMAS_PER_AGENT)
-            for sid in sid_list:
-                query = QmfQuery.create_wildcard_object_id(sid)
-                oid_list = self.console.do_query(agent, query)
-                self.assertTrue(oid_list and
-                                len(oid_list) == _OBJS_PER_AGENT) 
-                for oid in oid_list:
-                    self.assertTrue(isinstance(oid, basestring))
-
-        self.console.destroy(10)
-
-
-    def test_all_objects(self):
-        # create console
-        # find agents
-        # synchronous query for all objects by schema_id
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for agent_app in self.agents:
-            agent = self.console.find_agent(agent_app.agent.get_name(), timeout=3)
-            self.assertTrue(agent and agent.get_name() == agent_app.agent.get_name())
-
-            # get a list of all schema_ids
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_SCHEMA_ID)
-            sid_list = self.console.do_query(agent, query)
-            self.assertTrue(sid_list and len(sid_list) == _SCHEMAS_PER_AGENT)
-            for sid in sid_list:
-                query = QmfQuery.create_wildcard_object(sid)
-                obj_list = self.console.do_query(agent, query)
-                self.assertTrue(obj_list and
-                                len(obj_list) == _OBJS_PER_AGENT)
-                for obj in obj_list:
-                    self.assertTrue(isinstance(obj,
-                                               qmf2.console.QmfConsoleData))
-
-        self.console.destroy(10)
Index: extras/qmf/src/py/qmf2/tests/obj_gets.py
===================================================================
--- extras/qmf/src/py/qmf2/tests/obj_gets.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/tests/obj_gets.py	(working copy)
@@ -1,581 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-import unittest
-import logging
-import datetime
-from threading import Thread, Event
-
-import qpid.messaging
-from qmf2.common import (Notifier, SchemaObjectClass, SchemaClassId,
-                         SchemaProperty, qmfTypes, SchemaMethod, QmfQuery,
-                         QmfData)
-import qmf2.console
-from qmf2.agent import(QmfAgentData, Agent)
-
-
-class _testNotifier(Notifier):
-    def __init__(self):
-        self._event = Event()
-
-    def indication(self):
-        # note: called by qmf daemon thread
-        self._event.set()
-
-    def wait_for_work(self, timeout):
-        # note: called by application thread to wait
-        # for qmf to generate work
-        self._event.wait(timeout)
-        timed_out = self._event.isSet() == False
-        if not timed_out:
-            self._event.clear()
-            return True
-        return False
-
-
-class _agentApp(Thread):
-    def __init__(self, name, broker_url, heartbeat):
-        Thread.__init__(self)
-        self.notifier = _testNotifier()
-        self.broker_url = broker_url
-        self.agent = Agent(name,
-                           _notifier=self.notifier,
-                           heartbeat_interval=heartbeat)
-
-        # Management Database 
-        # - two different schema packages, 
-        # - two classes within one schema package
-        # - multiple objects per schema package+class
-        # - two "undescribed" objects
-
-        # "package1/class1"
-
-        _schema = SchemaObjectClass( _classId=SchemaClassId("package1", "class1"),
-                                     _desc="A test data schema - one",
-                                     _object_id_names=["key"] )
-
-        _schema.add_property( "key", SchemaProperty(qmfTypes.TYPE_LSTR))
-        _schema.add_property( "count1", SchemaProperty(qmfTypes.TYPE_UINT32))
-        _schema.add_property( "count2", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-        self.agent.register_object_class(_schema)
-
-        _obj = QmfAgentData( self.agent,
-                             _values={"key":"p1c1_key1"},
-                             _schema=_schema)
-        _obj.set_value("count1", 0)
-        _obj.set_value("count2", 0)
-        self.agent.add_object( _obj )
-
-        _obj = QmfAgentData( self.agent, 
-                             _values={"key":"p1c1_key2"},
-                             _schema=_schema )
-        _obj.set_value("count1", 9)
-        _obj.set_value("count2", 10)
-        self.agent.add_object( _obj )
-
-        # "package1/class2"
-
-        _schema = SchemaObjectClass( _classId=SchemaClassId("package1", "class2"),
-                                     _desc="A test data schema - two",
-                                     _object_id_names=["name"] )
-        # add properties
-        _schema.add_property( "name", SchemaProperty(qmfTypes.TYPE_LSTR))
-        _schema.add_property( "string1", SchemaProperty(qmfTypes.TYPE_LSTR))
-
-        self.agent.register_object_class(_schema)
-
-        _obj = QmfAgentData( self.agent, 
-                             _values={"name":"p1c2_name1"},
-                             _schema=_schema )
-        _obj.set_value("string1", "a data string")
-        self.agent.add_object( _obj )
-
-
-        # "package2/class1"
-
-        _schema = SchemaObjectClass( _classId=SchemaClassId("package2", "class1"),
-                                     _desc="A test data schema - second package",
-                                     _object_id_names=["key"] )
-
-        _schema.add_property( "key", SchemaProperty(qmfTypes.TYPE_LSTR))
-        _schema.add_property( "counter", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-        self.agent.register_object_class(_schema)
-
-        _obj = QmfAgentData( self.agent, 
-                             _values={"key":"p2c1_key1"},
-                             _schema=_schema )
-        _obj.set_value("counter", 0)
-        self.agent.add_object( _obj )
-
-        _obj = QmfAgentData( self.agent, 
-                             _values={"key":"p2c1_key2"},
-                             _schema=_schema )
-        _obj.set_value("counter", 2112)
-        self.agent.add_object( _obj )
-
-
-        # add two "unstructured" objects to the Agent
-
-        _obj = QmfAgentData(self.agent, _object_id="undesc-1")
-        _obj.set_value("field1", "a value")
-        _obj.set_value("field2", 2)
-        _obj.set_value("field3", {"a":1, "map":2, "value":3})
-        _obj.set_value("field4", ["a", "list", "value"])
-        self.agent.add_object(_obj)
-
-
-        _obj = QmfAgentData(self.agent, _object_id="undesc-2")
-        _obj.set_value("key-1", "a value")
-        _obj.set_value("key-2", 2)
-        self.agent.add_object(_obj)
-
-        self.running = False
-        self.ready = Event()
-
-    def start_app(self):
-        self.running = True
-        self.start()
-        self.ready.wait(10)
-        if not self.ready.is_set():
-            raise Exception("Agent failed to connect to broker.")
-
-    def stop_app(self):
-        self.running = False
-        self.notifier.indication() # hmmm... collide with daemon???
-        self.join(10)
-        if self.isAlive():
-            raise Exception("AGENT DID NOT TERMINATE AS EXPECTED!!!")
-
-    def run(self):
-        # broker_url = "user/passwd@hostname:port"
-        self.conn = qpid.messaging.Connection(self.broker_url)
-        self.conn.open()
-        self.agent.set_connection(self.conn)
-        self.ready.set()
-
-        while self.running:
-            self.notifier.wait_for_work(None)
-            wi = self.agent.get_next_workitem(timeout=0)
-            while wi is not None:
-                logging.error("UNEXPECTED AGENT WORKITEM RECEIVED=%s" % wi.get_type())
-                self.agent.release_workitem(wi)
-                wi = self.agent.get_next_workitem(timeout=0)
-
-        if self.conn:
-            self.agent.remove_connection(10)
-        self.agent.destroy(10)
-
-
-class BaseTest(unittest.TestCase):
-    agent_count = 5
-
-    def configure(self, config):
-        self.config = config
-        self.broker = config.broker
-        self.defines = self.config.defines
-
-    def setUp(self):
-        self.agents = []
-        for i in range(self.agent_count):
-            agent = _agentApp("agent-" + str(i), self.broker, 1)
-            agent.start_app()
-            self.agents.append(agent)
-        #print("!!!! STARTING TEST: %s" % datetime.datetime.utcnow())
-
-    def tearDown(self):
-        #print("!!!! STOPPING TEST: %s" % datetime.datetime.utcnow())
-        for agent in self.agents:
-            if agent is not None:
-                agent.stop_app()
-
-
-    def test_all_agents(self):
-        # create console
-        # find all agents
-        # synchronous query for all objects by id
-        # verify known object ids are returned
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for agent_app in self.agents:
-            aname = agent_app.agent.get_name()
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-        # console has discovered all agents, now query all undesc-2 objects
-        #print("!!!! STARTING GET: %s" % datetime.datetime.utcnow())
-        objs = self.console.get_objects(_object_id="undesc-2", _timeout=5)
-        #print("!!!! STOPPING GET: %s" % datetime.datetime.utcnow())
-        self.assertTrue(len(objs) == self.agent_count)
-        for obj in objs:
-            self.assertTrue(obj.get_object_id() == "undesc-2")
-
-        # now query all objects from schema "package1"
-        #print("!!!! STARTING GET: %s" % datetime.datetime.utcnow())
-        objs = self.console.get_objects(_pname="package1", _timeout=5)
-        #print("!!!! STOPPING GET: %s" % datetime.datetime.utcnow())
-        self.assertTrue(len(objs) == (self.agent_count * 3))
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package1")
-
-        # now query all objects from schema "package2"
-        #print("!!!! STARTING GET: %s" % datetime.datetime.utcnow())
-        objs = self.console.get_objects(_pname="package2", _timeout=5)
-        #print("!!!! STOPPING GET: %s" % datetime.datetime.utcnow())
-        self.assertTrue(len(objs) == (self.agent_count * 2))
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package2")
-
-        # now query all objects from schema "package1/class2"
-        #print("!!!! STARTING GET: %s" % datetime.datetime.utcnow())
-        objs = self.console.get_objects(_pname="package1", _cname="class2",
-                                        _timeout=5)
-        #print("!!!! STOPPING GET: %s" % datetime.datetime.utcnow())
-        self.assertTrue(len(objs) == self.agent_count)
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package1")
-            self.assertTrue(obj.get_schema_class_id().get_class_name() == "class2")
-
-        # given the schema identifier from the last query, repeat using the
-        # specific schema id
-        #print("!!!! STARTING GET: %s" % datetime.datetime.utcnow())
-        schema_id = objs[0].get_schema_class_id()
-        #print("!!!! STOPPING GET: %s" % datetime.datetime.utcnow())
-        objs = self.console.get_objects(_schema_id=schema_id, _timeout=5)
-        self.assertTrue(len(objs) == self.agent_count)
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id() == schema_id)
-
-
-        self.console.destroy(10)
-
-
-
-    def test_agent_subset(self):
-        # create console
-        # find all agents
-        # synchronous query for all objects by id
-        # verify known object ids are returned
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        agent_list = []
-        for agent_app in self.agents:
-            aname = agent_app.agent.get_name()
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-            agent_list.append(agent)
-
-        # Only use a subset of the agents:
-        agent_list = agent_list[:len(agent_list)/2]
-
-        # console has discovered all agents, now query all undesc-2 objects
-        objs = self.console.get_objects(_object_id="undesc-2",
-                                        _agents=agent_list, _timeout=5)
-        self.assertTrue(len(objs) == len(agent_list))
-        for obj in objs:
-            self.assertTrue(obj.get_object_id() == "undesc-2")
-
-        # now query all objects from schema "package1"
-        objs = self.console.get_objects(_pname="package1",
-                                        _agents=agent_list,
-                                        _timeout=5)
-        self.assertTrue(len(objs) == (len(agent_list) * 3))
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package1")
-
-        # now query all objects from schema "package2"
-        objs = self.console.get_objects(_pname="package2", 
-                                        _agents=agent_list,
-                                        _timeout=5)
-        self.assertTrue(len(objs) == (len(agent_list) * 2))
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package2")
-
-        # now query all objects from schema "package1/class2"
-        objs = self.console.get_objects(_pname="package1", _cname="class2", 
-                                        _agents=agent_list,
-                                        _timeout=5)
-        self.assertTrue(len(objs) == len(agent_list))
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package1")
-            self.assertTrue(obj.get_schema_class_id().get_class_name() == "class2")
-
-        # given the schema identifier from the last query, repeat using the
-        # specific schema id
-        schema_id = objs[0].get_schema_class_id()
-        objs = self.console.get_objects(_schema_id=schema_id, 
-                                        _agents=agent_list,
-                                        _timeout=5)
-        self.assertTrue(len(objs) == len(agent_list))
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id() == schema_id)
-
-
-        self.console.destroy(10)
-
-
-
-    def test_single_agent(self):
-        # create console
-        # find all agents
-        # synchronous query for all objects by id
-        # verify known object ids are returned
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        agent_list = []
-        for agent_app in self.agents:
-            aname = agent_app.agent.get_name()
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-            agent_list.append(agent)
-
-        # Only use one agent
-        agent = agent_list[0]
-
-        # console has discovered all agents, now query all undesc-2 objects
-        objs = self.console.get_objects(_object_id="undesc-2",
-                                        _agents=agent, _timeout=5)
-        self.assertTrue(len(objs) == 1)
-        for obj in objs:
-            self.assertTrue(obj.get_object_id() == "undesc-2")
-
-        # now query all objects from schema "package1"
-        objs = self.console.get_objects(_pname="package1",
-                                        _agents=agent,
-                                        _timeout=5)
-        self.assertTrue(len(objs) == 3)
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package1")
-
-        # now query all objects from schema "package2"
-        objs = self.console.get_objects(_pname="package2", 
-                                        _agents=agent,
-                                        _timeout=5)
-        self.assertTrue(len(objs) == 2)
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package2")
-
-        # now query all objects from schema "package1/class2"
-        objs = self.console.get_objects(_pname="package1", _cname="class2", 
-                                        _agents=agent,
-                                        _timeout=5)
-        self.assertTrue(len(objs) == 1)
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package1")
-            self.assertTrue(obj.get_schema_class_id().get_class_name() == "class2")
-
-        # given the schema identifier from the last query, repeat using the
-        # specific schema id
-        schema_id = objs[0].get_schema_class_id()
-        objs = self.console.get_objects(_schema_id=schema_id, 
-                                        _agents=agent,
-                                        _timeout=5)
-        self.assertTrue(len(objs) == 1)
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id() == schema_id)
-
-
-        self.console.destroy(10)
-
-
-
-    def test_all_objs_by_oid(self):
-        # create console
-        # find all agents
-        # synchronous query for all described objects by:
-        #    oid & schema_id
-        #    oid & package name
-        #    oid & package and class name
-        # verify known object ids are returned
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for agent_app in self.agents:
-            aname = agent_app.agent.get_name()
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-        # now query all objects from schema "package1"
-        #print("!!!! STARTING GET: %s" % datetime.datetime.utcnow())
-        objs = self.console.get_objects(_pname="package1",
-                                        _object_id="p1c1_key1", _timeout=5)
-        #print("!!!! STOPPING GET: %s" % datetime.datetime.utcnow())
-        self.assertTrue(len(objs) == self.agent_count)
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package1")
-            self.assertTrue(obj.get_schema_class_id().get_class_name() == "class1")
-            self.assertTrue(obj.get_object_id() == "p1c1_key1")
-        # mooch the schema for a later test
-        schema_id_p1c1 = objs[0].get_schema_class_id()
-
-        #print("!!!! STARTING GET: %s" % datetime.datetime.utcnow())
-        objs = self.console.get_objects(_pname="package1",
-                                        _object_id="p1c2_name1", _timeout=5)
-        #print("!!!! STOPPING GET: %s" % datetime.datetime.utcnow())
-        self.assertTrue(len(objs) == self.agent_count)
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package1")
-            self.assertTrue(obj.get_schema_class_id().get_class_name() == "class2")
-            self.assertTrue(obj.get_object_id() == "p1c2_name1")
-
-        #print("!!!! STARTING GET: %s" % datetime.datetime.utcnow())
-        objs = self.console.get_objects(_pname="package2", _cname="class1",
-                                        _object_id="p2c1_key1", _timeout=5)
-        #print("!!!! STOPPING GET: %s" % datetime.datetime.utcnow())
-        self.assertTrue(len(objs) == self.agent_count)
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package2")
-            self.assertTrue(obj.get_schema_class_id().get_class_name() == "class1")
-            self.assertTrue(obj.get_object_id() == "p2c1_key1")
-
-        #print("!!!! STARTING GET: %s" % datetime.datetime.utcnow())
-        objs = self.console.get_objects(_schema_id=schema_id_p1c1,
-                                        _object_id="p1c1_key2", _timeout=5)
-        #print("!!!! STOPPING GET: %s" % datetime.datetime.utcnow())
-        self.assertTrue(len(objs) == self.agent_count)
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package1")
-            self.assertTrue(obj.get_schema_class_id().get_class_name() == "class1")
-            self.assertTrue(obj.get_object_id() == "p1c1_key2")
-
-        # this should return all "undescribed" objects
-        #print("!!!! STARTING GET: %s" % datetime.datetime.utcnow())
-        objs = self.console.get_objects(_timeout=5)
-        #print("!!!! STOPPING GET: %s" % datetime.datetime.utcnow())
-        self.assertTrue(len(objs) == (self.agent_count * 2))
-        for obj in objs:
-            self.assertTrue(obj.get_object_id() == "undesc-1" or
-                            obj.get_object_id() == "undesc-2")
-
-        # these should fail
-        #print("!!!! STARTING GET: %s" % datetime.datetime.utcnow())
-        objs = self.console.get_objects(_schema_id=schema_id_p1c1,
-                                        _object_id="does not exist",
-                                        _timeout=5)
-        #print("!!!! STOPPING GET: %s" % datetime.datetime.utcnow())
-        self.assertTrue(objs == None)
-
-        #print("!!!! STARTING GET: %s" % datetime.datetime.utcnow())
-        objs = self.console.get_objects(_pname="package2",
-                                        _object_id="does not exist",
-                                        _timeout=5)
-        #print("!!!! STOPPING GET: %s" % datetime.datetime.utcnow())
-        self.assertTrue(objs == None)
-
-        #print("!!!! STARTING GET: %s" % datetime.datetime.utcnow())
-        objs = self.console.get_objects(_pname="package3",
-                                        _object_id="does not exist",
-                                        _timeout=5)
-        #print("!!!! STOPPING GET: %s" % datetime.datetime.utcnow())
-        self.assertTrue(objs == None)
-
-        self.console.destroy(10)
-
-
-    def test_wildcard_schema_id(self):
-        # create console
-        # find all agents
-        # synchronous query for all described objects by:
-        #    oid & wildcard schema_id
-        #    wildcard schema_id
-        # verify known object ids are returned
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for agent_app in self.agents:
-            aname = agent_app.agent.get_name()
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-        wild_schema_id = SchemaClassId("package1", "class1")
-        objs = self.console.get_objects(_schema_id=wild_schema_id, _timeout=5)
-        self.assertTrue(len(objs) == (self.agent_count * 2))
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package1")
-            self.assertTrue(obj.get_schema_class_id().get_class_name() == "class1")
-
-        wild_schema_id = SchemaClassId("package1", "class2")
-        objs = self.console.get_objects(_schema_id=wild_schema_id, _timeout=5)
-        self.assertTrue(len(objs) == self.agent_count)
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package1")
-            self.assertTrue(obj.get_schema_class_id().get_class_name() == "class2")
-            self.assertTrue(obj.get_object_id() == "p1c2_name1")
-
-        wild_schema_id = SchemaClassId("package2", "class1")
-        objs = self.console.get_objects(_schema_id=wild_schema_id, _timeout=5)
-        self.assertTrue(len(objs) == (self.agent_count * 2))
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package2")
-            self.assertTrue(obj.get_schema_class_id().get_class_name() == "class1")
-
-        wild_schema_id = SchemaClassId("package1", "class1")
-        objs = self.console.get_objects(_schema_id=wild_schema_id,
-                                        _object_id="p1c1_key2", _timeout=5)
-        self.assertTrue(len(objs) == self.agent_count)
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package1")
-            self.assertTrue(obj.get_schema_class_id().get_class_name() == "class1")
-            self.assertTrue(obj.get_object_id() == "p1c1_key2")
-
-        # should fail
-        objs = self.console.get_objects(_schema_id=wild_schema_id,
-                                        _object_id="does not exist",
-                                        _timeout=5)
-        self.assertTrue(objs == None)
-
-        wild_schema_id = SchemaClassId("package2", "class1")
-        objs = self.console.get_objects(_schema_id=wild_schema_id,
-                                        _object_id="p2c1_key2", _timeout=5)
-        self.assertTrue(len(objs) == self.agent_count)
-        for obj in objs:
-            self.assertTrue(obj.get_schema_class_id().get_package_name() == "package2")
-            self.assertTrue(obj.get_schema_class_id().get_class_name() == "class1")
-            self.assertTrue(obj.get_object_id() == "p2c1_key2")
-
-        # should fail
-        wild_schema_id = SchemaClassId("package1", "bad-class")
-        objs = self.console.get_objects(_schema_id=wild_schema_id,
-                                        _object_id="p1c1_key2", _timeout=5)
-        self.assertTrue(objs == None)
-
-        self.console.destroy(10)
-
Index: extras/qmf/src/py/qmf2/tests/agent_test.py
===================================================================
--- extras/qmf/src/py/qmf2/tests/agent_test.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/tests/agent_test.py	(working copy)
@@ -1,167 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-import logging
-import time
-import unittest
-from threading import Semaphore
-
-
-from qpid.messaging import *
-from qmf2.common import (qmfTypes, SchemaProperty, SchemaObjectClass, QmfData,
-                         QmfEvent, SchemaMethod, Notifier, SchemaClassId,
-                         WorkItem) 
-from qmf2.agent import (Agent, QmfAgentData)
-
-
-
-class ExampleNotifier(Notifier):
-    def __init__(self):
-        self._sema4 = Semaphore(0)   # locked
-
-    def indication(self):
-        self._sema4.release()
-
-    def waitForWork(self):
-        print("Waiting for event...")
-        self._sema4.acquire()
-        print("...event present")
-
-
-
-
-class QmfTest(unittest.TestCase):
-    def test_begin(self):
-        print("!!! being test")
-
-    def test_end(self):
-        print("!!! end test")
-
-
-#
-# An example agent application
-#
-
-
-if __name__ == '__main__':
-    _notifier = ExampleNotifier()
-    _agent = Agent( "qmf.testAgent", _notifier=_notifier )
-
-    # Dynamically construct a class schema
-
-    _schema = SchemaObjectClass( _classId=SchemaClassId("MyPackage", "MyClass"),
-                                 _desc="A test data schema",
-                                 _object_id_names=["index1", "index2"] )
-    # add properties
-    _schema.add_property( "index1", SchemaProperty(qmfTypes.TYPE_UINT8))
-    _schema.add_property( "index2", SchemaProperty(qmfTypes.TYPE_LSTR))
-
-    # these two properties are statistics
-    _schema.add_property( "query_count", SchemaProperty(qmfTypes.TYPE_UINT32))
-    _schema.add_property( "method_call_count", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-    # These two properties can be set via the method call
-    _schema.add_property( "set_string", SchemaProperty(qmfTypes.TYPE_LSTR))
-    _schema.add_property( "set_int", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-
-    # add method
-    _meth = SchemaMethod( _desc="Method to set string and int in object." )
-    _meth.add_argument( "arg_int", SchemaProperty(qmfTypes.TYPE_UINT32) )
-    _meth.add_argument( "arg_str", SchemaProperty(qmfTypes.TYPE_LSTR) )
-    _schema.add_method( "set_meth", _meth )
-
-    # Add schema to Agent
-
-    _agent.register_object_class(_schema)
-
-    # instantiate managed data objects matching the schema
-
-    _obj1 = QmfAgentData( _agent, _schema=_schema )
-    _obj1.set_value("index1", 100)
-    _obj1.set_value("index2", "a name" )
-    _obj1.set_value("set_string", "UNSET")
-    _obj1.set_value("set_int", 0)
-    _obj1.set_value("query_count", 0)
-    _obj1.set_value("method_call_count", 0)
-    _agent.add_object( _obj1 )
-
-    _agent.add_object( QmfAgentData( _agent, _schema=_schema,
-                                     _values={"index1":99, 
-                                              "index2": "another name",
-                                              "set_string": "UNSET",
-                                              "set_int": 0,
-                                              "query_count": 0,
-                                              "method_call_count": 0} ))
-
-    # add an "unstructured" object to the Agent
-    _obj2 = QmfAgentData(_agent, _object_id="01545")
-    _obj2.set_value("field1", "a value")
-    _obj2.set_value("field2", 2)
-    _obj2.set_value("field3", {"a":1, "map":2, "value":3})
-    _obj2.set_value("field4", ["a", "list", "value"])
-    _agent.add_object(_obj2)
-
-
-    ## Now connect to the broker
-
-    _c = Connection("localhost")
-    _c.connect()
-    _agent.setConnection(_c)
-
-    _error_data = QmfData.create({"code": -1, "description": "You made a boo-boo."})
-
-    _done = False
-    while not _done:
-        # try:
-        _notifier.waitForWork()
-
-        _wi = _agent.get_next_workitem(timeout=0)
-        while _wi:
-
-            if _wi.get_type() == WorkItem.METHOD_CALL:
-                mc = _wi.get_params()
-            
-                if mc.get_name() == "set_meth":
-                    print("!!! Calling 'set_meth' on Object_id = %s" % mc.get_object_id())
-                    print("!!! args='%s'" % str(mc.get_args()))
-                    print("!!! userid=%s" % str(mc.get_user_id()))
-                    print("!!! handle=%s" % _wi.get_handle())
-                    _agent.method_response(_wi.get_handle(),
-                                           {"rc1": 100, "rc2": "Success"})
-                else:
-                    print("!!! Unknown Method name = %s" % mc.get_name())
-                    _agent.method_response(_wi.get_handle(), _error=_error_data)
-            else:
-                print("TBD: work item %d:%s" % (_wi.get_type(), str(_wi.get_params())))
-
-            _agent.release_workitem(_wi)
-            _wi = _agent.get_next_workitem(timeout=0)
-            #    except:
-            #        print( "shutting down...")
-            #        _done = True
-
-    print( "Removing connection... TBD!!!" )
-    #_myConsole.remove_connection( _c, 10 )
-
-    print( "Destroying agent... TBD!!!" )
-    #_myConsole.destroy( 10 )
-
-    print( "******** agent test done ********" )
-
-
-
Index: extras/qmf/src/py/qmf2/tests/async_method.py
===================================================================
--- extras/qmf/src/py/qmf2/tests/async_method.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/tests/async_method.py	(working copy)
@@ -1,353 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-import unittest
-import logging
-from threading import Thread, Event
-
-import qpid.messaging
-from qmf2.common import (Notifier, SchemaObjectClass, SchemaClassId,
-                           SchemaProperty, qmfTypes, SchemaMethod, QmfQuery,
-                           QmfData, WorkItem) 
-import qmf2.console
-from qmf2.agent import(QmfAgentData, Agent, MethodCallParams)
-
-
-class _testNotifier(Notifier):
-    def __init__(self):
-        self._event = Event()
-
-    def indication(self):
-        # note: called by qmf daemon thread
-        self._event.set()
-
-    def wait_for_work(self, timeout):
-        # note: called by application thread to wait
-        # for qmf to generate work
-        self._event.wait(timeout)
-        timed_out = self._event.isSet() == False
-        if not timed_out:
-            self._event.clear()
-            return True
-        return False
-
-
-class _agentApp(Thread):
-    def __init__(self, name, broker_url, heartbeat):
-        Thread.__init__(self)
-        self.notifier = _testNotifier()
-        self.broker_url = broker_url
-        self.agent = Agent(name,
-                           _notifier=self.notifier,
-                           heartbeat_interval=heartbeat)
-
-        # Dynamically construct a management database
-
-        _schema = SchemaObjectClass( _classId=SchemaClassId("MyPackage", "MyClass"),
-                                     _desc="A test data schema",
-                                     _object_id_names=["index1", "index2"] )
-        # add properties
-        _schema.add_property( "index1", SchemaProperty(qmfTypes.TYPE_UINT8))
-        _schema.add_property( "index2", SchemaProperty(qmfTypes.TYPE_LSTR))
-
-        # these two properties are statistics
-        _schema.add_property( "query_count", SchemaProperty(qmfTypes.TYPE_UINT32))
-        _schema.add_property( "method_call_count", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-        # These two properties can be set via the method call
-        _schema.add_property( "set_string", SchemaProperty(qmfTypes.TYPE_LSTR))
-        _schema.add_property( "set_int", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-        # add method
-        _meth = SchemaMethod( _desc="Method to set string and int in object." )
-        _meth.add_argument( "arg_int", SchemaProperty(qmfTypes.TYPE_UINT32) )
-        _meth.add_argument( "arg_str", SchemaProperty(qmfTypes.TYPE_LSTR) )
-        # the input value of cookie is returned in the response
-        _meth.add_argument( "cookie", SchemaProperty(qmfTypes.TYPE_LSTR,
-                                                     kwargs={"dir":"IO"}))
-        _schema.add_method( "set_meth", _meth )
-
-        # Add schema to Agent
-
-        self.agent.register_object_class(_schema)
-
-        # instantiate managed data objects matching the schema
-
-        _obj1 = QmfAgentData( self.agent, _schema=_schema,
-                              _values={"index1":100, "index2":"a name"})
-        _obj1.set_value("set_string", "UNSET")
-        _obj1.set_value("set_int", 0)
-        _obj1.set_value("query_count", 0)
-        _obj1.set_value("method_call_count", 0)
-        self.agent.add_object( _obj1 )
-
-        self.agent.add_object( QmfAgentData( self.agent, _schema=_schema,
-                                             _values={"index1":99, 
-                                                      "index2": "another name",
-                                                      "set_string": "UNSET",
-                                                      "set_int": 0,
-                                                      "query_count": 0,
-                                                      "method_call_count": 0} ))
-
-        # add an "unstructured" object to the Agent
-        _obj2 = QmfAgentData(self.agent, _object_id="01545")
-        _obj2.set_value("field1", "a value")
-        _obj2.set_value("field2", 2)
-        _obj2.set_value("field3", {"a":1, "map":2, "value":3})
-        _obj2.set_value("field4", ["a", "list", "value"])
-        self.agent.add_object(_obj2)
-
-        self.running = False
-        self.ready = Event()
-
-    def start_app(self):
-        self.running = True
-        self.start()
-        self.ready.wait(10)
-        if not self.ready.is_set():
-            raise Exception("Agent failed to connect to broker.")
-
-    def stop_app(self):
-        self.running = False
-        # wake main thread
-        self.notifier.indication() # hmmm... collide with daemon???
-        self.join(10)
-        if self.isAlive():
-            raise Exception("AGENT DID NOT TERMINATE AS EXPECTED!!!")
-
-    def run(self):
-        # broker_url = "user/passwd@hostname:port"
-        self.conn = qpid.messaging.Connection(self.broker_url)
-        self.conn.open()
-        self.agent.set_connection(self.conn)
-        self.ready.set()
-
-        # Agent application main processing loop
-        while self.running:
-            self.notifier.wait_for_work(None)
-            wi = self.agent.get_next_workitem(timeout=0)
-            while wi is not None:
-                if wi.get_type() == WorkItem.METHOD_CALL:
-                    mc = wi.get_params()
-                    if not isinstance(mc, MethodCallParams):
-                        raise Exception("Unexpected method call parameters")
-
-                    if mc.get_name() == "set_meth":
-                        obj = self.agent.get_object(mc.get_object_id(),
-                                                    mc.get_schema_id())
-                        if obj is None:
-                            error_info = QmfData.create({"code": -2, 
-                                                         "description":
-                                                             "Bad Object Id."},
-                                                        _object_id="_error")
-                            self.agent.method_response(wi.get_handle(),
-                                                       _error=error_info)
-                        else:
-                            obj.inc_value("method_call_count")
-                            out_args = {"code" : 0}
-                            if "cookie" in mc.get_args():
-                                out_args["cookie"] = mc.get_args()["cookie"]
-                            if "arg_int" in mc.get_args():
-                                obj.set_value("set_int", mc.get_args()["arg_int"])
-                            if "arg_str" in mc.get_args():
-                                obj.set_value("set_string", mc.get_args()["arg_str"])
-                            self.agent.method_response(wi.get_handle(),
-                                                       out_args)
-                    elif mc.get_name() == "a_method":
-                        obj = self.agent.get_object(mc.get_object_id(),
-                                                    mc.get_schema_id())
-                        if obj is None:
-                            error_info = QmfData.create({"code": -3, 
-                                                         "description":
-                                                             "Unknown object id."},
-                                                        _object_id="_error")
-                            self.agent.method_response(wi.get_handle(),
-                                                       _error=error_info)
-                        elif obj.get_object_id() != "01545":
-                            error_info = QmfData.create( {"code": -4, 
-                                                          "description":
-                                                              "Unexpected id."},
-                                                         _object_id="_error")
-                            self.agent.method_response(wi.get_handle(),
-                                                       _error=error_info)
-                        else:
-                            args = mc.get_args()
-                            if ("arg1" in args and args["arg1"] == 1 and
-                                "arg2" in args and args["arg2"] == "Now set!"
-                                and "arg3" in args and args["arg3"] == 1966): 
-                                out_args = {"code" : 0}
-                                if "cookie" in mc.get_args():
-                                    out_args["cookie"] = mc.get_args()["cookie"]
-                                self.agent.method_response(wi.get_handle(),
-                                                           out_args)
-                            else:
-                                error_info = QmfData.create(
-                                    {"code": -5, 
-                                     "description":
-                                         "Bad Args."},
-                                    _object_id="_error")
-                                self.agent.method_response(wi.get_handle(),
-                                                           _error=error_info)
-                    else:
-                        error_info = QmfData.create( {"code": -1, 
-                                                     "description":
-                                                         "Unknown method call."},
-                                                     _object_id="_error")
-                        self.agent.method_response(wi.get_handle(), _error=error_info)
-
-                self.agent.release_workitem(wi)
-                wi = self.agent.get_next_workitem(timeout=0)
-
-        if self.conn:
-            self.agent.remove_connection(10)
-        self.agent.destroy(10)
-
-
-
-class BaseTest(unittest.TestCase):
-    def configure(self, config):
-        self.config = config
-        self.broker = config.broker
-        self.defines = self.config.defines
-
-    def setUp(self):
-        # one second agent heartbeat interval
-        self.agent_heartbeat = 1
-        self.agent1 = _agentApp("agent1", self.broker, self.agent_heartbeat)
-        self.agent1.start_app()
-        self.agent2 = _agentApp("agent2", self.broker, self.agent_heartbeat)
-        self.agent2.start_app()
-
-    def tearDown(self):
-        if self.agent1:
-            self.agent1.stop_app()
-            self.agent1 = None
-        if self.agent2:
-            self.agent2.stop_app()
-            self.agent2 = None
-
-    def test_described_obj(self):
-        # create console
-        # find agents
-        # synchronous query for all objects in schema
-        # method call on each object
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                            agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        i_count = 0
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_SCHEMA_ID)
-
-            sid_list = self.console.do_query(agent, query)
-            self.assertTrue(sid_list and len(sid_list) == 1)
-            for sid in sid_list:
-                t_params = {QmfData.KEY_SCHEMA_ID: sid}
-                query = QmfQuery.create_wildcard(QmfQuery.TARGET_OBJECT,
-                                                _target_params=t_params)
-                obj_list = self.console.do_query(agent, query)
-                self.assertTrue(len(obj_list) == 2)
-                for obj in obj_list:
-                    cookie = "cookie-" + str(i_count)
-                    i_count += 1
-                    mr = obj.invoke_method( "set_meth", 
-                                            {"arg_int": -99,
-                                             "arg_str": "Now set!",
-                                             "cookie": cookie},
-                                            _reply_handle=cookie,
-                                            _timeout=3)
-                    self.assertTrue(mr)
-
-        # done, now wait for async responses
-
-        r_count = 0
-        while self.notifier.wait_for_work(3):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                r_count += 1
-                self.assertTrue(wi.get_type() == WorkItem.METHOD_RESPONSE)
-                reply = wi.get_params()
-                self.assertTrue(isinstance(reply, qmf2.console.MethodResult))
-                self.assertTrue(reply.succeeded())
-                self.assertTrue(reply.get_argument("cookie") == wi.get_handle())
-
-                wi = self.console.get_next_workitem(timeout=0)
-
-        self.assertTrue(r_count == i_count)
-
-        self.console.destroy(10)
-
-
-    def test_managed_obj(self):
-        # create console
-        # find agents
-        # synchronous query for a managed object
-        # method call on each object
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        i_count = 0
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            query = QmfQuery.create_id(QmfQuery.TARGET_OBJECT, "01545")
-            obj_list = self.console.do_query(agent, query)
-
-            self.assertTrue(isinstance(obj_list, type([])))
-            self.assertTrue(len(obj_list) == 1)
-            obj = obj_list[0]
-
-            cookie = "cookie-" + str(i_count)
-            i_count += 1
-            mr = obj.invoke_method("a_method",
-                                   {"arg1": 1,
-                                    "arg2": "Now set!",
-                                    "arg3": 1966,
-                                    "cookie": cookie},
-                                   _reply_handle=cookie,
-                                   _timeout=3)
-            self.assertTrue(mr)
-
-        # done, now wait for async responses
-
-        r_count = 0
-        while self.notifier.wait_for_work(3):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                r_count += 1
-                self.assertTrue(wi.get_type() == WorkItem.METHOD_RESPONSE)
-                reply = wi.get_params()
-                self.assertTrue(isinstance(reply, qmf2.console.MethodResult))
-                self.assertTrue(reply.succeeded())
-                self.assertTrue(reply.get_argument("cookie") == wi.get_handle())
-
-                wi = self.console.get_next_workitem(timeout=0)
-
-        self.assertTrue(r_count == i_count)
-
-        self.console.destroy(10)
Index: extras/qmf/src/py/qmf2/tests/__init__.py
===================================================================
--- extras/qmf/src/py/qmf2/tests/__init__.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/tests/__init__.py	(working copy)
@@ -1,30 +0,0 @@
-# Do not delete - marks this directory as a python package.
-
-#
-# 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 agent_discovery
-import basic_query
-import basic_method
-import obj_gets
-import events
-import multi_response
-import async_query
-import async_method
-import subscriptions
Index: extras/qmf/src/py/qmf2/tests/basic_method.py
===================================================================
--- extras/qmf/src/py/qmf2/tests/basic_method.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/tests/basic_method.py	(working copy)
@@ -1,391 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-import unittest
-import logging
-from threading import Thread, Event
-
-import qpid.messaging
-from qmf2.common import (Notifier, SchemaObjectClass, SchemaClassId,
-                           SchemaProperty, qmfTypes, SchemaMethod, QmfQuery,
-                           QmfData, WorkItem) 
-import qmf2.console
-from qmf2.agent import(QmfAgentData, Agent, MethodCallParams)
-
-
-class _testNotifier(Notifier):
-    def __init__(self):
-        self._event = Event()
-
-    def indication(self):
-        # note: called by qmf daemon thread
-        self._event.set()
-
-    def wait_for_work(self, timeout):
-        # note: called by application thread to wait
-        # for qmf to generate work
-        self._event.wait(timeout)
-        timed_out = self._event.isSet() == False
-        if not timed_out:
-            self._event.clear()
-            return True
-        return False
-
-
-class _agentApp(Thread):
-    def __init__(self, name, broker_url, heartbeat):
-        Thread.__init__(self)
-        self.notifier = _testNotifier()
-        self.broker_url = broker_url
-        self.agent = Agent(name,
-                           _notifier=self.notifier,
-                           heartbeat_interval=heartbeat)
-
-        # Dynamically construct a management database
-
-        _schema = SchemaObjectClass( _classId=SchemaClassId("MyPackage", "MyClass"),
-                                     _desc="A test data schema",
-                                     _object_id_names=["index1", "index2"] )
-        # add properties
-        _schema.add_property( "index1", SchemaProperty(qmfTypes.TYPE_UINT8))
-        _schema.add_property( "index2", SchemaProperty(qmfTypes.TYPE_LSTR))
-
-        # these two properties are statistics
-        _schema.add_property( "query_count", SchemaProperty(qmfTypes.TYPE_UINT32))
-        _schema.add_property( "method_call_count", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-        # These two properties can be set via the method call
-        _schema.add_property( "set_string", SchemaProperty(qmfTypes.TYPE_LSTR))
-        _schema.add_property( "set_int", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-        # add method
-        _meth = SchemaMethod( _desc="Method to set string and int in object." )
-        _meth.add_argument( "arg_int", SchemaProperty(qmfTypes.TYPE_UINT32) )
-        _meth.add_argument( "arg_str", SchemaProperty(qmfTypes.TYPE_LSTR) )
-        _schema.add_method( "set_meth", _meth )
-
-        # Add schema to Agent
-
-        self.agent.register_object_class(_schema)
-
-        # instantiate managed data objects matching the schema
-
-        _obj1 = QmfAgentData( self.agent, _schema=_schema,
-                              _values={"index1":100, "index2":"a name"})
-        _obj1.set_value("set_string", "UNSET")
-        _obj1.set_value("set_int", 0)
-        _obj1.set_value("query_count", 0)
-        _obj1.set_value("method_call_count", 0)
-        self.agent.add_object( _obj1 )
-
-        self.agent.add_object( QmfAgentData( self.agent, _schema=_schema,
-                                             _values={"index1":99, 
-                                                      "index2": "another name",
-                                                      "set_string": "UNSET",
-                                                      "set_int": 0,
-                                                      "query_count": 0,
-                                                      "method_call_count": 0} ))
-
-        # add an "unstructured" object to the Agent
-        _obj2 = QmfAgentData(self.agent, _object_id="01545")
-        _obj2.set_value("field1", "a value")
-        _obj2.set_value("field2", 2)
-        _obj2.set_value("field3", {"a":1, "map":2, "value":3})
-        _obj2.set_value("field4", ["a", "list", "value"])
-        self.agent.add_object(_obj2)
-
-        self.running = False
-        self.ready = Event()
-
-    def start_app(self):
-        self.running = True
-        self.start()
-        self.ready.wait(10)
-        if not self.ready.is_set():
-            raise Exception("Agent failed to connect to broker.")
-
-    def stop_app(self):
-        self.running = False
-        # wake main thread
-        self.notifier.indication() # hmmm... collide with daemon???
-        self.join(10)
-        if self.isAlive():
-            raise Exception("AGENT DID NOT TERMINATE AS EXPECTED!!!")
-
-    def run(self):
-        # broker_url = "user/passwd@hostname:port"
-        self.conn = qpid.messaging.Connection(self.broker_url)
-        self.conn.open()
-        self.agent.set_connection(self.conn)
-        self.ready.set()
-
-        # Agent application main processing loop
-        while self.running:
-            self.notifier.wait_for_work(None)
-            wi = self.agent.get_next_workitem(timeout=0)
-            while wi is not None:
-                if wi.get_type() == WorkItem.METHOD_CALL:
-                    mc = wi.get_params()
-                    if not isinstance(mc, MethodCallParams):
-                        raise Exception("Unexpected method call parameters")
-
-                    if mc.get_name() == "set_meth":
-                        obj = self.agent.get_object(mc.get_object_id(),
-                                                    mc.get_schema_id())
-                        if obj is None:
-                            error_info = QmfData.create({"code": -2, 
-                                                         "description":
-                                                             "Bad Object Id."},
-                                                        _object_id="_error")
-                            self.agent.method_response(wi.get_handle(),
-                                                       _error=error_info)
-                        else:
-                            obj.inc_value("method_call_count")
-                            if "arg_int" in mc.get_args():
-                                obj.set_value("set_int", mc.get_args()["arg_int"])
-                            if "arg_str" in mc.get_args():
-                                obj.set_value("set_string", mc.get_args()["arg_str"])
-                            self.agent.method_response(wi.get_handle(),
-                                                       {"code" : 0})
-                    elif mc.get_name() == "a_method":
-                        obj = self.agent.get_object(mc.get_object_id(),
-                                                    mc.get_schema_id())
-                        if obj is None:
-                            error_info = QmfData.create({"code": -3, 
-                                                         "description":
-                                                             "Unknown object id."},
-                                                        _object_id="_error")
-                            self.agent.method_response(wi.get_handle(),
-                                                       _error=error_info)
-                        elif obj.get_object_id() != "01545":
-                            error_info = QmfData.create( {"code": -4, 
-                                                          "description":
-                                                              "Unexpected id."},
-                                                         _object_id="_error")
-                            self.agent.method_response(wi.get_handle(),
-                                                       _error=error_info)
-                        else:
-                            args = mc.get_args()
-                            if ("arg1" in args and args["arg1"] == 1 and
-                                "arg2" in args and args["arg2"] == "Now set!"
-                                and "arg3" in args and args["arg3"] == 1966): 
-                                self.agent.method_response(wi.get_handle(),
-                                                           {"code" : 0})
-                            else:
-                                error_info = QmfData.create(
-                                    {"code": -5, 
-                                     "description":
-                                         "Bad Args."},
-                                    _object_id="_error")
-                                self.agent.method_response(wi.get_handle(),
-                                                           _error=error_info)
-                    else:
-                        error_info = QmfData.create( {"code": -1, 
-                                                     "description":
-                                                         "Unknown method call."},
-                                                     _object_id="_error")
-                        self.agent.method_response(wi.get_handle(), _error=error_info)
-
-                self.agent.release_workitem(wi)
-                wi = self.agent.get_next_workitem(timeout=0)
-
-        if self.conn:
-            self.agent.remove_connection(10)
-        self.agent.destroy(10)
-
-
-
-class BaseTest(unittest.TestCase):
-    def configure(self, config):
-        self.config = config
-        self.broker = config.broker
-        self.defines = self.config.defines
-
-    def setUp(self):
-        # one second agent heartbeat interval
-        self.agent_heartbeat = 1
-        self.agent1 = _agentApp("agent1", self.broker, self.agent_heartbeat)
-        self.agent1.start_app()
-        self.agent2 = _agentApp("agent2", self.broker, self.agent_heartbeat)
-        self.agent2.start_app()
-
-    def tearDown(self):
-        if self.agent1:
-            self.agent1.stop_app()
-            self.agent1 = None
-        if self.agent2:
-            self.agent2.stop_app()
-            self.agent2 = None
-
-    def test_described_obj(self):
-        # create console
-        # find agents
-        # synchronous query for all objects in schema
-        # method call on each object
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                            agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_SCHEMA_ID)
-
-            sid_list = self.console.do_query(agent, query)
-            self.assertTrue(sid_list and len(sid_list) == 1)
-            for sid in sid_list:
-                t_params = {QmfData.KEY_SCHEMA_ID: sid}
-                query = QmfQuery.create_wildcard(QmfQuery.TARGET_OBJECT,
-                                                _target_params=t_params)
-                obj_list = self.console.do_query(agent, query)
-                self.assertTrue(len(obj_list) == 2)
-                for obj in obj_list:
-                    mr = obj.invoke_method( "set_meth", {"arg_int": -99,
-                                                         "arg_str": "Now set!"},
-                                            _timeout=3)
-                    self.assertTrue(isinstance(mr, qmf2.console.MethodResult))
-                    self.assertTrue(mr.succeeded())
-                    self.assertTrue(mr.get_argument("code") == 0)
-
-                    self.assertTrue(obj.get_value("method_call_count") == 0)
-                    self.assertTrue(obj.get_value("set_string") == "UNSET")
-                    self.assertTrue(obj.get_value("set_int") == 0)
-
-                    obj.refresh()
-
-                    self.assertTrue(obj.get_value("method_call_count") == 1)
-                    self.assertTrue(obj.get_value("set_string") == "Now set!")
-                    self.assertTrue(obj.get_value("set_int") == -99)
-
-        self.console.destroy(10)
-
-
-    def test_bad_method_schema(self):
-        # create console
-        # find agents
-        # synchronous query for all objects with schema
-        # invalid method call on each object
-        #  - should throw a ValueError - NOT YET.
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_SCHEMA_ID)
-
-            sid_list = self.console.do_query(agent, query)
-            self.assertTrue(sid_list and len(sid_list) == 1)
-            for sid in sid_list:
-
-                t_params = {QmfData.KEY_SCHEMA_ID: sid}
-                query = QmfQuery.create_predicate(QmfQuery.TARGET_OBJECT,
-                                                  [QmfQuery.TRUE],
-                                                  _target_params=t_params)
-
-                obj_list = self.console.do_query(agent, query)
-                self.assertTrue(len(obj_list) == 2)
-                for obj in obj_list:
-                    mr = obj.invoke_method("unknown_method",
-                                           {"arg1": -99, "arg2": "Now set!"},
-                                           _timeout=3)
-                    # self.failUnlessRaises(ValueError,
-                    #                       obj.invoke_method,
-                    #                       "unknown_meth", 
-                    #                       {"arg1": -99, "arg2": "Now set!"},
-                    #                       _timeout=3)
-                    self.assertTrue(isinstance(mr, qmf2.console.MethodResult))
-                    self.assertFalse(mr.succeeded())
-                    self.assertTrue(isinstance(mr.get_exception(), QmfData))
-
-        self.console.destroy(10)
-
-    def test_bad_method_no_schema(self):
-        # create console
-        # find agents
-        # synchronous query for all objects with no schema
-        # invalid method call on each object
-        #  - should throw a ValueError
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_OBJECT)
-
-            obj_list = self.console.do_query(agent, query)
-            self.assertTrue(len(obj_list) == 1)
-            for obj in obj_list:
-                self.assertTrue(obj.get_schema_class_id() == None)
-                mr = obj.invoke_method("unknown_meth", 
-                                       {"arg1": -99, "arg2": "Now set!"},
-                                       _timeout=3)
-                self.assertTrue(isinstance(mr, qmf2.console.MethodResult))
-                self.assertFalse(mr.succeeded())
-                self.assertTrue(isinstance(mr.get_exception(), QmfData))
-
-        self.console.destroy(10)
-
-    def test_managed_obj(self):
-        # create console
-        # find agents
-        # synchronous query for a managed object
-        # method call on each object
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            query = QmfQuery.create_id(QmfQuery.TARGET_OBJECT, "01545")
-            obj_list = self.console.do_query(agent, query)
-
-            self.assertTrue(isinstance(obj_list, type([])))
-            self.assertTrue(len(obj_list) == 1)
-            obj = obj_list[0]
-
-            mr = obj.invoke_method("a_method",
-                                   {"arg1": 1,
-                                    "arg2": "Now set!",
-                                    "arg3": 1966},
-                                   _timeout=3)
-            self.assertTrue(isinstance(mr, qmf2.console.MethodResult))
-            self.assertTrue(mr.succeeded())
-            self.assertTrue(mr.get_argument("code") == 0)
-            # @todo refresh and verify changes
-
-        self.console.destroy(10)
Index: extras/qmf/src/py/qmf2/tests/console_test.py
===================================================================
--- extras/qmf/src/py/qmf2/tests/console_test.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/tests/console_test.py	(working copy)
@@ -1,175 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-import logging
-import time
-from threading import Semaphore
-
-
-from qpid.messaging import *
-from qmf2.common import (Notifier, QmfQuery, QmfQueryPredicate, MsgKey,
-                       SchemaClassId, SchemaClass, QmfData) 
-from qmf2.console import Console
-
-
-class ExampleNotifier(Notifier):
-    def __init__(self):
-        self._sema4 = Semaphore(0)   # locked
-
-    def indication(self):
-        self._sema4.release()
-
-    def waitForWork(self):
-        print("Waiting for event...")
-        self._sema4.acquire()
-        print("...event present")
-
-
-logging.getLogger().setLevel(logging.INFO)
-
-print( "Starting Connection" )
-_c = Connection("localhost")
-_c.connect()
-
-print( "Starting Console" )
-
-_notifier = ExampleNotifier()
-_myConsole = Console(notifier=_notifier)
-_myConsole.addConnection( _c )
-
-# Allow discovery only for the agent named "qmf.testAgent"
-# @todo: replace "manual" query construction with 
-# a formal class-based Query API
-_query = QmfQuery.create_predicate(QmfQuery.TARGET_AGENT, 
-                                   QmfQueryPredicate({QmfQuery.CMP_EQ:
-                                                          [QmfQuery.KEY_AGENT_NAME,
-                                                           "qmf.testAgent"]}))
-_myConsole.enable_agent_discovery(_query)
-
-_done = False
-while not _done:
-#    try:
-    _notifier.waitForWork()
-
-    _wi = _myConsole.get_next_workitem(timeout=0)
-    while _wi:
-        print("!!! work item received %d:%s" % (_wi.get_type(),
-                                                str(_wi.get_params())))
-
-
-        if _wi.get_type() == _wi.AGENT_ADDED:
-            _agent = _wi.get_params().get("agent")
-            if not _agent:
-                print("!!!! AGENT IN REPLY IS NULL !!! ")
-
-            _query = QmfQuery.create_wildcard(QmfQuery.TARGET_OBJECT_ID)
-            oid_list = _myConsole.doQuery(_agent, _query)
-
-            print("!!!************************** REPLY=%s" % oid_list)
-
-            for oid in oid_list:
-                _query = QmfQuery.create_id(QmfQuery.TARGET_OBJECT, 
-                                            oid)
-                obj_list = _myConsole.doQuery(_agent, _query)
-
-                print("!!!************************** REPLY=%s" % obj_list)
-
-                if obj_list is None:
-                    obj_list={}
-
-                for obj in obj_list:
-                    resp = obj.invoke_method( "set_meth", 
-                                              {"arg_int": -11,
-                                               "arg_str": "are we not goons?"},
-                                              None,
-                                              3)
-                    if resp is None:
-                        print("!!!*** NO RESPONSE FROM METHOD????") 
-                    else:
-                        print("!!! method succeeded()=%s" % resp.succeeded())
-                        print("!!! method exception()=%s" % resp.get_exception())
-                        print("!!! method get args() = %s" % resp.get_arguments())
-
-                    if not obj.is_described():
-                        resp = obj.invoke_method( "bad method", 
-                                                  {"arg_int": -11,
-                                                   "arg_str": "are we not goons?"},
-                                                  None,
-                                                  3)
-                        if resp is None:
-                            print("!!!*** NO RESPONSE FROM METHOD????") 
-                        else:
-                            print("!!! method succeeded()=%s" % resp.succeeded())
-                            print("!!! method exception()=%s" % resp.get_exception())
-                            print("!!! method get args() = %s" % resp.get_arguments())
-
-
-            #---------------------------------
-            #_query = QmfQuery.create_id(QmfQuery.TARGET_OBJECT, "99another name")
-
-            #obj_list = _myConsole.doQuery(_agent, _query)
-
-            #---------------------------------
-
-            # _query = QmfQuery.create_wildcard(QmfQuery.TARGET_PACKAGES)
-
-            # package_list = _myConsole.doQuery(_agent, _query)
-
-            # for pname in package_list:
-            #     print("!!! Querying for schema from package: %s" % pname)
-            #     _query = QmfQuery.create_predicate(QmfQuery.TARGET_SCHEMA_ID,
-            #                                        QmfQueryPredicate(
-            #             {QmfQuery.CMP_EQ: [SchemaClassId.KEY_PACKAGE, pname]}))
-
-            #     schema_id_list = _myConsole.doQuery(_agent, _query)
-            #     for sid in schema_id_list:
-            #         _query = QmfQuery.create_predicate(QmfQuery.TARGET_SCHEMA,
-            #                                            QmfQueryPredicate(
-            #                 {QmfQuery.CMP_EQ: [SchemaClass.KEY_SCHEMA_ID,
-            #                                    sid.map_encode()]}))
-
-            #         schema_list = _myConsole.doQuery(_agent, _query)
-            #         for schema in schema_list:
-            #             sid = schema.get_class_id()
-            #             _query = QmfQuery.create_predicate(
-            #                 QmfQuery.TARGET_OBJECT_ID,
-            #                 QmfQueryPredicate({QmfQuery.CMP_EQ:
-            #                                        [QmfData.KEY_SCHEMA_ID,
-            #                                         sid.map_encode()]}))
-
-            #             oid_list = _myConsole.doQuery(_agent, _query)
-            #             for oid in oid_list:
-            #                 _query = QmfQuery.create_id(
-            #                     QmfQuery.TARGET_OBJECT, oid)
-            #                 _reply = _myConsole.doQuery(_agent, _query)
-
-            #                 print("!!!************************** REPLY=%s" % _reply)
-
-
-        _myConsole.release_workitem(_wi)
-        _wi = _myConsole.get_next_workitem(timeout=0)
-#    except:
-#        logging.info( "shutting down..." )
-#        _done = True
-
-print( "Removing connection" )
-_myConsole.removeConnection( _c, 10 )
-
-print( "Destroying console:" )
-_myConsole.destroy( 10 )
-
-print( "******** console test done ********" )
Index: extras/qmf/src/py/qmf2/tests/async_query.py
===================================================================
--- extras/qmf/src/py/qmf2/tests/async_query.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/tests/async_query.py	(working copy)
@@ -1,444 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-import unittest
-import logging
-from threading import Thread, Event
-
-import qpid.messaging
-from qmf2.common import (Notifier, SchemaObjectClass, SchemaClassId,
-                           SchemaProperty, qmfTypes, SchemaMethod, QmfQuery,
-                           QmfData, WorkItem)
-import qmf2.console
-from qmf2.agent import(QmfAgentData, Agent)
-
-
-class _testNotifier(Notifier):
-    def __init__(self):
-        self._event = Event()
-
-    def indication(self):
-        # note: called by qmf daemon thread
-        self._event.set()
-
-    def wait_for_work(self, timeout):
-        # note: called by application thread to wait
-        # for qmf to generate work
-        self._event.wait(timeout)
-        timed_out = self._event.isSet() == False
-        if not timed_out:
-            self._event.clear()
-            return True
-        return False
-
-
-class _agentApp(Thread):
-    def __init__(self, name, broker_url, heartbeat):
-        Thread.__init__(self)
-        self.notifier = _testNotifier()
-        self.broker_url = broker_url
-        self.agent = Agent(name,
-                           _notifier=self.notifier,
-                           heartbeat_interval=heartbeat)
-
-        # Dynamically construct a management database
-
-        _schema = SchemaObjectClass( _classId=SchemaClassId("MyPackage", "MyClass"),
-                                     _desc="A test data schema",
-                                     _object_id_names=["index1", "index2"] )
-        # add properties
-        _schema.add_property( "index1", SchemaProperty(qmfTypes.TYPE_UINT8))
-        _schema.add_property( "index2", SchemaProperty(qmfTypes.TYPE_LSTR))
-
-        # these two properties are statistics
-        _schema.add_property( "query_count", SchemaProperty(qmfTypes.TYPE_UINT32))
-        _schema.add_property( "method_call_count", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-        # These two properties can be set via the method call
-        _schema.add_property( "set_string", SchemaProperty(qmfTypes.TYPE_LSTR))
-        _schema.add_property( "set_int", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-        # add method
-        _meth = SchemaMethod( _desc="Method to set string and int in object." )
-        _meth.add_argument( "arg_int", SchemaProperty(qmfTypes.TYPE_UINT32) )
-        _meth.add_argument( "arg_str", SchemaProperty(qmfTypes.TYPE_LSTR) )
-        _schema.add_method( "set_meth", _meth )
-
-        # Add schema to Agent
-
-        self.agent.register_object_class(_schema)
-
-        # instantiate managed data objects matching the schema
-
-        _obj1 = QmfAgentData( self.agent, _schema=_schema,
-                              _values={"index1":100, "index2":"a name"})
-        _obj1.set_value("set_string", "UNSET")
-        _obj1.set_value("set_int", 0)
-        _obj1.set_value("query_count", 0)
-        _obj1.set_value("method_call_count", 0)
-        self.agent.add_object( _obj1 )
-
-        self.agent.add_object( QmfAgentData( self.agent, _schema=_schema,
-                                             _values={"index1":99,
-                                                      "index2": "another name",
-                                                      "set_string": "UNSET",
-                                                      "set_int": 0,
-                                                      "query_count": 0,
-                                                      "method_call_count": 0} ))
-
-        self.agent.add_object( QmfAgentData( self.agent, _schema=_schema,
-                                             _values={"index1":50,
-                                                      "index2": "my name",
-                                                      "set_string": "SET",
-                                                      "set_int": 0,
-                                                      "query_count": 0,
-                                                      "method_call_count": 0} ))
-
-
-        # add an "unstructured" object to the Agent
-        _obj2 = QmfAgentData(self.agent, _object_id="01545")
-        _obj2.set_value("field1", "a value")
-        _obj2.set_value("field2", 2)
-        _obj2.set_value("field3", {"a":1, "map":2, "value":3})
-        _obj2.set_value("field4", ["a", "list", "value"])
-        _obj2.set_value("index1", 50)
-        self.agent.add_object(_obj2)
-
-        _obj2 = QmfAgentData(self.agent, _object_id="01546")
-        _obj2.set_value("field1", "a value")
-        _obj2.set_value("field2", 3)
-        _obj2.set_value("field3", {"a":1, "map":2, "value":3})
-        _obj2.set_value("field4", ["a", "list", "value"])
-        _obj2.set_value("index1", 51)
-        self.agent.add_object(_obj2)
-
-        _obj2 = QmfAgentData(self.agent, _object_id="01544")
-        _obj2.set_value("field1", "a value")
-        _obj2.set_value("field2", 4)
-        _obj2.set_value("field3", {"a":1, "map":2, "value":3})
-        _obj2.set_value("field4", ["a", "list", "value"])
-        _obj2.set_value("index1", 49)
-        self.agent.add_object(_obj2)
-
-        _obj2 = QmfAgentData(self.agent, _object_id="01543")
-        _obj2.set_value("field1", "a value")
-        _obj2.set_value("field2", 4)
-        _obj2.set_value("field3", {"a":1, "map":2, "value":3})
-        _obj2.set_value("field4", ["a", "list", "value"])
-        _obj2.set_value("index1", 48)
-        self.agent.add_object(_obj2)
-
-        self.running = False
-        self.ready = Event()
-
-    def start_app(self):
-        self.running = True
-        self.start()
-        self.ready.wait(10)
-        if not self.ready.is_set():
-            raise Exception("Agent failed to connect to broker.")
-
-    def stop_app(self):
-        self.running = False
-        # wake main thread
-        self.notifier.indication() # hmmm... collide with daemon???
-        self.join(10)
-        if self.isAlive():
-            raise Exception("AGENT DID NOT TERMINATE AS EXPECTED!!!")
-
-    def run(self):
-        # broker_url = "user/passwd@hostname:port"
-        self.conn = qpid.messaging.Connection(self.broker_url)
-        self.conn.open()
-        self.agent.set_connection(self.conn)
-        self.ready.set()
-
-        while self.running:
-            self.notifier.wait_for_work(None)
-            wi = self.agent.get_next_workitem(timeout=0)
-            while wi is not None:
-                logging.error("UNEXPECTED AGENT WORKITEM RECEIVED=%s" % wi.get_type())
-                self.agent.release_workitem(wi)
-                wi = self.agent.get_next_workitem(timeout=0)
-
-        if self.conn:
-            self.agent.remove_connection(10)
-        self.agent.destroy(10)
-
-
-
-
-class BaseTest(unittest.TestCase):
-    def configure(self, config):
-        self.config = config
-        self.broker = config.broker
-        self.defines = self.config.defines
-
-    def setUp(self):
-        # one second agent indication interval
-        self.agent_heartbeat = 1
-        self.agent1 = _agentApp("agent1", self.broker, self.agent_heartbeat)
-        self.agent1.start_app()
-        self.agent2 = _agentApp("agent2", self.broker, self.agent_heartbeat)
-        self.agent2.start_app()
-
-    def tearDown(self):
-        if self.agent1:
-            self.agent1.stop_app()
-            self.agent1 = None
-        if self.agent2:
-            self.agent2.stop_app()
-            self.agent2 = None
-
-    def test_all_schema_ids(self):
-        # create console
-        # find agents
-        # asynchronous query for all schema ids
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            # send queries
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_SCHEMA_ID)
-            rc = self.console.do_query(agent, query,
-                                       _reply_handle=aname)
-            self.assertTrue(rc)
-
-        # done.  Now wait for async responses
-
-        count = 0
-        while self.notifier.wait_for_work(3):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                count += 1
-                self.assertTrue(wi.get_type() == WorkItem.QUERY_COMPLETE)
-                self.assertTrue(wi.get_handle() == "agent1" or
-                                wi.get_handle() == "agent2")
-                reply = wi.get_params()
-                self.assertTrue(len(reply) == 1)
-                self.assertTrue(isinstance(reply[0], SchemaClassId))
-                self.assertTrue(reply[0].get_package_name() == "MyPackage")
-                self.assertTrue(reply[0].get_class_name() == "MyClass")
-                self.console.release_workitem(wi)
-                wi = self.console.get_next_workitem(timeout=0)
-
-        self.assertTrue(count == 2)
-        self.console.destroy(10)
-
-
-
-    def test_undescribed_objs(self):
-        # create console
-        # find agents
-        # asynchronous query for all non-schema objects
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            # send queries
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_OBJECT)
-            rc = self.console.do_query(agent, query, _reply_handle=aname)
-            self.assertTrue(rc)
-
-        # done.  Now wait for async responses
-
-        count = 0
-        while self.notifier.wait_for_work(3):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                count += 1
-                self.assertTrue(wi.get_type() == WorkItem.QUERY_COMPLETE)
-                self.assertTrue(wi.get_handle() == "agent1" or
-                                wi.get_handle() == "agent2")
-                reply = wi.get_params()
-                self.assertTrue(len(reply) == 4)
-                self.assertTrue(isinstance(reply[0], qmf2.console.QmfConsoleData))
-                self.assertFalse(reply[0].is_described()) # no schema
-                self.console.release_workitem(wi)
-                wi = self.console.get_next_workitem(timeout=0)
-
-        self.assertTrue(count == 2)
-        self.console.destroy(10)
-
-
-
-    def test_described_objs(self):
-        # create console
-        # find agents
-        # asynchronous query for all schema-based objects
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            #
-            t_params = {QmfData.KEY_SCHEMA_ID: SchemaClassId("MyPackage", "MyClass")}
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_OBJECT, t_params)
-            #
-            rc = self.console.do_query(agent, query, _reply_handle=aname)
-            self.assertTrue(rc)
-
-        # done.  Now wait for async responses
-
-        count = 0
-        while self.notifier.wait_for_work(3):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                count += 1
-                self.assertTrue(wi.get_type() == WorkItem.QUERY_COMPLETE)
-                self.assertTrue(wi.get_handle() == "agent1" or
-                                wi.get_handle() == "agent2")
-                reply = wi.get_params()
-                self.assertTrue(len(reply) == 3)
-                self.assertTrue(isinstance(reply[0], qmf2.console.QmfConsoleData))
-                self.assertTrue(reply[0].is_described()) # has schema
-                self.console.release_workitem(wi)
-                wi = self.console.get_next_workitem(timeout=0)
-
-        self.assertTrue(count == 2)
-        # @todo test if the console has learned the corresponding schemas....
-        self.console.destroy(10)
-
-
-
-    def test_all_schemas(self):
-        # create console
-        # find agents
-        # asynchronous query for all schemas
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        # test internal state using non-api calls:
-        # no schemas present yet
-        self.assertTrue(len(self.console._schema_cache) == 0)
-        # end test
-
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            # send queries
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_SCHEMA)
-            rc = self.console.do_query(agent, query, _reply_handle=aname)
-            self.assertTrue(rc)
-
-        # done.  Now wait for async responses
-
-        count = 0
-        while self.notifier.wait_for_work(3):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                count += 1
-                self.assertTrue(wi.get_type() == WorkItem.QUERY_COMPLETE)
-                self.assertTrue(wi.get_handle() == "agent1" or
-                                wi.get_handle() == "agent2")
-                reply = wi.get_params()
-                self.assertTrue(len(reply) == 1)
-                self.assertTrue(isinstance(reply[0], qmf2.common.SchemaObjectClass))
-                self.assertTrue(reply[0].get_class_id().get_package_name() == "MyPackage")
-                self.assertTrue(reply[0].get_class_id().get_class_name() == "MyClass")
-                self.console.release_workitem(wi)
-                wi = self.console.get_next_workitem(timeout=0)
-
-        self.assertTrue(count == 2)
-
-        # test internal state using non-api calls:
-        # schema has been learned
-        self.assertTrue(len(self.console._schema_cache) == 1)
-        # end test
-
-        self.console.destroy(10)
-
-
-
-    def test_query_expiration(self):
-        # create console
-        # find agents
-        # kill the agents
-        # send async query
-        # wait for & verify expiration
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=30)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        # find the agents
-        agents = []
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-            agents.append(agent)
-
-        # now nuke the agents from orbit.  It's the only way to be sure.
-
-        self.agent1.stop_app()
-        self.agent1 = None
-        self.agent2.stop_app()
-        self.agent2 = None
-
-        # now send queries to agents that no longer exist
-        for agent in agents:
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_SCHEMA)
-            rc = self.console.do_query(agent, query,
-                                       _reply_handle=agent.get_name(),
-                                       _timeout=2)
-            self.assertTrue(rc)
-
-        # done.  Now wait for async responses due to timeouts
-
-        count = 0
-        while self.notifier.wait_for_work(3):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                count += 1
-                self.assertTrue(wi.get_type() == WorkItem.QUERY_COMPLETE)
-                self.assertTrue(wi.get_handle() == "agent1" or
-                                wi.get_handle() == "agent2")
-                reply = wi.get_params()
-                self.assertTrue(len(reply) == 0)  # empty
-
-                self.console.release_workitem(wi)
-                wi = self.console.get_next_workitem(timeout=0)
-
-        self.assertTrue(count == 2)
-        self.console.destroy(10)
Index: extras/qmf/src/py/qmf2/tests/events.py
===================================================================
--- extras/qmf/src/py/qmf2/tests/events.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/tests/events.py	(working copy)
@@ -1,202 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-import unittest
-import time
-import datetime
-import logging
-from threading import Thread, Event
-
-import qpid.messaging
-from qpid.harness import Skipped
-from qmf2.common import (Notifier, SchemaObjectClass, SchemaClassId,
-                         SchemaProperty, qmfTypes, SchemaMethod, QmfQuery,
-                         QmfData, SchemaEventClass,
-                         QmfEvent)
-import qmf2.console
-from qmf2.agent import(QmfAgentData, Agent)
-
-
-class _testNotifier(Notifier):
-    def __init__(self):
-        self._event = Event()
-
-    def indication(self):
-        # note: called by qmf daemon thread
-        self._event.set()
-
-    def wait_for_work(self, timeout):
-        # note: called by application thread to wait
-        # for qmf to generate work
-        self._event.wait(timeout)
-        timed_out = self._event.isSet() == False
-        if not timed_out:
-            self._event.clear()
-            return True
-        return False
-
-
-class _agentApp(Thread):
-    def __init__(self, name, broker_url, heartbeat):
-        Thread.__init__(self)
-        self.timeout = 3
-        self.broker_url = broker_url
-        self.notifier = _testNotifier()
-        self.agent = Agent(name,
-                           _notifier=self.notifier,
-                           heartbeat_interval=heartbeat)
-
-        # Dynamically construct a management database
-
-        _schema = SchemaEventClass(_classId=SchemaClassId("MyPackage",
-                                                          "MyClass",
-                                                          stype=SchemaClassId.TYPE_EVENT),
-                                   _desc="A test event schema")
-        # add properties
-        _schema.add_property( "prop-1", SchemaProperty(qmfTypes.TYPE_UINT8))
-        _schema.add_property( "prop-2", SchemaProperty(qmfTypes.TYPE_LSTR))
-
-        # Add schema to Agent
-        self.schema = _schema
-        self.agent.register_object_class(_schema)
-
-        self.running = False
-        self.ready = Event()
-
-    def start_app(self):
-        self.running = True
-        self.start()
-        self.ready.wait(10)
-        if not self.ready.is_set():
-            raise Exception("Agent failed to connect to broker.")
-        # time.sleep(1)
-
-    def stop_app(self):
-        self.running = False
-        # wake main thread
-        self.notifier.indication() # hmmm... collide with daemon???
-        self.join(self.timeout)
-        if self.isAlive():
-            raise Exception("AGENT DID NOT TERMINATE AS EXPECTED!!!")
-
-    def run(self):
-        # broker_url = "user/passwd@hostname:port"
-        conn = qpid.messaging.Connection(self.broker_url)
-        try:
-            conn.open()
-        except qpid.messaging.ConnectError, e:
-            raise Skipped(e)
-
-        self.agent.set_connection(conn)
-        self.ready.set()
-
-        counter = 1
-        while self.running:
-            # post an event every second
-            event = QmfEvent.create(long(time.time() * 1000),
-                                    QmfEvent.SEV_WARNING,
-                                    {"prop-1": counter,
-                                     "prop-2": str(datetime.datetime.utcnow())},
-                                    _schema_id=self.schema.get_class_id())
-            counter += 1
-            self.agent.raise_event(event)
-            wi = self.agent.get_next_workitem(timeout=0)
-            while wi is not None:
-                logging.error("UNEXPECTED AGENT WORKITEM RECEIVED=%s" % wi.get_type())
-                self.agent.release_workitem(wi)
-                wi = self.agent.get_next_workitem(timeout=0)
-            self.notifier.wait_for_work(1)
-
-        self.agent.remove_connection(self.timeout)
-        self.agent.destroy(self.timeout)
-
-
-
-class BaseTest(unittest.TestCase):
-    def configure(self, config):
-        self.config = config
-        self.broker = config.broker
-        self.defines = self.config.defines
-
-    def setUp(self):
-        # one second agent indication interval
-        self.agent1 = _agentApp("agent1", self.broker, 1)
-        self.agent1.start_app()
-        self.agent2 = _agentApp("agent2", self.broker, 1)
-        self.agent2.start_app()
-
-    def tearDown(self):
-        if self.agent1:
-            self.agent1.stop_app()
-            self.agent1 = None
-        if self.agent2:
-            self.agent2.stop_app()
-            self.agent2 = None
-
-    def test_get_events(self):
-        # create console
-        # find agents
-
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                            agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        try:
-            self.conn.open()
-        except qpid.messaging.ConnectError, e:
-            raise Skipped(e)
-
-        self.console.add_connection(self.conn)
-
-        # find the agents
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-        # now wait for events
-        agent1_events = agent2_events = 0
-        wi = self.console.get_next_workitem(timeout=4)
-        while wi:
-            if wi.get_type() == wi.EVENT_RECEIVED:
-                event = wi.get_params().get("event")
-                self.assertTrue(isinstance(event, QmfEvent))
-                self.assertTrue(event.get_severity() == QmfEvent.SEV_WARNING)
-                self.assertTrue(event.get_value("prop-1") > 0)
-
-                agent = wi.get_params().get("agent")
-                if not agent or not isinstance(agent, qmf2.console.Agent):
-                    self.fail("Unexpected workitem from agent")
-                else:
-                    if agent.get_name() == "agent1":
-                        agent1_events += 1
-                    elif agent.get_name() == "agent2":
-                        agent2_events += 1
-                    else:
-                        self.fail("Unexpected agent name received: %s" %
-                                  agent.get_name())
-                    if agent1_events and agent2_events:
-                        break;
-
-            wi = self.console.get_next_workitem(timeout=4)
-
-        self.assertTrue(agent1_events > 0 and agent2_events > 0)
-
-        self.console.destroy(10)
-
-
-
-
Index: extras/qmf/src/py/qmf2/tests/agent_discovery.py
===================================================================
--- extras/qmf/src/py/qmf2/tests/agent_discovery.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/tests/agent_discovery.py	(working copy)
@@ -1,464 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-import unittest
-import logging
-import time
-from threading import Thread, Event
-
-import qpid.messaging
-import qmf2.common
-import qmf2.console
-import qmf2.agent
-
-
-class _testNotifier(qmf2.common.Notifier):
-    def __init__(self):
-        self._event = Event()
-
-    def indication(self):
-        # note: called by qmf daemon thread
-        self._event.set()
-
-    def wait_for_work(self, timeout):
-        # note: called by application thread to wait
-        # for qmf to generate work
-        self._event.wait(timeout)
-        timed_out = self._event.isSet() == False
-        if not timed_out:
-            self._event.clear()
-            return True
-        return False
-
-
-class _agentApp(Thread):
-    def __init__(self, name, broker_url, heartbeat):
-        Thread.__init__(self)
-        self.timeout = 3
-        self.broker_url = broker_url
-        self.notifier = _testNotifier()
-        self.agent = qmf2.agent.Agent(name,
-                                      _notifier=self.notifier,
-                                      heartbeat_interval=heartbeat)
-        # No database needed for this test
-        self.running = False
-        self.ready = Event()
-
-    def start_app(self):
-        self.running = True
-        self.start()
-        self.ready.wait(10)
-        if not self.ready.is_set():
-            raise Exception("Agent failed to connect to broker.")
-
-    def stop_app(self):
-        self.running = False
-        # wake main thread
-        self.notifier.indication() # hmmm... collide with daemon???
-        self.join(10)
-        if self.isAlive():
-            raise Exception("AGENT DID NOT TERMINATE AS EXPECTED!!!")
-
-    def run(self):
-        # Connect the agent to the broker,
-        # broker_url = "user/passwd@hostname:port"
-        
-        conn = qpid.messaging.Connection(self.broker_url)
-        conn.open()
-        self.agent.set_connection(conn)
-        self.ready.set()
-
-        while self.running:
-            self.notifier.wait_for_work(None)
-            wi = self.agent.get_next_workitem(timeout=0)
-            while wi is not None:
-                logging.error("UNEXPECTED AGENT WORKITEM RECEIVED=%s" % wi.get_type())
-                self.agent.release_workitem(wi)
-                wi = self.agent.get_next_workitem(timeout=0)
-
-        # done, cleanup agent
-        self.agent.remove_connection(self.timeout)
-        self.agent.destroy(self.timeout)
-
-
-class BaseTest(unittest.TestCase):
-    def configure(self, config):
-        self.config = config
-        self.broker = config.broker
-        self.defines = self.config.defines
-
-    def setUp(self):
-        # one second agent indication interval
-        self.agent_heartbeat = 1
-        self.agent1 = _agentApp("agent1", self.broker, self.agent_heartbeat)
-        self.agent1.start_app()
-        self.agent2 = _agentApp("agent2", self.broker, self.agent_heartbeat)
-        self.agent2.start_app()
-
-    def tearDown(self):
-        if self.agent1:
-            self.agent1.stop_app()
-            self.agent1 = None
-        if self.agent2:
-            self.agent2.stop_app()
-            self.agent2 = None
-
-    def test_discover_all(self):
-        """
-        create console
-        enable agent discovery
-        wait
-        expect agent add for agent1 and agent2
-        """
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-        self.console.enable_agent_discovery()
-
-        agent1_found = agent2_found = False
-        wi = self.console.get_next_workitem(timeout=3)
-        while wi and not (agent1_found and agent2_found):
-            if wi.get_type() == wi.AGENT_ADDED:
-                agent = wi.get_params().get("agent")
-                if not agent or not isinstance(agent, qmf2.console.Agent):
-                    self.fail("Unexpected workitem from agent")
-                else:
-                    if agent.get_name() == "agent1":
-                        agent1_found = True
-                    elif agent.get_name() == "agent2":
-                        agent2_found = True
-                    else:
-                        self.fail("Unexpected agent name received: %s" %
-                                  agent.get_name())
-                    if agent1_found and agent2_found:
-                        break;
-
-            wi = self.console.get_next_workitem(timeout=3)
-
-        self.assertTrue(agent1_found and agent2_found, "All agents not discovered")
-
-        self.console.destroy(10)
-
-
-    def test_discover_one(self):
-        """
-        create console
-        enable agent discovery, filter for agent1 only
-        wait until timeout
-        expect agent add for agent1 only
-        """
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        query = qmf2.common.QmfQuery.create_predicate(
-            qmf2.common.QmfQuery.TARGET_AGENT,
-            [qmf2.common.QmfQuery.EQ, qmf2.common.QmfQuery.KEY_AGENT_NAME, 
-             [qmf2.common.QmfQuery.QUOTE, "agent1"]])
-        self.console.enable_agent_discovery(query)
-
-        agent1_found = agent2_found = False
-        wi = self.console.get_next_workitem(timeout=3)
-        while wi:
-            if wi.get_type() == wi.AGENT_ADDED:
-                agent = wi.get_params().get("agent")
-                if not agent or not isinstance(agent, qmf2.console.Agent):
-                    self.fail("Unexpected workitem from agent")
-                else:
-                    if agent.get_name() == "agent1":
-                        agent1_found = True
-                    elif agent.get_name() == "agent2":
-                        agent2_found = True
-                    else:
-                        self.fail("Unexpected agent name received: %s" %
-                                  agent.get_name())
-
-            wi = self.console.get_next_workitem(timeout=2)
-
-        self.assertTrue(agent1_found and not agent2_found, "Unexpected agent discovered")
-
-        self.console.destroy(10)
-
-
-    def test_heartbeat(self):
-        """
-        create console with 2 sec agent timeout
-        enable agent discovery, find all agents
-        stop agent1, expect timeout notification
-        stop agent2, expect timeout notification
-        """
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=2)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-        self.console.enable_agent_discovery()
-
-        agent1_found = agent2_found = False
-        wi = self.console.get_next_workitem(timeout=4)
-        while wi and not (agent1_found and agent2_found):
-            if wi.get_type() == wi.AGENT_ADDED:
-                agent = wi.get_params().get("agent")
-                if not agent or not isinstance(agent, qmf2.console.Agent):
-                    self.fail("Unexpected workitem from agent")
-                else:
-                    if agent.get_name() == "agent1":
-                        agent1_found = True
-                    elif agent.get_name() == "agent2":
-                        agent2_found = True
-                    else:
-                        self.fail("Unexpected agent name received: %s" %
-                                  agent.get_name())
-                    if agent1_found and agent2_found:
-                        break;
-
-            wi = self.console.get_next_workitem(timeout=4)
-
-        self.assertTrue(agent1_found and agent2_found, "All agents not discovered")
-
-        # now kill agent1 and wait for expiration
-
-        agent1 = self.agent1
-        self.agent1 = None
-        agent1.stop_app()
-
-        wi = self.console.get_next_workitem(timeout=4)
-        while wi is not None:
-            if wi.get_type() == wi.AGENT_DELETED:
-                agent = wi.get_params().get("agent")
-                if not agent or not isinstance(agent, qmf2.console.Agent):
-                    self.fail("Unexpected workitem from agent")
-                else:
-                    if agent.get_name() == "agent1":
-                        agent1_found = False
-                    else:
-                        self.fail("Unexpected agent_deleted received: %s" %
-                                  agent.get_name())
-                    if not agent1_found:
-                        break;
-
-            wi = self.console.get_next_workitem(timeout=4)
-
-        self.assertFalse(agent1_found, "agent1 did not delete!")
-
-        # now kill agent2 and wait for expiration
-
-        agent2 = self.agent2
-        self.agent2 = None
-        agent2.stop_app()
-
-        wi = self.console.get_next_workitem(timeout=4)
-        while wi is not None:
-            if wi.get_type() == wi.AGENT_DELETED:
-                agent = wi.get_params().get("agent")
-                if not agent or not isinstance(agent, qmf2.console.Agent):
-                    self.fail("Unexpected workitem from agent")
-                else:
-                    if agent.get_name() == "agent2":
-                        agent2_found = False
-                    else:
-                        self.fail("Unexpected agent_deleted received: %s" %
-                                  agent.get_name())
-                    if not agent2_found:
-                        break;
-
-            wi = self.console.get_next_workitem(timeout=4)
-
-        self.assertFalse(agent2_found, "agent2 did not delete!")
-
-        self.console.destroy(10)
-
-
-    def test_find_agent(self):
-        """
-        create console
-        do not enable agent discovery
-        find agent1, expect success
-        find agent-none, expect failure
-        find agent2, expect success
-        """
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        agent1 = self.console.find_agent("agent1", timeout=3)
-        self.assertTrue(agent1 and agent1.get_name() == "agent1")
-
-        no_agent = self.console.find_agent("agent-none", timeout=3)
-        self.assertTrue(no_agent == None)
-
-        agent2 = self.console.find_agent("agent2", timeout=3)
-        self.assertTrue(agent2 and agent2.get_name() == "agent2")
-
-        self.console.remove_connection(self.conn, 10)
-        self.console.destroy(10)
-
-
-    def test_heartbeat_x2(self):
-        """
-        create 2 consoles with 2 sec agent timeout
-        enable agent discovery, find all agents
-        stop agent1, expect timeout notification on both consoles
-        stop agent2, expect timeout notification on both consoles
-        """
-        console_count = 2
-        self.consoles = []
-        for i in range(console_count):
-            console = qmf2.console.Console("test-console-" + str(i),
-                                           notifier=_testNotifier(),
-                                           agent_timeout=2)
-            conn = qpid.messaging.Connection(self.broker)
-            conn.open()
-            console.add_connection(conn)
-            console.enable_agent_discovery()
-            self.consoles.append(console)
-
-        # now wait for all consoles to discover all agents,
-        # agents send a heartbeat once a second
-        for console in self.consoles:
-            agent1_found = agent2_found = False
-            wi = console.get_next_workitem(timeout=2)
-            while wi and not (agent1_found and agent2_found):
-                if wi.get_type() == wi.AGENT_ADDED:
-                    agent = wi.get_params().get("agent")
-                    if not agent or not isinstance(agent, qmf2.console.Agent):
-                        self.fail("Unexpected workitem from agent")
-                    else:
-                        if agent.get_name() == "agent1":
-                            agent1_found = True
-                        elif agent.get_name() == "agent2":
-                            agent2_found = True
-                        else:
-                            self.fail("Unexpected agent name received: %s" %
-                                      agent.get_name())
-                        if agent1_found and agent2_found:
-                            break;
-                wi = console.get_next_workitem(timeout=2)
-
-            self.assertTrue(agent1_found and agent2_found, "All agents not discovered")
-
-        # now kill agent1 and wait for expiration
-
-        agent1 = self.agent1
-        self.agent1 = None
-        agent1.stop_app()
-
-        for console in self.consoles:
-            agent1_found = True
-            wi = console.get_next_workitem(timeout=4)
-            while wi is not None:
-                if wi.get_type() == wi.AGENT_DELETED:
-                    agent = wi.get_params().get("agent")
-                    if not agent or not isinstance(agent, qmf2.console.Agent):
-                        self.fail("Unexpected workitem from agent")
-                    else:
-                        if agent.get_name() == "agent1":
-                            agent1_found = False
-                            break
-                        else:
-                            self.fail("Unexpected agent_deleted received: %s" %
-                                      agent.get_name())
-
-                wi = console.get_next_workitem(timeout=4)
-
-            self.assertFalse(agent1_found, "agent1 did not delete!")
-
-        # now kill agent2 and wait for expiration
-
-        agent2 = self.agent2
-        self.agent2 = None
-        agent2.stop_app()
-
-        for console in self.consoles:
-            agent2_found = True
-            wi = console.get_next_workitem(timeout=4)
-            while wi is not None:
-                if wi.get_type() == wi.AGENT_DELETED:
-                    agent = wi.get_params().get("agent")
-                    if not agent or not isinstance(agent, qmf2.console.Agent):
-                        self.fail("Unexpected workitem from agent")
-                    else:
-                        if agent.get_name() == "agent2":
-                            agent2_found = False
-                            break
-                        else:
-                            self.fail("Unexpected agent_deleted received: %s" %
-                                      agent.get_name())
-
-                wi = console.get_next_workitem(timeout=4)
-
-            self.assertFalse(agent2_found, "agent2 did not delete!")
-
-
-        for console in self.consoles:
-            console.destroy(10)
-
-
-    def test_find_agent_x2(self):
-        """
-        create 2 consoles, do not enable agent discovery
-        console-1: find agent1, expect success
-        console-2: find agent2, expect success
-        Verify console-1 does -not- know agent2
-        Verify console-2 does -not- know agent1
-        """
-        console_count = 2
-        self.consoles = []
-        for i in range(console_count):
-            console = qmf2.console.Console("test-console-" + str(i),
-                                           notifier=_testNotifier(),
-                                           agent_timeout=2)
-            conn = qpid.messaging.Connection(self.broker)
-            conn.open()
-            console.add_connection(conn)
-            self.consoles.append(console)
-
-        agent1 = self.consoles[0].find_agent("agent1", timeout=3)
-        self.assertTrue(agent1 and agent1.get_name() == "agent1")
-
-        agent2 = self.consoles[1].find_agent("agent2", timeout=3)
-        self.assertTrue(agent2 and agent2.get_name() == "agent2")
-
-        # wait long enough for agent heartbeats to be sent...
-
-        time.sleep(self.agent_heartbeat * 2)
-
-        agents = self.consoles[0].get_agents()
-        self.assertTrue(len(agents) == 1 and agents[0].get_name() == "agent1")
-        agent1 = self.consoles[0].get_agent("agent1")
-        self.assertTrue(agent1 and agent1.get_name() == "agent1")
-
-
-        agents = self.consoles[1].get_agents()
-        self.assertTrue(len(agents) == 1 and agents[0].get_name() == "agent2")
-        agent2 = self.consoles[1].get_agent("agent2")
-        self.assertTrue(agent2 and agent2.get_name() == "agent2")
-
-        # verify no new agents were learned
-
-        for console in self.consoles:
-            console.destroy(10)
-
Index: extras/qmf/src/py/qmf2/tests/basic_query.py
===================================================================
--- extras/qmf/src/py/qmf2/tests/basic_query.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/tests/basic_query.py	(working copy)
@@ -1,492 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-import unittest
-import logging
-from threading import Thread, Event
-
-import qpid.messaging
-from qmf2.common import (Notifier, SchemaObjectClass, SchemaClassId,
-                           SchemaProperty, qmfTypes, SchemaMethod, QmfQuery,
-                           QmfData) 
-import qmf2.console
-from qmf2.agent import(QmfAgentData, Agent)
-
-
-class _testNotifier(Notifier):
-    def __init__(self):
-        self._event = Event()
-
-    def indication(self):
-        # note: called by qmf daemon thread
-        self._event.set()
-
-    def wait_for_work(self, timeout):
-        # note: called by application thread to wait
-        # for qmf to generate work
-        self._event.wait(timeout)
-        timed_out = self._event.isSet() == False
-        if not timed_out:
-            self._event.clear()
-            return True
-        return False
-
-
-class _agentApp(Thread):
-    def __init__(self, name, broker_url, heartbeat):
-        Thread.__init__(self)
-        self.notifier = _testNotifier()
-        self.broker_url = broker_url
-        self.agent = Agent(name,
-                           _notifier=self.notifier,
-                           heartbeat_interval=heartbeat)
-
-        # Dynamically construct a management database
-
-        _schema = SchemaObjectClass( _classId=SchemaClassId("MyPackage", "MyClass"),
-                                     _desc="A test data schema",
-                                     _object_id_names=["index1", "index2"] )
-        # add properties
-        _schema.add_property( "index1", SchemaProperty(qmfTypes.TYPE_UINT8))
-        _schema.add_property( "index2", SchemaProperty(qmfTypes.TYPE_LSTR))
-
-        # these two properties are statistics
-        _schema.add_property( "query_count", SchemaProperty(qmfTypes.TYPE_UINT32))
-        _schema.add_property( "method_call_count", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-        # These two properties can be set via the method call
-        _schema.add_property( "set_string", SchemaProperty(qmfTypes.TYPE_LSTR))
-        _schema.add_property( "set_int", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-        # add method
-        _meth = SchemaMethod( _desc="Method to set string and int in object." )
-        _meth.add_argument( "arg_int", SchemaProperty(qmfTypes.TYPE_UINT32) )
-        _meth.add_argument( "arg_str", SchemaProperty(qmfTypes.TYPE_LSTR) )
-        _schema.add_method( "set_meth", _meth )
-
-        # Add schema to Agent
-
-        self.agent.register_object_class(_schema)
-
-        # instantiate managed data objects matching the schema
-
-        _obj1 = QmfAgentData( self.agent, _schema=_schema,
-                              _values={"index1":100, "index2":"a name"})
-        _obj1.set_value("set_string", "UNSET")
-        _obj1.set_value("set_int", 0)
-        _obj1.set_value("query_count", 0)
-        _obj1.set_value("method_call_count", 0)
-        self.agent.add_object( _obj1 )
-
-        self.agent.add_object( QmfAgentData( self.agent, _schema=_schema,
-                                             _values={"index1":99,
-                                                      "index2": "another name",
-                                                      "set_string": "UNSET",
-                                                      "set_int": 0,
-                                                      "query_count": 0,
-                                                      "method_call_count": 0} ))
-
-        self.agent.add_object( QmfAgentData( self.agent, _schema=_schema,
-                                             _values={"index1":50,
-                                                      "index2": "my name",
-                                                      "set_string": "SET",
-                                                      "set_int": 0,
-                                                      "query_count": 0,
-                                                      "method_call_count": 0} ))
-
-
-        # add an "unstructured" object to the Agent
-        _obj2 = QmfAgentData(self.agent, _object_id="01545")
-        _obj2.set_value("field1", "a value")
-        _obj2.set_value("field2", 2)
-        _obj2.set_value("field3", {"a":1, "map":2, "value":3})
-        _obj2.set_value("field4", ["a", "list", "value"])
-        _obj2.set_value("index1", 50)
-        self.agent.add_object(_obj2)
-
-        _obj2 = QmfAgentData(self.agent, _object_id="01546")
-        _obj2.set_value("field1", "a value")
-        _obj2.set_value("field2", 3)
-        _obj2.set_value("field3", {"a":1, "map":2, "value":3})
-        _obj2.set_value("field4", ["a", "list", "value"])
-        _obj2.set_value("index1", 51)
-        self.agent.add_object(_obj2)
-
-        _obj2 = QmfAgentData(self.agent, _object_id="01544")
-        _obj2.set_value("field1", "a value")
-        _obj2.set_value("field2", 4)
-        _obj2.set_value("field3", {"a":1, "map":2, "value":3})
-        _obj2.set_value("field4", ["a", "list", "value"])
-        _obj2.set_value("index1", 49)
-        self.agent.add_object(_obj2)
-
-        _obj2 = QmfAgentData(self.agent, _object_id="01543")
-        _obj2.set_value("field1", "a value")
-        _obj2.set_value("field2", 4)
-        _obj2.set_value("field3", {"a":1, "map":2, "value":3})
-        _obj2.set_value("field4", ["a", "list", "value"])
-        _obj2.set_value("index1", 48)
-        self.agent.add_object(_obj2)
-
-        self.running = False
-        self.ready = Event()
-
-    def start_app(self):
-        self.running = True
-        self.start()
-        self.ready.wait(10)
-        if not self.ready.is_set():
-            raise Exception("Agent failed to connect to broker.")
-
-    def stop_app(self):
-        self.running = False
-        # wake main thread
-        self.notifier.indication() # hmmm... collide with daemon???
-        self.join(10)
-        if self.isAlive():
-            raise Exception("AGENT DID NOT TERMINATE AS EXPECTED!!!")
-
-    def run(self):
-        # broker_url = "user/passwd@hostname:port"
-        self.conn = qpid.messaging.Connection(self.broker_url)
-        self.conn.open()
-        self.agent.set_connection(self.conn)
-        self.ready.set()
-
-        while self.running:
-            self.notifier.wait_for_work(None)
-            wi = self.agent.get_next_workitem(timeout=0)
-            while wi is not None:
-                logging.error("UNEXPECTED AGENT WORKITEM RECEIVED=%s" % wi.get_type())
-                self.agent.release_workitem(wi)
-                wi = self.agent.get_next_workitem(timeout=0)
-
-        if self.conn:
-            self.agent.remove_connection(10)
-        self.agent.destroy(10)
-
-
-
-
-class BaseTest(unittest.TestCase):
-    def configure(self, config):
-        self.config = config
-        self.broker = config.broker
-        self.defines = self.config.defines
-
-    def setUp(self):
-        # one second agent indication interval
-        self.agent_heartbeat = 1
-        self.agent1 = _agentApp("agent1", self.broker, self.agent_heartbeat)
-        self.agent1.start_app()
-        self.agent2 = _agentApp("agent2", self.broker, self.agent_heartbeat)
-        self.agent2.start_app()
-
-    def tearDown(self):
-        if self.agent1:
-            self.agent1.stop_app()
-            self.agent1 = None
-        if self.agent2:
-            self.agent2.stop_app()
-            self.agent2 = None
-
-    def test_all_oids(self):
-        # create console
-        # find agents
-        # synchronous query for all schemas
-        # synchronous query for all objects per schema
-        # verify known object ids are returned
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            # first, find objects per schema
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_SCHEMA_ID)
-            sid_list = self.console.do_query(agent, query)
-            self.assertTrue(sid_list and len(sid_list) == 1)
-            for sid in sid_list:
-                t_params = {QmfData.KEY_SCHEMA_ID: sid}
-                query = QmfQuery.create_wildcard(QmfQuery.TARGET_OBJECT_ID,
-                                                _target_params=t_params)
-
-                oid_list = self.console.do_query(agent, query)
-
-                self.assertTrue(isinstance(oid_list, type([])), 
-                                "Unexpected return type")
-                self.assertTrue(len(oid_list) == 3, "Wrong count")
-                self.assertTrue('100a name' in oid_list)
-                self.assertTrue('99another name' in oid_list)
-                self.assertTrue('50my name' in oid_list)
-                self.assertTrue('01545' not in oid_list)
-
-
-            # now, find all unmanaged objects (no schema)
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_OBJECT_ID)
-            oid_list = self.console.do_query(agent, query)
-
-            self.assertTrue(isinstance(oid_list, type([])), 
-                            "Unexpected return type")
-            self.assertTrue(len(oid_list) == 4, "Wrong count")
-            self.assertTrue('100a name' not in oid_list)
-            self.assertTrue('99another name' not in oid_list)
-            self.assertTrue('01545' in oid_list)
-            self.assertTrue('01544' in oid_list)
-            self.assertTrue('01543' in oid_list)
-            self.assertTrue('01546' in oid_list)
-
-        self.console.destroy(10)
-
-
-    def test_direct_oids(self):
-        # create console
-        # find agents
-        # synchronous query for each objects
-        # verify objects and schemas are correct
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            # first, find objects per schema
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_SCHEMA_ID)
-            sid_list = self.console.do_query(agent, query)
-            self.assertTrue(sid_list and len(sid_list) == 1)
-
-            for oid in ['100a name', '99another name']:
-                query = QmfQuery.create_id_object(oid, sid_list[0])
-                obj_list = self.console.do_query(agent, query)
-
-                self.assertTrue(isinstance(obj_list, type([])), 
-                                "Unexpected return type")
-                self.assertTrue(len(obj_list) == 1)
-                obj = obj_list[0]
-                self.assertTrue(isinstance(obj, QmfData))
-                self.assertTrue(obj.get_object_id() == oid)
-                self.assertTrue(obj.get_schema_class_id() == sid_list[0])
-                schema_id = obj.get_schema_class_id()
-                self.assertTrue(isinstance(schema_id, SchemaClassId))
-                self.assertTrue(obj.is_described())
-
-            # now find schema-less objects
-            for oid in ['01545']:
-                query = QmfQuery.create_id_object(oid)
-                obj_list = self.console.do_query(agent, query)
-
-                self.assertTrue(isinstance(obj_list, type([])), 
-                                "Unexpected return type")
-                self.assertTrue(len(obj_list) == 1)
-                obj = obj_list[0]
-                self.assertTrue(isinstance(obj, QmfData))
-                self.assertTrue(obj.get_object_id() == oid)
-                self.assertFalse(obj.is_described())
-
-        self.console.destroy(10)
-
-
-
-    def test_packages(self):
-        # create console
-        # find agents
-        # synchronous query all package names
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            query = QmfQuery.create_wildcard(QmfQuery.TARGET_PACKAGES)
-            package_list = self.console.do_query(agent, query)
-            self.assertTrue(len(package_list) == 1)
-            self.assertTrue('MyPackage' in package_list)
-
-
-        self.console.destroy(10)
-
-
-
-    def test_predicate_schema_id(self):
-        # create console
-        # find agents
-        # synchronous query for all schema by package name
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            query = QmfQuery.create_predicate(QmfQuery.TARGET_SCHEMA,
-                                              [QmfQuery.EQ,
-                                               SchemaClassId.KEY_PACKAGE, 
-                                               [QmfQuery.QUOTE, "MyPackage"]])
-
-            schema_list = self.console.do_query(agent, query)
-            self.assertTrue(len(schema_list))
-            for schema in schema_list:
-                self.assertTrue(schema.get_class_id().get_package_name() ==
-                                "MyPackage")
-
-
-        self.console.destroy(10)
-
-
-
-    def test_predicate_no_match(self):
-        # create console
-        # find agents
-        # synchronous query for all schema by package name
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            query = QmfQuery.create_predicate(QmfQuery.TARGET_SCHEMA,
-                                              [QmfQuery.EQ,
-                                               [QmfQuery.UNQUOTE, SchemaClassId.KEY_PACKAGE],
-                                               [QmfQuery.QUOTE, "No-Such-Package"]])
-
-            schema_list = self.console.do_query(agent, query)
-            self.assertTrue(len(schema_list) == 0)
-
-        self.console.destroy(10)
-
-
-    def test_predicate_match_string(self):
-        # create console
-        # find agents
-        # synchronous query for all objects with a value named
-        #   set_string which is < or equal to "UNSET"
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            # get the schema id for MyPackage:MyClass schema
-            query = QmfQuery.create_predicate(QmfQuery.TARGET_SCHEMA_ID,
-                                              [QmfQuery.AND,
-                                               [QmfQuery.EQ, SchemaClassId.KEY_PACKAGE,
-                                                [QmfQuery.QUOTE, "MyPackage"]],
-                                               [QmfQuery.EQ, SchemaClassId.KEY_CLASS,
-                                                [QmfQuery.QUOTE, "MyClass"]]])
-            sid_list = self.console.do_query(agent, query)
-            self.assertTrue(len(sid_list) == 1)
-
-            query = QmfQuery.create_predicate(QmfQuery.TARGET_OBJECT,
-                    [QmfQuery.AND,
-                     [QmfQuery.EXISTS, [QmfQuery.QUOTE, "set_string"]],
-                     [QmfQuery.EQ, "set_string", [QmfQuery.QUOTE, "UNSET"]]],
-                    _target_params={QmfData.KEY_SCHEMA_ID: sid_list[0]})
-            obj_list = self.console.do_query(agent, query)
-            self.assertTrue(len(obj_list) == 2)
-            for obj in obj_list:
-                self.assertTrue(obj.has_value("set_string"))
-                self.assertTrue(obj.get_value("set_string") == "UNSET")
-
-        self.console.destroy(10)
-
-
-
-    def test_predicate_match_integer(self):
-        # create console
-        # find agents
-        # synchronous query for all objects with a value named
-        #   "index1" which is < or equal to various values
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        for aname in ["agent1", "agent2"]:
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            # Query the unmanaged (no schema) objects
-
-            # == 50
-            query = QmfQuery.create_predicate(QmfQuery.TARGET_OBJECT,
-                    [QmfQuery.AND,
-                     [QmfQuery.EXISTS, [QmfQuery.QUOTE, "index1"]],
-                     [QmfQuery.EQ, "index1", 50]])
-
-            obj_list = self.console.do_query(agent, query)
-            self.assertTrue(len(obj_list) == 1)
-            self.assertTrue(obj_list[0].has_value("index1"))
-            self.assertTrue(obj_list[0].get_value("index1") == 50)
-
-            # <= 50
-            query = QmfQuery.create_predicate(QmfQuery.TARGET_OBJECT,
-                    [QmfQuery.AND,
-                     [QmfQuery.EXISTS, [QmfQuery.QUOTE, "index1"]],
-                     [QmfQuery.LE, "index1", 50]])
-
-            obj_list = self.console.do_query(agent, query)
-            self.assertTrue(len(obj_list) == 3)
-            for obj in obj_list:
-                self.assertTrue(obj.has_value("index1"))
-                self.assertTrue(obj.get_value("index1") <= 50)
-
-
-            # > 50
-            query = QmfQuery.create_predicate(QmfQuery.TARGET_OBJECT,
-                    [QmfQuery.AND,
-                     [QmfQuery.EXISTS, [QmfQuery.QUOTE, "index1"]],
-                     [QmfQuery.GT, "index1", 50]])
-
-            obj_list = self.console.do_query(agent, query)
-            self.assertTrue(len(obj_list) == 1)
-            for obj in obj_list:
-                self.assertTrue(obj.has_value("index1"))
-                self.assertTrue(obj.get_value("index1") > 50)
-
-        self.console.destroy(10)
-
Index: extras/qmf/src/py/qmf2/tests/subscriptions.py
===================================================================
--- extras/qmf/src/py/qmf2/tests/subscriptions.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/tests/subscriptions.py	(working copy)
@@ -1,983 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-import unittest
-import logging
-import datetime
-import time
-from threading import Thread, Event
-
-import qpid.messaging
-from qmf2.common import (Notifier, SchemaObjectClass, SchemaClassId,
-                         SchemaProperty, qmfTypes, SchemaMethod, QmfQuery,
-                         QmfData, WorkItem)
-import qmf2.console
-from qmf2.agent import(QmfAgentData, Agent)
-
-
-class _testNotifier(Notifier):
-    def __init__(self):
-        self._event = Event()
-
-    def indication(self):
-        # note: called by qmf daemon thread
-        self._event.set()
-
-    def wait_for_work(self, timeout):
-        # note: called by application thread to wait
-        # for qmf to generate work
-        self._event.wait(timeout)
-        timed_out = self._event.isSet() == False
-        if not timed_out:
-            self._event.clear()
-            return True
-        return False
-
-
-class _agentApp(Thread):
-    def __init__(self, name, broker_url, heartbeat):
-        Thread.__init__(self)
-        self.notifier = _testNotifier()
-        self.broker_url = broker_url
-        self.agent = Agent(name,
-                           _notifier=self.notifier,
-                           heartbeat_interval=heartbeat,
-                           max_duration=10,
-                           default_duration=7,
-                           min_duration=5,
-                           min_interval=1,
-                           default_interval=2)
-
-        # Management Database
-        # - two different schema packages,
-        # - two classes within one schema package
-        # - multiple objects per schema package+class
-        # - two "undescribed" objects
-
-        # "package1/class1"
-
-        _schema = SchemaObjectClass( _classId=SchemaClassId("package1", "class1"),
-                                     _desc="A test data schema - one",
-                                     _object_id_names=["key"] )
-
-        _schema.add_property( "key", SchemaProperty(qmfTypes.TYPE_LSTR))
-
-        # note: count1 is continuous, count2 is not
-        count1_prop = SchemaProperty.create(qmfTypes.TYPE_UINT32,
-                                            continuous=True)
-        _schema.add_property( "count1", count1_prop)
-        count2_prop = SchemaProperty.create(qmfTypes.TYPE_UINT32,
-                                            continuous=False)
-        _schema.add_property( "count2", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-        self.agent.register_object_class(_schema)
-
-        _obj = QmfAgentData( self.agent,
-                             _values={"key":"p1c1_key1"},
-                             _schema=_schema)
-        _obj.set_value("count1", 0)
-        _obj.set_value("count2", 0)
-        self.agent.add_object( _obj )
-
-        _obj = QmfAgentData( self.agent, 
-                             _values={"key":"p1c1_key2"},
-                             _schema=_schema )
-        _obj.set_value("count1", 9)
-        _obj.set_value("count2", 10)
-        self.agent.add_object( _obj )
-
-        # "package1/class2"
-
-        _schema = SchemaObjectClass( _classId=SchemaClassId("package1", "class2"),
-                                     _desc="A test data schema - two",
-                                     _object_id_names=["name"] )
-        # add properties
-        _schema.add_property( "name", SchemaProperty(qmfTypes.TYPE_LSTR))
-        _schema.add_property( "string1", SchemaProperty(qmfTypes.TYPE_LSTR))
-
-        self.agent.register_object_class(_schema)
-
-        _obj = QmfAgentData( self.agent, 
-                             _values={"name":"p1c2_name1"},
-                             _schema=_schema )
-        _obj.set_value("string1", "a data string")
-        self.agent.add_object( _obj )
-
-        _obj = QmfAgentData( self.agent, 
-                             _values={"name":"p1c2_name2"},
-                             _schema=_schema )
-        _obj.set_value("string1", "another data string")
-        self.agent.add_object( _obj )
-
-
-        # "package2/class1"
-
-        _schema = SchemaObjectClass( _classId=SchemaClassId("package2", "class1"),
-                                     _desc="A test data schema - second package",
-                                     _object_id_names=["key"] )
-
-        _schema.add_property( "key", SchemaProperty(qmfTypes.TYPE_LSTR))
-        _schema.add_property( "counter", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-        self.agent.register_object_class(_schema)
-
-        _obj = QmfAgentData( self.agent, 
-                             _values={"key":"p2c1_key1"},
-                             _schema=_schema )
-        _obj.set_value("counter", 0)
-        self.agent.add_object( _obj )
-
-        _obj = QmfAgentData( self.agent, 
-                             _values={"key":"p2c1_key2"},
-                             _schema=_schema )
-        _obj.set_value("counter", 2112)
-        self.agent.add_object( _obj )
-
-
-        # add two "unstructured" objects to the Agent
-
-        _obj = QmfAgentData(self.agent, _object_id="undesc-1")
-        _obj.set_value("field1", "a value")
-        _obj.set_value("field2", 2)
-        _obj.set_value("field3", {"a":1, "map":2, "value":3})
-        _obj.set_value("field4", ["a", "list", "value"])
-        self.agent.add_object(_obj)
-
-
-        _obj = QmfAgentData(self.agent, _object_id="undesc-2")
-        _obj.set_value("key-1", "a value")
-        _obj.set_value("key-2", 2)
-        self.agent.add_object(_obj)
-
-        self.running = False
-        self.ready = Event()
-
-    def start_app(self):
-        self.running = True
-        self.start()
-        self.ready.wait(10)
-        if not self.ready.is_set():
-            raise Exception("Agent failed to connect to broker.")
-
-    def stop_app(self):
-        self.running = False
-        self.notifier.indication() # hmmm... collide with daemon???
-        self.join(10)
-        if self.isAlive():
-            raise Exception("AGENT DID NOT TERMINATE AS EXPECTED!!!")
-
-    def run(self):
-        # broker_url = "user/passwd@hostname:port"
-        self.conn = qpid.messaging.Connection(self.broker_url)
-        self.conn.open()
-        self.agent.set_connection(self.conn)
-        self.ready.set()
-
-        while self.running:
-            self.notifier.wait_for_work(None)
-            wi = self.agent.get_next_workitem(timeout=0)
-            while wi is not None:
-                logging.error("UNEXPECTED AGENT WORKITEM RECEIVED=%s" % wi.get_type())
-                self.agent.release_workitem(wi)
-                wi = self.agent.get_next_workitem(timeout=0)
-
-        if self.conn:
-            self.agent.remove_connection(10)
-        self.agent.destroy(10)
-
-
-class BaseTest(unittest.TestCase):
-    agent_count = 5
-
-    def configure(self, config):
-        self.config = config
-        self.broker = config.broker
-        self.defines = self.config.defines
-
-    def setUp(self):
-        self.agents = []
-        for i in range(self.agent_count):
-            agent = _agentApp("agent-" + str(i), self.broker, 1)
-            agent.start_app()
-            self.agents.append(agent)
-        #print("!!!! STARTING TEST: %s" % datetime.datetime.utcnow())
-
-    def tearDown(self):
-        #print("!!!! STOPPING TEST: %s" % datetime.datetime.utcnow())
-        for agent in self.agents:
-            if agent is not None:
-                agent.stop_app()
-
-
-    def test_sync_by_schema(self):
-        # create console
-        # find all agents
-        # subscribe to changes to any object in package1/class1
-        # should succeed - verify 1 publish
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        subscriptions = []
-        index = 0
-
-        # query to match all objects in schema package1/class1
-        sid = SchemaClassId.create("package1", "class1")
-        t_params = {QmfData.KEY_SCHEMA_ID: sid}
-        query = QmfQuery.create_wildcard(QmfQuery.TARGET_OBJECT,
-                                         _target_params=t_params)
-        for agent_app in self.agents:
-            aname = agent_app.agent.get_name()
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            # now subscribe to agent
-
-            sp = self.console.create_subscription(agent,
-                                                  query,
-                                                  index)
-            self.assertTrue(isinstance(sp, qmf2.console.SubscribeParams))
-            self.assertTrue(sp.succeeded())
-            self.assertTrue(sp.get_error() == None)
-            self.assertTrue(sp.get_duration() == 10)
-            self.assertTrue(sp.get_publish_interval() == 2)
-
-            subscriptions.append([sp, 0])
-            index += 1
-
-        # now wait for the (2 * interval) and count the updates
-        r_count = 0
-        while self.notifier.wait_for_work(4):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                r_count += 1
-                self.assertTrue(wi.get_type() == WorkItem.SUBSCRIBE_INDICATION)
-                reply = wi.get_params()
-                self.assertTrue(isinstance(reply, type([])))
-                self.assertTrue(len(reply) == 2)
-                for obj in reply:
-                    self.assertTrue(isinstance(obj, QmfData))
-                    self.assertTrue(obj.get_object_id() == "p1c1_key2" or
-                                    obj.get_object_id() == "p1c1_key1")
-                sid = reply[0].get_schema_class_id()
-                self.assertTrue(isinstance(sid, SchemaClassId))
-                self.assertTrue(sid.get_package_name() == "package1")
-                self.assertTrue(sid.get_class_name() == "class1")
-
-                self.assertTrue(wi.get_handle() < len(subscriptions))
-                subscriptions[wi.get_handle()][1] += 1
-
-                self.console.release_workitem(wi)
-
-                wi = self.console.get_next_workitem(timeout=0)
-
-        # expect 1 publish per subscription
-        self.assertTrue(r_count == 5)
-        for ii in range(len(subscriptions)):
-            self.assertTrue(subscriptions[ii][1] == 1)
-
-        self.console.destroy(10)
-
-
-    def test_sync_by_obj_id(self):
-        # create console
-        # find all agents
-        # subscribe to changes to any object in package1/class1
-        # should succeed
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        subscriptions = []
-        index = 0
-
-        # query to match all objects in schema package1/class1
-        # sid = SchemaClassId.create("package1", "class1")
-        # t_params = {QmfData.KEY_SCHEMA_ID: sid}
-        query = QmfQuery.create_id_object("undesc-2")
-
-        for agent_app in self.agents:
-            aname = agent_app.agent.get_name()
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            # now subscribe to agent
-
-            sp = self.console.create_subscription(agent,
-                                                  query,
-                                                  index)
-            self.assertTrue(isinstance(sp, qmf2.console.SubscribeParams))
-            self.assertTrue(sp.succeeded())
-            self.assertTrue(sp.get_error() == None)
-
-            subscriptions.append([sp, 0])
-            index += 1
-
-        # now wait for all subscriptions to expire (2x interval w/o
-        # indications)
-        r_count = 0
-        while self.notifier.wait_for_work(4):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                r_count += 1
-                self.assertTrue(wi.get_type() == WorkItem.SUBSCRIBE_INDICATION)
-                reply = wi.get_params()
-                self.assertTrue(isinstance(reply, type([])))
-                self.assertTrue(len(reply) == 1)
-                self.assertTrue(isinstance(reply[0], QmfData))
-                self.assertTrue(reply[0].get_object_id() == "undesc-2")
-                self.assertTrue(wi.get_handle() < len(subscriptions))
-                subscriptions[wi.get_handle()][1] += 1
-
-                self.console.release_workitem(wi)
-
-                wi = self.console.get_next_workitem(timeout=0)
-
-        # expect 1 publish per subscription
-        self.assertTrue(r_count == 5)
-        for ii in range(len(subscriptions)):
-            self.assertTrue(subscriptions[ii][1] == 1)
-
-        self.console.destroy(10)
-
-
-    def test_sync_by_obj_id_schema(self):
-        # create console
-        # find all agents
-        # subscribe to changes to any object in package1/class1
-        # should succeed
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        subscriptions = []
-        index = 0
-
-        # query to match object "p2c1_key2" in schema package2/class1
-        sid = SchemaClassId.create("package2", "class1")
-        query = QmfQuery.create_id_object("p2c1_key2", sid)
-
-        for agent_app in self.agents:
-            aname = agent_app.agent.get_name()
-            agent = self.console.find_agent(aname, timeout=3)
-            self.assertTrue(agent and agent.get_name() == aname)
-
-            # now subscribe to agent
-
-            sp = self.console.create_subscription(agent,
-                                                  query,
-                                                  index)
-            self.assertTrue(isinstance(sp, qmf2.console.SubscribeParams))
-            self.assertTrue(sp.succeeded())
-            self.assertTrue(sp.get_error() == None)
-
-            subscriptions.append([sp, 0])
-            index += 1
-
-        # now wait for all subscriptions to expire (2x interval w/o
-        # indications)
-        r_count = 0
-        while self.notifier.wait_for_work(4):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                r_count += 1
-                self.assertTrue(wi.get_type() == WorkItem.SUBSCRIBE_INDICATION)
-                reply = wi.get_params()
-                self.assertTrue(isinstance(reply, type([])))
-                self.assertTrue(len(reply) == 1)
-                self.assertTrue(isinstance(reply[0], QmfData))
-                self.assertTrue(reply[0].get_object_id() == "p2c1_key2")
-                sid = reply[0].get_schema_class_id()
-                self.assertTrue(isinstance(sid, SchemaClassId))
-                self.assertTrue(sid.get_package_name() == "package2")
-                self.assertTrue(sid.get_class_name() == "class1")
-                self.assertTrue(wi.get_handle() < len(subscriptions))
-                subscriptions[wi.get_handle()][1] += 1
-
-                self.console.release_workitem(wi)
-
-                wi = self.console.get_next_workitem(timeout=0)
-
-        # expect 1 publish per subscription
-        self.assertTrue(r_count == 5)
-        for ii in range(len(subscriptions)):
-            self.assertTrue(subscriptions[ii][1] == 1)
-
-        self.console.destroy(10)
-
-
-
-    def test_sync_refresh(self):
-        # create console
-        # find one agent
-        # subscribe to changes to any object in package1/class1
-        # after 3 data indications, refresh
-        # verify > 5 more data indications received
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        # query to match object "p1c1_key2" in schema package1/class1
-        sid = SchemaClassId.create("package1", "class1")
-        query = QmfQuery.create_id_object("p1c1_key2", sid)
-
-        agent_app = self.agents[0]
-        aname = agent_app.agent.get_name()
-        agent = self.console.find_agent(aname, timeout=3)
-        self.assertTrue(agent and agent.get_name() == aname)
-
-        # setup subscription on agent
-
-        sp = self.console.create_subscription(agent,
-                                              query,
-                                              "my-handle")
-        self.assertTrue(isinstance(sp, qmf2.console.SubscribeParams))
-        self.assertTrue(sp.succeeded())
-        self.assertTrue(sp.get_error() == None)
-
-        # refresh after three subscribe indications, count all
-        # indications to verify refresh worked
-        r_count = 0
-        while self.notifier.wait_for_work(4):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                r_count += 1
-                self.assertTrue(wi.get_type() == WorkItem.SUBSCRIBE_INDICATION)
-                reply = wi.get_params()
-                self.assertTrue(isinstance(reply, type([])))
-                self.assertTrue(len(reply) == 1)
-                self.assertTrue(isinstance(reply[0], QmfData))
-                self.assertTrue(reply[0].get_object_id() == "p1c1_key2")
-                sid = reply[0].get_schema_class_id()
-                self.assertTrue(isinstance(sid, SchemaClassId))
-                self.assertTrue(sid.get_package_name() == "package1")
-                self.assertTrue(sid.get_class_name() == "class1")
-                self.assertTrue(wi.get_handle() == "my-handle")
-
-                # count1 is continuous, touching it will force a
-                # publish on the interval
-                self.assertTrue(sid is not None)
-                test_obj = agent_app.agent.get_object("p1c1_key2", sid)
-                self.assertTrue(test_obj is not None)
-                test_obj.set_value("count1", r_count)
-
-                self.console.release_workitem(wi)
-
-                if r_count == 3:
-                    rp = self.console.refresh_subscription(sp.get_subscription_id())
-                    self.assertTrue(rp)
-
-                wi = self.console.get_next_workitem(timeout=0)
-
-        # expect 5 publish per subscription, more if refreshed
-        self.assertTrue(r_count > 5)
-
-        self.console.destroy(10)
-
-
-
-    def test_sync_cancel(self):
-        # create console
-        # find one agent
-        # subscribe to changes to any object in package1/class1
-        # after 2 data indications, cancel subscription
-        # verify < 5 data indications received
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        # query to match object "p1c1_key2" in schema package1/class1
-        sid = SchemaClassId.create("package1", "class1")
-        query = QmfQuery.create_id_object("p1c1_key2", sid)
-
-        agent_app = self.agents[0]
-        aname = agent_app.agent.get_name()
-        agent = self.console.find_agent(aname, timeout=3)
-        self.assertTrue(agent and agent.get_name() == aname)
-
-        # setup subscription on agent
-
-        sp = self.console.create_subscription(agent,
-                                              query,
-                                              "my-handle")
-        self.assertTrue(isinstance(sp, qmf2.console.SubscribeParams))
-        self.assertTrue(sp.succeeded())
-        self.assertTrue(sp.get_error() == None)
-
-        # refresh after three subscribe indications, count all
-        # indications to verify refresh worked
-        r_count = 0
-        while self.notifier.wait_for_work(4):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                r_count += 1
-                self.assertTrue(wi.get_type() == WorkItem.SUBSCRIBE_INDICATION)
-                reply = wi.get_params()
-                self.assertTrue(isinstance(reply, type([])))
-                self.assertTrue(len(reply) == 1)
-                self.assertTrue(isinstance(reply[0], QmfData))
-                self.assertTrue(reply[0].get_object_id() == "p1c1_key2")
-                sid = reply[0].get_schema_class_id()
-                self.assertTrue(isinstance(sid, SchemaClassId))
-                self.assertTrue(sid.get_package_name() == "package1")
-                self.assertTrue(sid.get_class_name() == "class1")
-                self.assertTrue(wi.get_handle() == "my-handle")
-
-                # count1 is continuous, touching it will force a
-                # publish on the interval
-                self.assertTrue(sid is not None)
-                test_obj = agent_app.agent.get_object("p1c1_key2", sid)
-                self.assertTrue(test_obj is not None)
-                test_obj.set_value("count1", r_count)
-
-                self.console.release_workitem(wi)
-
-                if r_count == 3:
-                    self.console.cancel_subscription(sp.get_subscription_id())
-
-                wi = self.console.get_next_workitem(timeout=0)
-
-        # expect only 3 publish received before cancel
-        self.assertTrue(r_count == 3)
-
-        self.console.destroy(10)
-
-
-    def test_async_by_obj_id_schema(self):
-        # create console
-        # find one agent
-        # async subscribe to changes to any object in package1/class1
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        # query to match object "p2c1_key2" in schema package2/class1
-        sid = SchemaClassId.create("package2", "class1")
-        query = QmfQuery.create_id_object("p2c1_key2", sid)
-
-        agent_app = self.agents[0]
-        aname = agent_app.agent.get_name()
-        agent = self.console.find_agent(aname, timeout=3)
-        self.assertTrue(agent and agent.get_name() == aname)
-
-        # setup subscription on agent
-
-        rc = self.console.create_subscription(agent,
-                                              query,
-                                              "my-handle",
-                                              _blocking=False)
-        self.assertTrue(rc)
-
-        r_count = 0
-        sp = None
-        while self.notifier.wait_for_work(4):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                r_count += 1
-                if wi.get_type() == WorkItem.SUBSCRIBE_RESPONSE:
-                    self.assertTrue(wi.get_handle() == "my-handle")
-                    sp = wi.get_params()
-                    self.assertTrue(isinstance(sp, qmf2.console.SubscribeParams))
-                    self.assertTrue(sp.succeeded())
-                    self.assertTrue(sp.get_error() == None)
-                else:
-                    self.assertTrue(wi.get_type() ==
-                                    WorkItem.SUBSCRIBE_INDICATION)
-                    # sp better be set up by now!
-                    self.assertTrue(isinstance(sp, qmf2.console.SubscribeParams))
-                    reply = wi.get_params()
-                    self.assertTrue(isinstance(reply, type([])))
-                    self.assertTrue(len(reply) == 1)
-                    self.assertTrue(isinstance(reply[0], QmfData))
-                    self.assertTrue(reply[0].get_object_id() == "p2c1_key2")
-                    sid = reply[0].get_schema_class_id()
-                    self.assertTrue(isinstance(sid, SchemaClassId))
-                    self.assertTrue(sid.get_package_name() == "package2")
-                    self.assertTrue(sid.get_class_name() == "class1")
-                    self.assertTrue(wi.get_handle() == "my-handle")
-
-                self.console.release_workitem(wi)
-
-                wi = self.console.get_next_workitem(timeout=0)
-
-        # one response + one publish = 2
-        self.assertTrue(r_count == 2)
-
-        self.console.destroy(10)
-
-    def test_async_refresh(self):
-        # create console
-        # find one agent
-        # async subscribe to changes to any object in package1/class1
-        # refresh after third data indication
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        # query to match object "p1c1_key2" in schema package1/class1
-        sid = SchemaClassId.create("package1", "class1")
-        query = QmfQuery.create_id_object("p1c1_key2", sid)
-
-        agent_app = self.agents[0]
-        aname = agent_app.agent.get_name()
-        agent = self.console.find_agent(aname, timeout=3)
-        self.assertTrue(agent and agent.get_name() == aname)
-
-        # setup subscription on agent
-
-        rc = self.console.create_subscription(agent,
-                                              query,
-                                              "my-handle",
-                                              _blocking=False)
-        self.assertTrue(rc)
-
-        # refresh after three subscribe indications, count all
-        # indications to verify refresh worked
-        r_count = 0
-        i_count = 0
-        sp = None
-        while self.notifier.wait_for_work(4):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                r_count += 1
-                if wi.get_type() == WorkItem.SUBSCRIBE_RESPONSE:
-                    self.assertTrue(wi.get_handle() == "my-handle")
-                    sp = wi.get_params()
-                    self.assertTrue(isinstance(sp, qmf2.console.SubscribeParams))
-                    self.assertTrue(sp.succeeded())
-                    self.assertTrue(sp.get_error() == None)
-                else:
-                    self.assertTrue(wi.get_type() ==
-                                    WorkItem.SUBSCRIBE_INDICATION)
-                    i_count += 1
-                    # sp better be set up by now!
-                    self.assertTrue(isinstance(sp, qmf2.console.SubscribeParams))
-                    reply = wi.get_params()
-                    self.assertTrue(isinstance(reply, type([])))
-                    self.assertTrue(len(reply) == 1)
-                    self.assertTrue(isinstance(reply[0], QmfData))
-                    self.assertTrue(reply[0].get_object_id() == "p1c1_key2")
-                    sid = reply[0].get_schema_class_id()
-                    self.assertTrue(isinstance(sid, SchemaClassId))
-                    self.assertTrue(sid.get_package_name() == "package1")
-                    self.assertTrue(sid.get_class_name() == "class1")
-                    self.assertTrue(wi.get_handle() == "my-handle")
-
-                    # count1 is continuous, touching it will force a
-                    # publish on the interval
-                    self.assertTrue(sid is not None)
-                    test_obj = agent_app.agent.get_object("p1c1_key2", sid)
-                    self.assertTrue(test_obj is not None)
-                    test_obj.set_value("count1", r_count)
-
-                    if r_count == 4:  # 3 data + 1 subscribe reply
-                        rp = self.console.refresh_subscription(sp.get_subscription_id())
-                        self.assertTrue(rp)
-
-                self.console.release_workitem(wi)
-
-                wi = self.console.get_next_workitem(timeout=0)
-
-        # expect 5 publish per subscription, more if refreshed
-        self.assertTrue(sp is not None)
-        self.assertTrue(i_count > 5)
-
-        self.console.destroy(10)
-
-
-    def test_async_cancel(self):
-        # create console
-        # find one agent
-        # async subscribe to changes to any object in package1/class1
-        # cancel after first data indication
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        # query to match object "p1c1_key2" in schema package1/class1
-        sid = SchemaClassId.create("package1", "class1")
-        query = QmfQuery.create_id_object("p1c1_key2", sid)
-
-        agent_app = self.agents[0]
-        aname = agent_app.agent.get_name()
-        agent = self.console.find_agent(aname, timeout=3)
-        self.assertTrue(agent and agent.get_name() == aname)
-
-        # setup subscription on agent
-
-        rc = self.console.create_subscription(agent,
-                                              query,
-                                              "my-handle",
-                                              _blocking=False)
-        self.assertTrue(rc)
-
-        r_count = 0
-        sp = None
-        while self.notifier.wait_for_work(4):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                r_count += 1
-                if wi.get_type() == WorkItem.SUBSCRIBE_RESPONSE:
-                    self.assertTrue(wi.get_handle() == "my-handle")
-                    sp = wi.get_params()
-                    self.assertTrue(isinstance(sp, qmf2.console.SubscribeParams))
-                    self.assertTrue(sp.succeeded())
-                    self.assertTrue(sp.get_error() == None)
-                else:
-                    self.assertTrue(wi.get_type() ==
-                                    WorkItem.SUBSCRIBE_INDICATION)
-                    # sp better be set up by now!
-                    self.assertTrue(isinstance(sp, qmf2.console.SubscribeParams))
-                    reply = wi.get_params()
-                    self.assertTrue(isinstance(reply, type([])))
-                    self.assertTrue(len(reply) == 1)
-                    self.assertTrue(isinstance(reply[0], QmfData))
-                    self.assertTrue(reply[0].get_object_id() == "p1c1_key2")
-                    sid = reply[0].get_schema_class_id()
-                    self.assertTrue(isinstance(sid, SchemaClassId))
-                    self.assertTrue(sid.get_package_name() == "package1")
-                    self.assertTrue(sid.get_class_name() == "class1")
-                    self.assertTrue(wi.get_handle() == "my-handle")
-
-                    # count1 is continuous, touching it will force a
-                    # publish on the interval
-                    self.assertTrue(sid is not None)
-                    test_obj = agent_app.agent.get_object("p1c1_key2", sid)
-                    self.assertTrue(test_obj is not None)
-                    test_obj.set_value("count1", r_count)
-
-                if r_count == 3:
-                    self.console.cancel_subscription(sp.get_subscription_id())
-
-                self.console.release_workitem(wi)
-
-                wi = self.console.get_next_workitem(timeout=0)
-
-        # expect cancel after 3 replies
-        self.assertTrue(r_count == 3)
-
-        self.console.destroy(10)
-
-
-
-
-    def test_sync_periodic_publish_continuous(self):
-        # create console
-        # find all agents
-        # subscribe to changes to any object in package1/class1
-        # should succeed - verify 1 publish
-        # Change continuous property on each publish,
-        # should only see 1 publish per interval
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        subscriptions = []
-        index = 0
-
-        # query to match all objects in schema package1/class1
-        sid = SchemaClassId.create("package1", "class1")
-        t_params = {QmfData.KEY_SCHEMA_ID: sid}
-        query = QmfQuery.create_wildcard(QmfQuery.TARGET_OBJECT,
-                                         _target_params=t_params)
-        # find an agent
-        agent_app = self.agents[0]
-        aname = agent_app.agent.get_name()
-        agent = self.console.find_agent(aname, timeout=3)
-        self.assertTrue(agent and agent.get_name() == aname)
-
-        # setup subscription on agent
-
-        sp = self.console.create_subscription(agent,
-                                              query,
-                                              "some-handle")
-        self.assertTrue(isinstance(sp, qmf2.console.SubscribeParams))
-        self.assertTrue(sp.succeeded())
-        self.assertTrue(sp.get_error() == None)
-        self.assertTrue(sp.get_duration() == 10)
-        self.assertTrue(sp.get_publish_interval() == 2)
-
-        # now wait for the (2 * interval) and count the updates
-        r_count = 0
-        sid = None
-        while self.notifier.wait_for_work(4):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                r_count += 1
-                self.assertTrue(wi.get_type() == WorkItem.SUBSCRIBE_INDICATION)
-                self.assertTrue(wi.get_handle() == "some-handle")
-                if r_count == 1:
-                    # first indication - returns all matching objects
-                    reply = wi.get_params()
-                    self.assertTrue(isinstance(reply, type([])))
-                    self.assertTrue(len(reply) == 2)
-                    for obj in reply:
-                        self.assertTrue(isinstance(obj, QmfData))
-                        self.assertTrue(obj.get_object_id() == "p1c1_key2" or
-                                        obj.get_object_id() == "p1c1_key1")
-                        sid = obj.get_schema_class_id()
-                        self.assertTrue(isinstance(sid, SchemaClassId))
-                        self.assertTrue(sid.get_package_name() == "package1")
-                        self.assertTrue(sid.get_class_name() == "class1")
-
-                else:
-                    # verify publish of modified object only!
-                    reply = wi.get_params()
-                    self.assertTrue(isinstance(reply, type([])))
-                    self.assertTrue(len(reply) == 1)
-                    obj = reply[0]
-                    self.assertTrue(isinstance(obj, QmfData))
-                    self.assertTrue(obj.get_object_id() == "p1c1_key2")
-                    self.assertTrue(obj.get_value("count1") == r_count - 1)
-                    # fail test if we receive more than expected
-                    self.assertTrue(r_count < 10)
-
-
-                # now update one of the objects!
-                self.assertTrue(sid is not None)
-                test_obj = agent_app.agent.get_object("p1c1_key2", sid)
-                self.assertTrue(test_obj is not None)
-                test_obj.set_value("count1", r_count)
-
-                self.console.release_workitem(wi)
-
-                wi = self.console.get_next_workitem(timeout=0)
-
-        # expect at most 1 publish per interval seen
-        self.assertTrue(r_count < 10)
-
-        self.console.destroy(10)
-
-
-
-
-    def test_sync_periodic_publish_noncontinuous(self):
-        # create console, find agent
-        # subscribe to changes to any object in package1/class1
-        # should succeed - verify 1 publish
-        # Change noncontinuous property on each publish,
-        # should only see 1 publish per each update 
-        self.notifier = _testNotifier()
-        self.console = qmf2.console.Console(notifier=self.notifier,
-                                              agent_timeout=3)
-        self.conn = qpid.messaging.Connection(self.broker)
-        self.conn.open()
-        self.console.add_connection(self.conn)
-
-        subscriptions = []
-        index = 0
-
-        # query to match all objects in schema package1/class1
-        sid = SchemaClassId.create("package1", "class1")
-        t_params = {QmfData.KEY_SCHEMA_ID: sid}
-        query = QmfQuery.create_wildcard(QmfQuery.TARGET_OBJECT,
-                                         _target_params=t_params)
-        # find an agent
-        agent_app = self.agents[0]
-        aname = agent_app.agent.get_name()
-        agent = self.console.find_agent(aname, timeout=3)
-        self.assertTrue(agent and agent.get_name() == aname)
-
-        # setup subscription on agent
-
-        sp = self.console.create_subscription(agent,
-                                              query,
-                                              "some-handle")
-        self.assertTrue(isinstance(sp, qmf2.console.SubscribeParams))
-        self.assertTrue(sp.succeeded())
-        self.assertTrue(sp.get_error() == None)
-        self.assertTrue(sp.get_duration() == 10)
-        self.assertTrue(sp.get_publish_interval() == 2)
-
-        # now wait for the (2 * interval) and count the updates
-        r_count = 0
-        sid = None
-        while self.notifier.wait_for_work(4):
-            wi = self.console.get_next_workitem(timeout=0)
-            while wi is not None:
-                r_count += 1
-                self.assertTrue(wi.get_type() == WorkItem.SUBSCRIBE_INDICATION)
-                self.assertTrue(wi.get_handle() == "some-handle")
-                if r_count == 1:
-                    # first indication - returns all matching objects
-                    reply = wi.get_params()
-                    self.assertTrue(isinstance(reply, type([])))
-                    self.assertTrue(len(reply) == 2)
-                    for obj in reply:
-                        self.assertTrue(isinstance(obj, QmfData))
-                        self.assertTrue(obj.get_object_id() == "p1c1_key2" or
-                                        obj.get_object_id() == "p1c1_key1")
-                        sid = obj.get_schema_class_id()
-                        self.assertTrue(isinstance(sid, SchemaClassId))
-                        self.assertTrue(sid.get_package_name() == "package1")
-                        self.assertTrue(sid.get_class_name() == "class1")
-
-                else:
-                    # verify publish of modified object only!
-                    reply = wi.get_params()
-                    self.assertTrue(isinstance(reply, type([])))
-                    self.assertTrue(len(reply) == 1)
-                    obj = reply[0]
-                    self.assertTrue(isinstance(obj, QmfData))
-                    self.assertTrue(obj.get_object_id() == "p1c1_key2")
-                    self.assertTrue(obj.get_value("count2") == r_count - 1)
-                    # fail test if we receive more than expected
-                    self.assertTrue(r_count < 30)
-
-
-                # now update the noncontinuous field of one of the objects!
-                if r_count < 20:
-                    self.assertTrue(sid is not None)
-                    test_obj = agent_app.agent.get_object("p1c1_key2", sid)
-                    self.assertTrue(test_obj is not None)
-                    test_obj.set_value("count2", r_count)
-
-                self.console.release_workitem(wi)
-
-                wi = self.console.get_next_workitem(timeout=0)
-
-        # expect at least 1 publish per update
-        self.assertTrue(r_count > 10)
-
-        self.console.destroy(10)
Index: extras/qmf/src/py/qmf2/agent.py
===================================================================
--- extras/qmf/src/py/qmf2/agent.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/agent.py	(working copy)
@@ -1,1380 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-import sys
-import datetime
-import time
-import Queue
-from logging import getLogger
-from threading import Thread, RLock, currentThread, Event
-from qpid.messaging import Connection, Message, Empty, SendError
-from uuid import uuid4
-from common import (OpCode, QmfQuery, ContentType, SchemaObjectClass,
-                    QmfData, QmfAddress, SchemaClass, SchemaClassId, WorkItem,
-                    SchemaMethod, timedelta_to_secs, QMF_APP_ID)
-
-# global flag that indicates which thread (if any) is
-# running the agent notifier callback
-_callback_thread=None
-
-log = getLogger("qmf")
-trace = getLogger("qmf.agent")
-
-
-  ##==============================================================================
-  ## METHOD CALL
-  ##==============================================================================
-
-class _MethodCallHandle(object):
-    """
-    Private class used to hold context when handing off a method call to the
-    application.  Given to the app in a WorkItem, provided to the agent when
-    method_response() is invoked.
-    """
-    def __init__(self, correlation_id, reply_to, meth_name, _oid=None,
-                 _schema_id=None):
-        self.correlation_id = correlation_id
-        self.reply_to = reply_to
-        self.meth_name = meth_name
-        self.oid = _oid
-        self.schema_id = _schema_id
-
-class MethodCallParams(object):
-    """
-    """
-    def __init__(self, name, _oid=None, _schema_id=None, _in_args=None,
-                 _user_id=None):
-        self._meth_name = name
-        self._oid = _oid
-        self._schema_id = _schema_id
-        self._in_args = _in_args
-        self._user_id = _user_id
-
-    def get_name(self):
-        return self._meth_name
-
-    def get_object_id(self):
-        return self._oid
-
-    def get_schema_id(self):
-        return self._schema_id
-
-    def get_args(self):
-        return self._in_args
-
-    def get_user_id(self):
-        return self._user_id
-
-
-  ##==============================================================================
-  ## SUBSCRIPTIONS
-  ##==============================================================================
-
-
-class _SubscriptionState(object):
-    """
-    An internally-managed subscription.
-    """
-    def __init__(self, reply_to, cid, query, interval, duration):
-        self.reply_to = reply_to
-        self.correlation_id = cid
-        self.query = query
-        self.interval = interval
-        self.duration = duration
-        now = datetime.datetime.utcnow()
-        self.next_update = now  # do an immediate update
-        self.expiration = now + datetime.timedelta(seconds=duration)
-        self.last_update = None
-        self.id = 0
-
-    def resubscribe(self, now, _duration=None):
-        if _duration is not None:
-            self.duration = _duration
-        self.expiration = now + datetime.timedelta(seconds=self.duration)
-
-    def published(self, now):
-        self.next_update = now + datetime.timedelta(seconds=self.interval)
-        self.last_update = now
-
-
-  ##==============================================================================
-  ## AGENT
-  ##==============================================================================
-
-class Agent(Thread):
-    def __init__(self, name, _domain=None, _notifier=None, **options):
-        Thread.__init__(self)
-        self._running = False
-        self._ready = Event()
-
-        self.name = str(name)
-        self._domain = _domain
-        self._address = QmfAddress.direct(self.name, self._domain)
-        self._notifier = _notifier
-
-        # configurable parameters
-        #
-        self._heartbeat_interval = options.get("heartbeat_interval", 30)
-        self._capacity = options.get("capacity", 10)
-        self._default_duration = options.get("default_duration", 300)
-        self._max_duration = options.get("max_duration", 3600)
-        self._min_duration = options.get("min_duration", 10)
-        self._default_interval = options.get("default_interval", 30)
-        self._min_interval = options.get("min_interval", 5)
-
-        # @todo: currently, max # of objects in a single reply message, would
-        # be better if it were max bytesize of per-msg content...
-        self._max_msg_size = options.get("max_msg_size", 0)
-
-        self._conn = None
-        self._session = None
-        self._direct_receiver = None
-        self._topic_receiver = None
-        self._direct_sender = None
-        self._topic_sender = None
-
-        self._lock = RLock()
-        self._packages = {}
-        self._schema_timestamp = long(0)
-        self._schema = {}
-        # _described_data holds QmfData objects that are associated with schema
-        # it is index by schema_id, object_id
-        self._described_data = {}
-        # _undescribed_data holds unstructured QmfData objects - these objects
-        # have no schema.  it is indexed by object_id only.
-        self._undescribed_data = {}
-        self._work_q = Queue.Queue()
-        self._work_q_put = False
-        # subscriptions
-        self._subscription_id = long(time.time())
-        self._subscriptions = {}
-        self._next_subscribe_event = None
-
-        # prevents multiple _wake_thread() calls
-        self._noop_pending = False
-
-
-    def destroy(self, timeout=None):
-        """
-        Must be called before the Agent is deleted.  
-        Frees up all resources and shuts down all background threads.
-
-        @type timeout: float
-        @param timeout: maximum time in seconds to wait for all background threads to terminate.  Default: forever.
-        """
-        trace.debug("Destroying Agent %s" % self.name)
-        if self._conn:
-            self.remove_connection(timeout)
-        trace.debug("Agent Destroyed")
-
-
-    def get_name(self):
-        return self.name
-
-    def set_connection(self, conn):
-        self._conn = conn
-        self._session = self._conn.session()
-
-        # for messages directly addressed to me
-        self._direct_receiver = self._session.receiver(str(self._address) +
-                                                       ";{create:always,"
-                                                       " node:"
-                                                       " {type:topic,"
-                                                       " x-declare:"
-                                                       " {type:direct}}}",
-                                                       capacity=self._capacity)
-        trace.debug("my direct addr=%s" % self._direct_receiver.source)
-
-        # for sending directly addressed messages.
-        self._direct_sender = self._session.sender(str(self._address.get_node()) +
-                                                   ";{create:always,"
-                                                   " node:"
-                                                   " {type:topic,"
-                                                   " x-declare:"
-                                                   " {type:direct}}}")
-        trace.debug("my default direct send addr=%s" % self._direct_sender.target)
-
-        # for receiving "broadcast" messages from consoles
-        default_addr = QmfAddress.topic(QmfAddress.SUBJECT_CONSOLE_IND + ".#",
-                                        self._domain)
-        self._topic_receiver = self._session.receiver(str(default_addr) +
-                                                       ";{create:always,"
-                                                       " node:"
-                                                       " {type:topic}}",
-                                                       capacity=self._capacity)
-        trace.debug("console.ind addr=%s" % self._topic_receiver.source)
-
-        # for sending to topic subscribers
-        ind_addr = QmfAddress.topic(QmfAddress.SUBJECT_AGENT_IND,
-                                    self._domain)
-        self._topic_sender = self._session.sender(str(ind_addr) +
-                                                ";{create:always,"
-                                                " node:"
-                                                " {type:topic}}")
-        trace.debug("agent.ind addr=%s" % self._topic_sender.target)
-
-        self._running = True
-        self.start()
-        self._ready.wait(10)
-        if not self._ready.isSet():
-            raise Exception("Agent managment thread failed to start.")
-
-    def remove_connection(self, timeout=None):
-        # tell connection thread to shutdown
-        self._running = False
-        if self.isAlive():
-            # kick my thread to wake it up
-            self._wake_thread()
-            trace.debug("waiting for agent receiver thread to exit")
-            self.join(timeout)
-            if self.isAlive():
-                log.error( "Agent thread '%s' is hung..." % self.name)
-        self._direct_receiver.close()
-        self._direct_receiver = None
-        self._direct_sender.close()
-        self._direct_sender = None
-        self._topic_receiver.close()
-        self._topic_receiver = None
-        self._topic_sender.close()
-        self._topic_sender = None
-        self._session.close()
-        self._session = None
-        self._conn = None
-        trace.debug("agent connection removal complete")
-
-    def register_object_class(self, schema):
-        """
-        Register an instance of a SchemaClass with this agent
-        """
-        # @todo: need to update subscriptions
-        # @todo: need to mark schema as "non-const"
-        if not isinstance(schema, SchemaClass):
-            raise TypeError("SchemaClass instance expected")
-
-        classId = schema.get_class_id()
-        pname = classId.get_package_name()
-        cname = classId.get_class_name()
-        hstr = classId.get_hash_string()
-        if not hstr:
-            raise Exception("Schema hash is not set.")
-
-        self._lock.acquire()
-        try:
-            if pname not in self._packages:
-                self._packages[pname] = [cname]
-            else:
-                if cname not in self._packages[pname]:
-                    self._packages[pname].append(cname)
-            self._schema[classId] = schema
-            self._schema_timestamp = long(time.time() * 1000)
-        finally:
-            self._lock.release()
-
-    def register_event_class(self, schema):
-        return self.register_object_class(schema)
-
-    def raise_event(self, qmfEvent):
-        """
-        TBD
-        """
-        if not self._topic_sender:
-            raise Exception("No connection available")
-
-        # @todo: should we validate against the schema?
-        msg = Message(id=QMF_APP_ID,
-                      subject=QmfAddress.SUBJECT_AGENT_EVENT + "." +
-                      qmfEvent.get_severity() + "." + self.name,
-                      properties={"method":"indication",
-                                  "qmf.opcode":OpCode.data_ind,
-                                  "qmf.content": ContentType.event,
-                                  "qmf.agent":self.name},
-                      content=[qmfEvent.map_encode()])
-        # TRACE
-        # log.error("!!! Agent %s sending Event (%s)" % 
-        # (self.name, str(msg)))
-        self._topic_sender.send(msg)
-
-    def add_object(self, data):
-        """
-        Register an instance of a QmfAgentData object.
-        """
-        # @todo: need to mark schema as "non-const"
-        if not isinstance(data, QmfAgentData):
-            raise TypeError("QmfAgentData instance expected")
-
-        oid = data.get_object_id()
-        if not oid:
-            raise TypeError("No identifier assigned to QmfAgentData!")
-
-        sid = data.get_schema_class_id()
-
-        self._lock.acquire()
-        try:
-            if sid:
-                if sid not in self._described_data:
-                    self._described_data[sid] = {oid: data}
-                else:
-                    self._described_data[sid][oid] = data
-            else:
-                self._undescribed_data[oid] = data
-
-            # does the new object match any subscriptions?
-            now = datetime.datetime.utcnow()
-            for sid,sub in self._subscriptions.iteritems():
-                if sub.query.evaluate(data):
-                    # matched.  Mark the subscription as needing to be
-                    # serviced. The _publish() method will notice the new
-                    # object and will publish it next time it runs.
-                    sub.next_update = now
-                    self._next_subscribe_event = None
-                    # @todo: should we immediately publish?
-
-        finally:
-            self._lock.release()
-
-    def get_object(self, oid, schema_id):
-        data = None
-        self._lock.acquire()
-        try:
-            if schema_id:
-                data = self._described_data.get(schema_id)
-                if data:
-                    data = data.get(oid)
-            else:
-                data = self._undescribed_data.get(oid)
-        finally:
-            self._lock.release()
-        return data
-
-
-    def method_response(self, handle, _out_args=None, _error=None):
-        """
-        """
-        if not isinstance(handle, _MethodCallHandle):
-            raise TypeError("Invalid handle passed to method_response!")
-
-        _map = {SchemaMethod.KEY_NAME:handle.meth_name}
-        if handle.oid is not None:
-            _map[QmfData.KEY_OBJECT_ID] = handle.oid
-        if handle.schema_id is not None:
-            _map[QmfData.KEY_SCHEMA_ID] = handle.schema_id.map_encode()
-        if _out_args is not None:
-            _map[SchemaMethod.KEY_ARGUMENTS] = _out_args.copy()
-        if _error is not None:
-            if not isinstance(_error, QmfData):
-                raise TypeError("Invalid type for error - must be QmfData")
-            _map[SchemaMethod.KEY_ERROR] = _error.map_encode()
-
-        msg = Message(id=QMF_APP_ID,
-                      properties={"method":"response",
-                                  "qmf.opcode":OpCode.method_rsp},
-                      content=_map)
-        msg.correlation_id = handle.correlation_id
-
-        self._send_reply(msg, handle.reply_to)
-
-    def get_workitem_count(self): 
-        """ 
-        Returns the count of pending WorkItems that can be retrieved.
-        """
-        return self._work_q.qsize()
-
-    def get_next_workitem(self, timeout=None): 
-        """
-        Obtains the next pending work item, or None if none available. 
-        """
-        try:
-            wi = self._work_q.get(True, timeout)
-        except Queue.Empty:
-            return None
-        return wi
-
-    def release_workitem(self, wi): 
-        """
-        Releases a WorkItem instance obtained by getNextWorkItem(). Called when 
-        the application has finished processing the WorkItem. 
-        """
-        pass
-
-
-    def run(self):
-        global _callback_thread
-        next_heartbeat = datetime.datetime.utcnow()
-        batch_limit = 10 # a guess
-
-        self._ready.set()
-
-        while self._running:
-
-            #
-            # Process inbound messages
-            #
-            trace.debug("%s processing inbound messages..." % self.name)
-            for i in range(batch_limit):
-                try:
-                    msg = self._topic_receiver.fetch(timeout=0)
-                except Empty:
-                    break
-                # TRACE
-                # log.error("!!! Agent %s: msg on %s [%s]" %
-                # (self.name, self._topic_receiver.source, msg))
-                self._dispatch(msg, _direct=False)
-
-            for i in range(batch_limit):
-                try:
-                    msg = self._direct_receiver.fetch(timeout=0)
-                except Empty:
-                    break
-                # TRACE
-                # log.error("!!! Agent %s: msg on %s [%s]" %
-                # (self.name, self._direct_receiver.source, msg))
-                self._dispatch(msg, _direct=True)
-
-            #
-            # Send Heartbeat Notification
-            #
-            now = datetime.datetime.utcnow()
-            if now >= next_heartbeat:
-                trace.debug("%s sending heartbeat..." % self.name)
-                ind = Message(id=QMF_APP_ID,
-                              subject=QmfAddress.SUBJECT_AGENT_HEARTBEAT,
-                              properties={"method":"indication",
-                                          "qmf.opcode":OpCode.agent_heartbeat_ind,
-                                          "qmf.agent":self.name},
-                              content=self._makeAgentInfoBody())
-                # TRACE
-                #log.error("!!! Agent %s sending Heartbeat (%s)" % 
-                # (self.name, str(ind)))
-                self._topic_sender.send(ind)
-                trace.debug("Agent Indication Sent")
-                next_heartbeat = now + datetime.timedelta(seconds = self._heartbeat_interval)
-
-            #
-            # Monitor Subscriptions
-            #
-            self._lock.acquire()
-            try:
-                now = datetime.datetime.utcnow()
-                if (self._next_subscribe_event is None or
-                    now >= self._next_subscribe_event):
-                    trace.debug("%s polling subscriptions..." % self.name)
-                    self._next_subscribe_event = now + datetime.timedelta(seconds=
-                                                                      self._max_duration)
-                    dead_ss = {}
-                    for sid,ss in self._subscriptions.iteritems():
-                        if now >= ss.expiration:
-                            dead_ss[sid] = ss
-                            continue
-                        if now >= ss.next_update:
-                            self._publish(ss)
-                        next_timeout = min(ss.expiration, ss.next_update)
-                        if next_timeout < self._next_subscribe_event:
-                            self._next_subscribe_event = next_timeout
-
-                    for sid,ss in dead_ss.iteritems():
-                        del self._subscriptions[sid]
-                        self._unpublish(ss)
-            finally:
-                self._lock.release()
-
-            #
-            # notify application of pending WorkItems
-            #
-            if self._work_q_put and self._notifier:
-                trace.debug("%s notifying application..." % self.name)
-                # new stuff on work queue, kick the the application...
-                self._work_q_put = False
-                _callback_thread = currentThread()
-                trace.debug("Calling agent notifier.indication")
-                self._notifier.indication()
-                _callback_thread = None
-
-            #
-            # Sleep until messages arrive or something times out
-            #
-            now = datetime.datetime.utcnow()
-            next_timeout = next_heartbeat
-            self._lock.acquire()
-            try:
-                # the mailbox expire flag may be cleared by the
-                # app thread(s) in order to force an immediate publish
-                if self._next_subscribe_event is None:
-                    next_timeout = now
-                elif self._next_subscribe_event < next_timeout:
-                    next_timeout = self._next_subscribe_event
-            finally:
-                self._lock.release()
-
-            timeout = timedelta_to_secs(next_timeout - now)
-
-            if self._running and timeout > 0.0:
-                trace.debug("%s sleeping %s seconds..." % (self.name,
-                                                             timeout))
-                try:
-                    self._session.next_receiver(timeout=timeout)
-                except Empty:
-                    pass
-
-
-        trace.debug("Shutting down Agent %s thread" % self.name)
-
-    #
-    # Private:
-    #
-
-    def _makeAgentInfoBody(self):
-        """
-        Create an agent indication message body identifying this agent
-        """
-        return QmfData.create({"_name": self.get_name(),
-                              "_schema_timestamp": self._schema_timestamp}).map_encode()
-
-    def _send_reply(self, msg, reply_to):
-        """
-        Send a reply message to the given reply_to address
-        """
-        if not isinstance(reply_to, QmfAddress):
-            try:
-                reply_to = QmfAddress.from_string(str(reply_to))
-            except ValueError:
-                log.error("Invalid reply-to address '%s'" % reply_to)
-
-        msg.subject = reply_to.get_subject()
-
-        try:
-            if reply_to.is_direct():
-                # TRACE
-                #log.error("!!! Agent %s direct REPLY-To:%s (%s)" % 
-                # (self.name, str(reply_to), str(msg)))
-                self._direct_sender.send(msg)
-            else:
-                # TRACE
-                # log.error("!!! Agent %s topic REPLY-To:%s (%s)" % 
-                # (self.name, str(reply_to), str(msg)))
-                self._topic_sender.send(msg)
-            trace.debug("reply msg sent to [%s]" % str(reply_to))
-        except SendError, e:
-            log.error("Failed to send reply msg '%s' (%s)" % (msg, str(e)))
-
-    def _send_query_response(self, content_type, cid, reply_to, objects):
-        """
-        Send a response to a query, breaking the result into multiple
-        messages based on the agent's _max_msg_size config parameter
-        """
-
-        total = len(objects)
-        if self._max_msg_size:
-            max_count = self._max_msg_size
-        else:
-            max_count = total
-
-        start = 0
-        end = min(total, max_count)
-        # send partial response if too many objects present
-        while end < total:
-            m = Message(id=QMF_APP_ID,
-                        properties={"method":"response",
-                                    "partial":None,
-                                    "qmf.opcode":OpCode.data_ind,
-                                    "qmf.content":content_type,
-                                    "qmf.agent":self.name},
-                        correlation_id = cid,
-                        content=objects[start:end])
-            self._send_reply(m, reply_to)
-            start = end
-            end = min(total, end + max_count)
-
-        m = Message(id=QMF_APP_ID,
-                    properties={"method":"response",
-                                "qmf.opcode":OpCode.data_ind,
-                                "qmf.content":content_type,
-                                "qmf.agent":self.name},
-                    correlation_id = cid,
-                    content=objects[start:end])
-        self._send_reply(m, reply_to)
-
-    def _dispatch(self, msg, _direct=False):
-        """
-        Process a message from a console.
-
-        @param _direct: True if msg directly addressed to this agent.
-        """
-        trace.debug( "Message received from Console! [%s]" % msg )
-
-        opcode = msg.properties.get("qmf.opcode")
-        if not opcode:
-            log.warning("Ignoring unrecognized message '%s'" % msg)
-            return
-        version = 2  # @todo: fix me
-        cmap = {}; props={}
-        if msg.content_type == "amqp/map":
-            cmap = msg.content
-        if msg.properties:
-            props = msg.properties
-
-        if opcode == OpCode.agent_locate_req:
-            self._handleAgentLocateMsg( msg, cmap, props, version, _direct )
-        elif opcode == OpCode.query_req:
-            self._handleQueryMsg( msg, cmap, props, version, _direct )
-        elif opcode == OpCode.method_req:
-            self._handleMethodReqMsg(msg, cmap, props, version, _direct)
-        elif opcode == OpCode.subscribe_req:
-            self._handleSubscribeReqMsg(msg, cmap, props, version, _direct)
-        elif opcode == OpCode.subscribe_refresh_ind:
-            self._handleResubscribeReqMsg(msg, cmap, props, version, _direct)
-        elif opcode == OpCode.subscribe_cancel_ind:
-            self._handleUnsubscribeReqMsg(msg, cmap, props, version, _direct)
-        elif opcode == OpCode.noop:
-            self._noop_pending = False
-            trace.debug("No-op msg received.")
-        else:
-            log.warning("Ignoring message with unrecognized 'opcode' value: '%s'"
-                            % opcode)
-
-    def _handleAgentLocateMsg( self, msg, cmap, props, version, direct ):
-        """
-        Process a received agent-locate message
-        """
-        trace.debug("_handleAgentLocateMsg")
-
-        reply = False
-        if props.get("method") == "request":
-            # if the message is addressed to me or wildcard, process it
-            if (msg.subject == "console.ind" or
-                msg.subject == "console.ind.locate" or
-                msg.subject == "console.ind.locate." + self.name):
-                pred = msg.content
-                if not pred:
-                    reply = True
-                elif isinstance(pred, type([])):
-                    # fake a QmfData containing my identifier for the query compare
-                    query = QmfQuery.create_predicate(QmfQuery.TARGET_AGENT, pred)
-                    tmpData = QmfData.create({QmfQuery.KEY_AGENT_NAME:
-                                                  self.get_name()},
-                                             _object_id="my-name")
-                    reply = query.evaluate(tmpData)
-
-        if reply:
-            m = Message(id=QMF_APP_ID,
-                        properties={"method":"response",
-                                    "qmf.opcode":OpCode.agent_locate_rsp},
-                        content=self._makeAgentInfoBody())
-            m.correlation_id = msg.correlation_id
-            self._send_reply(m, msg.reply_to)
-        else:
-            trace.debug("agent-locate msg not mine - no reply sent")
-
-
-    def _handleQueryMsg(self, msg, cmap, props, version, _direct ):
-        """
-        Handle received query message
-        """
-        trace.debug("_handleQueryMsg")
-
-        if "method" in props and props["method"] == "request":
-            if cmap:
-                try:
-                    query = QmfQuery.from_map(cmap)
-                except TypeError:
-                    log.error("Invalid Query format: '%s'" % str(cmap))
-                    return
-                target = query.get_target()
-                if target == QmfQuery.TARGET_PACKAGES:
-                    self._queryPackagesReply( msg, query )
-                elif target == QmfQuery.TARGET_SCHEMA_ID:
-                    self._querySchemaReply( msg, query, _idOnly=True )
-                elif target == QmfQuery.TARGET_SCHEMA:
-                    self._querySchemaReply( msg, query)
-                elif target == QmfQuery.TARGET_AGENT:
-                    log.warning("!!! @todo: Query TARGET=AGENT TBD !!!")
-                elif target == QmfQuery.TARGET_OBJECT_ID:
-                    self._queryDataReply(msg, query, _idOnly=True)
-                elif target == QmfQuery.TARGET_OBJECT:
-                    self._queryDataReply(msg, query)
-                else:
-                    log.warning("Unrecognized query target: '%s'" % str(target))
-
-
-
-    def _handleMethodReqMsg(self, msg, cmap, props, version, _direct):
-        """
-        Process received Method Request
-        """
-        if "method" in props and props["method"] == "request":
-            mname = cmap.get(SchemaMethod.KEY_NAME)
-            if not mname:
-                log.warning("Invalid method call from '%s': no name"
-                                % msg.reply_to)
-                return
-
-            in_args = cmap.get(SchemaMethod.KEY_ARGUMENTS)
-            oid = cmap.get(QmfData.KEY_OBJECT_ID)
-            schema_id = cmap.get(QmfData.KEY_SCHEMA_ID)
-            if schema_id:
-                schema_id = SchemaClassId.from_map(schema_id)
-            handle = _MethodCallHandle(msg.correlation_id,
-                                       msg.reply_to,
-                                       mname,
-                                       oid, schema_id)
-            param = MethodCallParams( mname, oid, schema_id, in_args,
-                                      msg.user_id)
-
-            # @todo: validate the method against the schema:
-            # if self._schema:
-            #     # validate
-            #     _in_args = _in_args.copy()
-            #     ms = self._schema.get_method(name)
-            #     if ms is None:
-            #         raise ValueError("Method '%s' is undefined." % name)
-
-            #     for aname,prop in ms.get_arguments().iteritems():
-            #         if aname not in _in_args:
-            #             if prop.get_default():
-            #                 _in_args[aname] = prop.get_default()
-            #             elif not prop.is_optional():
-            #                 raise ValueError("Method '%s' requires argument '%s'"
-            #                                  % (name, aname))
-            #     for aname in _in_args.iterkeys():
-            #         prop = ms.get_argument(aname)
-            #         if prop is None:
-            #             raise ValueError("Method '%s' does not define argument"
-            #                              " '%s'" % (name, aname))
-            #         if "I" not in prop.get_direction():
-            #             raise ValueError("Method '%s' argument '%s' is not an"
-            #                              " input." % (name, aname)) 
-
-            #     # @todo check if value is correct (type, range, etc)
-
-            self._work_q.put(WorkItem(WorkItem.METHOD_CALL, handle, param))
-            self._work_q_put = True
-
-    def _handleSubscribeReqMsg(self, msg, cmap, props, version, _direct):
-        """
-        Process received Subscription Request
-        """
-        if "method" in props and props["method"] == "request":
-            query_map = cmap.get("_query")
-            interval = cmap.get("_interval")
-            duration = cmap.get("_duration")
-
-            try:
-                query = QmfQuery.from_map(query_map)
-            except TypeError:
-                log.warning("Invalid query for subscription: %s" %
-                                str(query_map))
-                return
-
-            if isinstance(self, AgentExternal):
-                # param = SubscriptionParams(_ConsoleHandle(console_handle,
-                #                                           msg.reply_to),
-                #                            query,
-                #                            interval,
-                #                            duration,
-                #                            msg.user_id)
-                # self._work_q.put(WorkItem(WorkItem.SUBSCRIBE_REQUEST,
-                #                           msg.correlation_id, param))
-                # self._work_q_put = True
-                log.error("External Subscription TBD")
-                return
-
-            # validate the query - only specific objects, or
-            # objects wildcard, are currently supported.
-            if (query.get_target() != QmfQuery.TARGET_OBJECT or
-                (query.get_selector() == QmfQuery.PREDICATE and
-                 query.get_predicate())):
-                log.error("Subscriptions only support (wildcard) Object"
-                              " Queries.")
-                err = QmfData.create(
-                    {"reason": "Unsupported Query type for subscription.",
-                     "query": str(query.map_encode())})
-                m = Message(id=QMF_APP_ID,
-                            properties={"method":"response",
-                                        "qmf.opcode":OpCode.subscribe_rsp},
-                            correlation_id = msg.correlation_id,
-                            content={"_error": err.map_encode()})
-                self._send_reply(m, msg.reply_to)
-                return
-
-            if duration is None:
-                duration = self._default_duration
-            else:
-                try:
-                    duration = float(duration)
-                    if duration > self._max_duration:
-                        duration = self._max_duration
-                    elif duration < self._min_duration:
-                        duration = self._min_duration
-                except:
-                    log.warning("Bad duration value: %s" % str(msg))
-                    duration = self._default_duration
-
-            if interval is None:
-                interval = self._default_interval
-            else:
-                try:
-                    interval = float(interval)
-                    if interval < self._min_interval:
-                        interval = self._min_interval
-                except:
-                    log.warning("Bad interval value: %s" % str(msg))
-                    interval = self._default_interval
-
-            ss = _SubscriptionState(msg.reply_to,
-                                    msg.correlation_id,
-                                    query,
-                                    interval,
-                                    duration)
-            self._lock.acquire()
-            try:
-                sid = self._subscription_id
-                self._subscription_id += 1
-                ss.id = sid
-                self._subscriptions[sid] = ss
-                self._next_subscribe_event = None
-            finally:
-                self._lock.release()
-
-            sr_map = {"_subscription_id": sid,
-                      "_interval": interval,
-                      "_duration": duration}
-            m = Message(id=QMF_APP_ID,
-                        properties={"method":"response",
-                                   "qmf.opcode":OpCode.subscribe_rsp},
-                        correlation_id = msg.correlation_id,
-                        content=sr_map)
-            self._send_reply(m, msg.reply_to)
-
-
-
-    def _handleResubscribeReqMsg(self, msg, cmap, props, version, _direct):
-        """
-        Process received Renew Subscription Request
-        """
-        if props.get("method") == "request":
-            sid = cmap.get("_subscription_id")
-            if not sid:
-                log.error("Invalid subscription refresh msg: %s" %
-                              str(msg))
-                return
-
-            self._lock.acquire()
-            try:
-                ss = self._subscriptions.get(sid)
-                if not ss:
-                    log.error("Ignoring unknown subscription: %s" %
-                                  str(sid))
-                    return
-                duration = cmap.get("_duration")
-                if duration is not None:
-                    try:
-                        duration = float(duration)
-                        if duration > self._max_duration:
-                            duration = self._max_duration
-                        elif duration < self._min_duration:
-                            duration = self._min_duration
-                    except:
-                        log.error("Bad duration value: %s" % str(msg))
-                        duration = None  # use existing duration
-
-                ss.resubscribe(datetime.datetime.utcnow(), duration)
-
-                new_duration = ss.duration
-                new_interval = ss.interval
-
-            finally:
-                self._lock.release()
-
-
-            sr_map = {"_subscription_id": sid,
-                      "_interval": new_interval,
-                      "_duration": new_duration}
-            m = Message(id=QMF_APP_ID,
-                        properties={"method":"response",
-                                   "qmf.opcode":OpCode.subscribe_rsp},
-                        correlation_id = msg.correlation_id,
-                        content=sr_map)
-            self._send_reply(m, msg.reply_to)
-
-
-    def _handleUnsubscribeReqMsg(self, msg, cmap, props, version, _direct):
-        """
-        Process received Cancel Subscription Request
-        """
-        if props.get("method") == "request":
-            sid = cmap.get("_subscription_id")
-            if not sid:
-                log.warning("No subscription id supplied: %s" % msg)
-                return
-
-            self._lock.acquire()
-            try:
-                if sid in self._subscriptions:
-                    dead_sub = self._subscriptions[sid]
-                    del self._subscriptions[sid]
-            finally:
-                self._lock.release()
-
-            self._unpublish(dead_sub)
-
-
-    def _queryPackagesReply(self, msg, query):
-        """
-        Run a query against the list of known packages
-        """
-        pnames = []
-        self._lock.acquire()
-        try:
-            for name in self._packages.iterkeys():
-                qmfData = QmfData.create({SchemaClassId.KEY_PACKAGE:name},
-                                         _object_id="_package")
-                if query.evaluate(qmfData):
-                    pnames.append(name)
-
-            self._send_query_response(ContentType.schema_package,
-                                      msg.correlation_id,
-                                      msg.reply_to,
-                                      pnames)
-        finally:
-            self._lock.release()
-
-
-    def _querySchemaReply( self, msg, query, _idOnly=False ):
-        """
-        """
-        schemas = []
-
-        self._lock.acquire()
-        try:
-            # if querying for a specific schema, do a direct lookup
-            if query.get_selector() == QmfQuery.ID:
-                found = self._schema.get(query.get_id())
-                if found:
-                    if _idOnly:
-                        schemas.append(query.get_id().map_encode())
-                    else:
-                        schemas.append(found.map_encode())
-            else: # otherwise, evaluate all schema
-                for sid,val in self._schema.iteritems():
-                    if query.evaluate(val):
-                        if _idOnly:
-                            schemas.append(sid.map_encode())
-                        else:
-                            schemas.append(val.map_encode())
-            if _idOnly:
-                msgkey = ContentType.schema_id
-            else:
-                msgkey = ContentType.schema_class
-
-            self._send_query_response(msgkey,
-                                      msg.correlation_id,
-                                      msg.reply_to,
-                                      schemas)
-        finally:
-            self._lock.release()
-
-
-    def _queryDataReply( self, msg, query, _idOnly=False ):
-        """
-        """
-        # hold the (recursive) lock for the duration so the Agent
-        # won't send data that is currently being modified by the
-        # app.
-        self._lock.acquire()
-        try:
-            response = []
-            data_objs = self._queryData(query)
-            if _idOnly:
-                for obj in data_objs:
-                    response.append(obj.get_object_id())
-            else:
-                for obj in data_objs:
-                    response.append(obj.map_encode())
-
-            if _idOnly:
-                msgkey = ContentType.object_id
-            else:
-                msgkey = ContentType.data
-
-            self._send_query_response(msgkey,
-                                      msg.correlation_id,
-                                      msg.reply_to,
-                                      response)
-        finally:
-            self._lock.release()
-
-
-    def _queryData(self, query):
-        """
-        Return a list of QmfData objects that match a given query
-        """
-        data_objs = []
-        # extract optional schema_id from target params
-        sid = None
-        t_params = query.get_target_param()
-        if t_params:
-            sid = t_params.get(QmfData.KEY_SCHEMA_ID)
-
-        self._lock.acquire()
-        try:
-            # if querying for a specific object, do a direct lookup
-            if query.get_selector() == QmfQuery.ID:
-                oid = query.get_id()
-                if sid and not sid.get_hash_string():
-                    # wildcard schema_id match, check each schema
-                    for name,db in self._described_data.iteritems():
-                        if (name.get_class_name() == sid.get_class_name()
-                            and name.get_package_name() == sid.get_package_name()):
-                            found = db.get(oid)
-                            if found:
-                                data_objs.append(found)
-                else:
-                    found = None
-                    if sid:
-                        db = self._described_data.get(sid)
-                        if db:
-                            found = db.get(oid)
-                    else:
-                        found = self._undescribed_data.get(oid)
-                    if found:
-                        data_objs.append(found)
-
-            else: # otherwise, evaluate all data
-                if sid and not sid.get_hash_string():
-                    # wildcard schema_id match, check each schema
-                    for name,db in self._described_data.iteritems():
-                        if (name.get_class_name() == sid.get_class_name()
-                            and name.get_package_name() == sid.get_package_name()):
-                            for oid,data in db.iteritems():
-                                if query.evaluate(data):
-                                    data_objs.append(data)
-                else:
-                    if sid:
-                        db = self._described_data.get(sid)
-                    else:
-                        db = self._undescribed_data
-
-                    if db:
-                        for oid,data in db.iteritems():
-                            if query.evaluate(data):
-                                data_objs.append(data)
-        finally:
-            self._lock.release()
-
-        return data_objs
-
-    def _publish(self, sub):
-        """ Publish a subscription.
-        """
-        response = []
-        now = datetime.datetime.utcnow()
-        objs = self._queryData(sub.query)
-        if objs:
-            for obj in objs:
-                if sub.id not in obj._subscriptions:
-                    # new to subscription - publish it
-                    obj._subscriptions[sub.id] = sub
-                    response.append(obj.map_encode())
-                elif obj._dtime:
-                    # obj._dtime is millisec since utc.  Convert to datetime
-                    utcdt = datetime.datetime.utcfromtimestamp(obj._dtime/1000.0)
-                    if utcdt > sub.last_update:
-                        response.append(obj.map_encode())
-                else:
-                    # obj._utime is millisec since utc.  Convert to datetime
-                    utcdt = datetime.datetime.utcfromtimestamp(obj._utime/1000.0)
-                    if utcdt > sub.last_update:
-                        response.append(obj.map_encode())
-
-            if response:
-                trace.debug("!!! %s publishing %s!!!" % (self.name, sub.correlation_id))
-                self._send_query_response( ContentType.data,
-                                           sub.correlation_id,
-                                           sub.reply_to,
-                                           response)
-        sub.published(now)
-
-    def _unpublish(self, sub):
-        """ This subscription is about to be deleted, remove it from any
-        referencing objects.
-        """
-        objs = self._queryData(sub.query)
-        if objs:
-            for obj in objs:
-                if sub.id in obj._subscriptions:
-                    del obj._subscriptions[sub.id]
-
-
-
-    def _wake_thread(self):
-        """
-        Make the agent management thread loop wakeup from its next_receiver
-        sleep.
-        """
-        self._lock.acquire()
-        try:
-            if not self._noop_pending:
-                trace.debug("Sending noop to wake up [%s]" % self._address)
-                msg = Message(id=QMF_APP_ID,
-                              subject=self.name,
-                              properties={"method":"indication",
-                                          "qmf.opcode":OpCode.noop},
-                              content={})
-                try:
-                    self._direct_sender.send( msg, sync=True )
-                    self._noop_pending = True
-                except SendError, e:
-                    log.error(str(e))
-        finally:
-            self._lock.release()
-
-
-  ##==============================================================================
-  ## EXTERNAL DATABASE AGENT
-  ##==============================================================================
-
-class AgentExternal(Agent):
-    """
-    An Agent which uses an external management database.
-    """
-    def __init__(self, name, _domain=None, _notifier=None,
-                 _heartbeat_interval=30, _max_msg_size=0, _capacity=10):
-        super(AgentExternal, self).__init__(name, _domain, _notifier,
-                                            _heartbeat_interval,
-                                            _max_msg_size, _capacity)
-        log.error("AgentExternal TBD")
-
-
-
-  ##==============================================================================
-  ## DATA MODEL
-  ##==============================================================================
-
-
-class QmfAgentData(QmfData):
-    """
-    A managed data object that is owned by an agent.
-    """
-
-    def __init__(self, agent, _values={}, _subtypes={}, _tag=None,
-                 _object_id=None, _schema=None):
-        schema_id = None
-        if _schema:
-            schema_id = _schema.get_class_id()
-
-        if _object_id is None:
-            if not isinstance(_schema, SchemaObjectClass):
-                raise Exception("An object_id must be provided if the object"
-                                "doesn't have an associated schema.")
-            ids = _schema.get_id_names()
-            if not ids:
-                raise Exception("Object must have an Id or a schema that"
-                                " provides an Id")
-            _object_id = u""
-            for key in ids:
-                value = _values.get(key)
-                if value is None:
-                    raise Exception("Object must have a value for key"
-                                    " attribute '%s'" % str(key))
-                try:
-                    _object_id += unicode(value)
-                except:
-                    raise Exception("Cannot create object_id from key" 
-                                    " value '%s'" % str(value))
-
-        # timestamp in millisec since epoch UTC
-        ctime = long(time.time() * 1000)
-        super(QmfAgentData, self).__init__(_values=_values, _subtypes=_subtypes,
-                                           _tag=_tag, _ctime=ctime,
-                                           _utime=ctime, _object_id=_object_id,
-                                           _schema_id=schema_id, _const=False)
-        self._agent = agent
-        self._validated = False
-        self._modified = True
-        self._subscriptions = {}
-
-    def destroy(self): 
-        self._dtime = long(time.time() * 1000)
-        self._touch()
-        # @todo: publish change
-
-    def is_deleted(self): 
-        return self._dtime == 0
-
-    def set_value(self, _name, _value, _subType=None):
-        super(QmfAgentData, self).set_value(_name, _value, _subType)
-        self._utime = long(time.time() * 1000)
-        self._touch(_name)
-        # @todo: publish change
-
-    def inc_value(self, name, delta=1):
-        """ add the delta to the property """
-        # @todo: need to take write-lock
-        val = self.get_value(name)
-        try:
-            val += delta
-        except:
-            raise
-        self.set_value(name, val)
-
-    def dec_value(self, name, delta=1): 
-        """ subtract the delta from the property """
-        # @todo: need to take write-lock
-        val = self.get_value(name)
-        try:
-            val -= delta
-        except:
-            raise
-        self.set_value(name, val)
-
-    def validate(self):
-        """
-        Compares this object's data against the associated schema.  Throws an
-        exception if the data does not conform to the schema.
-        """
-        props = self._schema.get_properties()
-        for name,val in props.iteritems():
-            # @todo validate: type compatible with amqp_type?
-            # @todo validate: primary keys have values
-            if name not in self._values:
-                if val._isOptional:
-                    # ok not to be present, put in dummy value
-                    # to simplify access
-                    self._values[name] = None
-                else:
-                    raise Exception("Required property '%s' not present." % name)
-        self._validated = True
-
-    def _touch(self, field=None):
-        """
-        Mark this object as modified.  Used to force a publish of this object
-        if on subscription.
-        """
-        now = datetime.datetime.utcnow()
-        publish = False
-        if field:
-            # if the named field is not continuous, mark any subscriptions as
-            # needing to be published.
-            sid = self.get_schema_class_id()
-            if sid:
-                self._agent._lock.acquire()
-                try:
-                    schema = self._agent._schema.get(sid)
-                    if schema:
-                        prop = schema.get_property(field)
-                        if prop and not prop.is_continuous():
-                            for sid,sub in self._subscriptions.iteritems():
-                                sub.next_update = now
-                                publish = True
-                    if publish:
-                        self._agent._next_subscribe_event = None
-                        self._agent._wake_thread()
-                finally:
-                    self._agent._lock.release()
-
-
-
-################################################################################
-################################################################################
-################################################################################
-################################################################################
-
-if __name__ == '__main__':
-    # static test cases - no message passing, just exercise API
-    import logging
-    from common import (AgentName, SchemaProperty, qmfTypes, SchemaEventClass)
-
-    logging.getLogger().setLevel(logging.INFO)
-
-    logging.info( "Create an Agent" )
-    _agent_name = AgentName("redhat.com", "agent", "tross")
-    _agent = Agent(str(_agent_name))
-
-    logging.info( "Get agent name: '%s'" % _agent.get_name())
-
-    logging.info( "Create SchemaObjectClass" )
-
-    _schema = SchemaObjectClass(SchemaClassId("MyPackage", "MyClass"),
-                                _desc="A test data schema",
-                                _object_id_names=["index1", "index2"])
-    # add properties
-    _schema.add_property("index1", SchemaProperty(qmfTypes.TYPE_UINT8))
-    _schema.add_property("index2", SchemaProperty(qmfTypes.TYPE_LSTR)) 
-
-    # these two properties are statistics
-    _schema.add_property("query_count", SchemaProperty(qmfTypes.TYPE_UINT32))
-    _schema.add_property("method_call_count", SchemaProperty(qmfTypes.TYPE_UINT32))
-    # These two properties can be set via the method call
-    _schema.add_property("set_string", SchemaProperty(qmfTypes.TYPE_LSTR))
-    _schema.add_property("set_int", SchemaProperty(qmfTypes.TYPE_UINT32))
-
-    # add method
-    _meth = SchemaMethod(_desc="Method to set string and int in object." )
-    _meth.add_argument( "arg_int", SchemaProperty(qmfTypes.TYPE_UINT32) )
-    _meth.add_argument( "arg_str", SchemaProperty(qmfTypes.TYPE_LSTR) )
-    _schema.add_method( "set_meth", _meth )
-
-    # Add schema to Agent
-
-    print("Schema Map='%s'" % str(_schema.map_encode()))
-
-    _agent.register_object_class(_schema)
-
-    # instantiate managed data objects matching the schema
-
-    logging.info( "Create QmfAgentData" )
-
-    _obj = QmfAgentData( _agent, _schema=_schema )
-    _obj.set_value("index1", 100)
-    _obj.set_value("index2", "a name" )
-    _obj.set_value("set_string", "UNSET")
-    _obj.set_value("set_int", 0)
-    _obj.set_value("query_count", 0)
-    _obj.set_value("method_call_count", 0)
-
-    print("Obj1 Map='%s'" % str(_obj.map_encode()))
-
-    _agent.add_object( _obj )
-
-    _obj = QmfAgentData( _agent, 
-                         _values={"index1":99, 
-                                  "index2": "another name",
-                                  "set_string": "UNSET",
-                                  "set_int": 0,
-                                  "query_count": 0,
-                                  "method_call_count": 0},
-                         _schema=_schema)
-
-    print("Obj2 Map='%s'" % str(_obj.map_encode()))
-
-    _agent.add_object(_obj)
-
-    ##############
-
-
-
-    logging.info( "Create SchemaEventClass" )
-
-    _event = SchemaEventClass(SchemaClassId("MyPackage", "MyEvent",
-                                            stype=SchemaClassId.TYPE_EVENT),
-                              _desc="A test data schema",
-                              _props={"edata_1": SchemaProperty(qmfTypes.TYPE_UINT32)})
-    _event.add_property("edata_2", SchemaProperty(qmfTypes.TYPE_LSTR)) 
-
-    print("Event Map='%s'" % str(_event.map_encode()))
-
-    _agent.register_event_class(_event)
Index: extras/qmf/src/py/qmf2/__init__.py
===================================================================
--- extras/qmf/src/py/qmf2/__init__.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/__init__.py	(working copy)
@@ -1,18 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-# 
-#   http://www.apache.org/licenses/LICENSE-2.0
-# 
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
Index: extras/qmf/src/py/qmf2/console.py
===================================================================
--- extras/qmf/src/py/qmf2/console.py	(revision 1056407)
+++ extras/qmf/src/py/qmf2/console.py	(working copy)
@@ -1,2626 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-import sys
-import os
-import platform
-import time
-import datetime
-import Queue
-from logging import getLogger
-from threading import Thread, Event
-from threading import RLock
-from threading import currentThread
-from threading import Condition
-
-from qpid.messaging import Connection, Message, Empty, SendError
-
-from common import (QMF_APP_ID, OpCode, QmfQuery, Notifier, ContentType,
-                    QmfData, QmfAddress, SchemaClass, SchemaClassId,
-                    SchemaEventClass, SchemaObjectClass, WorkItem,
-                    SchemaMethod, QmfEvent, timedelta_to_secs)
-
-
-# global flag that indicates which thread (if any) is
-# running the console notifier callback
-_callback_thread=None
-
-
-log = getLogger("qmf")
-trace = getLogger("qmf.console")
-
-
-##==============================================================================
-## Console Transaction Management
-##
-## At any given time, a console application may have multiple outstanding
-## message transactions with agents.  The following objects allow the console 
-## to track these outstanding transactions.
-##==============================================================================
-
-
-class _Mailbox(object):
-    """
-    Virtual base class for all Mailbox-like objects.
-    """
-    def __init__(self, console):
-        self.console = console
-        self.cid = 0
-        self.console._add_mailbox(self)
-
-    def get_address(self):
-        return self.cid
-
-    def deliver(self, data):
-        """
-        Invoked by Console Management thread when a message arrives for
-        this mailbox.
-        """
-        raise Exception("_Mailbox deliver() method must be provided")
-
-    def destroy(self):
-        """
-        Release the mailbox.  Once called, the mailbox should no longer be
-        referenced. 
-        """
-        self.console._remove_mailbox(self.cid)
-
-
-class _SyncMailbox(_Mailbox):
-    """
-    A simple mailbox that allows a consumer to wait for delivery of data.
-    """
-    def __init__(self, console):
-        """
-        Invoked by application thread.
-        """
-        super(_SyncMailbox, self).__init__(console)
-        self._cv = Condition()
-        self._data = []
-        self._waiting = False
-
-    def deliver(self, data):
-        """
-        Drop data into the mailbox, waking any waiters if necessary.
-        Invoked by Console Management thread only.
-        """
-        self._cv.acquire()
-        try:
-            self._data.append(data)
-            # if was empty, notify waiters
-            if len(self._data) == 1:
-                self._cv.notify()
-        finally:
-            self._cv.release()
-
-    def fetch(self, timeout=None):
-        """
-        Get one data item from a mailbox, with timeout.
-        Invoked by application thread.
-        """
-        self._cv.acquire()
-        try:
-            if len(self._data) == 0:
-                self._cv.wait(timeout)
-            if len(self._data):
-                return self._data.pop(0)
-            return None
-        finally:
-            self._cv.release()
-
-
-class _AsyncMailbox(_Mailbox):
-    """
-    A Mailbox for asynchronous delivery, with a timeout value.
-    """
-    def __init__(self, console, 
-                 _timeout=None):
-        """
-        Invoked by application thread.
-        """
-        super(_AsyncMailbox, self).__init__(console)
-        self.console = console
-
-        if _timeout is None:
-            _timeout = console._reply_timeout
-        self.expiration_date = (datetime.datetime.utcnow() +
-                                datetime.timedelta(seconds=_timeout))
-        console._lock.acquire()
-        try:
-            console._async_mboxes[self.cid] = self
-            console._next_mbox_expire = None
-        finally:
-            console._lock.release()
-
-        # now that an async mbox has been created, wake the
-        # console mgmt thread so it will know about the mbox expiration
-        # date (and adjust its idle sleep period correctly)
-
-        console._wake_thread()
-
-    def reset_timeout(self, _timeout=None):
-        """ Reset the expiration date for this mailbox.
-        """
-        if _timeout is None:
-            _timeout = self.console._reply_timeout
-        self.console._lock.acquire()
-        try:
-            self.expiration_date = (datetime.datetime.utcnow() +
-                                    datetime.timedelta(seconds=_timeout))
-            self.console._next_mbox_expire = None
-        finally:
-            self.console._lock.release()
-
-        # wake the console mgmt thread so it will learn about the mbox
-        # expiration date (and adjust its idle sleep period correctly)
-
-        self.console._wake_thread()
-
-    def deliver(self, msg):
-        """
-        """
-        raise Exception("deliver() method must be provided")
-
-    def expire(self):
-        raise Exception("expire() method must be provided")
-
-
-    def destroy(self):
-        self.console._lock.acquire()
-        try:
-            if self.cid in self.console._async_mboxes:
-                del self.console._async_mboxes[self.cid]
-        finally:
-            self.console._lock.release()
-        super(_AsyncMailbox, self).destroy()
-
-
-
-class _QueryMailbox(_AsyncMailbox):
-    """
-    A mailbox used for asynchronous query requests.
-    """
-    def __init__(self, console, 
-                 agent_name,
-                 context,
-                 target,
-                 _timeout=None):
-        """
-        Invoked by application thread.
-        """
-        super(_QueryMailbox, self).__init__(console,
-                                            _timeout)
-        self.agent_name = agent_name
-        self.target = target
-        self.context = context
-        self.result = []
-
-    def deliver(self, reply):
-        """
-        Process query response messages delivered to this mailbox.
-        Invoked by Console Management thread only.
-        """
-        trace.debug("Delivering to query mailbox (agent=%s)." % self.agent_name)
-        objects = reply.content
-        if isinstance(objects, type([])):
-            # convert from map to native types if needed
-            if self.target == QmfQuery.TARGET_SCHEMA_ID:
-                for sid_map in objects:
-                    self.result.append(SchemaClassId.from_map(sid_map))
-
-            elif self.target == QmfQuery.TARGET_SCHEMA:
-                for schema_map in objects:
-                    # extract schema id, convert based on schema type
-                    sid_map = schema_map.get(SchemaClass.KEY_SCHEMA_ID)
-                    if sid_map:
-                        sid = SchemaClassId.from_map(sid_map)
-                        if sid:
-                            if sid.get_type() == SchemaClassId.TYPE_DATA:
-                                schema = SchemaObjectClass.from_map(schema_map)
-                            else:
-                                schema = SchemaEventClass.from_map(schema_map)
-                            self.console._add_schema(schema)  # add to schema cache
-                            self.result.append(schema)
-
-            elif self.target == QmfQuery.TARGET_OBJECT:
-                for obj_map in objects:
-                    # @todo: need the agent name - ideally from the
-                    # reply message iself.
-                    agent = self.console.get_agent(self.agent_name)
-                    if agent:
-                        obj = QmfConsoleData(map_=obj_map, agent=agent)
-                        # start fetch of schema if not known
-                        sid = obj.get_schema_class_id()
-                        if sid:
-                            self.console._prefetch_schema(sid, agent)
-                        self.result.append(obj)
-
-
-            else:
-                # no conversion needed.
-                self.result += objects
-
-        if not "partial" in reply.properties:
-            # log.error("QUERY COMPLETE for %s" % str(self.context))
-            wi = WorkItem(WorkItem.QUERY_COMPLETE, self.context, self.result)
-            self.console._work_q.put(wi)
-            self.console._work_q_put = True
-
-            self.destroy()
-
-
-    def expire(self):
-        trace.debug("Expiring query mailbox (agent=%s)." % self.agent_name)
-        # send along whatever (possibly none) has been received so far
-        wi = WorkItem(WorkItem.QUERY_COMPLETE, self.context, self.result)
-        self.console._work_q.put(wi)
-        self.console._work_q_put = True
-
-        self.destroy()
-
-
-
-class _SchemaPrefetchMailbox(_AsyncMailbox):
-    """
-    Handles responses to schema fetches made by the console.
-    """
-    def __init__(self, console,
-                 schema_id,
-                 _timeout=None):
-        """
-        Invoked by application thread.
-        """
-        super(_SchemaPrefetchMailbox, self).__init__(console,
-                                                     _timeout)
-        self.schema_id = schema_id
-
-    def deliver(self, reply):
-        """
-        Process schema response messages.
-        """
-        trace.debug("Delivering schema mailbox (id=%s)." % self.schema_id)
-        done = False
-        schemas = reply.content
-        if schemas and isinstance(schemas, type([])):
-            for schema_map in schemas:
-                # extract schema id, convert based on schema type
-                sid_map = schema_map.get(SchemaClass.KEY_SCHEMA_ID)
-                if sid_map:
-                    sid = SchemaClassId.from_map(sid_map)
-                    if sid:
-                        if sid.get_type() == SchemaClassId.TYPE_DATA:
-                            schema = SchemaObjectClass.from_map(schema_map)
-                        else:
-                            schema = SchemaEventClass.from_map(schema_map)
-                        self.console._add_schema(schema)  # add to schema cache
-        self.destroy()
-
-
-    def expire(self):
-        trace.debug("Expiring schema mailbox (id=%s)." % self.schema_id)
-        self.destroy()
-
-
-
-class _MethodMailbox(_AsyncMailbox):
-    """
-    A mailbox used for asynchronous method requests.
-    """
-    def __init__(self, console, 
-                 context,
-                 _timeout=None):
-        """
-        Invoked by application thread.
-        """
-        super(_MethodMailbox, self).__init__(console,
-                                             _timeout)
-        self.context = context
-
-    def deliver(self, reply):
-        """
-        Process method response messages delivered to this mailbox.
-        Invoked by Console Management thread only.
-        """
-        trace.debug("Delivering to method mailbox.")
-        _map = reply.content
-        if not _map or not isinstance(_map, type({})):
-            log.error("Invalid method call reply message")
-            result = None
-        else:
-            error=_map.get(SchemaMethod.KEY_ERROR)
-            if error:
-                error = QmfData.from_map(error)
-                result = MethodResult(_error=error)
-            else:
-                result = MethodResult(_out_args=_map.get(SchemaMethod.KEY_ARGUMENTS))
-
-        # create workitem
-        wi = WorkItem(WorkItem.METHOD_RESPONSE, self.context, result)
-        self.console._work_q.put(wi)
-        self.console._work_q_put = True
-
-        self.destroy()
-
-
-    def expire(self):
-        """
-        The mailbox expired without receiving a reply.
-        Invoked by the Console Management thread only.
-        """
-        trace.debug("Expiring method mailbox.")
-        # send along an empty response
-        wi = WorkItem(WorkItem.METHOD_RESPONSE, self.context, None)
-        self.console._work_q.put(wi)
-        self.console._work_q_put = True
-
-        self.destroy()
-
-
-
-class _SubscriptionMailbox(_AsyncMailbox):
-    """
-    A Mailbox for a single subscription.  Allows only sychronous "subscribe"
-    and "refresh" requests.
-    """
-    def __init__(self, console, context, agent, duration, interval):
-        """
-        Invoked by application thread.
-        """
-        super(_SubscriptionMailbox, self).__init__(console, duration)
-        self.cv = Condition()
-        self.data = []
-        self.result = []
-        self.context = context
-        self.duration = duration
-        self.interval = interval
-        self.agent_name = agent.get_name()
-        self.agent_subscription_id = None          # from agent
-
-    def subscribe(self, query):
-        agent = self.console.get_agent(self.agent_name)
-        if not agent:
-            log.warning("subscribed failed - unknown agent '%s'" %
-                            self.agent_name)
-            return False
-        try:
-            trace.debug("Sending Subscribe to Agent (%s)" % self.agent_name)
-            agent._send_subscribe_req(query, self.get_address(), self.interval,
-                                      self.duration)
-        except SendError, e:
-            log.error(str(e))
-            return False
-        return True
-
-    def resubscribe(self):
-        agent = self.console.get_agent(self.agent_name)
-        if not agent:
-            log.warning("resubscribed failed - unknown agent '%s'",
-                        self.agent_name)
-            return False
-        try:
-            trace.debug("Sending resubscribe to Agent %s", self.agent_name)
-            agent._send_resubscribe_req(self.get_address(),
-                                        self.agent_subscription_id)
-        except SendError, e:
-            log.error(str(e))
-            return False
-        return True
-
-    def deliver(self, msg):
-        """
-        """
-        opcode = msg.properties.get("qmf.opcode")
-        if (opcode == OpCode.subscribe_rsp):
-
-            error = msg.content.get("_error")
-            if error:
-                try:
-                    e_map = QmfData.from_map(error)
-                except TypeError:
-                    log.warning("Invalid QmfData map received: '%s'"
-                                    % str(error))
-                    e_map = QmfData.create({"error":"Unknown error"})
-                sp = SubscribeParams(None, None, None, e_map)
-            else:
-                self.agent_subscription_id = msg.content.get("_subscription_id")
-                self.duration = msg.content.get("_duration", self.duration)
-                self.interval = msg.content.get("_interval", self.interval)
-                self.reset_timeout(self.duration)
-                sp = SubscribeParams(self.get_address(),
-                                     self.interval,
-                                     self.duration,
-                                     None)
-            self.cv.acquire()
-            try:
-                self.data.append(sp)
-                # if was empty, notify waiters
-                if len(self.data) == 1:
-                    self.cv.notify()
-            finally:
-                self.cv.release()
-            return
-
-        # else: data indication
-        agent_name = msg.properties.get("qmf.agent")
-        if not agent_name:
-            log.warning("Ignoring data_ind - no agent name given: %s" %
-                            msg)
-            return
-        agent = self.console.get_agent(agent_name)
-        if not agent:
-            log.warning("Ignoring data_ind - unknown agent '%s'" %
-                            agent_name)
-            return
-
-        objects = msg.content
-        for obj_map in objects:
-            obj = QmfConsoleData(map_=obj_map, agent=agent)
-            # start fetch of schema if not known
-            sid = obj.get_schema_class_id()
-            if sid:
-                self.console._prefetch_schema(sid, agent)
-            self.result.append(obj)
-
-        if not "partial" in msg.properties:
-            wi = WorkItem(WorkItem.SUBSCRIBE_INDICATION, self.context, self.result)
-            self.result = []
-            self.console._work_q.put(wi)
-            self.console._work_q_put = True
-
-    def fetch(self, timeout=None):
-        """
-        Get one data item from a mailbox, with timeout.
-        Invoked by application thread.
-        """
-        self.cv.acquire()
-        try:
-            if len(self.data) == 0:
-                self.cv.wait(timeout)
-            if len(self.data):
-                return self.data.pop(0)
-            return None
-        finally:
-            self.cv.release()
-
-    def expire(self):
-        """ The subscription expired.
-        """
-        self.destroy()
-
-
-
-
-class _AsyncSubscriptionMailbox(_SubscriptionMailbox):
-    """
-    A Mailbox for a single subscription.  Allows only asychronous "subscribe"
-    and "refresh" requests.
-    """
-    def __init__(self, console, context, agent, duration, interval):
-        """
-        Invoked by application thread.
-        """
-        super(_AsyncSubscriptionMailbox, self).__init__(console, context,
-                                                        agent, duration,
-                                                        interval)
-        self.subscribe_pending = False
-
-    def subscribe(self, query, reply_timeout):
-        if super(_AsyncSubscriptionMailbox, self).subscribe(query):
-            self.subscribe_pending = True
-            self.reset_timeout(reply_timeout)
-            return True
-        return False
-
-    def deliver(self, msg):
-        """
-        """
-        super(_AsyncSubscriptionMailbox, self).deliver(msg)
-        sp = self.fetch(0)
-        if sp and self.subscribe_pending:
-            wi = WorkItem(WorkItem.SUBSCRIBE_RESPONSE, self.context, sp)
-            self.console._work_q.put(wi)
-            self.console._work_q_put = True
-
-            self.subscribe_pending = False
-
-            if not sp.succeeded():
-                self.destroy()
-
-
-    def expire(self):
-        """ Either the subscription expired, or a request timedout.
-        """
-        if self.subscribe_pending:
-            wi = WorkItem(WorkItem.SUBSCRIBE_RESPONSE, self.context, None)
-            self.console._work_q.put(wi)
-            self.console._work_q_put = True
-        self.destroy()
-
-
-##==============================================================================
-## DATA MODEL
-##==============================================================================
-
-
-class QmfConsoleData(QmfData):
-    """
-    Console's representation of an managed QmfData instance.  
-    """
-    def __init__(self, map_, agent):
-        super(QmfConsoleData, self).__init__(_map=map_,
-                                             _const=True) 
-        self._agent = agent
-
-    def get_timestamps(self): 
-        """
-        Returns a list of timestamps describing the lifecycle of
-        the object.  All timestamps are represented by the AMQP
-        timestamp type.  [0] = time of last update from Agent,
-                         [1] = creation timestamp 
-                         [2] = deletion timestamp, or zero if not
-        deleted.
-        """
-        return [self._utime, self._ctime, self._dtime]
-
-    def get_create_time(self): 
-        """
-        returns the creation timestamp
-        """
-        return self._ctime
-
-    def get_update_time(self): 
-        """
-        returns the update timestamp
-        """
-        return self._utime
-
-    def get_delete_time(self): 
-        """
-        returns the deletion timestamp, or zero if not yet deleted.
-        """
-        return self._dtime
-
-    def is_deleted(self): 
-        """
-        True if deletion timestamp not zero.
-        """
-        return self._dtime != long(0)
-
-    def refresh(self, _reply_handle=None, _timeout=None): 
-        """
-        request that the Agent update the value of this object's
-        contents.
-        """
-        if _reply_handle is not None:
-            log.error(" ASYNC REFRESH TBD!!!")
-            return None
-
-        assert self._agent
-        assert self._agent._console
-
-        if _timeout is None:
-            _timeout = self._agent._console._reply_timeout
-
-        # create query to agent using this objects ID
-        query = QmfQuery.create_id_object(self.get_object_id(),
-                                          self.get_schema_class_id())
-        obj_list = self._agent._console.do_query(self._agent, query,
-                                                _timeout=_timeout)
-        if obj_list is None or len(obj_list) != 1:
-            return None
-
-        self._update(obj_list[0])
-        return self
-
-
-    def invoke_method(self, name, _in_args={}, _reply_handle=None,
-                      _timeout=None):
-        """
-        Invoke the named method on this object.
-        """
-        assert self._agent
-        assert self._agent._console
-
-        oid = self.get_object_id()
-        if oid is None:
-            raise ValueError("Cannot invoke methods on unmanaged objects.")
-
-        if _timeout is None:
-            _timeout = self._agent._console._reply_timeout
-
-        if _reply_handle is not None:
-            mbox = _MethodMailbox(self._agent._console,
-                                  _reply_handle)
-        else:
-            mbox = _SyncMailbox(self._agent._console)
-        cid = mbox.get_address()
-
-        _map = {self.KEY_OBJECT_ID:str(oid),
-                SchemaMethod.KEY_NAME:name}
-
-        sid = self.get_schema_class_id()
-        if sid:
-            _map[self.KEY_SCHEMA_ID] = sid.map_encode()
-        if _in_args:
-            _map[SchemaMethod.KEY_ARGUMENTS] = _in_args
-
-        trace.debug("Sending method req to Agent (%s)" % time.time())
-        try:
-            self._agent._send_method_req(_map, cid)
-        except SendError, e:
-            log.error(str(e))
-            mbox.destroy()
-            return None
-
-        if _reply_handle is not None:
-            return True
-
-        trace.debug("Waiting for response to method req (%s)" % _timeout)
-        replyMsg = mbox.fetch(_timeout)
-        mbox.destroy()
-
-        if not replyMsg:
-            trace.debug("Agent method req wait timed-out.")
-            return None
-
-        _map = replyMsg.content
-        if not _map or not isinstance(_map, type({})):
-            log.error("Invalid method call reply message")
-            return None
-
-        error=_map.get(SchemaMethod.KEY_ERROR)
-        if error:
-            return MethodResult(_error=QmfData.from_map(error))
-        else:
-            return MethodResult(_out_args=_map.get(SchemaMethod.KEY_ARGUMENTS))
-
-    def _update(self, newer):
-        super(QmfConsoleData,self).__init__(_values=newer._values, _subtypes=newer._subtypes,
-                                           _tag=newer._tag, _object_id=newer._object_id,
-                                           _ctime=newer._ctime, _utime=newer._utime, 
-                                           _dtime=newer._dtime,
-                                           _schema_id=newer._schema_id, _const=True)
-
-class QmfLocalData(QmfData):
-    """
-    Console's representation of an unmanaged QmfData instance.  There
-    is no remote agent associated with this instance. The Console has
-    full control over this instance.
-    """
-    def __init__(self, values, _subtypes={}, _tag=None, _object_id=None,
-                 _schema=None):
-        # timestamp in millisec since epoch UTC
-        ctime = long(time.time() * 1000)
-        super(QmfLocalData, self).__init__(_values=values,
-                                           _subtypes=_subtypes, _tag=_tag, 
-                                           _object_id=_object_id,
-                                           _schema=_schema, _ctime=ctime,
-                                           _utime=ctime, _const=False)
-
-
-class Agent(object):
-    """
-    A local representation of a remote agent managed by this console.
-    """
-    def __init__(self, name, console):
-        """
-        @type name: string
-        @param name: uniquely identifies this agent in the AMQP domain.
-        """
-
-        if not isinstance(console, Console):
-            raise TypeError("parameter must be an instance of class Console")
-
-        self._name = name
-        self._address = QmfAddress.direct(name, console._domain)
-        self._console = console
-        self._sender = None
-        self._packages = {} # map of {package-name:[list of class-names], } for this agent
-        self._subscriptions = [] # list of active standing subscriptions for this agent
-        self._announce_timestamp = None # datetime when last announce received
-        trace.debug( "Created Agent with address: [%s]" % self._address )
-
-
-    def get_name(self):
-        return self._name
-
-    def is_active(self):
-        return self._announce_timestamp != None
-
-    def _send_msg(self, msg, correlation_id=None):
-        """
-        Low-level routine to asynchronously send a message to this agent.
-        """
-        msg.reply_to = str(self._console._address)
-        if correlation_id:
-            msg.correlation_id = str(correlation_id)
-        # TRACE
-        #log.error("!!! Console %s sending to agent %s (%s)" % 
-        #              (self._console._name, self._name, str(msg)))
-        self._sender.send(msg)
-        # return handle
-
-    def get_packages(self):
-        """
-        Return a list of the names of all packages known to this agent.
-        """
-        return self._packages.keys()
-
-    def get_classes(self):
-        """
-        Return a dictionary [key:class] of classes known to this agent.
-        """
-        return self._packages.copy()
-
-    def get_objects(self, query, kwargs={}):
-        """
-        Return a list of objects that satisfy the given query.
-
-        @type query: dict, or common.Query
-        @param query: filter for requested objects
-        @type kwargs: dict
-        @param kwargs: ??? used to build match selector and query ???
-        @rtype: list
-        @return: list of matching objects, or None.
-        """
-        pass
-
-    def get_object(self, query, kwargs={}):
-        """
-        Get one object - query is expected to match only one object.
-        ??? Recommended: explicit timeout param, default None ???
-
-        @type query: dict, or common.Query
-        @param query: filter for requested objects
-        @type kwargs: dict
-        @param kwargs: ??? used to build match selector and query ???
-        @rtype: qmfConsole.ObjectProxy
-        @return: one matching object, or none
-        """
-        pass
-
-
-    def create_subscription(self, query):
-        """
-        Factory for creating standing subscriptions based on a given query.
-
-        @type query: common.Query object
-        @param query: determines the list of objects for which this subscription applies
-        @rtype: qmfConsole.Subscription
-        @returns: an object representing the standing subscription.
-        """
-        pass
-
-
-    def invoke_method(self, name, _in_args={}, _reply_handle=None,
-                      _timeout=None): 
-        """
-        Invoke the named method on this agent.
-        """
-        assert self._console
-
-        if _timeout is None:
-            _timeout = self._console._reply_timeout
-
-        if _reply_handle is not None:
-            mbox = _MethodMailbox(self._console,
-                                  _reply_handle)
-        else:
-            mbox = _SyncMailbox(self._console)
-        cid = mbox.get_address()
-
-        _map = {SchemaMethod.KEY_NAME:name}
-        if _in_args:
-            _map[SchemaMethod.KEY_ARGUMENTS] = _in_args.copy()
-
-        trace.debug("Sending method req to Agent (%s)" % time.time())
-        try:
-            self._send_method_req(_map, cid)
-        except SendError, e:
-            log.error(str(e))
-            mbox.destroy()
-            return None
-
-        if _reply_handle is not None:
-            return True
-
-        trace.debug("Waiting for response to method req (%s)" % _timeout)
-        replyMsg = mbox.fetch(_timeout)
-        mbox.destroy()
-
-        if not replyMsg:
-            trace.debug("Agent method req wait timed-out.")
-            return None
-
-        _map = replyMsg.content
-        if not _map or not isinstance(_map, type({})):
-            log.error("Invalid method call reply message")
-            return None
-
-        return MethodResult(_out_args=_map.get(SchemaMethod.KEY_ARGUMENTS),
-                            _error=_map.get(SchemaMethod.KEY_ERROR))
-
-    def enable_events(self):
-        raise Exception("enable_events tbd")
-
-    def disable_events(self):
-        raise Exception("disable_events tbd")
-
-    def destroy(self):
-        raise Exception("destroy tbd")
-
-    def __repr__(self):
-        return str(self._address)
-    
-    def __str__(self):
-        return self.__repr__()
-
-    def _send_query(self, query, correlation_id=None):
-        """
-        """
-        msg = Message(id=QMF_APP_ID,
-                      properties={"method":"request",
-                                  "qmf.opcode":OpCode.query_req},
-                      content=query.map_encode())
-        self._send_msg( msg, correlation_id )
-
-
-    def _send_method_req(self, mr_map, correlation_id=None):
-        """
-        """
-        msg = Message(id=QMF_APP_ID,
-                      properties={"method":"request",
-                                  "qmf.opcode":OpCode.method_req},
-                      content=mr_map)
-        self._send_msg( msg, correlation_id )
-
-    def _send_subscribe_req(self, query, correlation_id, _interval=None,
-                            _lifetime=None):
-        """
-        """
-        sr_map = {"_query":query.map_encode()}
-        if _interval is not None:
-            sr_map["_interval"] = _interval
-        if _lifetime is not None:
-            sr_map["_duration"] = _lifetime
-
-        msg = Message(id=QMF_APP_ID,
-                      properties={"method":"request",
-                                  "qmf.opcode":OpCode.subscribe_req},
-                      content=sr_map)
-        self._send_msg(msg, correlation_id)
-
-
-    def _send_resubscribe_req(self, correlation_id,
-                              subscription_id):
-        """
-        """
-        sr_map = {"_subscription_id":subscription_id}
-
-        msg = Message(id=QMF_APP_ID,
-                      properties={"method":"request",
-                                  "qmf.opcode":OpCode.subscribe_refresh_ind},
-                      content=sr_map)
-        self._send_msg(msg, correlation_id)
-
-
-    def _send_unsubscribe_ind(self, correlation_id, subscription_id):
-        """
-        """
-        sr_map = {"_subscription_id":subscription_id}
-
-        msg = Message(id=QMF_APP_ID,
-                      properties={"method":"request",
-                                  "qmf.opcode":OpCode.subscribe_cancel_ind},
-                      content=sr_map)
-        self._send_msg(msg, correlation_id)
-
-
-  ##==============================================================================
-  ## METHOD CALL
-  ##==============================================================================
-
-class MethodResult(object):
-    def __init__(self, _out_args=None, _error=None):
-        self._error = _error
-        self._out_args = _out_args
-
-    def succeeded(self): 
-        return self._error is None
-
-    def get_exception(self):
-        return self._error
-
-    def get_arguments(self): 
-        return self._out_args
-
-    def get_argument(self, name): 
-        arg = None
-        if self._out_args:
-            arg = self._out_args.get(name)
-        return arg
-
-
-
-  ##==============================================================================
-  ## SUBSCRIPTION
-  ##==============================================================================
-
-class SubscribeParams(object):
-    """ Represents a standing subscription for this console.
-    """
-    def __init__(self, sid, interval, duration, _error=None):
-        self._sid = sid
-        self._interval = interval
-        self._duration = duration
-        self._error = _error
-
-    def succeeded(self):
-        return self._error is None
-
-    def get_error(self):
-        return self._error
-
-    def get_subscription_id(self):
-        return self._sid
-
-    def get_publish_interval(self):
-        return self._interval
-
-    def get_duration(self):
-        return self._duration
-
-
-  ##==============================================================================
-  ## CONSOLE
-  ##==============================================================================
-
-
-
-
-
-
-class Console(Thread):
-    """
-    A Console manages communications to a collection of agents on behalf of an application.
-    """
-    def __init__(self, name=None, _domain=None, notifier=None, 
-                 reply_timeout = 60,
-                 # agent_timeout = 120,
-                 agent_timeout = 60,
-                 kwargs={}):
-        """
-        @type name: str
-        @param name: identifier for this console.  Must be unique.
-        @type notifier: qmfConsole.Notifier
-        @param notifier: invoked when events arrive for processing.
-        @type kwargs: dict
-        @param kwargs: ??? Unused
-        """
-        Thread.__init__(self)
-        self._operational = False
-        self._ready = Event()
-
-        if not name:
-            self._name = "qmfc-%s.%d" % (platform.node(), os.getpid())
-        else:
-            self._name = str(name)
-        self._domain = _domain
-        self._address = QmfAddress.direct(self._name, self._domain)
-        self._notifier = notifier
-        self._lock = RLock()
-        self._conn = None
-        self._session = None
-        # dict of "agent-direct-address":class Agent entries
-        self._agent_map = {}
-        self._direct_recvr = None
-        self._announce_recvr = None
-        self._locate_sender = None
-        self._schema_cache = {}
-        self._pending_schema_req = []
-        self._agent_discovery_filter = None
-        self._reply_timeout = reply_timeout
-        self._agent_timeout = agent_timeout
-        self._subscribe_timeout = 300  # @todo: parameterize
-        self._next_agent_expire = None
-        self._next_mbox_expire = None
-        # for passing WorkItems to the application
-        self._work_q = Queue.Queue()
-        self._work_q_put = False
-        # Correlation ID and mailbox storage
-        self._correlation_id = long(time.time())  # pseudo-randomize
-        self._post_office = {} # indexed by cid
-        self._async_mboxes = {} # indexed by cid, used to expire them
-
-    def destroy(self, timeout=None):
-        """
-        Must be called before the Console is deleted.  
-        Frees up all resources and shuts down all background threads.
-
-        @type timeout: float
-        @param timeout: maximum time in seconds to wait for all background threads to terminate.  Default: forever.
-        """
-        trace.debug("Destroying Console...")
-        if self._conn:
-            self.remove_connection(self._conn, timeout)
-        trace.debug("Console Destroyed")
-
-    def add_connection(self, conn):
-        """
-        Add a AMQP connection to the console.  The console will setup a session over the
-        connection.  The console will then broadcast an Agent Locate Indication over
-        the session in order to discover present agents.
-
-        @type conn: qpid.messaging.Connection
-        @param conn: the connection to the AMQP messaging infrastructure.
-        """
-        if self._conn:
-            raise Exception( "Multiple connections per Console not supported." );
-        self._conn = conn
-        self._session = conn.session(name=self._name)
-
-        # for messages directly addressed to me
-        self._direct_recvr = self._session.receiver(str(self._address) +
-                                                    ";{create:always,"
-                                                    " node:"
-                                                    " {type:topic,"
-                                                    " x-declare:"
-                                                    " {type:direct}}}", 
-                                                    capacity=1)
-        trace.debug("my direct addr=%s" % self._direct_recvr.source)
-
-        self._direct_sender = self._session.sender(str(self._address.get_node()) +
-                                                   ";{create:always,"
-                                                   " node:"
-                                                   " {type:topic,"
-                                                   " x-declare:"
-                                                   " {type:direct}}}")
-        trace.debug("my direct sender=%s" % self._direct_sender.target)
-
-        # for receiving "broadcast" messages from agents
-        default_addr = QmfAddress.topic(QmfAddress.SUBJECT_AGENT_IND + ".#", 
-                                        self._domain)
-        self._topic_recvr = self._session.receiver(str(default_addr) +
-                                                   ";{create:always,"
-                                                   " node:{type:topic}}",
-                                                   capacity=1)
-        trace.debug("default topic recv addr=%s" % self._topic_recvr.source)
-
-
-        # for sending to topic subscribers
-        topic_addr = QmfAddress.topic(QmfAddress.SUBJECT_CONSOLE_IND, self._domain)
-        self._topic_sender = self._session.sender(str(topic_addr) +
-                                                  ";{create:always,"
-                                                  " node:{type:topic}}")
-        trace.debug("default topic send addr=%s" % self._topic_sender.target)
-
-        #
-        # Now that receivers are created, fire off the receive thread...
-        #
-        self._operational = True
-        self.start()
-        self._ready.wait(10)
-        if not self._ready.isSet():
-            raise Exception("Console managment thread failed to start.")
-
-
-
-    def remove_connection(self, conn, timeout=None):
-        """
-        Remove an AMQP connection from the console.  Un-does the add_connection() operation,
-        and releases any agents and sessions associated with the connection.
-
-        @type conn: qpid.messaging.Connection
-        @param conn: connection previously added by add_connection()
-        """
-        if self._conn and conn and conn != self._conn:
-            log.error( "Attempt to delete unknown connection: %s" % str(conn))
-
-        # tell connection thread to shutdown
-        self._operational = False
-        if self.isAlive():
-            # kick my thread to wake it up
-            self._wake_thread()
-            trace.debug("waiting for console receiver thread to exit")
-            self.join(timeout)
-            if self.isAlive():
-                log.error( "Console thread '%s' is hung..." % self.getName() )
-        self._direct_recvr.close()
-        self._direct_sender.close()
-        self._topic_recvr.close()
-        self._topic_sender.close()
-        self._session.close()
-        self._session = None
-        self._conn = None
-        trace.debug("console connection removal complete")
-
-
-    def get_address(self):
-        """
-        The AMQP address this Console is listening to.
-        """
-        return self._address
-
-
-    def destroy_agent( self, agent ):
-        """
-        Undoes create.
-        """
-        if not isinstance(agent, Agent):
-            raise TypeError("agent must be an instance of class Agent")
-
-        self._lock.acquire()
-        try:
-            if agent._name in self._agent_map:
-                del self._agent_map[agent._name]
-        finally:
-            self._lock.release()
-
-    def find_agent(self, name, timeout=None ):
-        """
-        Given the name of a particular agent, return an instance of class Agent
-        representing that agent.  Return None if the agent does not exist.
-        """
-
-        self._lock.acquire()
-        try:
-            agent = self._agent_map.get(name)
-            if agent:
-                return agent
-        finally:
-            self._lock.release()
-
-        # agent not present yet - ping it with an agent_locate
-
-        mbox = _SyncMailbox(self)
-        cid = mbox.get_address()
-
-        query = QmfQuery.create_id(QmfQuery.TARGET_AGENT, name)
-        msg = Message(id=QMF_APP_ID,
-                      subject="console.ind.locate." + name,
-                      properties={"method":"request",
-                                  "qmf.opcode":OpCode.agent_locate_req},
-                      content=query._predicate)
-        msg.content_type="amqp/list"
-        msg.reply_to = str(self._address)
-        msg.correlation_id = str(cid)
-        trace.debug("%s Sending Agent Locate (%s)", self._name, str(msg))
-        try:
-            self._topic_sender.send(msg)
-        except SendError, e:
-            log.error(str(e))
-            mbox.destroy()
-            return None
-
-        if timeout is None:
-            timeout = self._reply_timeout
-
-        new_agent = None
-        trace.debug("Waiting for response to Agent Locate (%s)" % timeout)
-        mbox.fetch(timeout)
-        mbox.destroy()
-        trace.debug("Agent Locate wait ended (%s)" % time.time())
-        self._lock.acquire()
-        try:
-            new_agent = self._agent_map.get(name)
-        finally:
-            self._lock.release()
-
-        return new_agent
-
-
-    def get_agents(self):
-        """
-        Return the list of known agents.
-        """
-        self._lock.acquire()
-        try:
-            agents = self._agent_map.values()
-        finally:
-            self._lock.release()
-        return agents
-
-
-    def get_agent(self, name):
-        """
-        Return the named agent, else None if not currently available.
-        """
-        self._lock.acquire()
-        try:
-            agent = self._agent_map.get(name)
-        finally:
-            self._lock.release()
-        return agent
-
-
-    def do_query(self, agent, query, _reply_handle=None, _timeout=None ):
-        """
-        """
-        target = query.get_target()
-
-        if _reply_handle is not None:
-            mbox = _QueryMailbox(self,
-                                 agent.get_name(),
-                                 _reply_handle,
-                                 target,
-                                 _timeout)
-        else:
-            mbox = _SyncMailbox(self)
-
-        cid = mbox.get_address()
-
-        try:
-            trace.debug("Sending Query to Agent (%s)" % time.time())
-            agent._send_query(query, cid)
-        except SendError, e:
-            log.error(str(e))
-            mbox.destroy()
-            return None
-
-        # return now if async reply expected
-        if _reply_handle is not None:
-            return True
-
-        if not _timeout:
-            _timeout = self._reply_timeout
-
-        trace.debug("Waiting for response to Query (%s)" % _timeout)
-        now = datetime.datetime.utcnow()
-        expire =  now + datetime.timedelta(seconds=_timeout)
-
-        response = []
-        while (expire > now):
-            _timeout = timedelta_to_secs(expire - now)
-            reply = mbox.fetch(_timeout)
-            if not reply:
-                trace.debug("Query wait timed-out.")
-                break
-
-            objects = reply.content
-            if not objects or not isinstance(objects, type([])):
-                break
-
-            # convert from map to native types if needed
-            if target == QmfQuery.TARGET_SCHEMA_ID:
-                for sid_map in objects:
-                    response.append(SchemaClassId.from_map(sid_map))
-
-            elif target == QmfQuery.TARGET_SCHEMA:
-                for schema_map in objects:
-                    # extract schema id, convert based on schema type
-                    sid_map = schema_map.get(SchemaClass.KEY_SCHEMA_ID)
-                    if sid_map:
-                        sid = SchemaClassId.from_map(sid_map)
-                        if sid:
-                            if sid.get_type() == SchemaClassId.TYPE_DATA:
-                                schema = SchemaObjectClass.from_map(schema_map)
-                            else:
-                                schema = SchemaEventClass.from_map(schema_map)
-                            self._add_schema(schema)  # add to schema cache
-                            response.append(schema)
-
-            elif target == QmfQuery.TARGET_OBJECT:
-                for obj_map in objects:
-                    obj = QmfConsoleData(map_=obj_map, agent=agent)
-                    # start fetch of schema if not known
-                    sid = obj.get_schema_class_id()
-                    if sid:
-                        self._prefetch_schema(sid, agent)
-                    response.append(obj)
-            else:
-                # no conversion needed.
-                response += objects
-
-            if not "partial" in reply.properties:
-                # reply not broken up over multiple msgs
-                break
-
-            now = datetime.datetime.utcnow()
-
-        mbox.destroy()
-        return response
-
-
-    def create_subscription(self, agent, query, console_handle,
-                            _interval=None, _duration=None,
-                            _blocking=True, _timeout=None):
-        if not _duration:
-            _duration = self._subscribe_timeout
-
-        if _timeout is None:
-            _timeout = self._reply_timeout
-
-        if not _blocking:
-            mbox = _AsyncSubscriptionMailbox(self, console_handle, agent,
-                                             _duration, _interval)
-            if not mbox.subscribe(query, _timeout):
-                mbox.destroy()
-                return False
-            return True
-        else:
-            mbox = _SubscriptionMailbox(self, console_handle, agent, _duration,
-                                        _interval)
-
-            if not mbox.subscribe(query):
-                mbox.destroy()
-                return None
-
-            trace.debug("Waiting for response to subscription (%s)" % _timeout)
-            # @todo: what if mbox expires here?
-            sp = mbox.fetch(_timeout)
-
-            if not sp:
-                trace.debug("Subscription request wait timed-out.")
-                mbox.destroy()
-                return None
-
-            if not sp.succeeded():
-                mbox.destroy()
-
-            return sp
-
-    def refresh_subscription(self, subscription_id,
-                             _duration=None,
-                             _timeout=None):
-        if _timeout is None:
-            _timeout = self._reply_timeout
-
-        mbox = self._get_mailbox(subscription_id)
-        if not mbox:
-            log.warning("Subscription %s not found." % subscription_id)
-            return None
-
-        if isinstance(mbox, _AsyncSubscriptionMailbox):
-            return mbox.resubscribe()
-        else:
-            # synchronous - wait for reply
-            if not mbox.resubscribe():
-                # @todo ???? mbox.destroy()
-                return None
-
-            # wait for reply
-
-            trace.debug("Waiting for response to subscription (%s)" % _timeout)
-            sp = mbox.fetch(_timeout)
-
-            if not sp:
-                trace.debug("re-subscribe request wait timed-out.")
-                # @todo???? mbox.destroy()
-                return None
-
-            return sp
-
-
-    def cancel_subscription(self, subscription_id):
-        """
-        """
-        mbox = self._get_mailbox(subscription_id)
-        if not mbox:
-            return
-
-        agent = self.get_agent(mbox.agent_name)
-        if agent:
-            try:
-                trace.debug("Sending UnSubscribe to Agent (%s)" % time.time())
-                agent._send_unsubscribe_ind(subscription_id,
-                                            mbox.agent_subscription_id)
-            except SendError, e:
-                log.error(str(e))
-
-        mbox.destroy()
-
-
-    def _wake_thread(self):
-        """
-        Make the console management thread loop wakeup from its next_receiver
-        sleep.
-        """
-        trace.debug("Sending noop to wake up [%s]" % self._address)
-        msg = Message(id=QMF_APP_ID,
-                      subject=self._name,
-                      properties={"method":"indication",
-                                  "qmf.opcode":OpCode.noop},
-                      content={})
-        try:
-            self._direct_sender.send( msg, sync=True )
-        except SendError, e:
-            log.error(str(e))
-
-
-    def run(self):
-        """
-        Console Management Thread main loop.
-        Handles inbound messages, agent discovery, async mailbox timeouts.
-        """
-        global _callback_thread
-
-        self._ready.set()
-
-        while self._operational:
-
-            # qLen = self._work_q.qsize()
-
-            while True:
-                try:
-                    msg = self._topic_recvr.fetch(timeout=0)
-                except Empty:
-                    break
-                # TRACE:
-                # log.error("!!! Console %s: msg on %s [%s]" %
-                # (self._name, self._topic_recvr.source, msg))
-                self._dispatch(msg, _direct=False)
-
-            while True:
-                try:
-                    msg = self._direct_recvr.fetch(timeout = 0)
-                except Empty:
-                    break
-                # TRACE
-                #log.error("!!! Console %s: msg on %s [%s]" %
-                # (self._name, self._direct_recvr.source, msg))
-                self._dispatch(msg, _direct=True)
-
-            self._expire_agents()   # check for expired agents
-            self._expire_mboxes()   # check for expired async mailbox requests
-
-            #if qLen == 0 and self._work_q.qsize() and self._notifier:
-            if self._work_q_put and self._notifier:
-                # new stuff on work queue, kick the the application...
-                self._work_q_put = False
-                _callback_thread = currentThread()
-                trace.debug("Calling console notifier.indication")
-                self._notifier.indication()
-                _callback_thread = None
-
-
-            # wait for a message to arrive, or an agent
-            # to expire, or a mailbox requrest to time out
-            now = datetime.datetime.utcnow()
-            next_expire = self._next_agent_expire
-
-            self._lock.acquire()
-            try:
-            # the mailbox expire flag may be cleared by the
-            # app thread(s) to force an immedate mailbox scan
-                if self._next_mbox_expire is None:
-                    next_expire = now
-                elif self._next_mbox_expire < next_expire:
-                    next_expire = self._next_mbox_expire
-            finally:
-                self._lock.release()
-
-            timeout = timedelta_to_secs(next_expire - now)
-
-            if self._operational and timeout > 0.0:
-                try:
-                    trace.debug("waiting for next rcvr (timeout=%s)..." % timeout)
-                    self._session.next_receiver(timeout = timeout)
-                except Empty:
-                    pass
-
-        trace.debug("Shutting down Console thread")
-
-    def get_objects(self,
-                    _object_id=None,
-                    _schema_id=None,
-                    _pname=None, _cname=None,
-                    _agents=None,
-                    _timeout=None):
-        """
-        Retrieve objects by id or schema.
-
-        By object_id: must specify schema_id or pname & cname if object defined
-        by a schema.  Undescribed objects: only object_id needed.
-        
-        By schema: must specify schema_id or pname & cname - all instances of
-        objects defined by that schema are returned.
-        """
-        if _agents is None:
-            # use copy of current agent list
-            self._lock.acquire()
-            try:
-                agent_list = self._agent_map.values()
-            finally:
-                self._lock.release()
-        elif isinstance(_agents, Agent):
-            agent_list = [_agents]
-        else:
-            agent_list = _agents
-            # @todo validate this list!
-
-        if _timeout is None:
-            _timeout = self._reply_timeout
-
-        # @todo: fix when async do_query done - query all agents at once, then
-        # wait for replies, instead of per-agent querying....
-
-        obj_list = []
-        expired = datetime.datetime.utcnow() + datetime.timedelta(seconds=_timeout)
-        for agent in agent_list:
-            if not agent.is_active():
-                continue
-            now = datetime.datetime.utcnow()
-            if now >= expired:
-                break
-
-            if _pname is None:
-                if _object_id:
-                    query = QmfQuery.create_id_object(_object_id,
-                                                      _schema_id)
-                else:
-                    if _schema_id is not None:
-                        t_params = {QmfData.KEY_SCHEMA_ID: _schema_id}
-                    else:
-                        t_params = None
-                    query = QmfQuery.create_wildcard(QmfQuery.TARGET_OBJECT,
-                                                     t_params)
-                timeout = timedelta_to_secs(expired - now)
-                reply = self.do_query(agent, query, _timeout=timeout)
-                if reply:
-                    obj_list = obj_list + reply
-            else:
-                # looking up by package name (and maybe class name), need to 
-                # find all schema_ids in that package, then lookup object by
-                # schema_id
-                if _cname is not None:
-                    pred = [QmfQuery.AND,
-                            [QmfQuery.EQ,
-                             SchemaClassId.KEY_PACKAGE,
-                             [QmfQuery.QUOTE, _pname]],
-                            [QmfQuery.EQ, SchemaClassId.KEY_CLASS,
-                             [QmfQuery.QUOTE, _cname]]]
-                else:
-                    pred = [QmfQuery.EQ,
-                            SchemaClassId.KEY_PACKAGE,
-                            [QmfQuery.QUOTE, _pname]]
-                query = QmfQuery.create_predicate(QmfQuery.TARGET_SCHEMA_ID, pred)
-                timeout = timedelta_to_secs(expired - now)
-                sid_list = self.do_query(agent, query, _timeout=timeout)
-                if sid_list:
-                    for sid in sid_list:
-                        now = datetime.datetime.utcnow()
-                        if now >= expired:
-                            break
-                        if _object_id is not None:
-                            query = QmfQuery.create_id_object(_object_id, sid)
-                        else:
-                            t_params = {QmfData.KEY_SCHEMA_ID: sid}
-                            query = QmfQuery.create_wildcard(QmfQuery.TARGET_OBJECT, t_params)
-                        timeout = timedelta_to_secs(expired - now)
-                        reply = self.do_query(agent, query, _timeout=timeout)
-                        if reply:
-                            obj_list = obj_list + reply
-        if obj_list:
-            return obj_list
-        return None
-
-
-
-    # called by run() thread ONLY
-    #
-    def _dispatch(self, msg, _direct=True):
-        """
-        PRIVATE: Process a message received from an Agent
-        """
-        trace.debug( "Message received from Agent! [%s]", msg )
-
-        opcode = msg.properties.get("qmf.opcode")
-        if not opcode:
-            log.error("Ignoring unrecognized message '%s'", msg)
-            return
-        version = 2 # @todo: fix me
-
-        cmap = {}; props = {}
-        if msg.content_type == "amqp/map":
-            cmap = msg.content
-        if msg.properties:
-            props = msg.properties
-
-        if opcode == OpCode.agent_heartbeat_ind:
-            self._handle_agent_ind_msg( msg, cmap, version, _direct )
-        elif opcode == OpCode.agent_locate_rsp:
-            self._handle_agent_ind_msg( msg, cmap, version, _direct )
-        elif msg.correlation_id:
-            self._handle_response_msg(msg, cmap, version, _direct)
-        elif opcode == OpCode.data_ind:
-            self._handle_indication_msg(msg, cmap, version, _direct)
-        elif opcode == OpCode.noop:
-             trace.debug("No-op msg received.")
-        else:
-            log.warning("Ignoring message with unrecognized 'opcode' value: '%s'", opcode)
-
-
-    def _handle_agent_ind_msg(self, msg, cmap, version, direct):
-        """
-        Process a received agent-ind message.  This message may be a response to a
-        agent-locate, or it can be an unsolicited agent announce.
-        """
-
-        trace.debug("%s _handle_agent_ind_msg '%s'", self._name, str(msg))
-
-        try:
-            tmp = QmfData.from_map(msg.content)
-        except:
-            log.warning("%s invalid Agent Indication msg format '%s'",
-                        self._name, str(msg))
-            return
-
-        try:
-            name = tmp.get_value("_name")
-        except:
-            log.warning("Bad Agent ind msg received: %s", str(msg))
-            return
-
-        correlated = False
-        if msg.correlation_id:
-            mbox = self._get_mailbox(msg.correlation_id)
-            correlated = mbox is not None
-
-        agent = None
-        self._lock.acquire()
-        try:
-            agent = self._agent_map.get(name)
-            if agent:
-                # agent already known, just update timestamp
-                agent._announce_timestamp = datetime.datetime.utcnow()
-        finally:
-            self._lock.release()
-
-        if not agent:
-            # need to create and add a new agent?
-            matched = False
-            if self._agent_discovery_filter:
-                matched = self._agent_discovery_filter.evaluate(tmp)
-
-            if (correlated or matched):
-                agent = self._create_agent(name)
-                if not agent:
-                    return   # failed to add agent
-                agent._announce_timestamp = datetime.datetime.utcnow()
-
-                if matched:
-                    # unsolicited, but newly discovered
-                    trace.debug("AGENT_ADDED for %s (%s)" % (agent, time.time()))
-                    wi = WorkItem(WorkItem.AGENT_ADDED, None, {"agent": agent})
-                    self._work_q.put(wi)
-                    self._work_q_put = True
-
-        if correlated:
-            # wake up all waiters
-            trace.debug("waking waiters for correlation id %s" % msg.correlation_id)
-            mbox.deliver(msg)
-
-    def _handle_response_msg(self, msg, cmap, version, direct):
-        """
-        Process a received data-ind message.
-        """
-        trace.debug("%s _handle_response_msg '%s'", self._name, str(msg))
-
-        mbox = self._get_mailbox(msg.correlation_id)
-        if not mbox:
-            log.warning("%s Response msg received with unknown correlation_id"
-                            " msg='%s'", self._name, str(msg))
-            return
-
-        # wake up all waiters
-        trace.debug("waking waiters for correlation id %s" % msg.correlation_id)
-        mbox.deliver(msg)
-
-    def _handle_indication_msg(self, msg, cmap, version, _direct):
-
-        aname = msg.properties.get("qmf.agent")
-        if not aname:
-            trace.debug("No agent name field in indication message.")
-            return
-
-        content_type = msg.properties.get("qmf.content")
-        if (content_type != ContentType.event or
-            not isinstance(msg.content, type([]))):
-            log.warning("Bad event indication message received: '%s'", msg)
-            return
-
-        emap = msg.content[0]
-        if not isinstance(emap, type({})):
-            trace.debug("Invalid event body in indication message: '%s'", msg)
-            return
-
-        agent = None
-        self._lock.acquire()
-        try:
-            agent = self._agent_map.get(aname)
-        finally:
-            self._lock.release()
-        if not agent:
-            trace.debug("Agent '%s' not known." % aname)
-            return
-        try:
-            # @todo: schema???
-            event = QmfEvent.from_map(emap)
-        except TypeError:
-            trace.debug("Invalid QmfEvent map received: %s" % str(emap))
-            return
-
-        # @todo: schema?  Need to fetch it, but not from this thread!
-        # This thread can not pend on a request.
-        trace.debug("Publishing event received from agent %s" % aname)
-        wi = WorkItem(WorkItem.EVENT_RECEIVED, None,
-                      {"agent":agent,
-                       "event":event})
-        self._work_q.put(wi)
-        self._work_q_put = True
-
-
-    def _expire_mboxes(self):
-        """
-        Check all async mailboxes for outstanding requests that have expired.
-        """
-        self._lock.acquire()
-        try:
-            now = datetime.datetime.utcnow()
-            if self._next_mbox_expire and now < self._next_mbox_expire:
-                return
-            expired_mboxes = []
-            self._next_mbox_expire = None
-            for mbox in self._async_mboxes.itervalues():
-                if now >= mbox.expiration_date:
-                    expired_mboxes.append(mbox)
-                else:
-                    if (self._next_mbox_expire is None or
-                        mbox.expiration_date < self._next_mbox_expire):
-                        self._next_mbox_expire = mbox.expiration_date
-
-            for mbox in expired_mboxes:
-                del self._async_mboxes[mbox.cid]
-        finally:
-            self._lock.release()
-
-        for mbox in expired_mboxes:
-            # note: expire() may deallocate the mbox, so don't touch
-            # it further.
-            mbox.expire()
-
-
-    def _expire_agents(self):
-        """
-        Check for expired agents and issue notifications when they expire.
-        """
-        now = datetime.datetime.utcnow()
-        if self._next_agent_expire and now < self._next_agent_expire:
-            return
-        lifetime_delta = datetime.timedelta(seconds = self._agent_timeout)
-        next_expire_delta = lifetime_delta
-        self._lock.acquire()
-        try:
-            trace.debug("!!! expiring agents '%s'" % now)
-            for agent in self._agent_map.itervalues():
-                if agent._announce_timestamp:
-                    agent_deathtime = agent._announce_timestamp + lifetime_delta
-                    if agent_deathtime <= now:
-                        trace.debug("AGENT_DELETED for %s" % agent)
-                        agent._announce_timestamp = None
-                        wi = WorkItem(WorkItem.AGENT_DELETED, None,
-                                      {"agent":agent})
-                        # @todo: remove agent from self._agent_map
-                        self._work_q.put(wi)
-                        self._work_q_put = True
-                    else:
-                        if (agent_deathtime - now) < next_expire_delta:
-                            next_expire_delta = agent_deathtime - now
-
-            self._next_agent_expire = now + next_expire_delta
-            trace.debug("!!! next expire cycle = '%s'" % self._next_agent_expire)
-        finally:
-            self._lock.release()
-
-
-
-    def _create_agent( self, name ):
-        """
-        Factory to create/retrieve an agent for this console
-        """
-        trace.debug("creating agent %s" % name)
-        self._lock.acquire()
-        try:
-            agent = self._agent_map.get(name)
-            if agent:
-                return agent
-
-            agent = Agent(name, self)
-            try:
-                agent._sender = self._session.sender(str(agent._address) + 
-                                                     ";{create:always,"
-                                                     " node:"
-                                                     " {type:topic,"
-                                                     " x-declare:"
-                                                     " {type:direct}}}") 
-            except:
-                log.warning("Unable to create sender for %s" % name)
-                return None
-            trace.debug("created agent sender %s" % agent._sender.target)
-
-            self._agent_map[name] = agent
-        finally:
-            self._lock.release()
-
-        # new agent - query for its schema database for
-        # seeding the schema cache (@todo)
-        # query = QmfQuery({QmfQuery.TARGET_SCHEMA_ID:None})
-        # agent._sendQuery( query )
-
-        return agent
-
-
-
-    def enable_agent_discovery(self, _query=None):
-        """
-        Called to enable the asynchronous Agent Discovery process.
-        Once enabled, AGENT_ADD work items can arrive on the WorkQueue.
-        """
-        # @todo: fix - take predicate only, not entire query!
-        if _query is not None:
-            if (not isinstance(_query, QmfQuery) or
-                _query.get_target() != QmfQuery.TARGET_AGENT):
-                raise TypeError("Type QmfQuery with target == TARGET_AGENT expected")
-            self._agent_discovery_filter = _query
-        else:
-            # create a match-all agent query (no predicate)
-            self._agent_discovery_filter = QmfQuery.create_wildcard(QmfQuery.TARGET_AGENT) 
-
-    def disable_agent_discovery(self):
-        """
-        Called to disable the async Agent Discovery process enabled by
-        calling enableAgentDiscovery()
-        """
-        self._agent_discovery_filter = None
-
-
-
-    def get_workitem_count(self):
-        """
-        Returns the count of pending WorkItems that can be retrieved.
-        """
-        return self._work_q.qsize()
-
-
-
-    def get_next_workitem(self, timeout=None):
-        """
-        Returns the next pending work item, or None if none available.
-        @todo: subclass and return an Empty event instead.
-        """
-        try:
-            wi = self._work_q.get(True, timeout)
-        except Queue.Empty:
-            return None
-        return wi
-
-
-    def release_workitem(self, wi):
-        """
-        Return a WorkItem to the Console when it is no longer needed.
-        @todo: call Queue.task_done() - only 2.5+
-
-        @type wi: class qmfConsole.WorkItem
-        @param wi: work item object to return.
-        """
-        pass
-
-    def _add_schema(self, schema):
-        """
-        @todo
-        """
-        if not isinstance(schema, SchemaClass):
-            raise TypeError("SchemaClass type expected")
-
-        self._lock.acquire()
-        try:
-            sid = schema.get_class_id()
-            if not self._schema_cache.has_key(sid):
-                self._schema_cache[sid] = schema
-                if sid in self._pending_schema_req:
-                    self._pending_schema_req.remove(sid)
-        finally:
-            self._lock.release()
-
-    def _prefetch_schema(self, schema_id, agent):
-        """
-        Send an async request for the schema identified by schema_id if the
-        schema is not available in the cache.
-        """
-        need_fetch = False
-        self._lock.acquire()
-        try:
-            if ((not self._schema_cache.has_key(schema_id)) and
-                schema_id not in self._pending_schema_req):
-                self._pending_schema_req.append(schema_id)
-                need_fetch = True
-        finally:
-            self._lock.release()
-
-        if need_fetch:
-            mbox = _SchemaPrefetchMailbox(self, schema_id)
-            query = QmfQuery.create_id(QmfQuery.TARGET_SCHEMA, schema_id)
-            trace.debug("Sending Schema Query to Agent (%s)" % time.time())
-            try:
-                agent._send_query(query, mbox.get_address())
-            except SendError, e:
-                log.error(str(e))
-                mbox.destroy()
-                self._lock.acquire()
-                try:
-                    self._pending_schema_req.remove(schema_id)
-                finally:
-                    self._lock.release()
-
-
-    def _fetch_schema(self, schema_id, _agent=None, _timeout=None):
-        """
-        Find the schema identified by schema_id.  If not in the cache, ask the
-        agent for it.
-        """
-        if not isinstance(schema_id, SchemaClassId):
-            raise TypeError("SchemaClassId type expected")
-
-        self._lock.acquire()
-        try:
-            schema = self._schema_cache.get(schema_id)
-            if schema:
-                return schema
-        finally:
-            self._lock.release()
-
-        if _agent is None:
-            return None
-
-        # note: do_query will add the new schema to the cache automatically.
-        slist = self.do_query(_agent,
-                              QmfQuery.create_id(QmfQuery.TARGET_SCHEMA, schema_id),
-                              _timeout=_timeout)
-        if slist:
-            return slist[0]
-        else:
-            return None
-
-    def _add_mailbox(self, mbox):
-        """ 
-        Add a mailbox to the post office, and assign it a unique address.
-        """
-        self._lock.acquire()
-        try:
-            mbox.cid = self._correlation_id
-            self._correlation_id += 1
-            self._post_office[mbox.cid] = mbox
-        finally:
-            self._lock.release()
-
-    def _get_mailbox(self, mid):
-        try:
-            mid = long(mid)
-        except TypeError:
-            log.error("Invalid mailbox id: %s" % str(mid))
-            return None
-
-        self._lock.acquire()
-        try:
-            return self._post_office.get(mid)
-        finally:
-            self._lock.release()
-
-
-    def _remove_mailbox(self, mid):
-        """ Remove a mailbox and its address from the post office """
-        try:
-            mid = long(mid)
-        except TypeError:
-            log.error("Invalid mailbox id: %s" % str(mid))
-            return None
-
-        self._lock.acquire()
-        try:
-            if mid in self._post_office:
-                del self._post_office[mid]
-        finally:
-            self._lock.release()
-
-    def __repr__(self):
-        return str(self._address)
-
-    # def get_packages(self):
-    #     plist = []
-    #     for i in range(self.impl.packageCount()):
-    #         plist.append(self.impl.getPackageName(i))
-    #     return plist
-    
-    
-    # def get_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, {"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 get_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 get_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 get_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 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:
-#                     trace.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:
-#                     trace.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:
-#                     trace.debug("Console Event NEW_PACKAGE received")
-#                     if self._handler:
-#                         self._handler.new_package(self._event.name)
-#                 elif self._event.kind == qmfengine.ConsoleEvent.NEW_CLASS:
-#                     trace.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:
-#                     trace.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:
-#                     trace.debug("Console Event EVENT_RECEIVED received")
-#                 elif self._event.kind == qmfengine.ConsoleEvent.AGENT_HEARTBEAT:
-#                     trace.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:
-#                     trace.debug("Console Event METHOD_RESPONSE received")
-#                 else:
-#                     trace.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 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):
-#         trace.debug("broker.shutdown() called.")
-#         self.console.impl.delConnection(self.impl)
-#         self.conn.del_conn_handler(self)
-#         if self._session:
-#             self.impl.sessionClosed()
-#             trace.debug("broker.shutdown() sessionClosed done.")
-#             self._session.destroy()
-#             trace.debug("broker.shutdown() session destroy done.")
-#             self._session = None
-#         self._operational = False
-#         trace.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:
-#                 trace.debug("Broker Event BROKER_INFO received");
-#             elif self._event.kind == qmfengine.BrokerEvent.DECLARE_QUEUE:
-#                 trace.debug("Broker Event DECLARE_QUEUE received");
-#                 self.conn.impl.declareQueue(self._session.handle, self._event.name)
-#             elif self._event.kind == qmfengine.BrokerEvent.DELETE_QUEUE:
-#                 trace.debug("Broker Event DELETE_QUEUE received");
-#                 self.conn.impl.deleteQueue(self._session.handle, self._event.name)
-#             elif self._event.kind == qmfengine.BrokerEvent.BIND:
-#                 trace.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:
-#                 trace.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:
-#                 trace.debug("Broker Event SETUP_COMPLETE received");
-#                 self.impl.startProtocol()
-#             elif self._event.kind == qmfengine.BrokerEvent.STABLE:
-#                 trace.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
-#             trace.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):
-#         trace.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):
-#         trace.debug("Broker: Connection event DISCONNECTED")
-#         pass
-    
-    
-#     def conn_event_visit(self):
-#         self._do_events()
-
-
-#     def sess_event_session_closed(self, context, error):
-#         trace.debug("Broker: Session event CLOSED")
-#         self.impl.sessionClosed()
-    
-    
-#     def sess_event_recv(self, context, message):
-#         trace.debug("Broker: Session event MSG_RECV")
-#         if not self._operational:
-#             log.warning("Unexpected session event message received by Broker proxy: context='%s'" % str(context))
-#         self.impl.handleRcvMessage(message)
-#         self._do_events()
-
-
-
-################################################################################
-################################################################################
-################################################################################
-################################################################################
-#                 TEMPORARY TEST CODE - TO BE DELETED
-################################################################################
-################################################################################
-################################################################################
-################################################################################
-
-if __name__ == '__main__':
-    # temp test code
-    import logging
-    from common import (qmfTypes, SchemaProperty)
-
-    logging.getLogger().setLevel(logging.INFO)
-
-    logging.info( "************* Creating Async Console **************" )
-
-    class MyNotifier(Notifier):
-        def __init__(self, context):
-            self._myContext = context
-            self.WorkAvailable = False
-
-        def indication(self):
-            print("Indication received! context=%d" % self._myContext)
-            self.WorkAvailable = True
-
-    _noteMe = MyNotifier( 666 )
-
-    _myConsole = Console(notifier=_noteMe)
-
-    _myConsole.enable_agent_discovery()
-    logging.info("Waiting...")
-
-
-    logging.info( "Destroying console:" )
-    _myConsole.destroy( 10 )
-
-    logging.info( "******** Messing around with Schema ********" )
-
-    _sec = SchemaEventClass( _classId=SchemaClassId("myPackage", "myClass",
-                                                    stype=SchemaClassId.TYPE_EVENT), 
-                             _desc="A typical event schema",
-                             _props={"Argument-1": SchemaProperty(_type_code=qmfTypes.TYPE_UINT8,
-                                                                  kwargs = {"min":0,
-                                                                            "max":100,
-                                                                            "unit":"seconds",
-                                                                            "desc":"sleep value"}),
-                                     "Argument-2": SchemaProperty(_type_code=qmfTypes.TYPE_LSTR,
-                                                                  kwargs={"maxlen":100,
-                                                                          "desc":"a string argument"})})
-    print("_sec=%s" % _sec.get_class_id())
-    print("_sec.gePropertyCount()=%d" % _sec.get_property_count() )
-    print("_sec.getProperty('Argument-1`)=%s" % _sec.get_property('Argument-1') )
-    print("_sec.getProperty('Argument-2`)=%s" % _sec.get_property('Argument-2') )
-    try:
-        print("_sec.getProperty('not-found')=%s" % _sec.get_property('not-found') )
-    except:
-        pass
-    print("_sec.getProperties()='%s'" % _sec.get_properties())
-
-    print("Adding another argument")
-    _arg3 = SchemaProperty( _type_code=qmfTypes.TYPE_BOOL,
-                            kwargs={"dir":"IO",
-                                    "desc":"a boolean argument"})
-    _sec.add_property('Argument-3', _arg3)
-    print("_sec=%s" % _sec.get_class_id())
-    print("_sec.getPropertyCount()=%d" % _sec.get_property_count() )
-    print("_sec.getProperty('Argument-1')=%s" % _sec.get_property('Argument-1') )
-    print("_sec.getProperty('Argument-2')=%s" % _sec.get_property('Argument-2') )
-    print("_sec.getProperty('Argument-3')=%s" % _sec.get_property('Argument-3') )
-
-    print("_arg3.mapEncode()='%s'" % _arg3.map_encode() )
-
-    _secmap = _sec.map_encode()
-    print("_sec.mapEncode()='%s'" % _secmap )
-
-    _sec2 = SchemaEventClass( _map=_secmap )
-
-    print("_sec=%s" % _sec.get_class_id())
-    print("_sec2=%s" % _sec2.get_class_id())
-
-    _soc = SchemaObjectClass( _map = {"_schema_id": {"_package_name": "myOtherPackage",
-                                                     "_class_name":   "myOtherClass",
-                                                     "_type":         "_data"},
-                                      "_desc": "A test data object",
-                                      "_values":
-                                          {"prop1": {"amqp_type": qmfTypes.TYPE_UINT8,
-                                                     "access": "RO",
-                                                     "index": True,
-                                                     "unit": "degrees"},
-                                           "prop2": {"amqp_type": qmfTypes.TYPE_UINT8,
-                                                     "access": "RW",
-                                                     "index": True,
-                                                     "desc": "The Second Property(tm)",
-                                                     "unit": "radians"},
-                                           "statistics": { "amqp_type": qmfTypes.TYPE_DELTATIME,
-                                                           "unit": "seconds",
-                                                           "desc": "time until I retire"},
-                                           "meth1": {"_desc": "A test method",
-                                                     "_arguments":
-                                                         {"arg1": {"amqp_type": qmfTypes.TYPE_UINT32,
-                                                                   "desc": "an argument 1",
-                                                                   "dir":  "I"},
-                                                          "arg2": {"amqp_type": qmfTypes.TYPE_BOOL,
-                                                                   "dir":  "IO",
-                                                                   "desc": "some weird boolean"}}},
-                                           "meth2": {"_desc": "A test method",
-                                                     "_arguments":
-                                                         {"m2arg1": {"amqp_type": qmfTypes.TYPE_UINT32,
-                                                                     "desc": "an 'nuther argument",
-                                                                     "dir":
-                                                                         "I"}}}},
-                                      "_subtypes":
-                                          {"prop1":"qmfProperty",
-                                           "prop2":"qmfProperty",
-                                           "statistics":"qmfProperty",
-                                           "meth1":"qmfMethod",
-                                           "meth2":"qmfMethod"},
-                                      "_primary_key_names": ["prop2", "prop1"]})
-
-    print("_soc='%s'" % _soc)
-
-    print("_soc.getPrimaryKeyList='%s'" % _soc.get_id_names())
-
-    print("_soc.getPropertyCount='%d'" % _soc.get_property_count())
-    print("_soc.getProperties='%s'" % _soc.get_properties())
-    print("_soc.getProperty('prop2')='%s'" % _soc.get_property('prop2'))
-
-    print("_soc.getMethodCount='%d'" % _soc.get_method_count())
-    print("_soc.getMethods='%s'" % _soc.get_methods())
-    print("_soc.getMethod('meth2')='%s'" % _soc.get_method('meth2'))
-
-    _socmap = _soc.map_encode()
-    print("_socmap='%s'" % _socmap)
-    _soc2 = SchemaObjectClass( _map=_socmap )
-    print("_soc='%s'" % _soc)
-    print("_soc2='%s'" % _soc2)
-
-    if _soc2.get_class_id() == _soc.get_class_id():
-        print("soc and soc2 are the same schema")
-
-
-    logging.info( "******** Messing around with ObjectIds ********" )
-
-
-    qd = QmfData( _values={"prop1":1, "prop2":True, "prop3": {"a":"map"}, "prop4": "astring"} )
-    print("qd='%s':" % qd)
-
-    print("prop1=%d prop2=%s prop3=%s prop4=%s" % (qd.prop1, qd.prop2, qd.prop3, qd.prop4))
-
-    print("qd map='%s'" % qd.map_encode())
-    print("qd getProperty('prop4')='%s'" % qd.get_value("prop4"))
-    qd.set_value("prop4", 4, "A test property called 4")
-    print("qd setProperty('prop4', 4)='%s'" % qd.get_value("prop4"))
-    qd.prop4 = 9
-    print("qd.prop4 = 9 ='%s'" % qd.prop4)
-    qd["prop4"] = 11
-    print("qd[prop4] = 11 ='%s'" % qd["prop4"])
-
-    print("qd.mapEncode()='%s'" % qd.map_encode())
-    _qd2 = QmfData( _map = qd.map_encode() )
-    print("_qd2.mapEncode()='%s'" % _qd2.map_encode())
-
-    _qmfDesc1 = QmfConsoleData( {"_values" : {"prop1": 1, "statistics": 666,
-                                              "prop2": 0}},
-                                agent="some agent name?",
-                                _schema = _soc)
-
-    print("_qmfDesc1 map='%s'" % _qmfDesc1.map_encode())
-
-    _qmfDesc1._set_schema( _soc )
-
-    print("_qmfDesc1 prop2 = '%s'" % _qmfDesc1.get_value("prop2"))
-    print("_qmfDesc1 primarykey = '%s'" % _qmfDesc1.get_object_id())
-    print("_qmfDesc1 classid = '%s'" % _qmfDesc1.get_schema_class_id())
-
-
-    _qmfDescMap = _qmfDesc1.map_encode()
-    print("_qmfDescMap='%s'" % _qmfDescMap)
-
-    _qmfDesc2 = QmfData( _map=_qmfDescMap, _schema=_soc )
-
-    print("_qmfDesc2 map='%s'" % _qmfDesc2.map_encode())
-    print("_qmfDesc2 prop2 = '%s'" % _qmfDesc2.get_value("prop2"))
-    print("_qmfDesc2 primary key = '%s'" % _qmfDesc2.get_object_id())
-
-
-    logging.info( "******** Messing around with QmfEvents ********" )
-
-
-    _qmfevent1 = QmfEvent( _timestamp = 1111,
-                           _schema = _sec,
-                           _values = {"Argument-1": 77, 
-                                      "Argument-3": True,
-                                      "Argument-2": "a string"})
-    print("_qmfevent1.mapEncode()='%s'" % _qmfevent1.map_encode())
-    print("_qmfevent1.getTimestamp()='%s'" % _qmfevent1.get_timestamp())
-
-    _qmfevent1Map = _qmfevent1.map_encode()
-
-    _qmfevent2 = QmfEvent(_map=_qmfevent1Map, _schema=_sec)
-    print("_qmfevent2.mapEncode()='%s'" % _qmfevent2.map_encode())
-
-
-    logging.info( "******** Messing around with Queries ********" )
-
-    _q1 = QmfQuery.create_predicate(QmfQuery.TARGET_AGENT,
-                                    [QmfQuery.AND,
-                                     [QmfQuery.EQ, "vendor", [QmfQuery.QUOTE, "AVendor"]],
-                                     [QmfQuery.EQ, [QmfQuery.QUOTE, "SomeProduct"], "product"],
-                                     [QmfQuery.EQ, [QmfQuery.UNQUOTE, "name"], [QmfQuery.QUOTE, "Thingy"]],
-                                     [QmfQuery.OR,
-                                      [QmfQuery.LE, "temperature", -10],
-                                      [QmfQuery.FALSE],
-                                      [QmfQuery.EXISTS, "namey"]]])
-
-    print("_q1.mapEncode() = [%s]" % _q1.map_encode())
Index: extras/qmf/setup.py
===================================================================
--- extras/qmf/setup.py	(revision 1056407)
+++ extras/qmf/setup.py	(working copy)
@@ -23,7 +23,7 @@
       version="0.8",
       author="Apache Qpid",
       author_email="dev@qpid.apache.org",
-      packages=["qmf", "qmf2", "qmf2.tests"],
+      packages=["qmf"],
       package_dir={"": "src/py"},
       url="http://qpid.apache.org/",
       license="Apache Software License",
Index: cpp/src/qpid/framing/SendContent.h
===================================================================
--- cpp/src/qpid/framing/SendContent.h	2009-03-12 16:55:34.000000000 -0400
+++ cpp/src/qpid/framing/SendContent.h	2011-02-07 16:12:01.949172798 -0500
@@ -37,7 +37,7 @@
  */
 class SendContent
 {
-    mutable FrameHandler& handler;
+    FrameHandler& handler;
     const uint16_t maxFrameSize;
     uint expectedFrameCount;
     uint frameCount;