[asterisk-scf-commits] asterisk-scf/release/ice-util-cpp.git branch "master" updated.

Commits to the Asterisk SCF project code repositories asterisk-scf-commits at lists.digium.com
Wed Jan 19 09:57:51 CST 2011


branch "master" has been updated
       via  19f9d661714335ba777a5c5898503c41a27ff446 (commit)
       via  13814ce199236dbbe5345f948bc5e2b151358070 (commit)
       via  fc7cecaba535aa94217df488cfbb5c9af9c6139a (commit)
       via  7ad9eb283116047b1aaafaced3116e67e01180cb (commit)
       via  4920b90d3935aa8b395111d4faa425769ac82611 (commit)
       via  e8ba04f58ce8cf49545cbea0a7d180ff0513ef3e (commit)
       via  a0161edf7b9144b720285b8828ee8a361773afd7 (commit)
       via  045352598cf6735a681ea3b060edd86d18ed2972 (commit)
       via  5ce5347b967452a5ac2b3bde85d35cb9b065bf76 (commit)
      from  281707ff4e318882812f1a6eae8fc248dc988042 (commit)

Summary of changes:
 AmiCollector/CMakeLists.txt                        |   11 ++
 AmiCollector/include/AsteriskSCF/AmiCollector.h    |  126 ++++++++++++++++++
 .../include/AsteriskSCF/ResponseCollector.h        |  135 ++++++++++++++++++++
 .../src/AmiCollector.cpp                           |   14 +--
 AmiCollector/test/CMakeLists.txt                   |   13 ++
 AmiCollector/test/IceIntegration-test.cpp          |  121 ++++++++++++++++++
 AmiCollector/test/ResponseCollector-test.cpp       |  134 +++++++++++++++++++
 AmiCollector/test/TestAmiCollector.h               |   85 ++++++++++++
 .../SmartProxy.cpp => AmiCollector/test/test.cpp   |   15 +--
 CMakeLists.txt                                     |    1 +
 10 files changed, 629 insertions(+), 26 deletions(-)
 create mode 100644 AmiCollector/CMakeLists.txt
 create mode 100644 AmiCollector/include/AsteriskSCF/AmiCollector.h
 create mode 100644 AmiCollector/include/AsteriskSCF/ResponseCollector.h
 copy SmartProxy/src/SmartProxy.cpp => AmiCollector/src/AmiCollector.cpp (73%)
 create mode 100644 AmiCollector/test/CMakeLists.txt
 create mode 100644 AmiCollector/test/IceIntegration-test.cpp
 create mode 100644 AmiCollector/test/ResponseCollector-test.cpp
 create mode 100644 AmiCollector/test/TestAmiCollector.h
 copy SmartProxy/src/SmartProxy.cpp => AmiCollector/test/test.cpp (73%)


- Log -----------------------------------------------------------------
commit 19f9d661714335ba777a5c5898503c41a27ff446
Author: David M. Lee <dlee at digium.com>
Date:   Wed Jan 19 09:56:21 2011 -0600

    Made ctor protected; explained the seperate init().
    
    See CR-ASTSCF-40.

diff --git a/AmiCollector/include/AsteriskSCF/ResponseCollector.h b/AmiCollector/include/AsteriskSCF/ResponseCollector.h
index 44e7b6c..de2e011 100644
--- a/AmiCollector/include/AsteriskSCF/ResponseCollector.h
+++ b/AmiCollector/include/AsteriskSCF/ResponseCollector.h
@@ -45,14 +45,6 @@ public:
     typedef T InvocationParamType;
     typedef ResponseCollector<T> ResponseCollectorT;
 
-    /**
-     * Constructor.  The collector is no good until it's been initialized.
-     */
-    ResponseCollector() :
-        mNumResponses(0)
-    {
-    }
-
     virtual ~ResponseCollector() {}
 
     /**
@@ -87,6 +79,17 @@ public:
     }
 
 protected:
+    /**
+     * Constructor.  The collector is no good until it's been initialized.
+     * We cannot initialize the collector in the constructor, because it may
+     * call the processCompletion() virtual function, which is a no-no from a
+     * ctor.
+     */
+    ResponseCollector() :
+        mNumResponses(0)
+    {
+    }
+
     /** Called to retrieve the results for an invocation */
     virtual void processInvocation(InvocationParamType param) = 0;
     /** Called after all the responses or exceptions have been accounted for. */

commit 13814ce199236dbbe5345f948bc5e2b151358070
Author: David M. Lee <dlee at digium.com>
Date:   Wed Jan 12 17:35:46 2011 -0600

    Fail test if exception is not passed through.
    
    See CR-ASTSCF-40.

diff --git a/AmiCollector/test/ResponseCollector-test.cpp b/AmiCollector/test/ResponseCollector-test.cpp
index 63f7403..af94552 100644
--- a/AmiCollector/test/ResponseCollector-test.cpp
+++ b/AmiCollector/test/ResponseCollector-test.cpp
@@ -76,7 +76,11 @@ BOOST_AUTO_TEST_CASE(test_one)
     BOOST_CHECK_EQUAL(0, uut.results);
     BOOST_CHECK_EQUAL(false, uut.complete);
 
-    try { uut.invoke(true); } catch (...) { /* ignore */ }
+    try
+    {
+        uut.invoke(true);
+        BOOST_FAIL("Expected exception to pass through");
+    } catch (const TestException&) { /* ignore */ }
 
     BOOST_CHECK_EQUAL(1, uut.results);
     BOOST_CHECK_EQUAL(true, uut.complete);

commit fc7cecaba535aa94217df488cfbb5c9af9c6139a
Author: David M. Lee <dlee at digium.com>
Date:   Fri Jan 7 15:45:35 2011 -0600

    Fixed AmiCollector for include file changes.

diff --git a/AmiCollector/CMakeLists.txt b/AmiCollector/CMakeLists.txt
index 47f2510..b5e6420 100644
--- a/AmiCollector/CMakeLists.txt
+++ b/AmiCollector/CMakeLists.txt
@@ -1,2 +1,11 @@
-add_subdirectory(src)
+asterisk_scf_component_init(ami-collector CXX)
+
+include_directories(include)
+
+asterisk_scf_component_add_file(ami-collector include/AsteriskSCF/AmiCollector.h)
+asterisk_scf_component_add_file(ami-collector include/AsteriskSCF/ResponseCollector.h)
+asterisk_scf_component_add_file(ami-collector src/AmiCollector.cpp)
+
+asterisk_scf_component_build_library(ami-collector)
+
 add_subdirectory(test)
diff --git a/AmiCollector/src/AmiCollector.h b/AmiCollector/include/AsteriskSCF/AmiCollector.h
similarity index 98%
rename from AmiCollector/src/AmiCollector.h
rename to AmiCollector/include/AsteriskSCF/AmiCollector.h
index 892a136..cd703c3 100644
--- a/AmiCollector/src/AmiCollector.h
+++ b/AmiCollector/include/AsteriskSCF/AmiCollector.h
@@ -22,7 +22,7 @@
 #include <IceUtil/Handle.h>
 #include <IceUtil/Shared.h>
 
