[qpid-cpp: 1/2] Rebased to sync with upstream's official 0.8 release, based on svn rev 1037942

Nuno Santos nsantos at fedoraproject.org
Mon Jan 10 22:09:38 UTC 2011


commit 1a4f6064e686abec58ece50b1b7ff84db98aaebd
Author: Nuno Santos <nsantos at redhat.com>
Date:   Mon Jan 10 17:05:42 2011 -0500

    Rebased to sync with upstream's official 0.8 release, based on svn rev 1037942

 .gitignore       |    2 +
 fedora.patch     |13676 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qpid-cpp.spec    |  117 +-
 sources          |    4 +-
 store-4411.patch |  118 +
 5 files changed, 13878 insertions(+), 39 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 7e9357c..e124ae2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
 qpid-cpp-0.7.946106.tar.gz
 store-0.7.3975.tar.gz
+/qpid-0.8.tar.gz
+/store-0.8.4411.tar.gz
diff --git a/fedora.patch b/fedora.patch
new file mode 100644
index 0000000..85a8f54
--- /dev/null
+++ b/fedora.patch
@@ -0,0 +1,13676 @@
+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"
+@@ -507,9 +508,9 @@
+     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));
+@@ -527,9 +528,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();
+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/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/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/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/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"
+@@ -111,6 +112,10 @@
+         bool externalStorage;
+         bool autoAllowQueries;
+         bool autoAllowMethods;
++        uint32_t maxSubscriptions;
++        uint32_t minSubInterval;
++        uint32_t subLifetime;
++        bool publicEvents;
+         uint64_t schemaUpdateTime;
+         string directBase;
+         string topicBase;
+@@ -129,6 +134,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();
+@@ -172,10 +178,11 @@
+     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),
+     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 +215,22 @@
+         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();
+     }
+ }
+ 
+@@ -421,20 +444,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 +482,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 +498,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());
+ }
+@@ -494,11 +513,11 @@
+     // TODO: add severity.package.class to key
+     //       or modify to send only to subscriptions with matching queries
+ 
+-    headers["method"] = "indication";
+-    headers["qmf.opcode"] = "_data_indication";
+-    headers["qmf.content"] = "_event";
+-    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_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("agent.ind.event");
+ 
+     encode(DataImplAccess::get(data).asMap(), msg);
+@@ -573,22 +592,20 @@
+     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();
+ }
+ 
+ 
+@@ -614,10 +631,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,6 +639,29 @@
+     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;
++        }
++
++        if (DataImplAccess::get(iter->second).getSchema().isValid())
++            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()));
+ }
+ 
+@@ -668,19 +704,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 +734,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 +778,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 +791,12 @@
+     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") {
++    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 +808,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 +817,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 +867,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 +885,36 @@
+ }
+ 
+ 
++void AgentSessionImpl::send(Message msg, const Address& to)
++{
++    Sender sender;
++
++    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 +927,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 +941,7 @@
+     //
+     if (seconds == lastVisit)
+         return;
++    //uint64_t thisInterval(seconds - lastVisit);
+     lastVisit = seconds;
+ 
+     //
+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,19 @@
+ #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::Duration;
++using qpid::messaging::Message;
+ using qpid::types::Variant;
+ 
+ typedef qmf::PrivateImplRef<ConsoleSession> PI;
+@@ -51,6 +56,8 @@
+ 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
+@@ -110,9 +117,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()));
+         }
+     }
+ 
+@@ -219,6 +234,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 +268,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 +296,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 +316,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 +328,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,9 +363,9 @@
+     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");
+@@ -355,9 +382,9 @@
+     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("agent-locate");
+@@ -382,17 +409,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 +453,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;
+@@ -444,16 +482,17 @@
+                 enqueueEventLH(ConsoleEvent(eventImpl.release()));
+             }
+ 
+-            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.
+                     //
+                     auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_SCHEMA_UPDATE));
+                     eventImpl->setAgent(agent);
++                    impl.setAttribute(iter->first, iter->second);
+                     enqueueEventLH(ConsoleEvent(eventImpl.release()));
+                 }
+             }
+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/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;
+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,13 @@
+     };
+ 
+     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& _reply, const std::string& _body, const std::string& _uid) :
++        cid(_cid), replyTo(_reply), body(_body), userId(_uid) {}
+ 
+         std::string cid;
+         std::string replyTo;
+         std::string body;
++        std::string userId;
+     };
+ 
+     typedef std::deque<QueuedMethod*> MethodQueue;
+@@ -282,11 +283,11 @@
+     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 invokeMethodRequest  (const std::string& body, const std::string& cid, const std::string& replyTo, 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 handleMethodRequest  (const std::string& body, const std::string& sequence, const std::string& replyTo, 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->replyTo, item->userId);
+             delete item;
+         }
+     }
+@@ -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& replyTo, const string& userId)
+ {
+     string  methodName;
+     bool    failed = false;
+@@ -606,7 +608,7 @@
+                               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();
+@@ -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& replyTo, const string& userId)
+ {
+     if (extThread) {
+         sys::Mutex::ScopedLock lock(agentLock);
+ 
+-        methodQueue.push_back(new QueuedMethod(cid, replyTo, body));
++        methodQueue.push_back(new QueuedMethod(cid, replyTo, 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, replyTo, userId);
+     }
+ 
+     QPID_LOG(trace, "RCVD MethodRequest");
+@@ -876,13 +878,17 @@
+         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 == "_method_request")       handleMethodRequest(msg.getData(), cid, replyToKey, userId);
+         else if (opcode == "_query_request")        handleGetQuery(msg.getData(), cid, replyToKey);
+         else {
+             QPID_LOG(warning, "Support for QMF V2 Opcode [" << opcode << "] TBD!!!");
+@@ -1161,10 +1167,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/ManagementObject.cpp
+===================================================================
+--- cpp/src/qpid/management/ManagementObject.cpp	(revision 1056407)
++++ cpp/src/qpid/management/ManagementObject.cpp	(working copy)
+@@ -262,6 +262,7 @@
+ 
+ void ManagementObject::resourceDestroy()
+ {
++    QPID_LOG(trace, "Management object marked deleted: " << getObjectId().getV2Key());
+     destroyTime = sys::Duration(sys::EPOCH, sys::now());
+     deleted     = true;
+ }
+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());
+     }
+ 
+@@ -840,9 +842,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);
+@@ -1083,8 +1085,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 +1117,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();
+@@ -1193,8 +1195,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] = iter->second->getPackageName();
+         params[acl::PROP_SCHEMACLASS]   = iter->second->getClassName();
+@@ -1214,7 +1216,7 @@
+ 
+     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();
+@@ -1853,9 +1855,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);
+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/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)
+@@ -385,10 +385,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
+@@ -444,6 +442,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:
+@@ -683,6 +682,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)
+@@ -101,7 +101,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 = []
+@@ -134,7 +134,7 @@
+     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)
+@@ -250,10 +250,8 @@
+         @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(arg.impl)
+       else
+         raise "Unsupported initializer for Data"
+       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()
+ 
+ 
+@@ -141,10 +143,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/cpp/agent.cpp
+===================================================================
+--- cpp/bindings/qmf2/examples/cpp/agent.cpp	(revision 1056407)
++++ cpp/bindings/qmf2/examples/cpp/agent.cpp	(working copy)
+@@ -60,7 +60,7 @@
+     //
+     // Create and open a messaging connection to a broker.
+     //
+-    connection = qpid::messaging::Connection(url);
++    connection = qpid::messaging::Connection(url, "{reconnect:True}");
+     connection.open();
+ 
+     //
+@@ -132,7 +132,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");
+@@ -178,7 +178,7 @@
+             if (event.getArguments()["useString"])
+                 session.raiseException(event, event.getArguments()["stringVal"]);
+             else {
+-                Data ex(sch_exception.getSchemaId());
++                Data ex(sch_exception);
+                 ex.setProperty("whatHappened", "It Failed");
+                 ex.setProperty("howBad", 75);
+                 ex.setProperty("details", event.getArguments()["details"]);
+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,11 @@
+ 
+ AM_CPPFLAGS = $(INCLUDE)
+ 
+-noinst_PROGRAMS=agent
++noinst_PROGRAMS=agent list_agents
++
+ 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
++
+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/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,16 @@
+          * 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
+          */
+         QMF_EXTERN AgentSession(qpid::messaging::Connection&, const std::string& options="");
+ 
+@@ -143,7 +148,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&);
+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 {
+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>
+@@ -67,6 +68,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/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,672 @@
++#!/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:
++      print "Exception in query command:", e
++
++  def do_call(self, data):
++    try:
++      self.dataObject.do_call(data)
++    except Exception, e:
++      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 arg[0] == '{' or arg[0] == '[' or arg.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: 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at qpid.apache.org",
+-      packages=["qmf", "qmf2", "qmf2.tests"],
++      packages=["qmf"],
+       package_dir={"": "src/py"},
+       url="http://qpid.apache.org/",
+       license="Apache Software License",
diff --git a/qpid-cpp.spec b/qpid-cpp.spec
index 40116bc..3ea69e3 100644
--- a/qpid-cpp.spec
+++ b/qpid-cpp.spec
@@ -22,11 +22,11 @@
 %global MRG_non_core 1
 
 # Release numbers
-%global qpid_release 0.7
-%global qpid_svnrev  946106
-%global store_svnrev 3975
+%global qpid_release 0.8
+%global qpid_svnrev  1037942
+%global store_svnrev 4411
 # Change this release number for each build of the same qpid_svnrev, otherwise set back to 1.
-%global release_num  4
+%global release_num  1
 
 # NOTE: these flags should not both be set at the same time!
 # RHEL-6 builds should have all flags set to 0.
@@ -59,6 +59,7 @@
 %global client            %{MRG_core}
 %global server            %{MRG_core}
 %global qmf               %{MRG_core}
+%global python_qmf        %{MRG_core}
 %global ruby_qmf          %{MRG_core}
 %global client_devel      %{MRG_core}
 %global client_devel_docs %{MRG_core}
@@ -80,33 +81,31 @@
 %global pkg_name qpid-cpp
 
 Name:           %{name}
-Version:        %{qpid_release}.%{qpid_svnrev}
+Version:        %{qpid_release}
 Release:        %{release_num}%{?dist}
 Summary:        Libraries for Qpid C++ client applications
 Group:          System Environment/Libraries
 License:        ASL 2.0
 URL:            http://qpid.apache.org
-Source0:        %{name}-%{version}.tar.gz
+Source0:        qpid-%{version}.tar.gz
 Source1:        store-%{qpid_release}.%{store_svnrev}.tar.gz
 %if ! %{rhel_4}
 Source2:        qpidd.pp
 %endif
 
+Patch0:         bootstrap.patch
+Patch1:         store-4411.patch
+
 %if %{fedora}
-#Patch0:         so_number.patch
-Patch0:         boost_system.patch
-Patch1:         swig.patch
+Patch2:         fedora.patch
 %endif
 
 %if %{rhel_4}
-Patch0:         RHEL4_SASL_Conf.patch
-Patch1:         qpidd.patch
-Patch2:         bz530364-rhel4.patch
+Patch3:         RHEL4_SASL_Conf.patch
+Patch4:         qpidd.patch
+Patch5:         bz530364-rhel4.patch
 %endif
 
-Patch5:         mrg_1.3.x.patch
-Patch6:         store_1.3.x.patch
-
 BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
 ExclusiveArch:  i386 i686 x86_64
@@ -179,9 +178,10 @@ the AMQP protocol.
 
 %files -n %{pkg_name}-client
 %defattr(-,root,root,-)
-%doc cpp/LICENSE cpp/NOTICE cpp/README cpp/INSTALL cpp/RELEASE_NOTES cpp/DESIGN
+%doc cpp/LICENSE cpp/NOTICE cpp/README.txt cpp/INSTALL cpp/RELEASE_NOTES cpp/DESIGN
 %_libdir/libqpidcommon.so.*
 %_libdir/libqpidclient.so.*
+%_libdir/libqpidtypes.so.*
 %_libdir/libqpidmessaging.so.*
 %dir %_libdir/qpid
 %dir %_libdir/qpid/client
@@ -234,6 +234,7 @@ in C++ using Qpid.  Qpid implements the AMQP messaging specification.
 %_includedir/qmf
 %_libdir/libqpidcommon.so
 %_libdir/libqpidclient.so
+%_libdir/libqpidtypes.so
 %_libdir/libqpidmessaging.so
 %if ! %{rhel_4}
 %_datadir/qpidc/examples/messaging
@@ -388,11 +389,12 @@ Group: System Environment/Daemons
 Requires: %{pkg_name}-client = %version-%release
 
 %description -n qmf
-An extensible managememt framework layered on QPID messaging.
+An extensible management framework layered on QPID messaging.
 
 %files -n qmf
 %defattr(-,root,root,-)
 %_libdir/libqmf.so.*
+%_libdir/libqmf2.so.*
 %_libdir/libqmfengine.so.*
 %_libdir/libqmfconsole.so.*
 
@@ -421,6 +423,7 @@ components.
 %files -n qmf-devel
 %defattr(-,root,root,-)
 %_libdir/libqmf.so
+%_libdir/libqmf2.so
 %_libdir/libqmfengine.so
 %_libdir/libqmfconsole.so
 %_bindir/qmf-gen
@@ -428,6 +431,26 @@ components.
 
 %endif
 
+# === Package: python-qmf ===
+
+%if %{python_qmf} && ! %{rhel_4}
+
+%package -n python-qmf
+Summary: The QPID Management Framework bindings for python
+Group: System Environment/Libraries
+Requires: %{pkg_name}-client = %version-%release
+
+%description -n python-qmf
+An extensible management framework layered on QPID messaging, bindings
+for python.
+
+%files -n python-qmf
+%defattr(-,root,root,-)
+%{python_sitelib}/cqmf2.py*
+%{python_sitelib}/qmf2.py*
+
+%endif
+
 # === Package: ruby-qmf ===
 
 %if %{ruby_qmf} && ! %{rhel_4}
@@ -438,13 +461,16 @@ Group: System Environment/Libraries
 Requires: %{pkg_name}-client = %version-%release
 
 %description -n ruby-qmf
-An extensible managememt framework layered on QPID messaging, bindings
+An extensible management framework layered on QPID messaging, bindings
 for ruby.
 
 %files -n ruby-qmf
 %defattr(-,root,root,-)
 %{ruby_sitelib}/qmf.rb
+%{ruby_sitelib}/qmf2.rb
 %{ruby_sitearch}/qmfengine.so
+%{ruby_sitearch}/cqpid.so
+%{ruby_sitearch}/cqmf2.so
 
 %endif
 
@@ -607,7 +633,7 @@ usermod -g ais -G root qpidd
 Summary: Red Hat persistence extension to the Qpid messaging system
 Group: System Environment/Libraries
 License: LGPL 2.1+
-Requires: %{pkg_name}-server = %{qpid_release}.%{qpid_svnrev}
+Requires: %{pkg_name}-server = %{qpid_release}
 Requires: db4
 Requires: libaio
 Obsoletes: rhm
@@ -658,8 +684,9 @@ to receive at all.
 # ===
 
 %prep
-%setup -q -n %{name}-%{version}
-%setup -q -T -D -b 1 -n %{name}-%{version}
+%setup -q -n qpid-%{version}
+%setup -q -T -D -b 1 -n qpid-%{version}
+
 %if %{rhel_4}
 # set up boost for rhel-4
 pushd ./cpp/boost-1.32-support
@@ -670,23 +697,20 @@ make apply
 popd
 
 # apply rhel-4 patches
-%patch0
-%patch1
-%patch2
-
+%patch3
+%patch4
+%patch5
 %endif
 
-%if %{fedora}
 %patch0
-%patch1
-%endif
 
-# apply qpid patch
-%patch5 -p2
+%if %{fedora}
+%patch2
+%endif
 
 # apply store patch
 pushd ../store-%{qpid_release}.%{store_svnrev}
-%patch6 -p1
+%patch1
 popd
 
 %global perftests "qpid-perftest qpid-topic-listener qpid-topic-publisher qpid-latency-test qpid-client-test qpid-txtest"
@@ -731,6 +755,19 @@ cat run_failover_soak.orig | sed -e "s#^src_root=..#src_root=/usr/sbin#" \
 popd
 popd
 
+%if %{fedora}
+pushd python
+./setup.py build
+popd
+pushd tools
+./setup.py build
+popd
+pushd extras/qmf
+./setup.py build
+popd
+%endif
+
+
 # Store
 pushd ../store-%{qpid_release}.%{store_svnrev}
 %if %{rhel_4}
@@ -739,15 +776,16 @@ export CXXFLAGS="%{optflags} -DNDEBUG -I/usr/include/qpid-boost"
 export CXXFLAGS="%{optflags} -DNDEBUG" 
 %endif
 ./bootstrap
-%configure --disable-static --disable-rpath --disable-dependency-tracking --with-qpid-checkout=%{_builddir}/%{name}-%{version}
+%configure --disable-static --disable-rpath --disable-dependency-tracking --with-qpid-checkout=%{_builddir}/qpid-%{version}
 make
 popd
 
 %install
 rm -rf %{buildroot}
 mkdir -p -m0755 %{buildroot}/%_bindir
-pushd %{_builddir}/%{name}-%{version}/cpp
+pushd %{_builddir}/qpid-%{version}/cpp
 make install DESTDIR=%{buildroot}
+
 install -Dp -m0755 etc/qpidd %{buildroot}%{_initrddir}/qpidd
 install -d -m0755 %{buildroot}%{_localstatedir}/lib/qpidd
 install -d -m0755 %{buildroot}%_libdir/qpidd
@@ -801,23 +839,25 @@ rm -rf %{buildroot}%_datadir/qpidc/examples/request-response
 rm -rf %{buildroot}%_datadir/qpidc/examples/tradedemo
 rm -rf %{buildroot}%_datadir/qpidc/examples/xml-exchange
 
-
 %if ! %{rhel_4}
 install -d %{buildroot}%{_datadir}/selinux/packages
-install -m 644 %{_builddir}/%{name}-%{version}/selinux/qpidd.pp %{buildroot}%{_datadir}/selinux/packages
+install -m 644 %{_builddir}/qpid-%{version}/selinux/qpidd.pp %{buildroot}%{_datadir}/selinux/packages
 %if %{ruby_qmf}
 install -d %{buildroot}%{ruby_sitelib}
 install -d %{buildroot}%{ruby_sitearch}
-install -pm 644 %{_builddir}/%{name}-%{version}/cpp/bindings/qmf/ruby/qmf.rb %{buildroot}%{ruby_sitelib}
-install -pm 755 %{_builddir}/%{name}-%{version}/cpp/bindings/qmf/ruby/.libs/qmfengine.so %{buildroot}%{ruby_sitearch}
+install -pm 644 %{_builddir}/qpid-%{version}/cpp/bindings/qmf/ruby/qmf.rb %{buildroot}%{ruby_sitelib}
+install -pm 755 %{_builddir}/qpid-%{version}/cpp/bindings/qmf/ruby/.libs/qmfengine.so %{buildroot}%{ruby_sitearch}
 %endif
 %endif
 
 rm -f %{buildroot}%_libdir/_*
+rm -f %{buildroot}%_libdir/pkgconfig/qpid.pc
 rm -fr %{buildroot}%_libdir/qpid/tests
 rm -fr %{buildroot}%_libexecdir/qpid/tests
 %if ! %{rhel_4}
 rm -f %{buildroot}%{ruby_sitearch}/qmfengine.la
+rm -f %{buildroot}%{ruby_sitearch}/cqpid.la
+rm -f %{buildroot}%{ruby_sitearch}/cqmf2.la
 %endif
 popd
 
@@ -944,6 +984,9 @@ rm -rf %{buildroot}
 %postun -p /sbin/ldconfig
 
 %changelog
+* Mon Jan 10 2011 Nuno Santos <nsantos at redhat.com> - 0.8-1
+- Rebased to sync with upstream's official 0.8 release, based on svn rev 1037942
+
 * Mon Nov 29 2010 Nuno Santos <nsantos at redhat.com> - 0.7.946106-4
 - BZ656680 - Update Spec File to use ghost macro on files in /var/run
 
diff --git a/sources b/sources
index f9e1bfd..a0d03b2 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
-b692848064ae37c52b5ca948fdd58f8c  qpid-cpp-0.7.946106.tar.gz
-a61ce61f6f8744e8ac835378474257a6  store-0.7.3975.tar.gz
+0184fef44ae9ca859bb4ef9ee22d5e1c  qpid-0.8.tar.gz
+261c37dc90a1a05b59100476ac9c2839  store-0.8.4411.tar.gz
diff --git a/store-4411.patch b/store-4411.patch
new file mode 100644
index 0000000..76b149a
--- /dev/null
+++ b/store-4411.patch
@@ -0,0 +1,118 @@
+Index: lib/gen/qmf/com/redhat/rhm/store/Store.h
+===================================================================
+--- lib/gen/qmf/com/redhat/rhm/store/Store.h	(revision 4411)
++++ lib/gen/qmf/com/redhat/rhm/store/Store.h	(working copy)
+@@ -104,7 +104,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;
+ 
+     uint32_t writePropertiesSize() const;
+@@ -113,7 +114,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);
+ 
+ 
+     writeSchemaCall_t getWriteSchemaCall() { return writeSchema; }
+Index: lib/gen/qmf/com/redhat/rhm/store/Journal.cpp
+===================================================================
+--- lib/gen/qmf/com/redhat/rhm/store/Journal.cpp	(revision 4411)
++++ lib/gen/qmf/com/redhat/rhm/store/Journal.cpp	(working copy)
+@@ -689,7 +689,7 @@
+     buf.getRawData(_sBuf, _bufLen);
+ }
+ 
+-void Journal::doMethod (string& methodName, const string& inStr, string& outStr)
++void Journal::doMethod (string& methodName, const string& inStr, string& outStr, const string& userId)
+ {
+     Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
+     std::string          text;
+@@ -709,7 +709,11 @@
+         _matched = true;
+         ArgsJournalExpand ioArgs;
+         ioArgs.i_by = inBuf.getLong();
+-        status = coreObject->ManagementMethod (METHOD_EXPAND, ioArgs, text);
++        bool allow = coreObject->AuthorizeMethod(METHOD_EXPAND, ioArgs, userId);
++        if (allow)
++            status = coreObject->ManagementMethod (METHOD_EXPAND, ioArgs, text);
++        else
++            status = Manageable::STATUS_FORBIDDEN;
+         outBuf.putLong        (status);
+         outBuf.putMediumString(::qpid::management::Manageable::StatusText (status, text));
+     }
+@@ -868,7 +872,7 @@
+ 
+ }
+ 
+-void Journal::doMethod (string& methodName, const ::qpid::types::Variant::Map& inMap, ::qpid::types::Variant::Map& outMap)
++void Journal::doMethod (string& methodName, const ::qpid::types::Variant::Map& inMap, ::qpid::types::Variant::Map& outMap, const string& userId)
+ {
+     Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
+     std::string          text;
+@@ -880,7 +884,11 @@
+         if ((_i = inMap.find("by")) != inMap.end()) {
+             ioArgs.i_by = _i->second;
+         }
+-        status = coreObject->ManagementMethod (METHOD_EXPAND, ioArgs, text);
++        bool allow = coreObject->AuthorizeMethod(METHOD_EXPAND, ioArgs, userId);
++        if (allow)
++            status = coreObject->ManagementMethod (METHOD_EXPAND, ioArgs, text);
++        else
++            status = Manageable::STATUS_FORBIDDEN;
+         outMap["_status_code"] = (uint32_t) status;
+         outMap["_status_text"] = ::qpid::management::Manageable::StatusText(status, text);
+         return;
+Index: lib/gen/qmf/com/redhat/rhm/store/Journal.h
+===================================================================
+--- lib/gen/qmf/com/redhat/rhm/store/Journal.h	(revision 4411)
++++ lib/gen/qmf/com/redhat/rhm/store/Journal.h	(working copy)
+@@ -134,7 +134,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;
+ 
+     uint32_t writePropertiesSize() const;
+@@ -143,7 +144,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);
+ 
+ 
+     writeSchemaCall_t getWriteSchemaCall() { return writeSchema; }
+Index: lib/gen/qmf/com/redhat/rhm/store/Store.cpp
+===================================================================
+--- lib/gen/qmf/com/redhat/rhm/store/Store.cpp	(revision 4411)
++++ lib/gen/qmf/com/redhat/rhm/store/Store.cpp	(working copy)
+@@ -452,7 +452,7 @@
+     buf.getRawData(_sBuf, _bufLen);
+ }
+ 
+-void Store::doMethod (string&, const string&, string& outStr)
++void Store::doMethod (string&, const string&, string& outStr, const string&)
+ {
+     Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
+     std::string          text;
+@@ -580,7 +580,7 @@
+ 
+ }
+ 
+-void Store::doMethod (string&, const ::qpid::types::Variant::Map&, ::qpid::types::Variant::Map& outMap)
++void Store::doMethod (string&, const ::qpid::types::Variant::Map&, ::qpid::types::Variant::Map& outMap, const string&)
+ {
+     Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
+     std::string          text;


More information about the scm-commits mailing list