[python-qpid] Rebased to sync with upstream's official 0.12 release

Nuno Santos nsantos at fedoraproject.org
Tue Sep 20 20:51:49 UTC 2011


commit 123fb9c1b1a21994e757d10295e0128bae4cdc12
Author: Nuno Santos <nsantos at redhat.com>
Date:   Tue Sep 20 16:51:30 2011 -0400

    Rebased to sync with upstream's official 0.12 release

 .gitignore       |    1 +
 fedora.patch     |13083 +++++++++++-------------------------------------------
 python-qpid.spec |    5 +-
 sources          |    2 +-
 4 files changed, 2486 insertions(+), 10605 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 4096bb9..11fe15a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@ python-qpid-0.7.946106.tar.gz
 /qpid-python-0.8.tar.gz
 /qpid-extras-0.8.tar.gz
 /qpid-python-0.10.tar.gz
+/qpid-python-0.12.tar.gz
diff --git a/fedora.patch b/fedora.patch
index e66a1be..8172f66 100644
--- a/fedora.patch
+++ b/fedora.patch
@@ -1,10620 +1,2497 @@
-Index: extras/qmf/src/py/qmf/console.py
-===================================================================
---- extras/qmf/src/py/qmf/console.py	(revision 1056413)
-+++ extras/qmf/src/py/qmf/console.py	(working copy)
-@@ -53,6 +53,10 @@
-     """ Invoked when a connection is established to a broker """
-     pass
+From 00d9b16761b7f237c6b1418995cc9367aebac0f5 Mon Sep 17 00:00:00 2001
+From: Ted Ross <tross at apache.org>
+Date: Tue, 12 Jul 2011 11:49:32 +0000
+Subject: [PATCH 01/14] QPID-3275 - QMF Console asynchronous correlation-id
+ should be scoped to the session, not the specific
+ agent
+
+git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1145557 13f79535-47bb-0310-9956-ffa450edef68
+---
+ qpid/cpp/src/qmf/Agent.cpp            |   29 ++++++-----------------------
+ qpid/cpp/src/qmf/AgentImpl.h          |    1 -
+ qpid/cpp/src/qmf/ConsoleSession.cpp   |    2 +-
+ qpid/cpp/src/qmf/ConsoleSessionImpl.h |    3 +++
+ 4 files changed, 10 insertions(+), 25 deletions(-)
+
+diff --git a/qpid/cpp/src/qmf/Agent.cpp b/qpid/cpp/src/qmf/Agent.cpp
+index 915f2a1..684f8e4 100644
+--- a/qpid/cpp/src/qmf/Agent.cpp
++++ b/qpid/cpp/src/qmf/Agent.cpp
+@@ -72,7 +72,7 @@ Schema Agent::getSchema(const SchemaId& s, Duration t) { return impl->getSchema(
  
-+  def brokerConnectionFailed(self, broker):
-+    """ Invoked when a connection to a broker fails """
-+    pass
-+
-   def brokerDisconnected(self, broker):
-     """ Invoked when the connection to a broker is lost """
-     pass
-@@ -376,7 +380,8 @@
-           dp.routing_key = self.getV2RoutingKey()
-           mp = self._broker.amqpSession.message_properties()
-           mp.content_type = "amqp/map"
--          mp.user_id = self._broker.authUser
-+          if self._broker.saslUser:
-+            mp.user_id = self._broker.saslUser
-           mp.correlation_id = str(seq)
-           mp.app_id = "qmf2"
-           mp.reply_to = self._broker.amqpSession.reply_to("qmf.default.direct", self._broker.v2_direct_queue)
-@@ -1206,11 +1211,11 @@
-     try:
-       agentName = ah["qmf.agent"]
-       values = content["_values"]
--      timestamp = values["timestamp"]
--      interval = values["heartbeat_interval"]
-+      timestamp = values["_timestamp"]
-+      interval = values["_heartbeat_interval"]
-       epoch = 0
--      if 'epoch' in values:
--        epoch = values['epoch']
-+      if '_epoch' in values:
-+        epoch = values['_epoch']
-     except Exception,e:
-       return
+ AgentImpl::AgentImpl(const std::string& n, uint32_t e, ConsoleSessionImpl& s) :
+     name(n), directSubject(n), epoch(e), session(s), touched(true), untouchedCount(0), capability(0),
+-    sender(session.directSender), nextCorrelator(1), schemaCache(s.schemaCache)
++    sender(session.directSender), schemaCache(s.schemaCache)
+ {
+ }
  
-@@ -1239,7 +1244,7 @@
-       agent.touch()
-     if self.rcvHeartbeats and self.console and agent:
-       self._heartbeatCallback(agent, timestamp)
--    agent.update_schema_timestamp(values.get("schema_timestamp", 0))
-+    agent.update_schema_timestamp(values.get("_schema_updated", 0))
+@@ -102,12 +102,11 @@ const Variant& AgentImpl::getAttribute(const string& k) const
+ ConsoleEvent AgentImpl::query(const Query& query, Duration timeout)
+ {
+     boost::shared_ptr<SyncContext> context(new SyncContext());
+-    uint32_t correlator;
++    uint32_t correlator(session.correlator());
+     ConsoleEvent result;
  
+     {
+         qpid::sys::Mutex::ScopedLock l(lock);
+-        correlator = nextCorrelator++;
+         contextMap[correlator] = context;
+     }
+     try {
+@@ -151,12 +150,7 @@ ConsoleEvent AgentImpl::query(const string& text, Duration timeout)
  
-   def _v2HandleAgentLocateRsp(self, broker, mp, ah, content):
-@@ -1488,7 +1493,8 @@
-           dp.routing_key = objectId.getV2RoutingKey()
-           mp = broker.amqpSession.message_properties()
-           mp.content_type = "amqp/map"
--          mp.user_id = broker.authUser
-+          if broker.saslUser:
-+            mp.user_id = broker.saslUser
-           mp.correlation_id = str(seq)
-           mp.app_id = "qmf2"
-           mp.reply_to = broker.amqpSession.reply_to("qmf.default.direct", broker.v2_direct_queue)
-@@ -2227,9 +2233,12 @@
-     self.port = port
-     self.mechanisms = authMechs
-     self.ssl = ssl
-+    if connTimeout is not None:
-+	connTimeout = float(connTimeout)
-     self.connTimeout = connTimeout
-     self.authUser = authUser
-     self.authPass = authPass
-+    self.saslUser = None
-     self.cv = Condition()
-     self.seqToAgentMap = {}
-     self.error = None
-@@ -2403,6 +2412,11 @@
-       self.conn.start()
-       sock.settimeout(oldTimeout)
-       self.conn.aborted = oldAborted
-+      uid = self.conn.user_id
-+      if uid.__class__ == tuple and len(uid) == 2:
-+        self.saslUser = uid[1]
-+      else:
-+        self.saslUser = None
+ uint32_t AgentImpl::queryAsync(const Query& query)
+ {
+-    uint32_t correlator;
+-
+-    {
+-        qpid::sys::Mutex::ScopedLock l(lock);
+-        correlator = nextCorrelator++;
+-    }
++    uint32_t correlator(session.correlator());
  
-       # prevent topic queues from filling up (and causing the agents to
-       # disconnect) by discarding the oldest queued messages when full.
-@@ -2508,6 +2522,8 @@
-     except Exception, e:
-       self.error = "Exception during connection setup: %s - %s" % (e.__class__.__name__, e)
-       self.conn_exc = e
-+      if self.session.console:
-+        self.session.console.brokerConnectionFailed(self)
-       return False     # connection failed
+     sendQuery(query, correlator);
+     return correlator;
+@@ -172,12 +166,11 @@ uint32_t AgentImpl::queryAsync(const string& text)
+ ConsoleEvent AgentImpl::callMethod(const string& method, const Variant::Map& args, const DataAddr& addr, Duration timeout)
+ {
+     boost::shared_ptr<SyncContext> context(new SyncContext());
+-    uint32_t correlator;
++    uint32_t correlator(session.correlator());
+     ConsoleEvent result;
  
-   def _updateAgent(self, obj):
-@@ -2571,7 +2587,7 @@
-       for agent in to_notify:
-         self.session._delAgentCallback(agent)
+     {
+         qpid::sys::Mutex::ScopedLock l(lock);
+-        correlator = nextCorrelator++;
+         contextMap[correlator] = context;
+     }
+     try {
+@@ -213,12 +206,7 @@ ConsoleEvent AgentImpl::callMethod(const string& method, const Variant::Map& arg
  
--  def _v2SendAgentLocate(self, predicate={}):
-+  def _v2SendAgentLocate(self, predicate=[]):
-     """
-     Broadcast an agent-locate request to cause all agents in the domain to tell us who they are.
-     """
-@@ -2579,13 +2595,14 @@
-     dp = self.amqpSession.delivery_properties()
-     dp.routing_key = "console.request.agent_locate"
-     mp = self.amqpSession.message_properties()
--    mp.content_type = "amqp/map"
--    mp.user_id = self.authUser
-+    mp.content_type = "amqp/list"
-+    if self.saslUser:
-+      mp.user_id = self.saslUser
-     mp.app_id = "qmf2"
-     mp.reply_to = self.amqpSession.reply_to("qmf.default.direct", self.v2_direct_queue)
-     mp.application_headers = {'qmf.opcode':'_agent_locate_request'}
-     sendCodec = Codec()
--    sendCodec.write_map(predicate)
-+    sendCodec.write_list(predicate)
-     msg = Message(dp, mp, sendCodec.encoded)
-     self._send(msg, "qmf.default.topic")
+ uint32_t AgentImpl::callMethodAsync(const string& method, const Variant::Map& args, const DataAddr& addr)
+ {
+-    uint32_t correlator;
+-
+-    {
+-        qpid::sys::Mutex::ScopedLock l(lock);
+-        correlator = nextCorrelator++;
+-    }
++    uint32_t correlator(session.correlator());
  
-@@ -2622,7 +2639,8 @@
-       dp.ttl = ttl
-     mp = self.amqpSession.message_properties()
-     mp.content_type = "x-application/qmf"
--    mp.user_id = self.authUser
-+    if self.saslUser:
-+      mp.user_id = self.saslUser
-     mp.reply_to = self.amqpSession.reply_to("amq.direct", self.replyName)
-     return Message(dp, mp, body)
+     sendMethod(method, args, addr, correlator);
+     return correlator;
+@@ -596,12 +584,7 @@ void AgentImpl::sendMethod(const string& method, const Variant::Map& args, const
  
-@@ -2853,7 +2871,7 @@
-             content = None
-         else:
-           content = None
--  
-+
-         if content != None:
-           ##
-           ## Directly handle agent heartbeats and agent locate responses as these are broker-scope (they are
-@@ -3366,6 +3384,9 @@
-     Handle a QMFv2 data indication from the agent.  Note: called from context
-     of the Broker thread.
-     """
-+    if content.__class__ != list:
-+      return
-+
-     if mp.correlation_id:
-       try:
-         self.lock.acquire()
-@@ -3382,8 +3403,6 @@
-     if "qmf.content" in ah:
-       kind = ah["qmf.content"]
-     if kind == "_data":
--      if content.__class__ != list:
--        return
-       for omap in content:
-         context.addV2QueryResult(omap)
-       context.processV2Data()
-@@ -3391,14 +3410,15 @@
-         context.signal()
+ void AgentImpl::sendSchemaRequest(const SchemaId& id)
+ {
+-    uint32_t correlator;
+-
+-    {
+-        qpid::sys::Mutex::ScopedLock l(lock);
+-        correlator = nextCorrelator++;
+-    }
++    uint32_t correlator(session.correlator());
  
-     elif kind == "_event":
--      event = Event(self, v2Map=content)
--      if event.classKey is None or event.schema:
--        # schema optional or present
--        context.doEvent(event)
--      else:
--        # schema not optional and not present
--        if context.addPendingEvent(event):
--          self._v2SendSchemaRequest(event.classKey)
-+      for omap in content:
-+        event = Event(self, v2Map=omap)
-+        if event.classKey is None or event.schema:
-+          # schema optional or present
-+          context.doEvent(event)
-+        else:
-+          # schema not optional and not present
-+          if context.addPendingEvent(event):
-+            self._v2SendSchemaRequest(event.classKey)
+     if (capability >= AGENT_CAPABILITY_V2_SCHEMA) {
+         Query query(QUERY_SCHEMA, id);
+diff --git a/qpid/cpp/src/qmf/AgentImpl.h b/qpid/cpp/src/qmf/AgentImpl.h
+index 7fa4f43..09754a3 100644
+--- a/qpid/cpp/src/qmf/AgentImpl.h
++++ b/qpid/cpp/src/qmf/AgentImpl.h
+@@ -99,7 +99,6 @@ namespace qmf {
+         uint32_t capability;
+         qpid::messaging::Sender sender;
+         qpid::types::Variant::Map attributes;
+-        uint32_t nextCorrelator;
+         std::map<uint32_t, boost::shared_ptr<SyncContext> > contextMap;
+         boost::shared_ptr<SchemaCache> schemaCache;
+         mutable std::set<std::string> packageSet;
+diff --git a/qpid/cpp/src/qmf/ConsoleSession.cpp b/qpid/cpp/src/qmf/ConsoleSession.cpp
+index 5df0d83..7b51d80 100644
+--- a/qpid/cpp/src/qmf/ConsoleSession.cpp
++++ b/qpid/cpp/src/qmf/ConsoleSession.cpp
+@@ -68,7 +68,7 @@ Subscription ConsoleSession::subscribe(const string& q, const string& f, const s
+ ConsoleSessionImpl::ConsoleSessionImpl(Connection& c, const string& options) :
+     connection(c), domain("default"), maxAgentAgeMinutes(5), listenOnDirect(true), strictSecurity(false),
+     opened(false), thread(0), threadCanceled(false), lastVisit(0), lastAgePass(0),
+-    connectedBrokerInAgentList(false), schemaCache(new SchemaCache())
++    connectedBrokerInAgentList(false), schemaCache(new SchemaCache()), nextCorrelator(1)
+ {
+     if (!options.empty()) {
+         qpid::messaging::AddressParser parser(options);
+diff --git a/qpid/cpp/src/qmf/ConsoleSessionImpl.h b/qpid/cpp/src/qmf/ConsoleSessionImpl.h
+index 411b3f0..429dfc4 100644
+--- a/qpid/cpp/src/qmf/ConsoleSessionImpl.h
++++ b/qpid/cpp/src/qmf/ConsoleSessionImpl.h
+@@ -90,6 +90,8 @@ namespace qmf {
+         std::string directBase;
+         std::string topicBase;
+         boost::shared_ptr<SchemaCache> schemaCache;
++        qpid::sys::Mutex corrlock;
++        uint32_t nextCorrelator;
  
-     elif kind == "_schema_id":
-       for sid in content:
-@@ -3533,7 +3553,8 @@
-     dp.routing_key = self.getV2RoutingKey()
-     mp = self.broker.amqpSession.message_properties()
-     mp.content_type = "amqp/map"
--    mp.user_id = self.broker.authUser
-+    if self.broker.saslUser:
-+      mp.user_id = self.broker.saslUser
-     mp.correlation_id = str(sequence)
-     mp.app_id = "qmf2"
-     mp.reply_to = self.broker.amqpSession.reply_to("qmf.default.direct", self.broker.v2_direct_queue)
-@@ -3976,6 +3997,9 @@
-   def brokerConnected(self, broker):
-     print "brokerConnected:", broker
+         void enqueueEvent(const ConsoleEvent&);
+         void enqueueEventLH(const ConsoleEvent&);
+@@ -100,6 +102,7 @@ namespace qmf {
+         void handleV1SchemaResponse(qpid::management::Buffer&, uint32_t, const qpid::messaging::Message&);
+         void periodicProcessing(uint64_t);
+         void run();
++        uint32_t correlator() { qpid::sys::Mutex::ScopedLock l(corrlock); return nextCorrelator++; }
  
-+  def brokerConnectionFailed(self, broker):
-+    print "brokerConnectionFailed:", broker
+         friend class AgentImpl;
+     };
+-- 
+1.7.4.4
+
+From 40738a2501f8bfe2f85e1d26790584ce034bd930 Mon Sep 17 00:00:00 2001
+From: Ted Ross <tross at apache.org>
+Date: Tue, 12 Jul 2011 16:11:34 +0000
+Subject: [PATCH 02/14] QPID-3344 - Comparisons of const DataAddr objects are
+ incorrect Applied patch from Zane Bitter
+
+git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1145644 13f79535-47bb-0310-9956-ffa450edef68
+---
+ qpid/cpp/include/qmf/DataAddr.h           |    3 +++
+ qpid/cpp/src/qmf/DataAddr.cpp             |    6 ++++--
+ qpid/cpp/src/qmf/DataAddrImpl.h           |    4 ++--
+ qpid/cpp/src/qpid/store/StorageProvider.h |    2 +-
+ 4 files changed, 10 insertions(+), 5 deletions(-)
+
+diff --git a/qpid/cpp/include/qmf/DataAddr.h b/qpid/cpp/include/qmf/DataAddr.h
+index 63d309c..20c4690 100644
+--- a/qpid/cpp/include/qmf/DataAddr.h
++++ b/qpid/cpp/include/qmf/DataAddr.h
+@@ -51,6 +51,9 @@ namespace qmf {
+         QMF_EXTERN uint32_t getAgentEpoch() const;
+         QMF_EXTERN qpid::types::Variant::Map asMap() const;
+ 
++        QMF_EXTERN bool operator==(const DataAddr&) const;
++        QMF_EXTERN bool operator<(const DataAddr&) const;
 +
-   def brokerDisconnected(self, broker):
-     print "brokerDisconnected:", broker
+ #ifndef SWIG
+     private:
+         friend class qmf::PrivateImplRef<DataAddr>;
+diff --git a/qpid/cpp/src/qmf/DataAddr.cpp b/qpid/cpp/src/qmf/DataAddr.cpp
+index fb51d57..d16e120 100644
+--- a/qpid/cpp/src/qmf/DataAddr.cpp
++++ b/qpid/cpp/src/qmf/DataAddr.cpp
+@@ -36,7 +36,9 @@ DataAddr::~DataAddr() { PI::dtor(*this); }
+ DataAddr& DataAddr::operator=(const DataAddr& s) { return PI::assign(*this, s); }
  
-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)
+ bool DataAddr::operator==(const DataAddr& o) { return *impl == *o.impl; }
++bool DataAddr::operator==(const DataAddr& o) const { return *impl == *o.impl; }
+ bool DataAddr::operator<(const DataAddr& o) { return *impl < *o.impl; }
++bool DataAddr::operator<(const DataAddr& o) const { return *impl < *o.impl; }
+ 
+ DataAddr::DataAddr(const qpid::types::Variant::Map& m) { PI::ctor(*this, new DataAddrImpl(m)); }
+ DataAddr::DataAddr(const string& n, const string& a, uint32_t e) { PI::ctor(*this, new DataAddrImpl(n, a, e)); }
+@@ -45,7 +47,7 @@ const string& DataAddr::getAgentName() const { return impl->getAgentName(); }
+ uint32_t DataAddr::getAgentEpoch() const { return impl->getAgentEpoch(); }
+ Variant::Map DataAddr::asMap() const { return impl->asMap(); }
+ 
+-bool DataAddrImpl::operator==(const DataAddrImpl& other)
++bool DataAddrImpl::operator==(const DataAddrImpl& other) const
+ {
+     return
+         agentName == other.agentName &&
+@@ -54,7 +56,7 @@ bool DataAddrImpl::operator==(const DataAddrImpl& other)
+ }
+ 
+ 
+-bool DataAddrImpl::operator<(const DataAddrImpl& other)
++bool DataAddrImpl::operator<(const DataAddrImpl& other) const
+ {
+     if (agentName < other.agentName) return true;
+     if (agentName > other.agentName) return false;
+diff --git a/qpid/cpp/src/qmf/DataAddrImpl.h b/qpid/cpp/src/qmf/DataAddrImpl.h
+index 3f9cae9..11d512f 100644
+--- a/qpid/cpp/src/qmf/DataAddrImpl.h
++++ b/qpid/cpp/src/qmf/DataAddrImpl.h
+@@ -38,8 +38,8 @@ namespace qmf {
+         //
+         // Methods from API handle
+         //
+-        bool operator==(const DataAddrImpl&);
+-        bool operator<(const DataAddrImpl&);
++        bool operator==(const DataAddrImpl&) const;
++        bool operator<(const DataAddrImpl&) const;
+         DataAddrImpl(const qpid::types::Variant::Map&);
+         DataAddrImpl(const std::string& _name, const std::string& _agentName, uint32_t _agentEpoch=0) :
+             agentName(_agentName), name(_name), agentEpoch(_agentEpoch) {}
+diff --git a/qpid/cpp/src/qpid/store/StorageProvider.h b/qpid/cpp/src/qpid/store/StorageProvider.h
+index bc8d187..d162cc5 100644
+--- a/qpid/cpp/src/qpid/store/StorageProvider.h
++++ b/qpid/cpp/src/qpid/store/StorageProvider.h
+@@ -54,7 +54,7 @@ struct QueueEntry {
+     QueueEntry(uint64_t id, TplStatus tpl = NONE, const std::string& x = "")
+         : queueId(id), tplStatus(tpl), xid(x) {}
+ 
+-    bool operator==(const QueueEntry& rhs) {
++    bool operator==(const QueueEntry& rhs) const {
+         if (queueId != rhs.queueId) return false;
+         if (tplStatus == NONE && rhs.tplStatus == NONE) return true;
+         return xid == rhs.xid;
+-- 
+1.7.4.4
+
+From e2257aece703af5d775b087440958e19702b92a0 Mon Sep 17 00:00:00 2001
+From: Ted Ross <tross at apache.org>
+Date: Fri, 5 Aug 2011 15:24:16 +0000
+Subject: [PATCH 03/14] NO-JIRA - Added missing template file in distribution.
+
+git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1154264 13f79535-47bb-0310-9956-ffa450edef68
+---
+ qpid/cpp/managementgen/Makefile.am |    1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+diff --git a/qpid/cpp/managementgen/Makefile.am b/qpid/cpp/managementgen/Makefile.am
+index e10dd63..4fc5edc 100644
+--- a/qpid/cpp/managementgen/Makefile.am
++++ b/qpid/cpp/managementgen/Makefile.am
+@@ -32,6 +32,7 @@ pkgpyexec_qmfgentmpl_PYTHON = \
+ 	qmfgen/templates/Args.h \
+ 	qmfgen/templates/Class.cpp \
+ 	qmfgen/templates/Class.h \
++	qmfgen/templates/CMakeLists.cmake \
+ 	qmfgen/templates/Event.cpp \
+ 	qmfgen/templates/Event.h \
+ 	qmfgen/templates/Makefile.mk \
+-- 
+1.7.4.4
+
+From f654585bacf244e33cdb5a8a1ece6c15b18ecdc1 Mon Sep 17 00:00:00 2001
+From: Ted Ross <tross at apache.org>
+Date: Mon, 15 Aug 2011 16:47:56 +0000
+Subject: [PATCH 04/14] QPID-3423 - Timing and Performance Improvements in QMF
+ Libraries
+
+git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1157907 13f79535-47bb-0310-9956-ffa450edef68
+---
+ qpid/cpp/include/qmf/AgentSession.h             |    5 +++
+ qpid/cpp/include/qmf/ConsoleSession.h           |    4 ++
+ qpid/cpp/src/qmf/AgentSession.cpp               |   41 ++++++++++++++++------
+ qpid/cpp/src/qmf/ConsoleSession.cpp             |   43 ++++++++++++++++-------
+ qpid/cpp/src/qmf/ConsoleSessionImpl.h           |    1 +
+ qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp |   19 ++++++++--
+ 6 files changed, 86 insertions(+), 27 deletions(-)
+
+diff --git a/qpid/cpp/include/qmf/AgentSession.h b/qpid/cpp/include/qmf/AgentSession.h
+index 1eeb252..5ecfb04 100644
+--- a/qpid/cpp/include/qmf/AgentSession.h
++++ b/qpid/cpp/include/qmf/AgentSession.h
+@@ -71,6 +71,11 @@ namespace qmf {
+          *                                    If False: Listen only on the routable direct address
+          *    strict-security:{True,False}  - If True:  Cooperate with the broker to enforce strict access control to the network
+          *                                  - If False: Operate more flexibly with regard to use of messaging facilities [default]
++         *    max-thread-wait-time:N     - Time (in seconds) the session thread will wait for messages from the network between
++         *                                 periodic background processing passes. [default: 5]
++         *                                 Must not be greater than 'interval'.  Larger numbers will cause fewer wake-ups but will
++         *                                 increase the time it takes to shut down the process.  This setting will not affect the
++         *                                 agent's response time for queries or method invocation.
+          */
+         QMF_EXTERN AgentSession(qpid::messaging::Connection& conn, const std::string& options="");
+ 
+diff --git a/qpid/cpp/include/qmf/ConsoleSession.h b/qpid/cpp/include/qmf/ConsoleSession.h
+index 6008036..5e3a091 100644
+--- a/qpid/cpp/include/qmf/ConsoleSession.h
++++ b/qpid/cpp/include/qmf/ConsoleSession.h
+@@ -61,6 +61,10 @@ namespace qmf {
+          *                                    If False: Listen only on the routable direct address
+          *    strict-security:{True,False}  - If True:  Cooperate with the broker to enforce strict access control to the network
+          *                                  - If False: Operate more flexibly with regard to use of messaging facilities [default]
++         *    max-thread-wait-time:N     - Time (in seconds) the session thread will wait for messages from the network between
++         *                                 periodic background processing passes.
++         *                                 Must not be greater than 60.  Larger numbers will cause fewer wake-ups but will
++         *                                 increase the time it takes to shut down the process. [default: 5]
+          */
+         QMF_EXTERN ConsoleSession(qpid::messaging::Connection& conn, const std::string& options="");
+ 
+diff --git a/qpid/cpp/src/qmf/AgentSession.cpp b/qpid/cpp/src/qmf/AgentSession.cpp
+index 71d3693..a88782d 100644
+--- a/qpid/cpp/src/qmf/AgentSession.cpp
++++ b/qpid/cpp/src/qmf/AgentSession.cpp
+@@ -120,6 +120,7 @@ namespace qmf {
+         bool publicEvents;
+         bool listenOnDirect;
+         bool strictSecurity;
++        uint32_t maxThreadWaitTime;
+         uint64_t schemaUpdateTime;
+         string directBase;
+         string topicBase;
+@@ -185,7 +186,7 @@ AgentSessionImpl::AgentSessionImpl(Connection& c, const string& options) :
+     bootSequence(1), interval(60), lastHeartbeat(0), lastVisit(0), forceHeartbeat(false),
+     externalStorage(false), autoAllowQueries(true), autoAllowMethods(true),
+     maxSubscriptions(64), minSubInterval(3000), subLifetime(300), publicEvents(true),
+-    listenOnDirect(true), strictSecurity(false),
++    listenOnDirect(true), strictSecurity(false), maxThreadWaitTime(5),
+     schemaUpdateTime(uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now())))
+ {
+     //
+@@ -246,7 +247,14 @@ AgentSessionImpl::AgentSessionImpl(Connection& c, const string& options) :
+         iter = optMap.find("strict-security");
+         if (iter != optMap.end())
+             strictSecurity = iter->second.asBool();
++
++        iter = optMap.find("max-thread-wait-time");
++        if (iter != optMap.end())
++            maxThreadWaitTime = iter->second.asUint32();
+     }
++
++    if (maxThreadWaitTime > interval)
++        maxThreadWaitTime = interval;
+ }
+ 
+ 
+@@ -254,6 +262,11 @@ AgentSessionImpl::~AgentSessionImpl()
+ {
+     if (opened)
+         close();
++
++    if (thread) {
++        thread->join();
++        delete thread;
++    }
+ }
+ 
+ 
+@@ -262,6 +275,12 @@ void AgentSessionImpl::open()
+     if (opened)
+         throw QmfException("The session is already open");
+ 
++    // If the thread exists, join and delete it before creating a new one.
++    if (thread) {
++        thread->join();
++        delete thread;
++    }
++
+     const string addrArgs(";{create:never,node:{type:topic}}");
+     const string routableAddr("direct-agent.route." + qpid::types::Uuid(true).str());
+     attributes["_direct_subject"] = routableAddr;
+@@ -304,13 +323,8 @@ void AgentSessionImpl::close()
+     if (!opened)
+         return;
+ 
+-    // Stop and join the receiver thread
++    // Stop the receiver thread.  Don't join it until the destructor is called or open() is called.
+     threadCanceled = true;
+-    thread->join();
+-    delete thread;
+-
+-    // Close the AMQP session
+-    session.close();
+     opened = false;
+ }
+ 
+@@ -320,9 +334,13 @@ bool AgentSessionImpl::nextEvent(AgentEvent& event, Duration timeout)
+     uint64_t milliseconds = timeout.getMilliseconds();
+     qpid::sys::Mutex::ScopedLock l(lock);
+ 
+-    if (eventQueue.empty() && milliseconds > 0)
+-        cond.wait(lock, qpid::sys::AbsTime(qpid::sys::now(),
+-                                           qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC)));
++    if (eventQueue.empty() && milliseconds > 0) {
++        int64_t nsecs(qpid::sys::TIME_INFINITE);
++        if ((uint64_t)(nsecs / 1000000) > milliseconds)
++            nsecs = (int64_t) milliseconds * 1000000;
++        qpid::sys::Duration then(nsecs);
++        cond.wait(lock, qpid::sys::AbsTime(qpid::sys::now(), then));
++    }
+ 
+     if (!eventQueue.empty()) {
+         event = eventQueue.front();
+@@ -1050,7 +1068,7 @@ void AgentSessionImpl::run()
+             periodicProcessing((uint64_t) qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()) / qpid::sys::TIME_SEC);
+ 
+             Receiver rx;
+-            bool valid = session.nextReceiver(rx, Duration::SECOND);
++            bool valid = session.nextReceiver(rx, Duration::SECOND * maxThreadWaitTime);
+             if (threadCanceled)
+                 break;
+             if (valid) {
+@@ -1067,6 +1085,7 @@ void AgentSessionImpl::run()
+         enqueueEvent(AgentEvent(new AgentEventImpl(AGENT_THREAD_FAILED)));
+     }
+ 
++    session.close();
+     QPID_LOG(debug, "AgentSession thread exiting for agent " << agentName);
+ }
+ 
+diff --git a/qpid/cpp/src/qmf/ConsoleSession.cpp b/qpid/cpp/src/qmf/ConsoleSession.cpp
+index 7b51d80..af83595 100644
+--- a/qpid/cpp/src/qmf/ConsoleSession.cpp
++++ b/qpid/cpp/src/qmf/ConsoleSession.cpp
+@@ -66,7 +66,7 @@ Subscription ConsoleSession::subscribe(const string& q, const string& f, const s
+ //========================================================================================
+ 
+ ConsoleSessionImpl::ConsoleSessionImpl(Connection& c, const string& options) :
+-    connection(c), domain("default"), maxAgentAgeMinutes(5), listenOnDirect(true), strictSecurity(false),
++    connection(c), domain("default"), maxAgentAgeMinutes(5), listenOnDirect(true), strictSecurity(false), maxThreadWaitTime(5),
+     opened(false), thread(0), threadCanceled(false), lastVisit(0), lastAgePass(0),
+     connectedBrokerInAgentList(false), schemaCache(new SchemaCache()), nextCorrelator(1)
+ {
+@@ -92,7 +92,14 @@ ConsoleSessionImpl::ConsoleSessionImpl(Connection& c, const string& options) :
+         iter = optMap.find("strict-security");
+         if (iter != optMap.end())
+             strictSecurity = iter->second.asBool();
++
++        iter = optMap.find("max-thread-wait-time");
++        if (iter != optMap.end())
++            maxThreadWaitTime = iter->second.asUint32();
+     }
++
++    if (maxThreadWaitTime > 60)
++        maxThreadWaitTime = 60;
+ }
+ 
+ 
+@@ -100,6 +107,11 @@ ConsoleSessionImpl::~ConsoleSessionImpl()
+ {
+     if (opened)
+         close();
++
++    if (thread) {
++        thread->join();
++        delete thread;
++    }
+ }
+ 
+ 
+@@ -154,6 +166,12 @@ void ConsoleSessionImpl::open()
+     if (opened)
+         throw QmfException("The session is already open");
+ 
++    // If the thread exists, join and delete it before creating a new one.
++    if (thread) {
++        thread->join();
++        delete thread;
++    }
++
+     // Establish messaging addresses
+     directBase = "qmf." + domain + ".direct";
+     topicBase = "qmf." + domain + ".topic";
+@@ -182,14 +200,13 @@ void ConsoleSessionImpl::open()
+ 
+     // Start the receiver thread
+     threadCanceled = false;
++    opened = true;
+     thread = new qpid::sys::Thread(*this);
+ 
+     // Send an agent_locate to direct address 'broker' to identify the connected-broker-agent.
+     sendBrokerLocate();
+     if (agentQuery)
+         sendAgentLocate();
 -
--    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 ********" )
+-    opened = true;
+ }
+ 
+ 
+@@ -198,13 +215,8 @@ void ConsoleSessionImpl::close()
+     if (!opened)
+         throw QmfException("The session is already closed");
+ 
+-    // Stop and join the receiver thread
++    // Stop the receiver thread.  Don't join it until the destructor is called or open() is called.
+     threadCanceled = true;
+-    thread->join();
+-    delete thread;
+-
+-    // Close the AMQP session
+-    session.close();
+     opened = false;
+ }
+ 
+@@ -214,9 +226,13 @@ bool ConsoleSessionImpl::nextEvent(ConsoleEvent& event, Duration timeout)
+     uint64_t milliseconds = timeout.getMilliseconds();
+     qpid::sys::Mutex::ScopedLock l(lock);
+ 
+-    if (eventQueue.empty() && milliseconds > 0)
+-        cond.wait(lock, qpid::sys::AbsTime(qpid::sys::now(),
+-                                           qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC)));
++    if (eventQueue.empty() && milliseconds > 0) {
++        int64_t nsecs(qpid::sys::TIME_INFINITE);
++        if ((uint64_t)(nsecs / 1000000) > milliseconds)
++            nsecs = (int64_t) milliseconds * 1000000;
++        qpid::sys::Duration then(nsecs);
++        cond.wait(lock, qpid::sys::AbsTime(qpid::sys::now(), then));
++    }
+ 
+     if (!eventQueue.empty()) {
+         event = eventQueue.front();
+@@ -596,7 +612,7 @@ void ConsoleSessionImpl::run()
+                                qpid::sys::TIME_SEC);
+ 
+             Receiver rx;
+-            bool valid = session.nextReceiver(rx, Duration::SECOND);
++            bool valid = session.nextReceiver(rx, Duration::SECOND * maxThreadWaitTime);
+             if (threadCanceled)
+                 break;
+             if (valid) {
+@@ -613,6 +629,7 @@ void ConsoleSessionImpl::run()
+         enqueueEvent(ConsoleEvent(new ConsoleEventImpl(CONSOLE_THREAD_FAILED)));
+     }
+ 
++    session.close();
+     QPID_LOG(debug, "ConsoleSession thread exiting");
+ }
+ 
+diff --git a/qpid/cpp/src/qmf/ConsoleSessionImpl.h b/qpid/cpp/src/qmf/ConsoleSessionImpl.h
+index 429dfc4..478d24e 100644
+--- a/qpid/cpp/src/qmf/ConsoleSessionImpl.h
++++ b/qpid/cpp/src/qmf/ConsoleSessionImpl.h
+@@ -76,6 +76,7 @@ namespace qmf {
+         uint32_t maxAgentAgeMinutes;
+         bool listenOnDirect;
+         bool strictSecurity;
++        uint32_t maxThreadWaitTime;
+         Query agentQuery;
+         bool opened;
+         std::queue<ConsoleEvent> eventQueue;
+diff --git a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp
+index 633401e..f183ff8 100644
+--- a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp
++++ b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp
+@@ -1378,13 +1378,26 @@ bool ManagementAgentImpl::ConnectionThread::isSleeping() const
+ 
+ void ManagementAgentImpl::PublishThread::run()
+ {
+-    uint16_t    totalSleep;
++    uint16_t totalSleep;
++    uint16_t sleepTime;
+ 
+     while (!shutdown) {
+         agent.periodicProcessing();
+         totalSleep = 0;
+-        while (totalSleep++ < agent.getInterval() && !shutdown) {
+-            ::sleep(1);
++
++        //
++        // Calculate a sleep time that is no greater than 5 seconds and
++        // no less than 1 second.
++        //
++        sleepTime = agent.getInterval();
++        if (sleepTime > 5)
++            sleepTime = 5;
++        else if (sleepTime == 0)
++            sleepTime = 1;
++
++        while (totalSleep < agent.getInterval() && !shutdown) {
++            ::sleep(sleepTime);
++            totalSleep += sleepTime;
+         }
+     }
+ }
+-- 
+1.7.4.4
+
+From 6ca9eda32ed8c809524b913dd169afa4a35eb58d Mon Sep 17 00:00:00 2001
+From: Ted Ross <ross at localhost.localdomain>
+Date: Mon, 15 Aug 2011 16:25:52 -0400
+Subject: [PATCH 05/14] Fixed EXTERN definitions
+
+---
+ qpid/cpp/include/qpid/framing/FieldTable.h |    4 ++--
+ 1 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/qpid/cpp/include/qpid/framing/FieldTable.h b/qpid/cpp/include/qpid/framing/FieldTable.h
+index e8ec524..bdcef6d 100644
+--- a/qpid/cpp/include/qpid/framing/FieldTable.h
++++ b/qpid/cpp/include/qpid/framing/FieldTable.h
+@@ -65,8 +65,8 @@ class FieldTable
+     QPID_COMMON_EXTERN void decode(Buffer& buffer);
+ 
+     QPID_COMMON_EXTERN int count() const;
+-    QPID_COMMON_EXTERN size_t size() const { return values.size(); }
+-    QPID_COMMON_EXTERN bool empty() { return size() == 0; }
++    QPID_COMMON_INLINE_EXTERN size_t size() const { return values.size(); }
++    QPID_COMMON_INLINE_EXTERN bool empty() { return size() == 0; }
+     QPID_COMMON_EXTERN void set(const std::string& name, const ValuePtr& value);
+     QPID_COMMON_EXTERN ValuePtr get(const std::string& name) const;
+     QPID_COMMON_INLINE_EXTERN bool isSet(const std::string& name) const { return get(name).get() != 0; }
+-- 
+1.7.4.4
+
+From 1f68475caffaa3c36ad921684bbaea35c68ce375 Mon Sep 17 00:00:00 2001
+From: Ted Ross <ross at localhost.localdomain>
+Date: Mon, 15 Aug 2011 17:10:15 -0400
+Subject: [PATCH 06/14] Enable qmf2 for mingw32
+
+---
+ qpid/cpp/src/CMakeLists.txt |    4 ++--
+ 1 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt
+index 80315b9..978d962 100644
+--- a/qpid/cpp/src/CMakeLists.txt
++++ b/qpid/cpp/src/CMakeLists.txt
+@@ -1081,7 +1081,7 @@ install (TARGETS qmf OPTIONAL
+          COMPONENT ${QPID_COMPONENT_QMF})
+ install_pdb (qmf ${QPID_COMPONENT_QMF})
+ 
+-if(NOT WIN32)
++#if(NOT WIN32)
+     set (qmf2_HEADERS
+         ../include/qmf/AgentEvent.h
+         ../include/qmf/Agent.h
+@@ -1156,7 +1156,7 @@ if(NOT WIN32)
+             DESTINATION ${QPID_INSTALL_INCLUDEDIR}/qmf
+             COMPONENT ${QPID_COMPONENT_QMF})
+     install_pdb (qmf2 ${QPID_COMPONENT_QMF})
+-endif (NOT WIN32)
++#endif (NOT WIN32)
+ 
+ set (qmfengine_SOURCES
+      qmf/engine/Agent.cpp
+-- 
+1.7.4.4
+
+From 337286e57705835975acd215aa1990f3597abc6a Mon Sep 17 00:00:00 2001
+From: Ted Ross <ross at localhost.localdomain>
+Date: Mon, 15 Aug 2011 17:36:24 -0400
+Subject: [PATCH 07/14] Fixed externs in AddressParser
+
+---
+ qpid/cpp/src/qpid/messaging/AddressParser.h |   10 +++++-----
+ 1 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/qpid/cpp/src/qpid/messaging/AddressParser.h b/qpid/cpp/src/qpid/messaging/AddressParser.h
+index 1635331..c51200c 100644
+--- a/qpid/cpp/src/qpid/messaging/AddressParser.h
++++ b/qpid/cpp/src/qpid/messaging/AddressParser.h
+@@ -26,13 +26,13 @@
+ namespace qpid {
+ namespace messaging {
+ 
+-class AddressParser
++class QPID_MESSAGING_CLASS_EXTERN AddressParser
+ {
+   public:
+-    AddressParser(const std::string&);
+-    bool parse(Address& address);
+-    bool parseMap(qpid::types::Variant::Map& map);
+-    bool parseList(qpid::types::Variant::List& list);
++    QPID_MESSAGING_EXTERN AddressParser(const std::string&);
++    QPID_MESSAGING_EXTERN bool parse(Address& address);
++    QPID_MESSAGING_EXTERN bool parseMap(qpid::types::Variant::Map& map);
++    QPID_MESSAGING_EXTERN bool parseList(qpid::types::Variant::List& list);
+   private:
+     const std::string& input;
+     std::string::size_type current;
+-- 
+1.7.4.4
+
+From 666f43e30629559d97a71bd737d36ab14d3eeb86 Mon Sep 17 00:00:00 2001
+From: Ted Ross <tross at apache.org>
+Date: Tue, 13 Sep 2011 19:34:38 +0000
+Subject: [PATCH 08/14] QPID-3484 - QMF Main-Loop Integration Applied patch
+ from Darryl Pierce.
+
+git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1170314 13f79535-47bb-0310-9956-ffa450edef68
+---
+ qpid/cpp/bindings/qmf2/examples/cpp/Makefile.am    |    5 +-
+ .../qmf2/examples/cpp/event_driven_list_agents.cpp |  107 ++++++++++++
+ qpid/cpp/include/qmf/AgentSession.h                |    1 +
+ qpid/cpp/include/qmf/ConsoleSession.h              |    1 +
+ qpid/cpp/include/qmf/posix/EventNotifier.h         |   62 +++++++
+ qpid/cpp/src/CMakeLists.txt                        |    5 +
+ qpid/cpp/src/qmf.mk                                |    4 +
+ qpid/cpp/src/qmf/AgentSession.cpp                  |  167 +++++--------------
+ qpid/cpp/src/qmf/AgentSessionImpl.h                |  175 ++++++++++++++++++++
+ qpid/cpp/src/qmf/ConsoleSession.cpp                |   38 ++++-
+ qpid/cpp/src/qmf/ConsoleSessionImpl.h              |   17 ++
+ qpid/cpp/src/qmf/EventNotifierImpl.cpp             |   56 +++++++
+ qpid/cpp/src/qmf/EventNotifierImpl.h               |   48 ++++++
+ qpid/cpp/src/qmf/PosixEventNotifier.cpp            |   63 +++++++
+ qpid/cpp/src/qmf/PosixEventNotifierImpl.cpp        |  108 ++++++++++++
+ qpid/cpp/src/qmf/PosixEventNotifierImpl.h          |   61 +++++++
+ qpid/cpp/src/tests/Qmf2.cpp                        |  104 ++++++++++++-
+ 17 files changed, 890 insertions(+), 132 deletions(-)
+ create mode 100644 qpid/cpp/bindings/qmf2/examples/cpp/event_driven_list_agents.cpp
+ create mode 100644 qpid/cpp/include/qmf/posix/EventNotifier.h
+ create mode 100644 qpid/cpp/src/qmf/AgentSessionImpl.h
+ create mode 100644 qpid/cpp/src/qmf/EventNotifierImpl.cpp
+ create mode 100644 qpid/cpp/src/qmf/EventNotifierImpl.h
+ create mode 100644 qpid/cpp/src/qmf/PosixEventNotifier.cpp
+ create mode 100644 qpid/cpp/src/qmf/PosixEventNotifierImpl.cpp
+ create mode 100644 qpid/cpp/src/qmf/PosixEventNotifierImpl.h
+
+diff --git a/qpid/cpp/bindings/qmf2/examples/cpp/Makefile.am b/qpid/cpp/bindings/qmf2/examples/cpp/Makefile.am
+index 84207d4..062fbd0 100644
+--- a/qpid/cpp/bindings/qmf2/examples/cpp/Makefile.am
++++ b/qpid/cpp/bindings/qmf2/examples/cpp/Makefile.am
+@@ -21,7 +21,7 @@ INCLUDE = -I$(top_srcdir)/include
+ 
+ AM_CPPFLAGS = $(INCLUDE)
+ 
+-noinst_PROGRAMS=agent list_agents print_events
++noinst_PROGRAMS=agent event_driven_list_agents list_agents print_events
+ 
+ agent_SOURCES=agent.cpp
+ agent_LDADD=$(top_builddir)/src/libqmf2.la
+@@ -29,5 +29,8 @@ agent_LDADD=$(top_builddir)/src/libqmf2.la
+ list_agents_SOURCES=list_agents.cpp
+ list_agents_LDADD=$(top_builddir)/src/libqmf2.la
+ 
++event_driven_list_agents_SOURCES=event_driven_list_agents.cpp
++event_driven_list_agents_LDADD=$(top_builddir)/src/libqmf2.la
++
+ print_events_SOURCES=print_events.cpp
+ print_events_LDADD=$(top_builddir)/src/libqmf2.la
+diff --git a/qpid/cpp/bindings/qmf2/examples/cpp/event_driven_list_agents.cpp b/qpid/cpp/bindings/qmf2/examples/cpp/event_driven_list_agents.cpp
+new file mode 100644
+index 0000000..c288aa6
+--- /dev/null
++++ b/qpid/cpp/bindings/qmf2/examples/cpp/event_driven_list_agents.cpp
+@@ -0,0 +1,107 @@
++/*
++ * 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 <sys/select.h>
++#include <time.h>
++
++#include <qpid/messaging/Connection.h>
++#include <qpid/messaging/Duration.h>
++#include <qmf/Agent.h>
++#include <qmf/ConsoleEvent.h>
++#include <qmf/ConsoleSession.h>
++#include <qpid/types/Variant.h>
++#include "qmf/posix/EventNotifier.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("");
++
++    posix::EventNotifier notifier(session);
++
++    int fd(notifier.getHandle());
++    time_t lastUpdate;
++    bool ftl = false;
++
++    time(&lastUpdate);
++
++    while (true) {
++        fd_set rfds;
++        struct timeval tv;
++        int nfds, retval;
++
++        FD_ZERO(&rfds);
++        FD_SET(fd, &rfds);
++        nfds = fd + 1;
++        tv.tv_sec = 10;
++        tv.tv_usec = 0;
++
++        retval = select(nfds, &rfds, NULL, NULL, &tv);
++
++        if (retval > 0 && FD_ISSET(fd, &rfds)) {
++            ConsoleEvent event;
++            while (session.nextEvent(event, Duration::IMMEDIATE)) {
++                string eventType = "";
++                switch(event.getType()) {
++                case CONSOLE_AGENT_ADD:             eventType = "Added"; break;
++                case CONSOLE_AGENT_DEL:             eventType = "Deleted"; break;
++                case CONSOLE_AGENT_RESTART:         eventType = "Restarted"; break;
++                case CONSOLE_AGENT_SCHEMA_UPDATE:   eventType = "Schema Updated"; break;
++                case CONSOLE_AGENT_SCHEMA_RESPONSE: eventType = "Schema Response"; break;
++                case CONSOLE_EVENT:                 eventType = "Event"; break;
++                case CONSOLE_QUERY_RESPONSE:        eventType = "Query Response"; break;
++                case CONSOLE_METHOD_RESPONSE:       eventType = "Method Response"; break;
++                case CONSOLE_EXCEPTION:             eventType = "Exception"; break;
++                case CONSOLE_SUBSCRIBE_ADD:         eventType = "Subscription Added"; break;
++                case CONSOLE_SUBSCRIBE_UPDATE:      eventType = "Subscription Updated"; break;
++                case CONSOLE_SUBSCRIBE_DEL:         eventType = "Subscription Deleted" ; break;
++                case CONSOLE_THREAD_FAILED:         eventType = "Thread Failure"; break;
++                default:                            eventType = "[UNDEFINED]";
++                }
++                cout << "Agent " << eventType << ": " << event.getAgent().getName() << endl;
++            }
++        } else {
++            cout << "No message received within waiting period." << endl;
++        }
++    }
++}
++
+diff --git a/qpid/cpp/include/qmf/AgentSession.h b/qpid/cpp/include/qmf/AgentSession.h
+index 5ecfb04..589d364 100644
+--- a/qpid/cpp/include/qmf/AgentSession.h
++++ b/qpid/cpp/include/qmf/AgentSession.h
+@@ -188,6 +188,7 @@ namespace qmf {
+ #ifndef SWIG
+     private:
+         friend class qmf::PrivateImplRef<AgentSession>;
++        friend struct AgentSessionImplAccess;
+ #endif
+     };
+ 
+diff --git a/qpid/cpp/include/qmf/ConsoleSession.h b/qpid/cpp/include/qmf/ConsoleSession.h
+index 5e3a091..022485c 100644
+--- a/qpid/cpp/include/qmf/ConsoleSession.h
++++ b/qpid/cpp/include/qmf/ConsoleSession.h
+@@ -123,6 +123,7 @@ namespace qmf {
+ #ifndef SWIG
+     private:
+         friend class qmf::PrivateImplRef<ConsoleSession>;
++        friend struct ConsoleSessionImplAccess;
+ #endif
+     };
+ 
+diff --git a/qpid/cpp/include/qmf/posix/EventNotifier.h b/qpid/cpp/include/qmf/posix/EventNotifier.h
+new file mode 100644
+index 0000000..91817cc
+--- /dev/null
++++ b/qpid/cpp/include/qmf/posix/EventNotifier.h
+@@ -0,0 +1,62 @@
++#ifndef __QMF_POSIX_EVENT_NOTIFIER_H
++#define __QMF_POSIX_EVENT_NOTIFIER_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 "qmf/AgentSession.h"
++#include "qmf/ConsoleSession.h"
++
++namespace qmf {
++
++    class PosixEventNotifierImpl;
++    class PosixEventNotifierImplAccess;
++
++namespace posix {
++
++#ifndef SWIG
++  template <class> class PrivateImplRef;
++#endif
++
++  class QMF_CLASS_EXTERN EventNotifier : public qmf::Handle<qmf::PosixEventNotifierImpl> {
++  public:
++      QMF_EXTERN EventNotifier(::qmf::AgentSession& agentSession);
++      QMF_EXTERN EventNotifier(::qmf::ConsoleSession& consoleSession);
++      QMF_EXTERN EventNotifier(const EventNotifier& that);
++
++      QMF_EXTERN ~EventNotifier();
++
++      QMF_EXTERN EventNotifier& operator=(const EventNotifier& that);
++
++      QMF_EXTERN int getHandle() const;
++
++#ifndef SWIG
++  private:
++      friend class qmf::PrivateImplRef<EventNotifier>;
++      friend struct qmf::PosixEventNotifierImplAccess;
++#endif
++
++  };
++
++}}
++
++#endif
++
+diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt
+index 978d962..2986a90 100644
+--- a/qpid/cpp/src/CMakeLists.txt
++++ b/qpid/cpp/src/CMakeLists.txt
+@@ -1093,6 +1093,7 @@ install_pdb (qmf ${QPID_COMPONENT_QMF})
+         ../include/qmf/exceptions.h
+         ../include/qmf/Handle.h
+         ../include/qmf/ImportExport.h
++        ../include/qmf/posix/EventNotifier.h
+         ../include/qmf/Query.h
+         ../include/qmf/Schema.h
+         ../include/qmf/SchemaId.h
+@@ -1122,6 +1123,10 @@ install_pdb (qmf ${QPID_COMPONENT_QMF})
+         qmf/DataAddrImpl.h
+         qmf/Data.cpp
+         qmf/DataImpl.h
++        qmf/EventNotifierImpl.h
++        qmf/EventNotifierImpl.cpp
++        qmf/PosixEventNotifier.cpp
++        qmf/PosixEventNotifierImpl.cpp
+         qmf/exceptions.cpp
+         qmf/Expression.cpp
+         qmf/Expression.h
+diff --git a/qpid/cpp/src/qmf.mk b/qpid/cpp/src/qmf.mk
+index f3462f1..4da8470 100644
+--- a/qpid/cpp/src/qmf.mk
++++ b/qpid/cpp/src/qmf.mk
+@@ -43,6 +43,7 @@ QMF2_API =				\
+   ../include/qmf/ConsoleSession.h	\
+   ../include/qmf/DataAddr.h		\
+   ../include/qmf/Data.h			\
++  ../include/qmf/posix/EventNotifier.h	\
+   ../include/qmf/exceptions.h		\
+   ../include/qmf/Handle.h		\
+   ../include/qmf/ImportExport.h		\
+@@ -104,6 +105,9 @@ libqmf2_la_SOURCES = 		\
+   qmf/DataAddrImpl.h		\
+   qmf/Data.cpp			\
+   qmf/DataImpl.h		\
++  qmf/EventNotifierImpl.cpp	\
++  qmf/PosixEventNotifier.cpp	\
++  qmf/PosixEventNotifierImpl.cpp \
+   qmf/exceptions.cpp		\
+   qmf/Expression.cpp		\
+   qmf/Expression.h		\
+diff --git a/qpid/cpp/src/qmf/AgentSession.cpp b/qpid/cpp/src/qmf/AgentSession.cpp
+index a88782d..d92b2a4 100644
+--- a/qpid/cpp/src/qmf/AgentSession.cpp
++++ b/qpid/cpp/src/qmf/AgentSession.cpp
+@@ -19,134 +19,7 @@
+  *
+  */
+ 
+-#include "qpid/RefCounted.h"
+-#include "qmf/PrivateImplRef.h"
+-#include "qmf/exceptions.h"
+-#include "qmf/AgentSession.h"
+-#include "qmf/AgentEventImpl.h"
+-#include "qmf/SchemaIdImpl.h"
+-#include "qmf/SchemaImpl.h"
+-#include "qmf/DataAddrImpl.h"
+-#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"
+-#include "qpid/sys/Runnable.h"
+-#include "qpid/log/Statement.h"
+-#include "qpid/messaging/Connection.h"
+-#include "qpid/messaging/Session.h"
+-#include "qpid/messaging/Receiver.h"
+-#include "qpid/messaging/Sender.h"
+-#include "qpid/messaging/Message.h"
+-#include "qpid/messaging/AddressParser.h"
+-#include "qpid/management/Buffer.h"
+-#include <queue>
+-#include <map>
+-#include <set>
+-#include <iostream>
+-#include <memory>
+-
+-using namespace std;
+-using namespace qpid::messaging;
+-using namespace qmf;
+-using qpid::types::Variant;
+-
+-namespace qmf {
+-    class AgentSessionImpl : public virtual qpid::RefCounted, public qpid::sys::Runnable {
+-    public:
+-        ~AgentSessionImpl();
+-
+-        //
+-        // Methods from API handle
+-        //
+-        AgentSessionImpl(Connection& c, const string& o);
+-        void setDomain(const string& d) { checkOpen(); domain = d; }
+-        void setVendor(const string& v) { checkOpen(); attributes["_vendor"] = v; }
+-        void setProduct(const string& p) { checkOpen(); attributes["_product"] = p; }
+-        void setInstance(const string& i) { checkOpen(); attributes["_instance"] = i; }
+-        void setAttribute(const string& k, const qpid::types::Variant& v) { checkOpen(); attributes[k] = v; }
+-        const string& getName() const { return agentName; }
+-        void open();
+-        void close();
+-        bool nextEvent(AgentEvent& e, Duration t);
+-        int pendingEvents() const;
+-
+-        void registerSchema(Schema& s);
+-        DataAddr addData(Data& d, const string& n, bool persist);
+-        void delData(const DataAddr&);
+-
+-        void authAccept(AgentEvent& e);
+-        void authReject(AgentEvent& e, const string& m);
+-        void raiseException(AgentEvent& e, const string& s);
+-        void raiseException(AgentEvent& e, const Data& d);
+-        void response(AgentEvent& e, const Data& d);
+-        void complete(AgentEvent& e);
+-        void methodSuccess(AgentEvent& e);
+-        void raiseEvent(const Data& d);
+-        void raiseEvent(const Data& d, int s);
+-
+-    private:
+-        typedef map<DataAddr, Data, DataAddrCompare> DataIndex;
+-        typedef map<SchemaId, Schema, SchemaIdCompare> SchemaMap;
+-
+-        mutable qpid::sys::Mutex lock;
+-        qpid::sys::Condition cond;
+-        Connection connection;
+-        Session session;
+-        Sender directSender;
+-        Sender topicSender;
+-        string domain;
+-        Variant::Map attributes;
+-        Variant::Map options;
+-        string agentName;
+-        bool opened;
+-        queue<AgentEvent> eventQueue;
+-        qpid::sys::Thread* thread;
+-        bool threadCanceled;
+-        uint32_t bootSequence;
+-        uint32_t interval;
+-        uint64_t lastHeartbeat;
+-        uint64_t lastVisit;
+-        bool forceHeartbeat;
+-        bool externalStorage;
+-        bool autoAllowQueries;
+-        bool autoAllowMethods;
+-        uint32_t maxSubscriptions;
+-        uint32_t minSubInterval;
+-        uint32_t subLifetime;
+-        bool publicEvents;
+-        bool listenOnDirect;
+-        bool strictSecurity;
+-        uint32_t maxThreadWaitTime;
+-        uint64_t schemaUpdateTime;
+-        string directBase;
+-        string topicBase;
+-
+-        SchemaMap schemata;
+-        DataIndex globalIndex;
+-        map<SchemaId, DataIndex, SchemaIdCompareNoHash> schemaIndex;
+-
+-        void checkOpen();
+-        void setAgentName();
+-        void enqueueEvent(const AgentEvent&);
+-        void handleLocateRequest(const Variant::List& content, const Message& msg);
+-        void handleMethodRequest(const Variant::Map& content, const Message& msg);
+-        void handleQueryRequest(const Variant::Map& content, const Message& msg);
+-        void handleSchemaRequest(AgentEvent&);
+-        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();
+-    };
+-}
+-
+-typedef qmf::PrivateImplRef<AgentSession> PI;
++#include "qmf/AgentSessionImpl.h"
+ 
+ AgentSession::AgentSession(AgentSessionImpl* impl) { PI::ctor(*this, impl); }
+ AgentSession::AgentSession(const AgentSession& s) : qmf::Handle<AgentSessionImpl>() { PI::copy(*this, s); }
+@@ -345,6 +218,8 @@ bool AgentSessionImpl::nextEvent(AgentEvent& event, Duration timeout)
+     if (!eventQueue.empty()) {
+         event = eventQueue.front();
+         eventQueue.pop();
++        if (eventQueue.empty())
++            alertEventNotifierLH(false);
+         return true;
+     }
+ 
+@@ -359,6 +234,19 @@ int AgentSessionImpl::pendingEvents() const
+ }
+ 
+ 
++void AgentSessionImpl::setEventNotifier(EventNotifierImpl* notifier)
++{
++    qpid::sys::Mutex::ScopedLock l(lock);
++    eventNotifier = notifier;
++}
++
++EventNotifierImpl* AgentSessionImpl::getEventNotifier() const
++{
++    qpid::sys::Mutex::ScopedLock l(lock);
++    return eventNotifier;
++}
++
++
+ void AgentSessionImpl::registerSchema(Schema& schema)
+ {
+     if (!schema.isFinalized())
+@@ -614,8 +502,10 @@ void AgentSessionImpl::enqueueEvent(const AgentEvent& event)
+     qpid::sys::Mutex::ScopedLock l(lock);
+     bool notify = eventQueue.empty();
+     eventQueue.push(event);
+-    if (notify)
++    if (notify) {
+         cond.notify();
++        alertEventNotifierLH(true);
++    }
+ }
+ 
+ 
+@@ -1059,6 +949,13 @@ void AgentSessionImpl::periodicProcessing(uint64_t seconds)
+ }
+ 
+ 
++void AgentSessionImpl::alertEventNotifierLH(bool readable)
++{
++    if (eventNotifier)
++        eventNotifier->setReadable(readable);
++}
++
++
+ void AgentSessionImpl::run()
+ {
+     QPID_LOG(debug, "AgentSession thread started for agent " << agentName);
+@@ -1089,3 +986,15 @@ void AgentSessionImpl::run()
+     QPID_LOG(debug, "AgentSession thread exiting for agent " << agentName);
+ }
+ 
++
++AgentSessionImpl& AgentSessionImplAccess::get(AgentSession& session)
++{
++    return *session.impl;
++}
++
++
++const AgentSessionImpl& AgentSessionImplAccess::get(const AgentSession& session)
++{
++    return *session.impl;
++}
++
+diff --git a/qpid/cpp/src/qmf/AgentSessionImpl.h b/qpid/cpp/src/qmf/AgentSessionImpl.h
+new file mode 100644
+index 0000000..cf1b1d7
+--- /dev/null
++++ b/qpid/cpp/src/qmf/AgentSessionImpl.h
+@@ -0,0 +1,175 @@
++#ifndef __QMF_AGENT_SESSION_IMPL_H
++#define __QMF_AGENT_SESSION_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/PrivateImplRef.h"
++#include "qmf/exceptions.h"
++#include "qmf/AgentSession.h"
++#include "qmf/AgentEventImpl.h"
++#include "qmf/EventNotifierImpl.h"
++#include "qpid/messaging/Connection.h"
++#include "qpid/sys/Runnable.h"
++#include "qpid/sys/Mutex.h"
++#include "qpid/sys/Condition.h"
++#include "qpid/sys/Thread.h"
++#include "qpid/sys/Runnable.h"
++#include "qpid/log/Statement.h"
++#include "qpid/messaging/Connection.h"
++#include "qpid/messaging/Session.h"
++#include "qpid/messaging/Receiver.h"
++#include "qpid/messaging/Sender.h"
++#include "qpid/messaging/Message.h"
++#include "qpid/messaging/AddressParser.h"
++#include "qpid/management/Buffer.h"
++#include "qpid/RefCounted.h"
++#include "qmf/PrivateImplRef.h"
++#include "qmf/AgentSession.h"
++#include "qmf/exceptions.h"
++#include "qmf/AgentSession.h"
++#include "qmf/SchemaIdImpl.h"
++#include "qmf/SchemaImpl.h"
++#include "qmf/DataAddrImpl.h"
++#include "qmf/DataImpl.h"
++#include "qmf/QueryImpl.h"
++#include "qmf/agentCapability.h"
++#include "qmf/constants.h"
++
++#include <queue>
++#include <map>
++#include <iostream>
++#include <memory>
++
++using namespace std;
++using namespace qpid::messaging;
++using namespace qmf;
++using qpid::types::Variant;
++using namespace boost;
++
++typedef qmf::PrivateImplRef<AgentSession> PI;
++
++namespace qmf {
++    class AgentSessionImpl : public virtual qpid::RefCounted, public qpid::sys::Runnable {
++    public:
++        ~AgentSessionImpl();
++
++        //
++        // Methods from API handle
++        //
++        AgentSessionImpl(Connection& c, const string& o);
++        void setDomain(const string& d) { checkOpen(); domain = d; }
++        void setVendor(const string& v) { checkOpen(); attributes["_vendor"] = v; }
++        void setProduct(const string& p) { checkOpen(); attributes["_product"] = p; }
++        void setInstance(const string& i) { checkOpen(); attributes["_instance"] = i; }
++        void setAttribute(const string& k, const qpid::types::Variant& v) { checkOpen(); attributes[k] = v; }
++        const string& getName() const { return agentName; }
++        void open();
++        void close();
++        bool nextEvent(AgentEvent& e, Duration t);
++        int pendingEvents() const;
++
++        void setEventNotifier(EventNotifierImpl* eventNotifier);
++        EventNotifierImpl* getEventNotifier() const;
++
++        void registerSchema(Schema& s);
++        DataAddr addData(Data& d, const string& n, bool persist);
++        void delData(const DataAddr&);
++
++        void authAccept(AgentEvent& e);
++        void authReject(AgentEvent& e, const string& m);
++        void raiseException(AgentEvent& e, const string& s);
++        void raiseException(AgentEvent& e, const Data& d);
++        void response(AgentEvent& e, const Data& d);
++        void complete(AgentEvent& e);
++        void methodSuccess(AgentEvent& e);
++        void raiseEvent(const Data& d);
++        void raiseEvent(const Data& d, int s);
++
++    private:
++        typedef map<DataAddr, Data, DataAddrCompare> DataIndex;
++        typedef map<SchemaId, Schema, SchemaIdCompare> SchemaMap;
++
++        mutable qpid::sys::Mutex lock;
++        qpid::sys::Condition cond;
++        Connection connection;
++        Session session;
++        Sender directSender;
++        Sender topicSender;
++        string domain;
++        Variant::Map attributes;
++        Variant::Map options;
++        string agentName;
++        bool opened;
++        queue<AgentEvent> eventQueue;
++        EventNotifierImpl* eventNotifier;
++        qpid::sys::Thread* thread;
++        bool threadCanceled;
++        uint32_t bootSequence;
++        uint32_t interval;
++        uint64_t lastHeartbeat;
++        uint64_t lastVisit;
++        bool forceHeartbeat;
++        bool externalStorage;
++        bool autoAllowQueries;
++        bool autoAllowMethods;
++        uint32_t maxSubscriptions;
++        uint32_t minSubInterval;
++        uint32_t subLifetime;
++        bool publicEvents;
++        bool listenOnDirect;
++        bool strictSecurity;
++        uint32_t maxThreadWaitTime;
++        uint64_t schemaUpdateTime;
++        string directBase;
++        string topicBase;
++
++        SchemaMap schemata;
++        DataIndex globalIndex;
++        map<SchemaId, DataIndex, SchemaIdCompareNoHash> schemaIndex;
++
++        void checkOpen();
++        void setAgentName();
++        void enqueueEvent(const AgentEvent&);
++        void alertEventNotifierLH(bool readable);
++        void handleLocateRequest(const Variant::List& content, const Message& msg);
++        void handleMethodRequest(const Variant::Map& content, const Message& msg);
++        void handleQueryRequest(const Variant::Map& content, const Message& msg);
++        void handleSchemaRequest(AgentEvent&);
++        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();
++    };
++
++    struct AgentSessionImplAccess {
++        static AgentSessionImpl& get(AgentSession& session);
++        static const AgentSessionImpl& get(const AgentSession& session);
++    };
++}
++
++
++#endif
++
+diff --git a/qpid/cpp/src/qmf/ConsoleSession.cpp b/qpid/cpp/src/qmf/ConsoleSession.cpp
+index af83595..d084b8a 100644
+--- a/qpid/cpp/src/qmf/ConsoleSession.cpp
++++ b/qpid/cpp/src/qmf/ConsoleSession.cpp
+@@ -237,6 +237,8 @@ bool ConsoleSessionImpl::nextEvent(ConsoleEvent& event, Duration timeout)
+     if (!eventQueue.empty()) {
+         event = eventQueue.front();
+         eventQueue.pop();
++        if (eventQueue.empty())
++            alertEventNotifierLH(false);
+         return true;
+     }
+ 
+@@ -251,6 +253,20 @@ int ConsoleSessionImpl::pendingEvents() const
+ }
+ 
+ 
++void ConsoleSessionImpl::setEventNotifier(EventNotifierImpl* notifier)
++{
++    qpid::sys::Mutex::ScopedLock l(lock);
++    this->eventNotifier = notifier;
++}
++
++
++EventNotifierImpl* ConsoleSessionImpl::getEventNotifier() const
++{
++    qpid::sys::Mutex::ScopedLock l(lock);
++    return this->eventNotifier;
++}
++
++
+ uint32_t ConsoleSessionImpl::getAgentCount() const
+ {
+     qpid::sys::Mutex::ScopedLock l(lock);
+@@ -292,8 +308,10 @@ void ConsoleSessionImpl::enqueueEventLH(const ConsoleEvent& event)
+ {
+     bool notify = eventQueue.empty();
+     eventQueue.push(event);
+-    if (notify)
++    if (notify) {
+         cond.notify();
++        alertEventNotifierLH(true);
++    }
+ }
+ 
+ 
+@@ -602,6 +620,13 @@ void ConsoleSessionImpl::periodicProcessing(uint64_t seconds)
+ }
+ 
+ 
++void ConsoleSessionImpl::alertEventNotifierLH(bool readable)
++{
++    if (eventNotifier)
++        eventNotifier->setReadable(readable);
++}
++
++
+ void ConsoleSessionImpl::run()
+ {
+     QPID_LOG(debug, "ConsoleSession thread started");
+@@ -633,3 +658,14 @@ void ConsoleSessionImpl::run()
+     QPID_LOG(debug, "ConsoleSession thread exiting");
+ }
+ 
++
++ConsoleSessionImpl& ConsoleSessionImplAccess::get(ConsoleSession& session)
++{
++  return *session.impl;
++}
++
++
++const ConsoleSessionImpl& ConsoleSessionImplAccess::get(const ConsoleSession& session)
++{
++  return *session.impl;
++}
+diff --git a/qpid/cpp/src/qmf/ConsoleSessionImpl.h b/qpid/cpp/src/qmf/ConsoleSessionImpl.h
+index 478d24e..660fc9b 100644
+--- a/qpid/cpp/src/qmf/ConsoleSessionImpl.h
++++ b/qpid/cpp/src/qmf/ConsoleSessionImpl.h
+@@ -27,6 +27,7 @@
+ #include "qmf/SchemaId.h"
+ #include "qmf/Schema.h"
+ #include "qmf/ConsoleEventImpl.h"
++#include "qmf/EventNotifierImpl.h"
+ #include "qmf/SchemaCache.h"
+ #include "qmf/Query.h"
+ #include "qpid/sys/Mutex.h"
+@@ -41,9 +42,14 @@
+ #include "qpid/messaging/Address.h"
+ #include "qpid/management/Buffer.h"
+ #include "qpid/types/Variant.h"
++
++#include <boost/shared_ptr.hpp>
+ #include <map>
+ #include <queue>
+ 
++using namespace boost;
++using namespace std;
++
+ namespace qmf {
+     class ConsoleSessionImpl : public virtual qpid::RefCounted, public qpid::sys::Runnable {
+     public:
+@@ -59,6 +65,10 @@ namespace qmf {
+         void close();
+         bool nextEvent(ConsoleEvent& e, qpid::messaging::Duration t);
+         int pendingEvents() const;
++
++        void setEventNotifier(EventNotifierImpl* notifier);
++        EventNotifierImpl* getEventNotifier() const;
++
+         uint32_t getAgentCount() const;
+         Agent getAgent(uint32_t i) const;
+         Agent getConnectedBrokerAgent() const { return connectedBrokerAgent; }
+@@ -80,6 +90,7 @@ namespace qmf {
+         Query agentQuery;
+         bool opened;
+         std::queue<ConsoleEvent> eventQueue;
++        EventNotifierImpl* eventNotifier;
+         qpid::sys::Thread* thread;
+         bool threadCanceled;
+         uint64_t lastVisit;
+@@ -102,11 +113,17 @@ namespace qmf {
+         void handleAgentUpdate(const std::string&, const qpid::types::Variant::Map&, const qpid::messaging::Message&);
+         void handleV1SchemaResponse(qpid::management::Buffer&, uint32_t, const qpid::messaging::Message&);
+         void periodicProcessing(uint64_t);
++        void alertEventNotifierLH(bool readable);
+         void run();
+         uint32_t correlator() { qpid::sys::Mutex::ScopedLock l(corrlock); return nextCorrelator++; }
+ 
+         friend class AgentImpl;
+     };
++
++    struct ConsoleSessionImplAccess {
++        static ConsoleSessionImpl& get(ConsoleSession& session);
++        static const ConsoleSessionImpl& get(const ConsoleSession& session);
++    };
+ }
+ 
+ #endif
+diff --git a/qpid/cpp/src/qmf/EventNotifierImpl.cpp b/qpid/cpp/src/qmf/EventNotifierImpl.cpp
+new file mode 100644
+index 0000000..20114aa
+--- /dev/null
++++ b/qpid/cpp/src/qmf/EventNotifierImpl.cpp
+@@ -0,0 +1,56 @@
++/*
++ * 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/EventNotifierImpl.h"
++#include "qmf/AgentSessionImpl.h"
++#include "qmf/ConsoleSessionImpl.h"
++
++EventNotifierImpl::EventNotifierImpl(AgentSession& agentSession)
++    : readable(false), agent(agentSession)
++{
++    AgentSessionImplAccess::get(agent).setEventNotifier(this);
++}
++
++
++EventNotifierImpl::EventNotifierImpl(ConsoleSession& consoleSession)
++    : readable(false), console(consoleSession)
++{
++    ConsoleSessionImplAccess::get(console).setEventNotifier(this);
++}
++
++
++EventNotifierImpl::~EventNotifierImpl()
++{
++    if (agent.isValid())
++        AgentSessionImplAccess::get(agent).setEventNotifier(NULL);
++    if (console.isValid())
++        ConsoleSessionImplAccess::get(console).setEventNotifier(NULL);
++}
++
++void EventNotifierImpl::setReadable(bool readable)
++{
++    update(readable);
++    this->readable = readable;
++}
++
++
++bool EventNotifierImpl::isReadable() const
++{
++    return this->readable;
++}
+diff --git a/qpid/cpp/src/qmf/EventNotifierImpl.h b/qpid/cpp/src/qmf/EventNotifierImpl.h
+new file mode 100644
+index 0000000..d85f997
+--- /dev/null
++++ b/qpid/cpp/src/qmf/EventNotifierImpl.h
+@@ -0,0 +1,48 @@
++#ifndef __QMF_EVENT_NOTIFIER_IMPL_H
++#define __QMF_EVENT_NOTIFIER_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 "qmf/AgentSession.h"
++#include "qmf/ConsoleSession.h"
++
++namespace qmf
++{
++    class EventNotifierImpl  {
++    private:
++        bool readable;
++        AgentSession agent;
++        ConsoleSession console;
++
++    public:
++        EventNotifierImpl(AgentSession& agentSession);
++        EventNotifierImpl(ConsoleSession& consoleSession);
++        virtual ~EventNotifierImpl();
++
++        void setReadable(bool readable);
++        bool isReadable() const;
++
++    protected:
++        virtual void update(bool readable) = 0;
++    };
++}
++
++#endif
++
+diff --git a/qpid/cpp/src/qmf/PosixEventNotifier.cpp b/qpid/cpp/src/qmf/PosixEventNotifier.cpp
+new file mode 100644
+index 0000000..b5c7121
+--- /dev/null
++++ b/qpid/cpp/src/qmf/PosixEventNotifier.cpp
+@@ -0,0 +1,63 @@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++
++#include "qmf/posix/EventNotifier.h"
++#include "qmf/PosixEventNotifierImpl.h"
++#include "qmf/PrivateImplRef.h"
++
++using namespace qmf;
++using namespace std;
++
++typedef qmf::PrivateImplRef<posix::EventNotifier> PI;
++
++posix::EventNotifier::EventNotifier(AgentSession& agentSession)
++{
++    PI::ctor(*this, new PosixEventNotifierImpl(agentSession));
++}
++
++
++posix::EventNotifier::EventNotifier(ConsoleSession& consoleSession)
++{
++    PI::ctor(*this, new PosixEventNotifierImpl(consoleSession));
++}
++
++
++posix::EventNotifier::EventNotifier(const posix::EventNotifier& that)
++    : Handle<PosixEventNotifierImpl>()
++{
++    PI::copy(*this, that);
++}
++
++
++posix::EventNotifier::~EventNotifier()
++{
++    PI::dtor(*this);
++}
++
++posix::EventNotifier& posix::EventNotifier::operator=(const posix::EventNotifier& that)
++{
++    return PI::assign(*this, that);
++}
++
++
++int posix::EventNotifier::getHandle() const
++{
++    return impl->getHandle();
++}
++
+diff --git a/qpid/cpp/src/qmf/PosixEventNotifierImpl.cpp b/qpid/cpp/src/qmf/PosixEventNotifierImpl.cpp
+new file mode 100644
+index 0000000..abc9cad
+--- /dev/null
++++ b/qpid/cpp/src/qmf/PosixEventNotifierImpl.cpp
+@@ -0,0 +1,108 @@
++/*
++ * 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 "PosixEventNotifierImpl.h"
++
++#include <fcntl.h>
++#include <unistd.h>
++
++#define BUFFER_SIZE 10
++
++using namespace qmf;
++
++PosixEventNotifierImpl::PosixEventNotifierImpl(AgentSession& agentSession)
++    : EventNotifierImpl(agentSession)
++{
++    openHandle();
++}
++
++
++PosixEventNotifierImpl::PosixEventNotifierImpl(ConsoleSession& consoleSession)
++    : EventNotifierImpl(consoleSession)
++{
++    openHandle();
++}
++
++
++PosixEventNotifierImpl::~PosixEventNotifierImpl()
++{
++    closeHandle();
++}
++
++
++void PosixEventNotifierImpl::update(bool readable)
++{
++    char buffer[BUFFER_SIZE];
++
++    if(readable && !this->isReadable()) {
++        (void) ::write(myHandle, "1", 1);
++    }
++    else if(!readable && this->isReadable()) {
++        (void) ::read(yourHandle, buffer, BUFFER_SIZE);
++    }
++}
++
++
++void PosixEventNotifierImpl::openHandle()
++{
++    int pair[2];
++
++    if(::pipe(pair) == -1)
++        throw QmfException("Unable to open event notifier handle.");
++
++    yourHandle = pair[0];
++    myHandle = pair[1];
++
++    int flags;
++
++    flags = ::fcntl(yourHandle, F_GETFL);
++    if((::fcntl(yourHandle, F_SETFL, flags | O_NONBLOCK)) == -1)
++        throw QmfException("Unable to make remote handle non-blocking.");
++
++    flags = ::fcntl(myHandle, F_GETFL);
++    if((::fcntl(myHandle, F_SETFL, flags | O_NONBLOCK)) == -1)
++        throw QmfException("Unable to make local handle non-blocking.");
++}
++
++
++void PosixEventNotifierImpl::closeHandle()
++{
++    if(myHandle > 0) {
++        ::close(myHandle);
++        myHandle = -1;
++    }
++
++    if(yourHandle > 0) {
++        ::close(yourHandle);
++        yourHandle = -1;
++    }
++}
++
++
++PosixEventNotifierImpl& PosixEventNotifierImplAccess::get(posix::EventNotifier& notifier)
++{
++    return *notifier.impl;
++}
++
++
++const PosixEventNotifierImpl& PosixEventNotifierImplAccess::get(const posix::EventNotifier& notifier)
++{
++    return *notifier.impl;
++}
++
+diff --git a/qpid/cpp/src/qmf/PosixEventNotifierImpl.h b/qpid/cpp/src/qmf/PosixEventNotifierImpl.h
+new file mode 100644
+index 0000000..c8a7446
+--- /dev/null
++++ b/qpid/cpp/src/qmf/PosixEventNotifierImpl.h
+@@ -0,0 +1,61 @@
++#ifndef __QMF_POSIX_EVENT_NOTIFIER_IMPL_H
++#define __QMF_POSIX_EVENT_NOTIFIER_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 "qmf/posix/EventNotifier.h"
++#include "qmf/EventNotifierImpl.h"
++#include "qpid/RefCounted.h"
++
++namespace qmf
++{
++    class AgentSession;
++    class ConsoleSession;
++
++    class PosixEventNotifierImpl : public EventNotifierImpl, public virtual qpid::RefCounted
++    {
++    public:
++        PosixEventNotifierImpl(AgentSession& agentSession);
++        PosixEventNotifierImpl(ConsoleSession& consoleSession);
++        virtual ~PosixEventNotifierImpl();
++
++        int getHandle() const { return yourHandle; }
++
++    private:
++        int myHandle;
++        int yourHandle;
++
++        void openHandle();
++        void closeHandle();
++
++    protected:
++        void update(bool readable);
++    };
++
++    struct PosixEventNotifierImplAccess
++    {
++        static PosixEventNotifierImpl& get(posix::EventNotifier& notifier);
++        static const PosixEventNotifierImpl& get(const posix::EventNotifier& notifier);
++    };
++
++}
++
++#endif
++
+diff --git a/qpid/cpp/src/tests/Qmf2.cpp b/qpid/cpp/src/tests/Qmf2.cpp
+index 66c774a..bc263d5 100644
+--- a/qpid/cpp/src/tests/Qmf2.cpp
++++ b/qpid/cpp/src/tests/Qmf2.cpp
+@@ -23,12 +23,36 @@
+ #include "qmf/QueryImpl.h"
+ #include "qmf/SchemaImpl.h"
+ #include "qmf/exceptions.h"
+-
++#include "qpid/messaging/Connection.h"
++#include "qmf/PosixEventNotifierImpl.h"
++#include "qmf/AgentSession.h"
++#include "qmf/AgentSessionImpl.h"
++#include "qmf/ConsoleSession.h"
++#include "qmf/ConsoleSessionImpl.h"
+ #include "unit_test.h"
+ 
++using namespace std;
+ using namespace qpid::types;
++using namespace qpid::messaging;
+ using namespace qmf;
+ 
++bool isReadable(int fd)
++{
++    fd_set rfds;
++    struct timeval tv;
++    int nfds, result;
++
++    FD_ZERO(&rfds);
++    FD_SET(fd, &rfds);
++    nfds = fd + 1;
++    tv.tv_sec = 0;
++    tv.tv_usec = 0;
++
++    result = select(nfds, &rfds, NULL, NULL, &tv);
++
++    return result > 0;
++}
++
+ namespace qpid {
+ namespace tests {
+ 
+@@ -315,6 +339,84 @@ QPID_AUTO_TEST_CASE(testSchema)
+     BOOST_CHECK_THROW(method.getArgument(3), QmfException);
+ }
+ 
++QPID_AUTO_TEST_CASE(testAgentSessionEventListener)
++{
++    Connection connection("localhost");
++    AgentSession session(connection, "");
++    posix::EventNotifier notifier(session);
++
++    AgentSessionImpl& sessionImpl = AgentSessionImplAccess::get(session);
++            
++    BOOST_CHECK(sessionImpl.getEventNotifier() != 0);
++}
++
++QPID_AUTO_TEST_CASE(testConsoleSessionEventListener)
++{
++    Connection connection("localhost");
++    ConsoleSession session(connection, "");
++    posix::EventNotifier notifier(session);
++
++    ConsoleSessionImpl& sessionImpl = ConsoleSessionImplAccess::get(session);
++
++    BOOST_CHECK(sessionImpl.getEventNotifier() != 0);
++}
++
++QPID_AUTO_TEST_CASE(testGetHandle)
++{
++    Connection connection("localhost");
++    ConsoleSession session(connection, "");
++    posix::EventNotifier notifier(session);
++
++    BOOST_CHECK(notifier.getHandle() > 0);
++}
++
++QPID_AUTO_TEST_CASE(testSetReadableToFalse)
++{
++    Connection connection("localhost");
++    ConsoleSession session(connection, "");
++    posix::EventNotifier notifier(session);
++    PosixEventNotifierImplAccess::get(notifier).setReadable(false);
++
++    bool readable(isReadable(notifier.getHandle()));
++    BOOST_CHECK(!readable);
++}
++
++QPID_AUTO_TEST_CASE(testSetReadable)
++{
++    Connection connection("localhost");
++    ConsoleSession session(connection, "");
++    posix::EventNotifier notifier(session);
++    PosixEventNotifierImplAccess::get(notifier).setReadable(true);
++
++    bool readable(isReadable(notifier.getHandle()));
++    BOOST_CHECK(readable);
++}
++
++QPID_AUTO_TEST_CASE(testSetReadableMultiple)
++{
++    Connection connection("localhost");
++    ConsoleSession session(connection, "");
++    posix::EventNotifier notifier(session);
++    for (int i = 0; i < 15; i++)
++        PosixEventNotifierImplAccess::get(notifier).setReadable(true);
++    PosixEventNotifierImplAccess::get(notifier).setReadable(false);
++
++    bool readable(isReadable(notifier.getHandle()));
++    BOOST_CHECK(!readable);
++}
++
++QPID_AUTO_TEST_CASE(testDeleteNotifier)
++{
++    Connection connection("localhost");
++    ConsoleSession session(connection, "");
++    ConsoleSessionImpl& sessionImpl = ConsoleSessionImplAccess::get(session);
++    {
++        posix::EventNotifier notifier(session);
++        BOOST_CHECK(sessionImpl.getEventNotifier() != 0);
++    }
++    BOOST_CHECK(sessionImpl.getEventNotifier() == 0);
++}
++
+ QPID_AUTO_TEST_SUITE_END()
+ 
+ }} // namespace qpid::tests
+-- 
+1.7.4.4
+
+From 0918bc94f925d413d8021a855cf566f6a55c654c Mon Sep 17 00:00:00 2001
+From: Ted Ross <tross at apache.org>
+Date: Wed, 14 Sep 2011 21:41:11 +0000
+Subject: [PATCH 09/14] QPID-3484 - Fixed handling of unused return values to
+ prevent compiler warnings.
+
+git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1170860 13f79535-47bb-0310-9956-ffa450edef68
+---
+ qpid/cpp/src/qmf/PosixEventNotifierImpl.cpp |    8 ++++++--
+ 1 files changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/qpid/cpp/src/qmf/PosixEventNotifierImpl.cpp b/qpid/cpp/src/qmf/PosixEventNotifierImpl.cpp
+index abc9cad..011dbcc 100644
+--- a/qpid/cpp/src/qmf/PosixEventNotifierImpl.cpp
++++ b/qpid/cpp/src/qmf/PosixEventNotifierImpl.cpp
+@@ -18,9 +18,11 @@
+  */
+ 
+ #include "PosixEventNotifierImpl.h"
++#include "qpid/log/Statement.h"
+ 
+ #include <fcntl.h>
+ #include <unistd.h>
++#include <errno.h>
+ 
+ #define BUFFER_SIZE 10
+ 
+@@ -51,10 +53,12 @@ void PosixEventNotifierImpl::update(bool readable)
+     char buffer[BUFFER_SIZE];
+ 
+     if(readable && !this->isReadable()) {
+-        (void) ::write(myHandle, "1", 1);
++        if (::write(myHandle, "1", 1) == -1)
++            QPID_LOG(error, "PosixEventNotifierImpl::update write failed: " << errno);
+     }
+     else if(!readable && this->isReadable()) {
+-        (void) ::read(yourHandle, buffer, BUFFER_SIZE);
++        if (::read(yourHandle, buffer, BUFFER_SIZE) == -1)
++            QPID_LOG(error, "PosixEventNotifierImpl::update read failed: " << errno);
+     }
+ }
+ 
+-- 
+1.7.4.4
+
+From c1fe836092355bf45c7118517e49d9307a12c7c8 Mon Sep 17 00:00:00 2001
+From: Ted Ross <tross at apache.org>
+Date: Fri, 16 Sep 2011 14:34:39 +0000
+Subject: [PATCH 10/14] QPID-3484 - Added missing constructor for
+ EventNotifier, fixed initialization bug.
+
+git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1171592 13f79535-47bb-0310-9956-ffa450edef68
+---
+ qpid/cpp/include/qmf/posix/EventNotifier.h |    1 +
+ qpid/cpp/src/qmf/AgentSession.cpp          |   16 ++++++++++++++--
+ qpid/cpp/src/qmf/AgentSessionImpl.h        |    1 +
+ qpid/cpp/src/qmf/ConsoleSession.cpp        |   20 ++++++++++++++++----
+ qpid/cpp/src/qmf/ConsoleSessionImpl.h      |    1 +
+ qpid/cpp/src/qmf/PosixEventNotifier.cpp    |    2 ++
+ 6 files changed, 35 insertions(+), 6 deletions(-)
+
+diff --git a/qpid/cpp/include/qmf/posix/EventNotifier.h b/qpid/cpp/include/qmf/posix/EventNotifier.h
+index 91817cc..ebc1cb5 100644
+--- a/qpid/cpp/include/qmf/posix/EventNotifier.h
++++ b/qpid/cpp/include/qmf/posix/EventNotifier.h
+@@ -38,6 +38,7 @@ namespace posix {
+ 
+   class QMF_CLASS_EXTERN EventNotifier : public qmf::Handle<qmf::PosixEventNotifierImpl> {
+   public:
++      QMF_EXTERN EventNotifier(PosixEventNotifierImpl* impl = 0);
+       QMF_EXTERN EventNotifier(::qmf::AgentSession& agentSession);
+       QMF_EXTERN EventNotifier(::qmf::ConsoleSession& consoleSession);
+       QMF_EXTERN EventNotifier(const EventNotifier& that);
+diff --git a/qpid/cpp/src/qmf/AgentSession.cpp b/qpid/cpp/src/qmf/AgentSession.cpp
+index d92b2a4..251c25f 100644
+--- a/qpid/cpp/src/qmf/AgentSession.cpp
++++ b/qpid/cpp/src/qmf/AgentSession.cpp
+@@ -55,7 +55,7 @@ void AgentSession::raiseEvent(const Data& d, int s) { impl->raiseEvent(d, s); }
+ //========================================================================================
+ 
+ AgentSessionImpl::AgentSessionImpl(Connection& c, const string& options) :
+-    connection(c), domain("default"), opened(false), thread(0), threadCanceled(false),
++    connection(c), domain("default"), opened(false), eventNotifier(0), 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),
+@@ -191,7 +191,7 @@ void AgentSessionImpl::open()
+ }
+ 
+ 
+-void AgentSessionImpl::close()
++void AgentSessionImpl::closeAsync()
+ {
+     if (!opened)
+         return;
+@@ -202,6 +202,18 @@ void AgentSessionImpl::close()
+ }
+ 
+ 
++void AgentSessionImpl::close()
++{
++    closeAsync();
++
++    if (thread) {
++        thread->join();
++        delete thread;
++        thread = 0;
++    }
++}
++
++
+ bool AgentSessionImpl::nextEvent(AgentEvent& event, Duration timeout)
+ {
+     uint64_t milliseconds = timeout.getMilliseconds();
+diff --git a/qpid/cpp/src/qmf/AgentSessionImpl.h b/qpid/cpp/src/qmf/AgentSessionImpl.h
+index cf1b1d7..9039a59 100644
+--- a/qpid/cpp/src/qmf/AgentSessionImpl.h
++++ b/qpid/cpp/src/qmf/AgentSessionImpl.h
+@@ -84,6 +84,7 @@ namespace qmf {
+         void setAttribute(const string& k, const qpid::types::Variant& v) { checkOpen(); attributes[k] = v; }
+         const string& getName() const { return agentName; }
+         void open();
++        void closeAsync();
+         void close();
+         bool nextEvent(AgentEvent& e, Duration t);
+         int pendingEvents() const;
+diff --git a/qpid/cpp/src/qmf/ConsoleSession.cpp b/qpid/cpp/src/qmf/ConsoleSession.cpp
+index d084b8a..2dfc894 100644
+--- a/qpid/cpp/src/qmf/ConsoleSession.cpp
++++ b/qpid/cpp/src/qmf/ConsoleSession.cpp
+@@ -67,7 +67,7 @@ Subscription ConsoleSession::subscribe(const string& q, const string& f, const s
+ 
+ ConsoleSessionImpl::ConsoleSessionImpl(Connection& c, const string& options) :
+     connection(c), domain("default"), maxAgentAgeMinutes(5), listenOnDirect(true), strictSecurity(false), maxThreadWaitTime(5),
+-    opened(false), thread(0), threadCanceled(false), lastVisit(0), lastAgePass(0),
++    opened(false), eventNotifier(0), thread(0), threadCanceled(false), lastVisit(0), lastAgePass(0),
+     connectedBrokerInAgentList(false), schemaCache(new SchemaCache()), nextCorrelator(1)
+ {
+     if (!options.empty()) {
+@@ -210,7 +210,7 @@ void ConsoleSessionImpl::open()
+ }
+ 
+ 
+-void ConsoleSessionImpl::close()
++void ConsoleSessionImpl::closeAsync()
+ {
+     if (!opened)
+         throw QmfException("The session is already closed");
+@@ -221,6 +221,18 @@ void ConsoleSessionImpl::close()
+ }
+ 
+ 
++void ConsoleSessionImpl::close()
++{
++    closeAsync();
++
++    if (thread) {
++        thread->join();
++        delete thread;
++        thread = 0;
++    }
++}
++
++
+ bool ConsoleSessionImpl::nextEvent(ConsoleEvent& event, Duration timeout)
+ {
+     uint64_t milliseconds = timeout.getMilliseconds();
+@@ -256,14 +268,14 @@ int ConsoleSessionImpl::pendingEvents() const
+ void ConsoleSessionImpl::setEventNotifier(EventNotifierImpl* notifier)
+ {
+     qpid::sys::Mutex::ScopedLock l(lock);
+-    this->eventNotifier = notifier;
++    eventNotifier = notifier;
+ }
+ 
+ 
+ EventNotifierImpl* ConsoleSessionImpl::getEventNotifier() const
+ {
+     qpid::sys::Mutex::ScopedLock l(lock);
+-    return this->eventNotifier;
++    return eventNotifier;
+ }
+ 
+ 
+diff --git a/qpid/cpp/src/qmf/ConsoleSessionImpl.h b/qpid/cpp/src/qmf/ConsoleSessionImpl.h
+index 660fc9b..2f1f631 100644
+--- a/qpid/cpp/src/qmf/ConsoleSessionImpl.h
++++ b/qpid/cpp/src/qmf/ConsoleSessionImpl.h
+@@ -62,6 +62,7 @@ namespace qmf {
+         void setDomain(const std::string& d) { domain = d; }
+         void setAgentFilter(const std::string& f);
+         void open();
++        void closeAsync();
+         void close();
+         bool nextEvent(ConsoleEvent& e, qpid::messaging::Duration t);
+         int pendingEvents() const;
+diff --git a/qpid/cpp/src/qmf/PosixEventNotifier.cpp b/qpid/cpp/src/qmf/PosixEventNotifier.cpp
+index b5c7121..a364cc1 100644
+--- a/qpid/cpp/src/qmf/PosixEventNotifier.cpp
++++ b/qpid/cpp/src/qmf/PosixEventNotifier.cpp
+@@ -26,6 +26,8 @@ using namespace std;
+ 
+ typedef qmf::PrivateImplRef<posix::EventNotifier> PI;
+ 
++posix::EventNotifier::EventNotifier(PosixEventNotifierImpl* impl) { PI::ctor(*this, impl); }
++
+ posix::EventNotifier::EventNotifier(AgentSession& agentSession)
+ {
+     PI::ctor(*this, new PosixEventNotifierImpl(agentSession));
+-- 
+1.7.4.4
+
+From e5dda70d9a87b570a7386071762860d9a5eae107 Mon Sep 17 00:00:00 2001
+From: Ted Ross <tross at redhat.com>
+Date: Wed, 14 Sep 2011 14:59:18 -0400
+Subject: [PATCH 11/14] unused-result.patch
+
+---
+ qpid/cpp/src/qmf/engine/ResilientConnection.cpp |    4 ++--
+ qpid/cpp/src/qpid/broker/Daemon.cpp             |   10 +++++-----
+ qpid/cpp/src/qpid/sys/posix/LockFile.cpp        |    2 +-
+ qpid/cpp/src/tests/BrokerMgmtAgent.cpp          |    2 +-
+ qpid/cpp/src/tests/ForkedBroker.cpp             |    2 +-
+ 5 files changed, 10 insertions(+), 10 deletions(-)
+
+diff --git a/qpid/cpp/src/qmf/engine/ResilientConnection.cpp b/qpid/cpp/src/qmf/engine/ResilientConnection.cpp
+index 41dd9ff..851193c 100644
+--- a/qpid/cpp/src/qmf/engine/ResilientConnection.cpp
++++ b/qpid/cpp/src/qmf/engine/ResilientConnection.cpp
+@@ -334,7 +334,7 @@ void ResilientConnectionImpl::notify()
+ {
+     if (notifyFd != -1)
+     {
+-        (void) ::write(notifyFd, ".", 1);
++        if (::write(notifyFd, ".", 1)) {}
+     }
+ }
+ 
+@@ -431,7 +431,7 @@ void ResilientConnectionImpl::EnqueueEvent(ResilientConnectionEvent::EventKind k
+ 
+     if (notifyFd != -1)
+     {
+-        (void) ::write(notifyFd, ".", 1);
++        if (::write(notifyFd, ".", 1)) {}
+     }
+ }
+ 
+diff --git a/qpid/cpp/src/qpid/broker/Daemon.cpp b/qpid/cpp/src/qpid/broker/Daemon.cpp
+index c36538b..281345b 100644
+--- a/qpid/cpp/src/qpid/broker/Daemon.cpp
++++ b/qpid/cpp/src/qpid/broker/Daemon.cpp
+@@ -93,13 +93,13 @@ void Daemon::fork()
+         catch (const exception& e) {
+             QPID_LOG(critical, "Unexpected error: " << e.what());
+             uint16_t port = 0;
+-            (void) write(pipeFds[1], &port, sizeof(uint16_t));
++            if (write(pipeFds[1], &port, sizeof(uint16_t))) {};
+ 
+             std::string pipeFailureMessage = e.what();
+-            (void) write ( pipeFds[1], 
+-                    pipeFailureMessage.c_str(), 
+-                    strlen(pipeFailureMessage.c_str())
+-                  );
++            if (write(pipeFds[1], 
++                      pipeFailureMessage.c_str(), 
++                      strlen(pipeFailureMessage.c_str())
++                      )) {};
+         }
+     }
+     else {                      // Parent
+diff --git a/qpid/cpp/src/qpid/sys/posix/LockFile.cpp b/qpid/cpp/src/qpid/sys/posix/LockFile.cpp
+index f5a6c29..c1f1c37 100755
+--- a/qpid/cpp/src/qpid/sys/posix/LockFile.cpp
++++ b/qpid/cpp/src/qpid/sys/posix/LockFile.cpp
+@@ -58,7 +58,7 @@ LockFile::~LockFile() {
+     if (impl) {
+         int f = impl->fd;
+         if (f >= 0) {
+-            (void) ::lockf(f, F_ULOCK, 0); // Suppress warnings about ignoring return value.
++            if(::lockf(f, F_ULOCK, 0)) {} // Suppress warnings about ignoring return value.
+             ::close(f);
+             impl->fd = -1;
+         }
+diff --git a/qpid/cpp/src/tests/BrokerMgmtAgent.cpp b/qpid/cpp/src/tests/BrokerMgmtAgent.cpp
+index 1d5289d..02aa87f 100644
+--- a/qpid/cpp/src/tests/BrokerMgmtAgent.cpp
++++ b/qpid/cpp/src/tests/BrokerMgmtAgent.cpp
+@@ -604,7 +604,7 @@ namespace qpid {
+                 std::stringstream key;
+                 key << "testobj-" << i;
+                 TestManageable *tm = new TestManageable(agent, key.str());
+-                (void) tm->GetManagementObject()->writePropertiesSize();
++                if (tm->GetManagementObject()->writePropertiesSize()) {}
+                 agent->addObject(tm->GetManagementObject(), key.str());
+                 tmv.push_back(tm);
+             }
+diff --git a/qpid/cpp/src/tests/ForkedBroker.cpp b/qpid/cpp/src/tests/ForkedBroker.cpp
+index 10674b5..de1b42d 100644
+--- a/qpid/cpp/src/tests/ForkedBroker.cpp
++++ b/qpid/cpp/src/tests/ForkedBroker.cpp
+@@ -68,7 +68,7 @@ ForkedBroker::~ForkedBroker() {
+     }
+     if (!dataDir.empty())
+     {
+-        (void) ::system(("rm -rf "+dataDir).c_str());
++        if(::system(("rm -rf "+dataDir).c_str())) {}
+     }
+ }
+ 
+-- 
+1.7.4.4
+
+From caec0216a72bf1c6bd8094b03337f0181f5ba07d Mon Sep 17 00:00:00 2001
+From: Ted Ross <tross at redhat.com>
+Date: Tue, 20 Sep 2011 11:09:10 -0400
+Subject: [PATCH 12/14] In CMake build, only build the posix event notifier
+ (qmf2) for linux, not Windows.
+
+---
+ qpid/cpp/src/CMakeLists.txt |   16 +++++++++++++---
+ 1 files changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt
+index 2986a90..0be42b8 100644
+--- a/qpid/cpp/src/CMakeLists.txt
++++ b/qpid/cpp/src/CMakeLists.txt
+@@ -1093,7 +1093,6 @@ install_pdb (qmf ${QPID_COMPONENT_QMF})
+         ../include/qmf/exceptions.h
+         ../include/qmf/Handle.h
+         ../include/qmf/ImportExport.h
+-        ../include/qmf/posix/EventNotifier.h
+         ../include/qmf/Query.h
+         ../include/qmf/Schema.h
+         ../include/qmf/SchemaId.h
+@@ -1125,8 +1124,6 @@ install_pdb (qmf ${QPID_COMPONENT_QMF})
+         qmf/DataImpl.h
+         qmf/EventNotifierImpl.h
+         qmf/EventNotifierImpl.cpp
+-        qmf/PosixEventNotifier.cpp
+-        qmf/PosixEventNotifierImpl.cpp
+         qmf/exceptions.cpp
+         qmf/Expression.cpp
+         qmf/Expression.h
+@@ -1149,6 +1146,19 @@ install_pdb (qmf ${QPID_COMPONENT_QMF})
+         qmf/SubscriptionImpl.h
+         )
+ 
++if(NOT WIN32)
++    set (qmf2_HEADERS
++        ${qmf2_HEADERS}
++        ../include/qmf/posix/EventNotifier.h
++        )
++
++    set (qmf2_SOURCES
++        ${qmf2_SOURCES}
++        qmf/PosixEventNotifier.cpp
++        qmf/PosixEventNotifierImpl.cpp
++        )
++endif (NOT WIN32)
++
+     add_msvc_version (qmf2 library dll)
+     add_library (qmf2 SHARED ${qmf2_SOURCES})
+     target_link_libraries (qmf2 qpidmessaging qpidtypes qpidclient qpidcommon)
+-- 
+1.7.4.4
+
+From 258294f134d42cf131e2bcbcb5d77482664efece Mon Sep 17 00:00:00 2001
+From: Nuno Santos <nsantos at apache.org>
+Date: Thu, 8 Sep 2011 20:45:33 +0000
+Subject: [PATCH 13/14] QPID-3437: qpid-config address option confusing in
+ help
+
+git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1166897 13f79535-47bb-0310-9956-ffa450edef68
+(cherry picked from commit 3ee5b3a8b77426987bff6b65f147b27a99b027c3)
+---
+ qpid/tools/src/py/qpid-config |    7 +------
+ 1 files changed, 1 insertions(+), 6 deletions(-)
+
+diff --git a/qpid/tools/src/py/qpid-config b/qpid/tools/src/py/qpid-config
+index b6eb055..4ed1ea7 100755
+--- a/qpid/tools/src/py/qpid-config
++++ b/qpid/tools/src/py/qpid-config
+@@ -39,11 +39,6 @@ Usage:  qpid-config [OPTIONS]
+         qpid-config [OPTIONS] unbind <exchange-name> <queue-name> [binding-key]"""
+ 
+ description = """
+-ADDRESS syntax:
 -
--    _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"]]])
+-      [username/password@] hostname [:<port>]
+-      [username/password@] ip-address [:<port>]
 -
--    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",
+ Examples:
+ 
+ $ qpid-config add queue q
+@@ -159,7 +154,7 @@ def OptionsAndArguments(argv):
+     group1 = OptionGroup(parser, "General Options")
+     group1.add_option("-t", "--timeout", action="store", type="int", default=10, metavar="<secs>", help="Maximum time to wait for broker connection (in seconds)")
+     group1.add_option("-b", "--bindings", action="store_true", help="Show bindings in queue or exchange list")
+-    group1.add_option("-a", "--broker-addr", action="store", type="string", default="localhost:5672", metavar="<address>", help="Address of qpidd broker")
++    group1.add_option("-a", "--broker-addr", action="store", type="string", default="localhost:5672", metavar="<address>", help="Address of qpidd broker with syntax: [username/password@] hostname | ip-address [:<port>]")
+     group1.add_option("--sasl-mechanism", action="store", type="string", metavar="<mech>", help="SASL mechanism for authentication (e.g. EXTERNAL, ANONYMOUS, PLAIN, CRAM-MD, DIGEST-MD5, GSSAPI). SASL automatically picks the most secure available mechanism - use this option to override.")
+     parser.add_option_group(group1)
+ 
+-- 
+1.7.4.4
+
+From 0b178e6d01d6a3c67fa55086394cf53132909cce Mon Sep 17 00:00:00 2001
+From: Nuno Santos <nsantos at apache.org>
+Date: Thu, 8 Sep 2011 20:27:49 +0000
+Subject: [PATCH 14/14] make 'qpid-config queues/exchanges
+ <queue/exchange_name>' return proper error code, for
+ scripting purposes
+
+git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1166888 13f79535-47bb-0310-9956-ffa450edef68
+(cherry picked from commit 7d5bce20c5ca6bc3869cbe2f6a73f1968c7a5abb)
+---
+ qpid/tools/src/py/qpid-config |   18 ++++++++++++++++--
+ 1 files changed, 16 insertions(+), 2 deletions(-)
+
+diff --git a/qpid/tools/src/py/qpid-config b/qpid/tools/src/py/qpid-config
+index 4ed1ea7..4af3c84 100755
+--- a/qpid/tools/src/py/qpid-config
++++ b/qpid/tools/src/py/qpid-config
+@@ -97,6 +97,7 @@ class Config:
+         self._flowStopSize      = None
+         self._flowResumeSize    = None
+         self._extra_arguments   = []
++        self._returnCode        = 0
+ 
+ config = Config()
+ 
+@@ -354,9 +355,16 @@ class BrokerManager:
+         caption1 = "Type      "
+         caption2 = "Exchange Name"
+         maxNameLen = len(caption2)
++        found = False
+         for ex in exchanges:
+             if self.match(ex.name, filter):
+                 if len(ex.name) > maxNameLen:  maxNameLen = len(ex.name)
++                found = True
++        if not found:
++            global config
++            config._returnCode = 1
++            return
++
+         print "%s%-*s  Attributes" % (caption1, maxNameLen, caption2)
+         line = ""
+         for i in range(((maxNameLen + len(caption1)) / 5) + 5):
+@@ -393,12 +401,18 @@ class BrokerManager:
+ 
+     def QueueList(self, filter):
+         queues = self.qmf.getObjects(_class="queue", _agent=self.brokerAgent)
+-
+         caption = "Queue Name"
+         maxNameLen = len(caption)
++        found = False
+         for q in queues:
+             if self.match(q.name, filter):
+                 if len(q.name) > maxNameLen:  maxNameLen = len(q.name)
++                found = True
++        if not found:
++            global config
++            config._returnCode = 1
++            return
++
+         print "%-*s  Attributes" % (maxNameLen, caption)
+         line = ""
+         for i in range((maxNameLen / 5) + 5):
+@@ -670,7 +684,7 @@ def main(argv=None):
+                 print "Failed: %s: %s" % (e.__class__.__name__, e)
+                 return 1
+ 
+-    return 0
++    return config._returnCode
+ 
+ if __name__ == "__main__":
+         sys.exit(main())
+-- 
+1.7.4.4
+
diff --git a/python-qpid.spec b/python-qpid.spec
index db2591f..8d5b174 100644
--- a/python-qpid.spec
+++ b/python-qpid.spec
@@ -2,7 +2,7 @@
 %{!?python_version: %global python_version %(%{__python} -c "from distutils.sysconfig import get_python_version; print get_python_version()")}
 
 Name:           python-qpid
-Version:        0.10
+Version:        0.12
 Release:        1%{?dist}
 Summary:        Python client library for AMQP
 
@@ -51,6 +51,9 @@ rm -rf $RPM_BUILD_ROOT
 %endif
 
 %changelog
+* Tue Sep 20 2011 Nuno Santos <nsantos at redhat.com> - 0.12-1
+- Rebased to sync with upstream's official 0.12 release
+
 * Mon May  2 2011 Nuno Santos <nsantos at redhat.com> - 0.10-1
 - Rebased to sync with upstream's official 0.10 release
 
diff --git a/sources b/sources
index fb6a399..f423eab 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-92de312e82bdbf6c57a4033bd7764026  qpid-python-0.10.tar.gz
+392a25e68672bdbc2e6b40b6eaa976cc  qpid-python-0.12.tar.gz


More information about the scm-commits mailing list