-#include "ResponseCollector.h"
+#include <AsteriskSCF/ResponseCollector.h>
 
 namespace AsteriskSCF
 {
diff --git a/AmiCollector/src/ResponseCollector.h b/AmiCollector/include/AsteriskSCF/ResponseCollector.h
similarity index 100%
rename from AmiCollector/src/ResponseCollector.h
rename to AmiCollector/include/AsteriskSCF/ResponseCollector.h
diff --git a/AmiCollector/src/AmiCollector.cpp b/AmiCollector/src/AmiCollector.cpp
index a5e4797..c0244fe 100644
--- a/AmiCollector/src/AmiCollector.cpp
+++ b/AmiCollector/src/AmiCollector.cpp
@@ -14,4 +14,4 @@
  * at the top of the source tree.
  */
 
-#include "AmiCollector.h"
+#include <AsteriskSCF/AmiCollector.h>
diff --git a/AmiCollector/src/CMakeLists.txt b/AmiCollector/src/CMakeLists.txt
deleted file mode 100644
index 319aea0..0000000
--- a/AmiCollector/src/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-asterisk_scf_component_init(ami-collector CXX)
-
-asterisk_scf_component_add_file(ami-collector AmiCollector.h)
-asterisk_scf_component_add_file(ami-collector AmiCollector.cpp)
-
-asterisk_scf_component_build_library(ami-collector)
diff --git a/AmiCollector/test/IceIntegration-test.cpp b/AmiCollector/test/IceIntegration-test.cpp
index 343ef1e..06ba168 100644
--- a/AmiCollector/test/IceIntegration-test.cpp
+++ b/AmiCollector/test/IceIntegration-test.cpp
@@ -20,7 +20,8 @@
 #include <Ice/Proxy.h>
 #include <boost/test/unit_test.hpp>
 
-#include "AmiCollector.h"
+#include <AsteriskSCF/AmiCollector.h>
+
 #include "TestAmiCollector.h"
 
 using namespace AsteriskSCF::IceUtil;
diff --git a/AmiCollector/test/ResponseCollector-test.cpp b/AmiCollector/test/ResponseCollector-test.cpp
index 34bb0f3..63f7403 100644
--- a/AmiCollector/test/ResponseCollector-test.cpp
+++ b/AmiCollector/test/ResponseCollector-test.cpp
@@ -16,7 +16,7 @@
 
 #include <boost/test/unit_test.hpp>
 
-#include "ResponseCollector.h"
+#include <AsteriskSCF/ResponseCollector.h>
 
 using namespace AsteriskSCF;
 
diff --git a/AmiCollector/test/TestAmiCollector.h b/AmiCollector/test/TestAmiCollector.h
index 11ae789..b714f13 100644
--- a/AmiCollector/test/TestAmiCollector.h
+++ b/AmiCollector/test/TestAmiCollector.h
@@ -17,7 +17,7 @@
 #include <boost/thread/condition_variable.hpp>
 #include <IceUtil/Handle.h>
 
-#include "AmiCollector.h"
+#include <AsteriskSCF/AmiCollector.h>
 
 namespace AsteriskSCF
 {

commit 7ad9eb283116047b1aaafaced3116e67e01180cb
Author: David M. Lee <dlee at digium.com>
Date:   Thu Jan 6 08:45:57 2011 -0600

    Extracted a base ResponseCollector from AmiCollector.

diff --git a/AmiCollector/src/AmiCollector.h b/AmiCollector/src/AmiCollector.h
index a5ddbb9..892a136 100644
--- a/AmiCollector/src/AmiCollector.h
+++ b/AmiCollector/src/AmiCollector.h
@@ -16,13 +16,14 @@
 
 #pragma once
 
-#include <boost/thread/mutex.hpp>
 #include <Ice/Object.h>
 #include <Ice/Proxy.h>
 #include <Ice/OutgoingAsync.h>
 #include <IceUtil/Handle.h>
 #include <IceUtil/Shared.h>
 
+#include "ResponseCollector.h"
+
 namespace AsteriskSCF
 {
 namespace IceUtil
@@ -46,7 +47,9 @@ namespace IceUtil
 template<typename T,
          typename P,
          T (P::element_type::*EndFunction)(const Ice::AsyncResultPtr&)>
-class AmiCollector : public ::IceUtil::Shared
+class AmiCollector :
+        public ::IceUtil::Shared,
+        public ResponseCollector<const Ice::AsyncResultPtr&>
 {
 public:
     /** Proxy handle type (i.e. ObjectPrx) */
@@ -56,41 +59,29 @@ public:
     /** Return type for the end function */
     typedef T AsyncResultType;
     /** The type for AmiCollector itself */
-    typedef AmiCollector<T, P, EndFunction> Self;
+    typedef AmiCollector<T, P, EndFunction> AmiCollectorT;
 
     /**
-     * Constructor.
-     * @param numResponses The number of responses expected.
-     * @param end Pointer to the end function to call for responses.
+     * Creates an Ice CallbackPtr wrapper around this object, for use with AMI.
      */
-    AmiCollector(size_t numResponses) :
-        mNumResponses(numResponses)
+    Ice::CallbackPtr newIceCallback()
     {
+        // this happens prior to any callbacks, to no need to lock
+        return Ice::newCallback(this, &AmiCollectorT::invoke);
     }
 
     /**
-     * Creates an Ice CallbackPtr wrapper around this object, for use with AMI.
+     * While this looks useless, it's necessary so that the Ice callback can
+     * call the invoke function on the pointer we pass to it.
      */
-    Ice::CallbackPtr newIceCallback()
+    void invoke(const Ice::AsyncResultPtr& r)
     {
-        // this happens prior to any callbacks, to no need to lock
-
-        // handle the case where we don't expect any responses
-        // can't handle this in the ctor, since you can't call virtual
-        // functions in the constructor
-        if (mNumResponses == 0)
-        {
-            processCompletion();
-        }
-        return Ice::newCallback(this, &Self::finished);
+        ResponseCollectorT::invoke(r);
     }
 
-    /** The number of outstanding responses remaining. */
-    size_t getRemainingResponses() const { return mNumResponses; }
-
 protected:
     /** Protected dtor prevents creating instances on the stack */
-    ~AmiCollector() {}
+    virtual ~AmiCollector() {}
     /** Called for each response received. */
     virtual void processResult(AsyncResultType result) = 0;
     /** Called for each exception received. */
@@ -98,32 +89,18 @@ protected:
     /** Called after all the responses or exceptions have been accounted for. */
     virtual void processCompletion() {}
 
-    /**
-     * Thread safety for response counting.  Derived classes may use this for
-     * their own thread safety needs as well.
-     */
-    boost::mutex mMutex;
-
+private:
     /** Callback from Ice */
-    void finished(const Ice::AsyncResultPtr& r)
+    void processInvocation(const Ice::AsyncResultPtr& r)
     {
         try
         {
             try
             {
-                // unit test may not have an AsyncResultPtr
-                if (r)
-                {
-                    IceProxy p = IceProxy::uncheckedCast(r->getProxy());
-                    // invoke the nd function on our proxy, passing in r
-                    // the results are passed to processResult
-                    processResult(((*p).*EndFunction)(r));
-                }
-                else
-                {
-                    // we didn't have an AsyncResultPtr, so make something up
-                    processResult(AsyncResultType());
-                }
+                IceProxy p = IceProxy::uncheckedCast(r->getProxy());
+                // invoke the end function on our proxy, passing in r
+                // the results are passed to processResult
+                processResult(((*p).*EndFunction)(r));
             }
             catch(const Ice::Exception& e)
             {
@@ -142,33 +119,6 @@ protected:
             std::clog << "Unexpected exception\n";
             assert(false);
         }
-        countResponse();
-    }
-
-private:
-    size_t mNumResponses;
-
-    /**
-     * Called from each AMI callback.  When this function sees that the expected
-     * number of responses have been received, it invokes
-     * <code>processCompletion</code>
-     */
-    void countResponse()
-    {
-        bool done = false;
-        {
-            boost::lock_guard<boost::mutex> lock(mMutex);
-            assert(mNumResponses > 0); // we got more responses than expected
-            if (--mNumResponses == 0)
-            {
-                done = true;
-            }
-        }
-
-        if (done)
-        {
-            processCompletion();
-        }
     }
 };
 
diff --git a/AmiCollector/src/ResponseCollector.h b/AmiCollector/src/ResponseCollector.h
new file mode 100644
index 0000000..44e7b6c
--- /dev/null
+++ b/AmiCollector/src/ResponseCollector.h
@@ -0,0 +1,132 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+
+#pragma once
+
+#include <boost/thread/mutex.hpp>
+#include <iostream>
+
+namespace AsteriskSCF
+{
+
+class TooManyInvocations : public std::exception
+{
+public:
+    const char* what() const throw ()
+    {
+        return "ResponseCollector invoked too many times";
+    }
+};
+
+/**
+ * Collects the results for multiple asynchronous responses.  While the response
+ * counting logic is properly locked and thread safe, it is incumbent upon the
+ * implementor of the <code>processInvocation</code> functions to write those
+ * functions to be thread safe.  Derived classes may use mMutex for their own
+ * locking.
+ */
+template<typename T>
+class ResponseCollector
+{
+public:
+    typedef T InvocationParamType;
+    typedef ResponseCollector<T> ResponseCollectorT;
+
+    /**
+     * Constructor.  The collector is no good until it's been initialized.
+     */
+    ResponseCollector() :
+        mNumResponses(0)
+    {
+    }
+
+    virtual ~ResponseCollector() {}
+
+    /**
+     * Initialize the collector.
+     * @param numResponses The number of responses expected.
+     */
+    void init(size_t numResponses)
+    {
+        mNumResponses = numResponses;
+        // handle the case where we expect no responses
+        if (numResponses == 0)
+        {
+            processCompletion();
+        }
+    }
+
+    /**
+     * Invoke this function for every response.
+     */
+    void invoke(InvocationParamType param)
+    {
+        try
+        {
+            processInvocation(param);
+        }
+        catch(...)
+        {
+            countResponse();
+            throw;
+        }
+        countResponse();
+    }
+
+protected:
+    /** Called to retrieve the results for an invocation */
+    virtual void processInvocation(InvocationParamType param) = 0;
+    /** Called after all the responses or exceptions have been accounted for. */
+    virtual void processCompletion() {}
+
+    /**
+     * Thread safety for response counting.  Derived classes may use this for
+     * their own thread safety needs as well.
+     */
+    boost::mutex mMutex;
+
+private:
+    size_t mNumResponses;
+
+    /**
+     * Called from each AMI callback.  When this function sees that the expected
+     * number of responses have been received, it invokes
+     * <code>processCompletion</code>
+     */
+    void countResponse()
+    {
+        bool done = false;
+        {
+            boost::lock_guard<boost::mutex> lock(mMutex);
+            if (mNumResponses <= 0)
+            {
+                throw TooManyInvocations();
+            }
+
+            if (--mNumResponses == 0)
+            {
+                done = true;
+            }
+        }
+
+        if (done)
+        {
+            processCompletion();
+        }
+    }
+};
+
+} // AsteriskSCF
diff --git a/AmiCollector/test/AmiCollector-test.cpp b/AmiCollector/test/AmiCollector-test.cpp
deleted file mode 100644
index 05f2973..0000000
--- a/AmiCollector/test/AmiCollector-test.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Asterisk SCF -- An open-source communications framework.
- *
- * Copyright (C) 2010, Digium, Inc.
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk SCF project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE.txt file
- * at the top of the source tree.
- */
-
-#include <boost/test/unit_test.hpp>
-
-#include "AmiCollector.h"
-#include "TestAmiCollector.h"
-
-using namespace AsteriskSCF::IceUtil;
-
-BOOST_AUTO_TEST_SUITE(AmiCollectorTest)
-
-BOOST_AUTO_TEST_CASE(test_zero)
-{
-    TestAmiCollectorPtr uut = new TestAmiCollector(0);
-
-    uut->newIceCallback();
-
-    BOOST_CHECK_EQUAL(0, uut->results);
-    BOOST_CHECK_EQUAL(0, uut->exceptions);
-    BOOST_CHECK_EQUAL(true, uut->complete);
-}
-
-BOOST_AUTO_TEST_CASE(test_one)
-{
-    TestAmiCollectorPtr uut = new TestAmiCollector(1);
-
-    uut->newIceCallback();
-
-    BOOST_CHECK_EQUAL(0, uut->results);
-    BOOST_CHECK_EQUAL(0, uut->exceptions);
-    BOOST_CHECK_EQUAL(false, uut->complete);
-
-    uut->finished(0);
-
-    BOOST_CHECK_EQUAL(1, uut->results);
-    BOOST_CHECK_EQUAL(0, uut->exceptions);
-    BOOST_CHECK_EQUAL(true, uut->complete);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/AmiCollector/test/CMakeLists.txt b/AmiCollector/test/CMakeLists.txt
index 952caf0..5dbb40b 100644
--- a/AmiCollector/test/CMakeLists.txt
+++ b/AmiCollector/test/CMakeLists.txt
@@ -1,7 +1,7 @@
 asterisk_scf_component_init(ami-collector-test CXX)
 include_directories("../src")
 
-asterisk_scf_component_add_file(ami-collector-test AmiCollector-test.cpp)
+asterisk_scf_component_add_file(ami-collector-test ResponseCollector-test.cpp)
 asterisk_scf_component_add_file(ami-collector-test IceIntegration-test.cpp)
 asterisk_scf_component_add_file(ami-collector-test TestAmiCollector.h)
 asterisk_scf_component_add_file(ami-collector-test test.cpp)
diff --git a/AmiCollector/test/ResponseCollector-test.cpp b/AmiCollector/test/ResponseCollector-test.cpp
new file mode 100644
index 0000000..34bb0f3
--- /dev/null
+++ b/AmiCollector/test/ResponseCollector-test.cpp
@@ -0,0 +1,130 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+
+#include <boost/test/unit_test.hpp>
+
+#include "ResponseCollector.h"
+
+using namespace AsteriskSCF;
+
+namespace
+{
+class TestException : std::exception
+{
+public:
+    const char* what() const throw() { return "TestException"; }
+};
+
+class TestCollector : public ResponseCollector<bool>
+{
+public:
+    /** Num results received */
+    int results;
+    /** Is complete? */
+    bool complete;
+
+    TestCollector() : results(0), complete(false) {}
+
+    void processInvocation(bool shouldThrow)
+    {
+        ++results;
+        if (shouldThrow)
+        {
+            throw TestException();
+        }
+    }
+    void processCompletion()
+    {
+        BOOST_CHECK_EQUAL(false, complete);
+        complete = true;
+    }
+};
+
+} // namespace
+
+BOOST_AUTO_TEST_SUITE(AmiCollectorTest)
+
+BOOST_AUTO_TEST_CASE(test_zero)
+{
+    TestCollector uut;
+
+    uut.init(0);
+
+    BOOST_CHECK_EQUAL(0, uut.results);
+    BOOST_CHECK_EQUAL(true, uut.complete);
+}
+
+BOOST_AUTO_TEST_CASE(test_one)
+{
+    TestCollector uut;
+
+    uut.init(1);
+
+    BOOST_CHECK_EQUAL(0, uut.results);
+    BOOST_CHECK_EQUAL(false, uut.complete);
+
+    try { uut.invoke(true); } catch (...) { /* ignore */ }
+
+    BOOST_CHECK_EQUAL(1, uut.results);
+    BOOST_CHECK_EQUAL(true, uut.complete);
+}
+
+BOOST_AUTO_TEST_CASE(test_one_exception)
+{
+    TestCollector uut;
+
+    uut.init(1);
+
+    uut.invoke(false);
+
+    BOOST_CHECK_EQUAL(1, uut.results);
+    BOOST_CHECK_EQUAL(true, uut.complete);
+}
+
+BOOST_AUTO_TEST_CASE(test_many)
+{
+    TestCollector uut;
+
+    uut.init(5);
+
+    try { uut.invoke(true); } catch (...) { /* ignore */ }
+    try { uut.invoke(true); } catch (...) { /* ignore */ }
+    try { uut.invoke(true); } catch (...) { /* ignore */ }
+    try { uut.invoke(true); } catch (...) { /* ignore */ }
+    BOOST_CHECK_EQUAL(false, uut.complete);
+    try { uut.invoke(true); } catch (...) { /* ignore */ }
+
+    BOOST_CHECK_EQUAL(5, uut.results);
+    BOOST_CHECK_EQUAL(true, uut.complete);
+}
+
+BOOST_AUTO_TEST_CASE(test_mixed)
+{
+    TestCollector uut;
+
+    uut.init(5);
+
+    try { uut.invoke(true); } catch (...) { /* ignore */ }
+    uut.invoke(false);
+    try { uut.invoke(true); } catch (...) { /* ignore */ }
+    uut.invoke(false);
+    try { uut.invoke(true); } catch (...) { /* ignore */ }
+
+    BOOST_CHECK_EQUAL(5, uut.results);
+    BOOST_CHECK_EQUAL(true, uut.complete);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/AmiCollector/test/TestAmiCollector.h b/AmiCollector/test/TestAmiCollector.h
index 6291702..11ae789 100644
--- a/AmiCollector/test/TestAmiCollector.h
+++ b/AmiCollector/test/TestAmiCollector.h
@@ -34,11 +34,12 @@ class TestAmiCollector : public AsteriskSCF::IceUtil::AmiCollector<
 {
 public:
     explicit TestAmiCollector(size_t n) :
-        Self(n),
         results(0),
         exceptions(0),
         complete(false)
-    {}
+    {
+        init(n);
+    }
     /** Num results received */
     int results;
     /** Num exceptions received */
@@ -46,12 +47,6 @@ public:
     /** Is complete? */
     bool complete;
 
-    /** Exposes the 'finished' callback function for unit testing. */
-    void finished(const Ice::AsyncResultPtr& r)
-    {
-        Self::finished(r);
-    }
-
     /** Blocks until processCompletion is called. */
     void waitForCompletion()
     {

commit 4920b90d3935aa8b395111d4faa425769ac82611
Author: David M. Lee <dlee at digium.com>
Date:   Wed Jan 5 16:02:10 2011 -0600

    cleanup

diff --git a/AmiCollector/src/AmiCollector.h b/AmiCollector/src/AmiCollector.h
index 5e7e605..a5ddbb9 100644
--- a/AmiCollector/src/AmiCollector.h
+++ b/AmiCollector/src/AmiCollector.h
@@ -33,8 +33,19 @@ namespace IceUtil
  * counting logic is properly locked and thread safe, it is incumbent upon the
  * implementor of the <code>process*</code> functions to write those functions
  * to be thread safe.  Derived classes may use mMutex for their own locking.
+ *
+ * An example of the template parameters would be:
+ * <code>
+ *   <bool, Ice::ObjectPrx, &Ice::ObjectPrx::element_type::end_ice_isA>
+ * </code>
+ *
+ * @param T The asynchronous result type (return value from end function)
+ * @param P The type of the proxy class
+ * @param EndFunction The pointer to member function for the end_ function
  */
-template<typename T, typename P, T (P::element_type::*EndFunction)(const Ice::AsyncResultPtr&)>
+template<typename T,
+         typename P,
+         T (P::element_type::*EndFunction)(const Ice::AsyncResultPtr&)>
 class AmiCollector : public ::IceUtil::Shared
 {
 public:
diff --git a/AmiCollector/test/TestAmiCollector.h b/AmiCollector/test/TestAmiCollector.h
index b3cb484..6291702 100644
--- a/AmiCollector/test/TestAmiCollector.h
+++ b/AmiCollector/test/TestAmiCollector.h
@@ -27,7 +27,10 @@ namespace IceUtil
 /**
  * Simple AMI collector for testing
  */
-class TestAmiCollector : public AsteriskSCF::IceUtil::AmiCollector<bool, Ice::ObjectPrx, &Ice::ObjectPrx::element_type::end_ice_isA>
+class TestAmiCollector : public AsteriskSCF::IceUtil::AmiCollector<
+    bool,
+    Ice::ObjectPrx,
+    &Ice::ObjectPrx::element_type::end_ice_isA>
 {
 public:
     explicit TestAmiCollector(size_t n) :

commit e8ba04f58ce8cf49545cbea0a7d180ff0513ef3e
Author: David M. Lee <dlee at digium.com>
Date:   Wed Jan 5 15:56:14 2011 -0600

    PTMF is now template param.

diff --git a/AmiCollector/src/AmiCollector.h b/AmiCollector/src/AmiCollector.h
index dcae2b9..5e7e605 100644
--- a/AmiCollector/src/AmiCollector.h
+++ b/AmiCollector/src/AmiCollector.h
@@ -34,29 +34,26 @@ namespace IceUtil
  * implementor of the <code>process*</code> functions to write those functions
  * to be thread safe.  Derived classes may use mMutex for their own locking.
  */
-template<typename T, typename P>
+template<typename T, typename P, T (P::element_type::*EndFunction)(const Ice::AsyncResultPtr&)>
 class AmiCollector : public ::IceUtil::Shared
 {
 public:
     /** Proxy handle type (i.e. ObjectPrx) */
     typedef P IceProxy;
     /** Proxy element type (i.e. Proxy::Ice::Object) */
-    typedef typename IceProxy::element_type ElementType;
+    typedef typename P::element_type ElementType;
     /** Return type for the end function */
     typedef T AsyncResultType;
     /** The type for AmiCollector itself */
-    typedef AmiCollector<T, P> Self;
-    /** Pointer to member function for the end function */
-    typedef AsyncResultType (ElementType::*EndFunction)(const Ice::AsyncResultPtr&);
+    typedef AmiCollector<T, P, EndFunction> Self;
 
     /**
      * Constructor.
      * @param numResponses The number of responses expected.
      * @param end Pointer to the end function to call for responses.
      */
-    AmiCollector(size_t numResponses, EndFunction end) :
-        mNumResponses(numResponses),
-        mEnd(end)
+    AmiCollector(size_t numResponses) :
+        mNumResponses(numResponses)
     {
     }
 
@@ -107,9 +104,9 @@ protected:
                 if (r)
                 {
                     IceProxy p = IceProxy::uncheckedCast(r->getProxy());
-                    // invoke the mEnd function on our proxy, passing in r
+                    // invoke the nd function on our proxy, passing in r
                     // the results are passed to processResult
-                    processResult(((*p).*mEnd)(r));
+                    processResult(((*p).*EndFunction)(r));
                 }
                 else
                 {
@@ -139,7 +136,6 @@ protected:
 
 private:
     size_t mNumResponses;
-    EndFunction mEnd;
 
     /**
      * Called from each AMI callback.  When this function sees that the expected
diff --git a/AmiCollector/test/TestAmiCollector.h b/AmiCollector/test/TestAmiCollector.h
index 6471897..b3cb484 100644
--- a/AmiCollector/test/TestAmiCollector.h
+++ b/AmiCollector/test/TestAmiCollector.h
@@ -27,11 +27,11 @@ namespace IceUtil
 /**
  * Simple AMI collector for testing
  */
-class TestAmiCollector : public AsteriskSCF::IceUtil::AmiCollector<bool, Ice::ObjectPrx>
+class TestAmiCollector : public AsteriskSCF::IceUtil::AmiCollector<bool, Ice::ObjectPrx, &Ice::ObjectPrx::element_type::end_ice_isA>
 {
 public:
     explicit TestAmiCollector(size_t n) :
-        Self(n, &IceProxy::element_type::end_ice_isA),
+        Self(n),
         results(0),
         exceptions(0),
         complete(false)

commit a0161edf7b9144b720285b8828ee8a361773afd7
Author: David M. Lee <dlee at digium.com>
Date:   Wed Jan 5 15:27:33 2011 -0600

    Now I feel good about the AmiCollector :-)

diff --git a/AmiCollector/src/AmiCollector.h b/AmiCollector/src/AmiCollector.h
index 98d0998..dcae2b9 100644
--- a/AmiCollector/src/AmiCollector.h
+++ b/AmiCollector/src/AmiCollector.h
@@ -29,90 +29,117 @@ namespace IceUtil
 {
 
 /**
- * Collects the results for multiple asynchronous responses.  While the vote
+ * Collects the results for multiple asynchronous responses.  While the response
  * counting logic is properly locked and thread safe, it is incumbent upon the
  * implementor of the <code>process*</code> functions to write those functions
- * to be thread safe.  Feel free to use mMutex (it's protected) if needed.
+ * to be thread safe.  Derived classes may use mMutex for their own locking.
  */
-template<typename T>
+template<typename T, typename P>
 class AmiCollector : public ::IceUtil::Shared
 {
 public:
+    /** Proxy handle type (i.e. ObjectPrx) */
+    typedef P IceProxy;
+    /** Proxy element type (i.e. Proxy::Ice::Object) */
+    typedef typename IceProxy::element_type ElementType;
+    /** Return type for the end function */
     typedef T AsyncResultType;
-    typedef AmiCollector<T> Self;
+    /** The type for AmiCollector itself */
+    typedef AmiCollector<T, P> Self;
+    /** Pointer to member function for the end function */
+    typedef AsyncResultType (ElementType::*EndFunction)(const Ice::AsyncResultPtr&);
 
-    AmiCollector(size_t numVotes) : mNumVotes(numVotes)
+    /**
+     * Constructor.
+     * @param numResponses The number of responses expected.
+     * @param end Pointer to the end function to call for responses.
+     */
+    AmiCollector(size_t numResponses, EndFunction end) :
+        mNumResponses(numResponses),
+        mEnd(end)
     {
     }
 
     /**
      * Creates an Ice CallbackPtr wrapper around this object, for use with AMI.
      */
-    Ice::CallbackPtr toIceCallback()
+    Ice::CallbackPtr newIceCallback()
     {
         // this happens prior to any callbacks, to no need to lock
 
         // handle the case where we don't expect any responses
         // can't handle this in the ctor, since you can't call virtual
         // functions in the constructor
-        if (mNumVotes == 0)
+        if (mNumResponses == 0)
         {
             processCompletion();
         }
         return Ice::newCallback(this, &Self::finished);
     }
 
-    size_t getRemainingVotes() const { return mNumVotes; }
+    /** The number of outstanding responses remaining. */
+    size_t getRemainingResponses() const { return mNumResponses; }
 
 protected:
     /** Protected dtor prevents creating instances on the stack */
     ~AmiCollector() {}
-    /**
-     * Wrapper to correctly end the remote function call.
-     * Should almost always look like:
-     * <code>
-     *  DerivedPrx d = DerivedPrx::uncheckedCast(r->getProxy());
-     *  return d->end_remoteFunction(r);
-     * </code>
-     */
-    virtual AsyncResultType end(const Ice::AsyncResultPtr& proxy) = 0;
-    /**
-     * Called for each response received.
-     */
+    /** Called for each response received. */
     virtual void processResult(AsyncResultType result) = 0;
-    /**
-     * Called for each exception received.
-     */
+    /** Called for each exception received. */
     virtual void processException(const Ice::Exception& e) = 0;
-    /**
-     * Called after all the responses or exceptions have been accounted for.
-     */
+    /** Called after all the responses or exceptions have been accounted for. */
     virtual void processCompletion() {}
 
+    /**
+     * Thread safety for response counting.  Derived classes may use this for
+     * their own thread safety needs as well.
+     */
     boost::mutex mMutex;
 
+    /** Callback from Ice */
     void finished(const Ice::AsyncResultPtr& r)
     {
         try
         {
             try
             {
-                processResult(end(r));
+                // unit test may not have an AsyncResultPtr
+                if (r)
+                {
+                    IceProxy p = IceProxy::uncheckedCast(r->getProxy());
+                    // invoke the mEnd function on our proxy, passing in r
+                    // the results are passed to processResult
+                    processResult(((*p).*mEnd)(r));
+                }
+                else
+                {
+                    // we didn't have an AsyncResultPtr, so make something up
+                    processResult(AsyncResultType());
+                }
             }
             catch(const Ice::Exception& e)
             {
                 processException(e);
             }
         }
+        catch(const std::exception& e)
+        {
+            // any exceptions thrown during processing are programming errors
+            std::clog << "Unexpected exception: " << e.what() << '\n';
+            assert(false);
+        }
         catch(...)
         {
-            // ignore any processing exceptions
+            // and _please_ don't just throw char pointers :-(
+            std::clog << "Unexpected exception\n";
+            assert(false);
         }
         countResponse();
     }
 
 private:
-    size_t mNumVotes;
+    size_t mNumResponses;
+    EndFunction mEnd;
 
     /**
      * Called from each AMI callback.  When this function sees that the expected
@@ -124,8 +151,8 @@ private:
         bool done = false;
         {
             boost::lock_guard<boost::mutex> lock(mMutex);
-            assert(mNumVotes > 0); // we got more responses than expected
-            if (--mNumVotes == 0)
+            assert(mNumResponses > 0); // we got more responses than expected
+            if (--mNumResponses == 0)
             {
                 done = true;
             }
diff --git a/AmiCollector/test/AmiCollector-test.cpp b/AmiCollector/test/AmiCollector-test.cpp
index 6f659e1..05f2973 100644
--- a/AmiCollector/test/AmiCollector-test.cpp
+++ b/AmiCollector/test/AmiCollector-test.cpp
@@ -14,74 +14,20 @@
  * at the top of the source tree.
  */
 
-#include <Ice/Proxy.h>
 #include <boost/test/unit_test.hpp>
 
 #include "AmiCollector.h"
+#include "TestAmiCollector.h"
 
 using namespace AsteriskSCF::IceUtil;
 
-namespace
-{
-class TestAmiCollector : public AsteriskSCF::IceUtil::AmiCollector<bool>
-{
-public:
-    explicit TestAmiCollector(size_t n) :
-        AmiCollector<bool>(n),
-        results(0),
-        exceptions(0),
-        complete(false)
-    {}
-    int results;
-    int exceptions;
-    bool complete;
-
-    void finished(const Ice::AsyncResultPtr& r)
-    {
-        Self::finished(r);
-    }
-
-protected:
-    ~TestAmiCollector() {}
-    virtual AsyncResultType end(const Ice::AsyncResultPtr& proxy)
-    {
-        // fake - do nothing
-        return false;
-    }
-    void processResult(AsyncResultType result)
-    {
-        ++results;
-    }
-    void processException(const Ice::Exception& e)
-    {
-        ++exceptions;
-    }
-    void processCompletion()
-    {
-        BOOST_CHECK_EQUAL(false, complete);
-        complete = true;
-    }
-};
-
-typedef IceUtil::Handle<TestAmiCollector> TestAmiCollectorPtr;
-
-template<typename T>
-Ice::CallbackPtr noCallbackPtr(const IceUtil::Handle<T>& ptr,
-    void (T::*cb)(const int&),
-    void (T::*excb)(const Ice::Exception&),
-    void (T::*sentcb)(bool) = 0)
-{
-    return Ice::CallbackPtr();
-}
-}
-
 BOOST_AUTO_TEST_SUITE(AmiCollectorTest)
 
 BOOST_AUTO_TEST_CASE(test_zero)
 {
     TestAmiCollectorPtr uut = new TestAmiCollector(0);
 
-    uut->toIceCallback();
+    uut->newIceCallback();
 
     BOOST_CHECK_EQUAL(0, uut->results);
     BOOST_CHECK_EQUAL(0, uut->exceptions);
@@ -92,7 +38,7 @@ BOOST_AUTO_TEST_CASE(test_one)
 {
     TestAmiCollectorPtr uut = new TestAmiCollector(1);
 
-    uut->toIceCallback();
+    uut->newIceCallback();
 
     BOOST_CHECK_EQUAL(0, uut->results);
     BOOST_CHECK_EQUAL(0, uut->exceptions);
diff --git a/AmiCollector/test/CMakeLists.txt b/AmiCollector/test/CMakeLists.txt
index eb59de6..952caf0 100644
--- a/AmiCollector/test/CMakeLists.txt
+++ b/AmiCollector/test/CMakeLists.txt
@@ -2,9 +2,11 @@ asterisk_scf_component_init(ami-collector-test CXX)
 include_directories("../src")
 
 asterisk_scf_component_add_file(ami-collector-test AmiCollector-test.cpp)
+asterisk_scf_component_add_file(ami-collector-test IceIntegration-test.cpp)
+asterisk_scf_component_add_file(ami-collector-test TestAmiCollector.h)
 asterisk_scf_component_add_file(ami-collector-test test.cpp)
 
-asterisk_scf_component_add_boost_libraries(ami-collector-test unit_test_framework)
+asterisk_scf_component_add_boost_libraries(ami-collector-test unit_test_framework thread)
 
 asterisk_scf_component_build_standalone(ami-collector-test)
 
diff --git a/AmiCollector/test/IceIntegration-test.cpp b/AmiCollector/test/IceIntegration-test.cpp
new file mode 100644
index 0000000..343ef1e
--- /dev/null
+++ b/AmiCollector/test/IceIntegration-test.cpp
@@ -0,0 +1,120 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+
+#include <unistd.h>
+
+#include <Ice/Ice.h>
+#include <Ice/Proxy.h>
+#include <boost/test/unit_test.hpp>
+
+#include "AmiCollector.h"
+#include "TestAmiCollector.h"
+
+using namespace AsteriskSCF::IceUtil;
+
+namespace
+{
+class MyObject : public Ice::Object{};
+
+struct Fixture
+{
+    Fixture() :
+        communicator(Ice::initialize()),
+        adapter(communicator->createObjectAdapterWithEndpoints("test", "default")),
+        object(new MyObject),
+        proxy(adapter->addWithUUID(object)->ice_collocationOptimized(false))
+    {
+        adapter->activate();
+    }
+
+    ~Fixture()
+    {
+        communicator->shutdown();
+        communicator->waitForShutdown();
+    }
+
+    Ice::CommunicatorPtr communicator;
+    Ice::ObjectAdapterPtr adapter;
+    Ice::ObjectPtr object;
+    Ice::ObjectPrx proxy;
+};
+} // anonymous namespace
+
+BOOST_FIXTURE_TEST_SUITE(AmiCollectorIceIntegration, Fixture)
+
+BOOST_AUTO_TEST_CASE(test_zero)
+{
+    TestAmiCollectorPtr uut = new TestAmiCollector(0);
+    uut->newIceCallback();
+
+    uut->waitForCompletion();
+
+    BOOST_CHECK_EQUAL(0, uut->results);
+    BOOST_CHECK_EQUAL(0, uut->exceptions);
+    BOOST_CHECK_EQUAL(true, uut->complete);
+}
+
+BOOST_AUTO_TEST_CASE(test_one)
+{
+    TestAmiCollectorPtr uut = new TestAmiCollector(1);
+
+    BOOST_CHECK_EQUAL(0, uut->results);
+    BOOST_CHECK_EQUAL(0, uut->exceptions);
+    BOOST_CHECK_EQUAL(false, uut->complete);
+
+    proxy->begin_ice_isA("::Ice::Object", uut->newIceCallback());
+
+    uut->waitForCompletion();
+
+    BOOST_CHECK_EQUAL(1, uut->results);
+    BOOST_CHECK_EQUAL(0, uut->exceptions);
+    BOOST_CHECK_EQUAL(true, uut->complete);
+}
+
+BOOST_AUTO_TEST_CASE(test_exception)
+{
+    TestAmiCollectorPtr uut = new TestAmiCollector(1);
+
+    // by destroying the adapter, we should get an exception back
+    adapter->destroy();
+
+    proxy->begin_ice_isA("object was shutdown", uut->newIceCallback());
+
+    uut->waitForCompletion();
+
+    BOOST_CHECK_EQUAL(0, uut->results);
+    BOOST_CHECK_EQUAL(1, uut->exceptions);
+    BOOST_CHECK_EQUAL(true, uut->complete);
+}
+
+BOOST_AUTO_TEST_CASE(test_some)
+{
+    TestAmiCollectorPtr uut = new TestAmiCollector(5);
+
+    proxy->begin_ice_isA("::Ice::Object", uut->newIceCallback());
+    proxy->begin_ice_isA("::Ice::Object", uut->newIceCallback());
+    proxy->begin_ice_isA("::Ice::Object", uut->newIceCallback());
+    proxy->begin_ice_isA("::Ice::Object", uut->newIceCallback());
+    proxy->begin_ice_isA("::Ice::Object", uut->newIceCallback());
+
+    uut->waitForCompletion();
+
+    BOOST_CHECK_EQUAL(5, uut->results);
+    BOOST_CHECK_EQUAL(0, uut->exceptions);
+    BOOST_CHECK_EQUAL(true, uut->complete);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/AmiCollector/test/TestAmiCollector.h b/AmiCollector/test/TestAmiCollector.h
new file mode 100644
index 0000000..6471897
--- /dev/null
+++ b/AmiCollector/test/TestAmiCollector.h
@@ -0,0 +1,87 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+
+#include <boost/thread/condition_variable.hpp>
+#include <IceUtil/Handle.h>
+
+#include "AmiCollector.h"
+
+namespace AsteriskSCF
+{
+namespace IceUtil
+{
+
+/**
+ * Simple AMI collector for testing
+ */
+class TestAmiCollector : public AsteriskSCF::IceUtil::AmiCollector<bool, Ice::ObjectPrx>
+{
+public:
+    explicit TestAmiCollector(size_t n) :
+        Self(n, &IceProxy::element_type::end_ice_isA),
+        results(0),
+        exceptions(0),
+        complete(false)
+    {}
+    /** Num results received */
+    int results;
+    /** Num exceptions received */
+    int exceptions;
+    /** Is complete? */
+    bool complete;
+
+    /** Exposes the 'finished' callback function for unit testing. */
+    void finished(const Ice::AsyncResultPtr& r)
+    {
+        Self::finished(r);
+    }
+
+    /** Blocks until processCompletion is called. */
+    void waitForCompletion()
+    {
+        boost::unique_lock<boost::mutex> lock(mMutex);
+        while (!complete)
+        {
+            cond.wait(lock);
+        }
+    }
+
+protected:
+    ~TestAmiCollector() {}
+    void processResult(AsyncResultType result)
+    {
+        ++results;
+    }
+    void processException(const Ice::Exception& e)
+    {
+        ++exceptions;
+    }
+    void processCompletion()
+    {
+        boost::unique_lock<boost::mutex> lock(mMutex);
+        BOOST_CHECK_EQUAL(false, complete);
+        complete = true;
+        cond.notify_all();
+    }
+
+private:
+    boost::condition_variable cond;
+};
+
+typedef ::IceUtil::Handle<TestAmiCollector> TestAmiCollectorPtr;
+
+} // IceUtil
+} // AsteriskSCF

commit 045352598cf6735a681ea3b060edd86d18ed2972
Author: David M. Lee <dlee at digium.com>
Date:   Wed Jan 5 13:27:22 2011 -0600

    Improved AmiCollector.
    
    By using the generic callback, ironically I've reduced the number of
    type problems I had with the code.

diff --git a/AmiCollector/src/AmiCollector.h b/AmiCollector/src/AmiCollector.h
index b01e74b..98d0998 100644
--- a/AmiCollector/src/AmiCollector.h
+++ b/AmiCollector/src/AmiCollector.h
@@ -48,12 +48,7 @@ public:
     /**
      * Creates an Ice CallbackPtr wrapper around this object, for use with AMI.
      */
-    template<typename CB, typename HN>
-    IceInternal::CallbackBasePtr toIceCallback(CB (*newCallbackFunction)
-        (HN,
-            void (Self::*cb)(T),
-            void (Self::*excb)(const ::Ice::Exception&),
-            void (Self::*sentcb)(bool)))
+    Ice::CallbackPtr toIceCallback()
     {
         // this happens prior to any callbacks, to no need to lock
 
@@ -64,10 +59,7 @@ public:
         {
             processCompletion();
         }
-        return newCallbackFunction(this,
-            &AmiCollector<AsyncResultType>::resultCallback,
-            &AmiCollector<AsyncResultType>::errorCallback,
-            0 /* sentCallback */);
+        return Ice::newCallback(this, &Self::finished);
     }
 
     size_t getRemainingVotes() const { return mNumVotes; }
@@ -76,6 +68,15 @@ protected:
     /** Protected dtor prevents creating instances on the stack */
     ~AmiCollector() {}
     /**
+     * Wrapper to correctly end the remote function call.
+     * Should almost always look like:
+     * <code>
+     *  DerivedPrx d = DerivedPrx::uncheckedCast(r->getProxy());
+     *  return d->end_remoteFunction(r);
+     * </code>
+     */
+    virtual AsyncResultType end(const Ice::AsyncResultPtr& proxy) = 0;
+    /**
      * Called for each response received.
      */
     virtual void processResult(AsyncResultType result) = 0;
@@ -88,41 +89,28 @@ protected:
      */
     virtual void processCompletion() {}
 
-    /**
-     * AMI result callback.
-     */
-    void resultCallback(AsyncResultType result)
-    {
-        try
-        {
-            processResult(result);
-        }
-        catch(...)
-        {
-            countResponse();
-            throw;
-        }
-        countResponse();
-    }
+    boost::mutex mMutex;
 
-    /**
-     * AMI exception callback.
-     */
-    void errorCallback(const Ice::Exception& e)
+    void finished(const Ice::AsyncResultPtr& r)
     {
         try
         {
-            processException(e);
+            try
+            {
+                processResult(end(r));
+            }
+            catch(const Ice::Exception& e)
+            {
+                processException(e);
+            }
         }
         catch(...)
         {
-            countResponse();
-            throw;
+            // ignore any processing exceptions
         }
         countResponse();
     }
 
-    boost::mutex mMutex;
 private:
     size_t mNumVotes;
 
diff --git a/AmiCollector/test/AmiCollector-test.cpp b/AmiCollector/test/AmiCollector-test.cpp
index 92f1b1c..6f659e1 100644
--- a/AmiCollector/test/AmiCollector-test.cpp
+++ b/AmiCollector/test/AmiCollector-test.cpp
@@ -36,17 +36,18 @@ public:
     int exceptions;
     bool complete;
 
-    void resultCallback(AsyncResultType result)
+    void finished(const Ice::AsyncResultPtr& r)
     {
-        AmiCollector<bool>::resultCallback(result);
-    }
-    void errorCallback(const Ice::Exception& e)
-    {
-        AmiCollector<bool>::errorCallback(e);
+        Self::finished(r);
     }
 
 protected:
     ~TestAmiCollector() {}
+    virtual AsyncResultType end(const Ice::AsyncResultPtr& proxy)
+    {
+        // fake - do nothing
+        return false;
+    }
     void processResult(AsyncResultType result)
     {
         ++results;
@@ -80,13 +81,7 @@ BOOST_AUTO_TEST_CASE(test_zero)
 {
     TestAmiCollectorPtr uut = new TestAmiCollector(0);
 
-    Ice::Callback_Object_ice_isAPtr (*factory)
-        (const IceUtil::Handle<AmiCollector<bool> >& instance,
-            void (AmiCollector<bool>::*cb)(bool),
-            void (AmiCollector<bool>::*excb)(const ::Ice::Exception&),
-            void (AmiCollector<bool>::*sentcb)(bool)) = Ice::newCallback_Object_ice_isA;
-
-    uut->toIceCallback(factory);
+    uut->toIceCallback();
 
     BOOST_CHECK_EQUAL(0, uut->results);
     BOOST_CHECK_EQUAL(0, uut->exceptions);
@@ -97,19 +92,13 @@ BOOST_AUTO_TEST_CASE(test_one)
 {
     TestAmiCollectorPtr uut = new TestAmiCollector(1);
 
-    Ice::Callback_Object_ice_isAPtr (*factory)
-        (const IceUtil::Handle<AmiCollector<bool> >& instance,
-            void (AmiCollector<bool>::*cb)(bool),
-            void (AmiCollector<bool>::*excb)(const ::Ice::Exception&),
-            void (AmiCollector<bool>::*sentcb)(bool)) = Ice::newCallback_Object_ice_isA;
-
-    uut->toIceCallback(factory);
+    uut->toIceCallback();
 
     BOOST_CHECK_EQUAL(0, uut->results);
     BOOST_CHECK_EQUAL(0, uut->exceptions);
     BOOST_CHECK_EQUAL(false, uut->complete);
 
-    uut->resultCallback(true);
+    uut->finished(0);
 
     BOOST_CHECK_EQUAL(1, uut->results);
     BOOST_CHECK_EQUAL(0, uut->exceptions);

commit 5ce5347b967452a5ac2b3bde85d35cb9b065bf76
Author: David M. Lee <dlee at digium.com>
Date:   Wed Jan 5 12:51:38 2011 -0600

    First attempt at AmiCollector.
    
    Typing of the callback factory class makes this unpleasant.

diff --git a/AmiCollector/CMakeLists.txt b/AmiCollector/CMakeLists.txt
new file mode 100644
index 0000000..47f2510
--- /dev/null
+++ b/AmiCollector/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(src)
+add_subdirectory(test)
diff --git a/AmiCollector/src/AmiCollector.cpp b/AmiCollector/src/AmiCollector.cpp
new file mode 100644
index 0000000..a5e4797
--- /dev/null
+++ b/AmiCollector/src/AmiCollector.cpp
@@ -0,0 +1,17 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+
+#include "AmiCollector.h"
diff --git a/AmiCollector/src/AmiCollector.h b/AmiCollector/src/AmiCollector.h
new file mode 100644
index 0000000..b01e74b
--- /dev/null
+++ b/AmiCollector/src/AmiCollector.h
@@ -0,0 +1,154 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+
+#pragma once
+
+#include <boost/thread/mutex.hpp>
+#include <Ice/Object.h>
+#include <Ice/Proxy.h>
+#include <Ice/OutgoingAsync.h>
+#include <IceUtil/Handle.h>
+#include <IceUtil/Shared.h>
+
+namespace AsteriskSCF
+{
+namespace IceUtil
+{
+
+/**
+ * Collects the results for multiple asynchronous responses.  While the vote
+ * counting logic is properly locked and thread safe, it is incumbent upon the
+ * implementor of the <code>process*</code> functions to write those functions
+ * to be thread safe.  Feel free to use mMutex (it's protected) if needed.
+ */
+template<typename T>
+class AmiCollector : public ::IceUtil::Shared
+{
+public:
+    typedef T AsyncResultType;
+    typedef AmiCollector<T> Self;
+
+    AmiCollector(size_t numVotes) : mNumVotes(numVotes)
+    {
+    }
+
+    /**
+     * Creates an Ice CallbackPtr wrapper around this object, for use with AMI.
+     */
+    template<typename CB, typename HN>
+    IceInternal::CallbackBasePtr toIceCallback(CB (*newCallbackFunction)
+        (HN,
+            void (Self::*cb)(T),
+            void (Self::*excb)(const ::Ice::Exception&),
+            void (Self::*sentcb)(bool)))
+    {
+        // this happens prior to any callbacks, to no need to lock
+
+        // handle the case where we don't expect any responses
+        // can't handle this in the ctor, since you can't call virtual
+        // functions in the constructor
+        if (mNumVotes == 0)
+        {
+            processCompletion();
+        }
+        return newCallbackFunction(this,
+            &AmiCollector<AsyncResultType>::resultCallback,
+            &AmiCollector<AsyncResultType>::errorCallback,
+            0 /* sentCallback */);
+    }
+
+    size_t getRemainingVotes() const { return mNumVotes; }
+
+protected:
+    /** Protected dtor prevents creating instances on the stack */
+    ~AmiCollector() {}
+    /**
+     * Called for each response received.
+     */
+    virtual void processResult(AsyncResultType result) = 0;
+    /**
+     * Called for each exception received.
+     */
+    virtual void processException(const Ice::Exception& e) = 0;
+    /**
+     * Called after all the responses or exceptions have been accounted for.
+     */
+    virtual void processCompletion() {}
+
+    /**
+     * AMI result callback.
+     */
+    void resultCallback(AsyncResultType result)
+    {
+        try
+        {
+            processResult(result);
+        }
+        catch(...)
+        {
+            countResponse();
+            throw;
+        }
+        countResponse();
+    }
+
+    /**
+     * AMI exception callback.
+     */
+    void errorCallback(const Ice::Exception& e)
+    {
+        try
+        {
+            processException(e);
+        }
+        catch(...)
+        {
+            countResponse();
+            throw;
+        }
+        countResponse();
+    }
+
+    boost::mutex mMutex;
+private:
+    size_t mNumVotes;
+
+    /**
+     * Called from each AMI callback.  When this function sees that the expected
+     * number of responses have been received, it invokes
+     * <code>processCompletion</code>
+     */
+    void countResponse()
+    {
+        bool done = false;
+        {
+            boost::lock_guard<boost::mutex> lock(mMutex);
+            assert(mNumVotes > 0); // we got more responses than expected
+            if (--mNumVotes == 0)
+            {
+                done = true;
+            }
+        }
+
+        if (done)
+        {
+            processCompletion();
+        }
+    }
+};
+
+} // IceUtil
+} // AsteriskSCF
diff --git a/AmiCollector/src/CMakeLists.txt b/AmiCollector/src/CMakeLists.txt
new file mode 100644
index 0000000..319aea0
--- /dev/null
+++ b/AmiCollector/src/CMakeLists.txt
@@ -0,0 +1,6 @@
+asterisk_scf_component_init(ami-collector CXX)
+
+asterisk_scf_component_add_file(ami-collector AmiCollector.h)
+asterisk_scf_component_add_file(ami-collector AmiCollector.cpp)
+
+asterisk_scf_component_build_library(ami-collector)
diff --git a/AmiCollector/test/AmiCollector-test.cpp b/AmiCollector/test/AmiCollector-test.cpp
new file mode 100644
index 0000000..92f1b1c
--- /dev/null
+++ b/AmiCollector/test/AmiCollector-test.cpp
@@ -0,0 +1,119 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+
+#include <Ice/Proxy.h>
+#include <boost/test/unit_test.hpp>
+
+#include "AmiCollector.h"
+
+using namespace AsteriskSCF::IceUtil;
+
+namespace
+{
+class TestAmiCollector : public AsteriskSCF::IceUtil::AmiCollector<bool>
+{
+public:
+    explicit TestAmiCollector(size_t n) :
+        AmiCollector<bool>(n),
+        results(0),
+        exceptions(0),
+        complete(false)
+    {}
+    int results;
+    int exceptions;
+    bool complete;
+
+    void resultCallback(AsyncResultType result)
+    {
+        AmiCollector<bool>::resultCallback(result);
+    }
+    void errorCallback(const Ice::Exception& e)
+    {
+        AmiCollector<bool>::errorCallback(e);
+    }
+
+protected:
+    ~TestAmiCollector() {}
+    void processResult(AsyncResultType result)
+    {
+        ++results;
+    }
+    void processException(const Ice::Exception& e)
+    {
+        ++exceptions;
+    }
+    void processCompletion()
+    {
+        BOOST_CHECK_EQUAL(false, complete);
+        complete = true;
+    }
+};
+
+typedef IceUtil::Handle<TestAmiCollector> TestAmiCollectorPtr;
+
+template<typename T>
+Ice::CallbackPtr noCallbackPtr(const IceUtil::Handle<T>& ptr,
+    void (T::*cb)(const int&),
+    void (T::*excb)(const Ice::Exception&),
+    void (T::*sentcb)(bool) = 0)
+{
+    return Ice::CallbackPtr();
+}
+}
+
+BOOST_AUTO_TEST_SUITE(AmiCollectorTest)
+
+BOOST_AUTO_TEST_CASE(test_zero)
+{
+    TestAmiCollectorPtr uut = new TestAmiCollector(0);
+
+    Ice::Callback_Object_ice_isAPtr (*factory)
+        (const IceUtil::Handle<AmiCollector<bool> >& instance,
+            void (AmiCollector<bool>::*cb)(bool),
+            void (AmiCollector<bool>::*excb)(const ::Ice::Exception&),
+            void (AmiCollector<bool>::*sentcb)(bool)) = Ice::newCallback_Object_ice_isA;
+
+    uut->toIceCallback(factory);
+
+    BOOST_CHECK_EQUAL(0, uut->results);
+    BOOST_CHECK_EQUAL(0, uut->exceptions);
+    BOOST_CHECK_EQUAL(true, uut->complete);
+}
+
+BOOST_AUTO_TEST_CASE(test_one)
+{
+    TestAmiCollectorPtr uut = new TestAmiCollector(1);
+
+    Ice::Callback_Object_ice_isAPtr (*factory)
+        (const IceUtil::Handle<AmiCollector<bool> >& instance,
+            void (AmiCollector<bool>::*cb)(bool),
+            void (AmiCollector<bool>::*excb)(const ::Ice::Exception&),
+            void (AmiCollector<bool>::*sentcb)(bool)) = Ice::newCallback_Object_ice_isA;
+
+    uut->toIceCallback(factory);
+
+    BOOST_CHECK_EQUAL(0, uut->results);
+    BOOST_CHECK_EQUAL(0, uut->exceptions);
+    BOOST_CHECK_EQUAL(false, uut->complete);
+
+    uut->resultCallback(true);
+
+    BOOST_CHECK_EQUAL(1, uut->results);
+    BOOST_CHECK_EQUAL(0, uut->exceptions);
+    BOOST_CHECK_EQUAL(true, uut->complete);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/AmiCollector/test/CMakeLists.txt b/AmiCollector/test/CMakeLists.txt
new file mode 100644
index 0000000..eb59de6
--- /dev/null
+++ b/AmiCollector/test/CMakeLists.txt
@@ -0,0 +1,11 @@
+asterisk_scf_component_init(ami-collector-test CXX)
+include_directories("../src")
+
+asterisk_scf_component_add_file(ami-collector-test AmiCollector-test.cpp)
+asterisk_scf_component_add_file(ami-collector-test test.cpp)
+
+asterisk_scf_component_add_boost_libraries(ami-collector-test unit_test_framework)
+
+asterisk_scf_component_build_standalone(ami-collector-test)
+
+boost_add_test(ami-collector-test)
diff --git a/AmiCollector/test/test.cpp b/AmiCollector/test/test.cpp
new file mode 100644
index 0000000..5503c6c
--- /dev/null
+++ b/AmiCollector/test/test.cpp
@@ -0,0 +1,18 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+
+#define BOOST_TEST_MODULE Logger-client
+#include <boost/test/unit_test.hpp>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8212b2a..67c5652 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,3 +5,4 @@ endif()
 
 add_subdirectory(SmartProxy)
 add_subdirectory(StateReplicator)
+add_subdirectory(AmiCollector)

-----------------------------------------------------------------------


-- 
asterisk-scf/release/ice-util-cpp.git



More information about the asterisk-scf-commits mailing list