[asterisk-scf-commits] asterisk-scf/integration/servicediscovery.git branch "retry_deux" updated.

Commits to the Asterisk SCF project code repositories asterisk-scf-commits at lists.digium.com
Fri Apr 6 08:09:47 CDT 2012


branch "retry_deux" has been updated
       via  f9677e4b65bb86cf31058c423dcae52d47558990 (commit)
      from  bf24a105985c98d18103138fab480ecf4e263f88 (commit)

Summary of changes:
 .../ServiceLocatorStateReplicationIf.ice           |   99 +++++-----
 src/ServiceLocator.cpp                             |  131 ++++++++++---
 src/ServiceLocatorManagement.cpp                   |  114 +++++++++---
 src/ServiceLocatorStateListener.cpp                |  200 ++++++++++++--------
 src/ServiceManagement.cpp                          |  135 ++++++++++----
 test/TestServiceLocator.cpp                        |  135 +++++++++++++-
 6 files changed, 587 insertions(+), 227 deletions(-)


- Log -----------------------------------------------------------------
commit f9677e4b65bb86cf31058c423dcae52d47558990
Author: David M. Lee <dlee at digium.com>
Date:   Fri Apr 6 08:09:10 2012 -0500

    Retry logic, thread safety and formatting.

diff --git a/slice/AsteriskSCF/Replication/ServiceLocator/ServiceLocatorStateReplicationIf.ice b/slice/AsteriskSCF/Replication/ServiceLocator/ServiceLocatorStateReplicationIf.ice
index 8fabc6c..65c0420 100755
--- a/slice/AsteriskSCF/Replication/ServiceLocator/ServiceLocatorStateReplicationIf.ice
+++ b/slice/AsteriskSCF/Replication/ServiceLocator/ServiceLocatorStateReplicationIf.ice
@@ -1,7 +1,7 @@
 /*
  * Asterisk SCF -- An open-source communications framework.
  *
- * Copyright (C) 2010, Digium, Inc.
+ * Copyright (C) 2010-2012, Digium, Inc.
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk SCF project. Please do not directly contact
@@ -33,52 +33,57 @@ module ServiceLocator
 ["suppress"]
 module V1
 {
-   const string StateReplicatorComponentCategory = "ServiceLocatorStateReplicatorComponent";
-   const string StateReplicatorDiscoveryCategory = "ServiceLocatorStateReplicator";
-
-   class ServiceLocatorStateItem
-   {
-      string key;
-   };
-
-   sequence<ServiceLocatorStateItem> ServiceLocatorStateItemSeq;
-
-   interface ServiceLocatorStateReplicatorListener
-   {
-	   void stateRemoved(AsteriskSCF::System::V1::OperationContext operationContext, Ice::StringSeq itemKeys);
-	   void stateSet(AsteriskSCF::System::V1::OperationContext operationContext, ServiceLocatorStateItemSeq items);
-   };
-
-   interface ServiceLocatorStateReplicator
-   {
-	   void addListener(AsteriskSCF::System::V1::OperationContext operationContext, ServiceLocatorStateReplicatorListener *listener);
-	   void removeListener(AsteriskSCF::System::V1::OperationContext operationContext, ServiceLocatorStateReplicatorListener *listener);
-	   void setState (AsteriskSCF::System::V1::OperationContext operationContext, ServiceLocatorStateItemSeq items);
-	   void removeState(AsteriskSCF::System::V1::OperationContext operationContext, Ice::StringSeq items);
-	   idempotent ServiceLocatorStateItemSeq getState(Ice::StringSeq itemKeys);
-	   idempotent ServiceLocatorStateItemSeq getAllState();
-   };
-
-   class ServiceLocatorServiceStateItem extends ServiceLocatorStateItem
-   {
-       bool suspended;
-       Object *service;
-       Ice::Identity managementIdentity;
-       string guid;
-   };
-
-   class ServiceLocatorParamsStateItem extends ServiceLocatorStateItem
-   {
-       string serviceKey;
-       AsteriskSCF::Core::Discovery::V1::ServiceLocatorParams params;
-       string compareGuid;
-   };
-
-   class ServiceLocatorComparatorStateItem extends ServiceLocatorStateItem
-   {
-       string name;
-       AsteriskSCF::Core::Discovery::V1::ServiceLocatorParamsCompare *comparator;
-   };
+
+const string StateReplicatorComponentCategory = "ServiceLocatorStateReplicatorComponent";
+const string StateReplicatorDiscoveryCategory = "ServiceLocatorStateReplicator";
+
+class ServiceLocatorStateItem
+{
+    string key;
+};
+
+sequence<ServiceLocatorStateItem> ServiceLocatorStateItemSeq;
+
+interface ServiceLocatorStateReplicatorListener
+{
+    idempotent void stateRemoved(AsteriskSCF::System::V1::OperationContext operationContext, Ice::StringSeq itemKeys);
+    idempotent void stateSet(AsteriskSCF::System::V1::OperationContext operationContext,
+        ServiceLocatorStateItemSeq items);
+};
+
+interface ServiceLocatorStateReplicator
+{
+    idempotent void addListener(AsteriskSCF::System::V1::OperationContext operationContext,
+        ServiceLocatorStateReplicatorListener *listener);
+    idempotent void removeListener(AsteriskSCF::System::V1::OperationContext operationContext,
+        ServiceLocatorStateReplicatorListener *listener);
+    idempotent void setState (AsteriskSCF::System::V1::OperationContext operationContext,
+        ServiceLocatorStateItemSeq items);
+    idempotent void removeState(AsteriskSCF::System::V1::OperationContext operationContext, Ice::StringSeq items);
+    idempotent ServiceLocatorStateItemSeq getState(Ice::StringSeq itemKeys);
+    idempotent ServiceLocatorStateItemSeq getAllState();
+};
+
+class ServiceLocatorServiceStateItem extends ServiceLocatorStateItem
+{
+    bool suspended;
+    Object *service;
+    Ice::Identity managementIdentity;
+    string guid;
+};
+
+class ServiceLocatorParamsStateItem extends ServiceLocatorStateItem
+{
+    string serviceKey;
+    AsteriskSCF::Core::Discovery::V1::ServiceLocatorParams params;
+    string compareGuid;
+};
+
+class ServiceLocatorComparatorStateItem extends ServiceLocatorStateItem
+{
+    string name;
+    AsteriskSCF::Core::Discovery::V1::ServiceLocatorParamsCompare *comparator;
+};
 
 }; /* module V1 */
 
diff --git a/src/ServiceLocator.cpp b/src/ServiceLocator.cpp
index d1ff0ca..ea37540 100644
--- a/src/ServiceLocator.cpp
+++ b/src/ServiceLocator.cpp
@@ -1,7 +1,7 @@
 /*
  * Asterisk SCF -- An open-source communications framework.
  *
- * Copyright (C) 2010, Digium, Inc.
+ * Copyright (C) 2010-2012, Digium, Inc.
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk SCF project. Please do not directly contact
@@ -13,12 +13,11 @@
  * the GNU General Public License Version 2. See the LICENSE.txt file
  * at the top of the source tree.
  */
-  
+
 //
 // These are moved up in include order because boost seems to have some kind of name collision on Windows.
 //
 #include <boost/thread.hpp>
-#include <boost/thread/shared_mutex.hpp>
 
 #include <Ice/Ice.h>
 #include <IceStorm/IceStorm.h>
@@ -32,19 +31,23 @@
 #include <AsteriskSCF/System/Component/ReplicaIf.h>
 #include <AsteriskSCF/CollocatedIceStorm/CollocatedIceStorm.h>
 #include <AsteriskSCF/Operations/OperationContext.h>
+#include <AsteriskSCF/Operations/OperationContextCache.h>
+#include <AsteriskSCF/Operations/OperationMonitor.h>
 
 #include "ServiceLocatorManagement.h"
 #include "ServiceManagement.h"
 #include "ServiceLocatorStateReplicator.h"
 
-using namespace std;
-using namespace AsteriskSCF;
-using namespace AsteriskSCF::System::Discovery;
-using namespace AsteriskSCF::System::Logging;
 using namespace AsteriskSCF::Core::Discovery::V1;
+using namespace AsteriskSCF::Operations;
 using namespace AsteriskSCF::Replication::ServiceLocator::V1;
 using namespace AsteriskSCF::ServiceDiscovery;
 using namespace AsteriskSCF::System::Component::V1;
+using namespace AsteriskSCF::System::Discovery;
+using namespace AsteriskSCF::System::Logging;
+using namespace AsteriskSCF::System::V1;
+using namespace AsteriskSCF;
+using namespace std;
 
 namespace
 {
@@ -78,53 +81,126 @@ private:
 class ReplicaImpl : public Replica
 {
 public:
-    ReplicaImpl(Ice::ObjectAdapterPtr adapter) : mAdapter(adapter), mPaused(false), mActive(true) { }
+    ReplicaImpl(Ice::ObjectAdapterPtr adapter) :
+        mOperationContextCache(OperationContextCache::create(DEFAULT_TTL_SECONDS)),
+        mAdapter(adapter),
+        mActive(true) { }
 
     bool isActive(const Ice::Current&)
     {
+        // naturally idempotent
+        boost::shared_lock<boost::shared_mutex> lock(mMutex);
         return mActive;
     }
 
-    bool activate(const AsteriskSCF::System::V1::OperationContextPtr& context, const Ice::Current&)
+    bool activate(const OperationContextPtr& context, const Ice::Current&)
     {
-        mActive = true;
+        ContextDataPtr data = checkAndThrow(mOperationContextCache, context);
+
+        if (!data)
+        {
+            // retry detected
+            return true; // always returns true.
+        }
+
+        try
+        {
+            // it's not good to loop through the listeners while holding the lock, so make a copy
+            vector<ReplicaListenerPrx> listeners;
+            {
+                boost::unique_lock<boost::shared_mutex> lock(mMutex);
+                mActive = true;
+                listeners = mListeners;
+            }
 
-        for (vector<AsteriskSCF::System::Component::V1::ReplicaListenerPrx>::const_iterator listener = mListeners.begin(); listener != mListeners.end(); ++listener)
+            for (vector<ReplicaListenerPrx>::const_iterator listener = listeners.begin();
+                 listener != listeners.end();
+                 ++listener)
+            {
+                (*listener)->activated(AsteriskSCF::Operations::createContext(context),
+                    ReplicaPrx::uncheckedCast(mAdapter->createDirectProxy(mAdapter->getCommunicator()->stringToIdentity(ReplicaServiceId))));
+            }
+        }
+        catch (const std::exception& e)
+        {
+            data->setException(e);
+        }
+        catch (...)
         {
-            (*listener)->activated(AsteriskSCF::Operations::createContext(context),
-                ReplicaPrx::uncheckedCast(mAdapter->createDirectProxy(mAdapter->getCommunicator()->stringToIdentity(ReplicaServiceId))));
+            data->setException();
         }
 
         return true;
     }
 
-    void standby(const AsteriskSCF::System::V1::OperationContextPtr& context, const Ice::Current&)
+    void standby(const OperationContextPtr& context, const Ice::Current&)
     {
-        mActive = false;
+        ContextDataPtr data = checkAndThrow(mOperationContextCache, context);
 
-        for (vector<AsteriskSCF::System::Component::V1::ReplicaListenerPrx>::const_iterator listener = mListeners.begin(); listener != mListeners.end(); ++listener)
+        if (!data)
         {
-            (*listener)->onStandby(AsteriskSCF::Operations::createContext(context),
-                ReplicaPrx::uncheckedCast(mAdapter->createDirectProxy(mAdapter->getCommunicator()->stringToIdentity(ReplicaServiceId))));
+            // retry detected
+            return;
+        }
+
+        try
+        {
+            // it's not good to loop through the listeners while holding the lock, so make a copy
+            vector<ReplicaListenerPrx> listeners;
+            {
+                boost::unique_lock<boost::shared_mutex> lock(mMutex);
+                mActive = false;
+                listeners = mListeners;
+            }
+
+            for (vector<ReplicaListenerPrx>::const_iterator listener = listeners.begin();
+                 listener != listeners.end();
+                 ++listener)
+            {
+                (*listener)->onStandby(AsteriskSCF::Operations::createContext(context),
+                    ReplicaPrx::uncheckedCast(mAdapter->createDirectProxy(mAdapter->getCommunicator()->stringToIdentity(ReplicaServiceId))));
+            }
+        }
+        catch (const std::exception& e)
+        {
+            data->setException(e);
+        }
+        catch (...)
+        {
+            data->setException();
         }
     }
 
-    void addListener(const AsteriskSCF::System::V1::OperationContextPtr&, const AsteriskSCF::System::Component::V1::ReplicaListenerPrx& listener, const Ice::Current&)
+    void addListener(const OperationContextPtr& context, const ReplicaListenerPrx& listener, const Ice::Current&)
     {
+        if (!mOperationContextCache->addOperationContext(context))
+        {
+            // retry detected
+            return;
+        }
+        boost::unique_lock<boost::shared_mutex> lock(mMutex);
         mListeners.push_back(listener);
     }
 
-    void removeListener(const AsteriskSCF::System::V1::OperationContextPtr&, const AsteriskSCF::System::Component::V1::ReplicaListenerPrx& listener, const Ice::Current&)
+    void removeListener(const OperationContextPtr& context, const ReplicaListenerPrx& listener, const Ice::Current&)
     {
+        if (!mOperationContextCache->addOperationContext(context))
+        {
+            // retry detected
+            return;
+        }
+        boost::unique_lock<boost::shared_mutex> lock(mMutex);
         mListeners.erase(std::remove(mListeners.begin(), mListeners.end(), listener), mListeners.end());
     }
 
 private:
-    Ice::ObjectAdapterPtr mAdapter;
+    boost::shared_mutex mMutex;
+
+    OperationContextCachePtr mOperationContextCache;
 
-    vector<AsteriskSCF::System::Component::V1::ReplicaListenerPrx> mListeners;
+    Ice::ObjectAdapterPtr mAdapter;
 
-    bool mPaused;
+    vector<ReplicaListenerPrx> mListeners;
 
     bool mActive;
 };
@@ -155,7 +231,7 @@ private:
      * actually stored. Our ServiceLocator implementation simply acts as a read-only
      *  frontend to it.
      */
-    ServiceLocatorManagementImplPtr mLocatorServiceManagement;
+    const ServiceLocatorManagementImplPtr mLocatorServiceManagement;
 };
 
 }
@@ -165,6 +241,7 @@ void ServiceLocatorImpl::locate_async(const AMD_ServiceLocator_locatePtr& cb,
     const ::Ice::Current&)
 {
     // delegate to the management object, where the data is kept
+    // mLocatorServiceManagement is const; no lock needed
     mLocatorServiceManagement->locate(cb, params);
 }
 
@@ -173,6 +250,7 @@ void ServiceLocatorImpl::locateAll_async(const AMD_ServiceLocator_locateAllPtr&
     const ::Ice::Current&)
 {
     // delegate to the management object, where the data is kept
+    // mLocatorServiceManagement is const; no lock needed
     mLocatorServiceManagement->locateAll(cb, params);
 }
 
@@ -333,10 +411,7 @@ void ServiceLocatorApp::stop()
     mIceStorm->stop();
 }
 
-extern "C"
-{
-ASTSCF_DLL_EXPORT IceBox::Service* create(Ice::CommunicatorPtr)
+extern "C" ASTSCF_DLL_EXPORT IceBox::Service* create(Ice::CommunicatorPtr)
 {
     return new ServiceLocatorApp;
 }
-}
diff --git a/src/ServiceLocatorManagement.cpp b/src/ServiceLocatorManagement.cpp
index a04fd3f..11e31dd 100644
--- a/src/ServiceLocatorManagement.cpp
+++ b/src/ServiceLocatorManagement.cpp
@@ -1,7 +1,7 @@
 /*
  * Asterisk SCF -- An open-source communications framework.
  *
- * Copyright (C) 2010, Digium, Inc.
+ * Copyright (C) 2010-2012, Digium, Inc.
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk SCF project. Please do not directly contact
@@ -28,19 +28,21 @@
 #include <AsteriskSCF/Logger.h>
 #include <AsteriskSCF/Async/ResponseCollector.h>
 #include <AsteriskSCF/Operations/OperationContext.h>
+#include <AsteriskSCF/Operations/OperationMonitor.h>
 
 #include "ServiceLocatorStateReplicationIf.h"
 
 #include "ServiceLocatorManagement.h"
 #include "ServiceManagement.h"
 
-using namespace std;
-using namespace AsteriskSCF::System::Discovery;
-using namespace AsteriskSCF::System::Logging;
 using namespace AsteriskSCF::Core::Discovery::V1;
+using namespace AsteriskSCF::Operations;
 using namespace AsteriskSCF::Replication::ServiceLocator::V1;
 using namespace AsteriskSCF::ServiceDiscovery;
+using namespace AsteriskSCF::System::Discovery;
+using namespace AsteriskSCF::System::Logging;
 using namespace AsteriskSCF;
+using namespace std;
 
 //
 // Normally would use an anonymous namespace here, but there is a compiler
@@ -51,6 +53,9 @@ namespace ServiceLocatorManagementImplNS
 
 const Logger& lg = getLoggerFactory().getLogger("AsteriskSCF.System.Discovery");
 
+typedef ContextResultData<ServiceManagementPrx> AddServiceResultData;
+typedef boost::shared_ptr<AddServiceResultData> AddServiceResultDataPtr;
+
 /** Parameter type for Locator, to allow use of ResponseCollector. */
 typedef std::pair<bool, ServiceManagementImplPtr> LocateParam;
 
@@ -321,9 +326,10 @@ public:
     ServiceLocatorManagementImplPriv(const Ice::ObjectAdapterPtr& adapter,
         const EventsPrx& serviceDiscoveryTopic,
 	const AsteriskSCF::System::Component::V1::ReplicaPtr replicaService) :
+        mOperationContextCache(OperationContextCache::create(DEFAULT_TTL_SECONDS)),
         mAdapter(adapter), mLocatorTopic(serviceDiscoveryTopic), mReplicaService(replicaService)
     {
-    };
+    }
 
     /**
      * Shared mutex lock which protects the services and comparators.
@@ -331,6 +337,11 @@ public:
     boost::shared_mutex mLock;
 
     /**
+     * Context cache for retry detection.
+     */
+    OperationContextCachePtr mOperationContextCache;
+
+    /**
      * Object adapter that our service management proxies originate from, it is
      * houses the main management service.
      */
@@ -424,9 +435,16 @@ void ServiceLocatorManagementImpl::locateAll(
  * Implementation of the addService method as defined in service_locator.ice
  */
 ServiceManagementPrx ServiceLocatorManagementImpl::addService(
-    const AsteriskSCF::System::V1::OperationContextPtr&,
+    const AsteriskSCF::System::V1::OperationContextPtr& context,
     const Ice::ObjectPrx& service, const string& guid, const Ice::Current&)
 {
+    std::pair<bool, AddServiceResultDataPtr> cacheHit = getContextSync<AddServiceResultDataPtr>(mImpl->mOperationContextCache, context);
+
+    if (cacheHit.first)
+    {
+        return cacheHit.second->getResult();
+    }
+
     lg(Debug) << "addService(" << guid << ')';
     boost::unique_lock<boost::shared_mutex> lock(mImpl->mLock);
 
@@ -439,10 +457,12 @@ ServiceManagementPrx ServiceLocatorManagementImpl::addService(
 
     mImpl->mServices.push_back(new_service);
 
-    return new_service->getServiceManagementPrx();
+    ServiceManagementPrx r = new_service->getServiceManagementPrx();
+    cacheHit.second->setResult(r);
+    return r;
 }
 
-ServiceManagementImplPtr ServiceLocatorManagementImpl::addService(const Ice::ObjectPrx& service, 
+ServiceManagementImplPtr ServiceLocatorManagementImpl::addService(const Ice::ObjectPrx& service,
     const std::string& guid, const Ice::Identity& identity)
 {
     lg(Debug) << "addService(" << guid << ')';
@@ -492,40 +512,84 @@ ServiceInfo ServiceLocatorManagementImpl::getService(const std::string &guid, co
 void ServiceLocatorManagementImpl::addCompare(const AsteriskSCF::System::V1::OperationContextPtr& context, const string& guid,
     const ServiceLocatorParamsComparePrx& service, const Ice::Current&)
 {
-    lg(Debug) << "addCompare(" << guid << ')';
-    boost::unique_lock<boost::shared_mutex> lock(mImpl->mLock);
-
-    pair<map<string, ServiceLocatorParamsComparePrx>::iterator, bool> insertPair =
-        mImpl->mCompares.insert(make_pair(guid, service));
+    ContextDataPtr data = checkAndThrow(mImpl->mOperationContextCache, context);
 
-    if (insertPair.second == false)
+    if (!data)
     {
-        throw DuplicateCompare();
+        // retry detected
+        return;
     }
 
-    if (mImpl->mLocatorTopic)
+    try
+    {
+        lg(Debug) << "addCompare(" << guid << ')';
+        boost::unique_lock<boost::shared_mutex> lock(mImpl->mLock);
+
+        pair<map<string, ServiceLocatorParamsComparePrx>::iterator, bool> insertPair =
+            mImpl->mCompares.insert(make_pair(guid, service));
+
+        if (insertPair.second == false)
+        {
+            throw DuplicateCompare();
+        }
+
+        if (mImpl->mLocatorTopic)
+        {
+            mImpl->mLocatorTopic->comparisonRegistered(AsteriskSCF::Operations::createContext(context), guid);
+        }
+        data->setCompleted();
+    }
+    catch(const std::exception& e)
     {
-        mImpl->mLocatorTopic->comparisonRegistered(AsteriskSCF::Operations::createContext(context), guid);
+        data->setException(e);
+        throw;
+    }
+    catch(...)
+    {
+        data->setException();
+        throw;
     }
 }
 
 /**
  * Implementation of the removeCompare method as defined in service_locator.ice
  */
-void ServiceLocatorManagementImpl::removeCompare(const AsteriskSCF::System::V1::OperationContextPtr& context, 
+void ServiceLocatorManagementImpl::removeCompare(const AsteriskSCF::System::V1::OperationContextPtr& context,
     const string& guid, const Ice::Current&)
 {
-    lg(Debug) << "removeCompare(" << guid << ')';
-    boost::unique_lock<boost::shared_mutex> lock(mImpl->mLock);
-    std::map<std::string, ServiceLocatorParamsComparePrx>::size_type erased = mImpl->mCompares.erase(guid);
+    ContextDataPtr data = checkAndThrow(mImpl->mOperationContextCache, context);
 
-    if (!erased)
+    if (!data)
     {
-        throw CompareNotFound();
+        // retry detected
+        return;
     }
-    else if (mImpl->mLocatorTopic)
+
+    try
+    {
+        lg(Debug) << "removeCompare(" << guid << ')';
+        boost::unique_lock<boost::shared_mutex> lock(mImpl->mLock);
+        std::map<std::string, ServiceLocatorParamsComparePrx>::size_type erased = mImpl->mCompares.erase(guid);
+
+        if (!erased)
+        {
+            throw CompareNotFound();
+        }
+        else if (mImpl->mLocatorTopic)
+        {
+            mImpl->mLocatorTopic->comparisonUnregistered(AsteriskSCF::Operations::createContext(context), guid);
+        }
+        data->setCompleted();
+    }
+    catch(const std::exception& e)
+    {
+        data->setException(e);
+        throw;
+    }
+    catch(...)
     {
-        mImpl->mLocatorTopic->comparisonUnregistered(AsteriskSCF::Operations::createContext(context), guid);
+        data->setException();
+        throw;
     }
 }
 
diff --git a/src/ServiceLocatorStateListener.cpp b/src/ServiceLocatorStateListener.cpp
index fbc6bf4..fd27b06 100644
--- a/src/ServiceLocatorStateListener.cpp
+++ b/src/ServiceLocatorStateListener.cpp
@@ -1,7 +1,7 @@
 /*
  * Asterisk SCF -- An open-source communications framework.
  *
- * Copyright (C) 2010, Digium, Inc.
+ * Copyright (C) 2010-2012, Digium, Inc.
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk SCF project. Please do not directly contact
@@ -20,11 +20,11 @@
 #include <IceUtil/UUID.h>
 
 #include <AsteriskSCF/System/Component/ReplicaIf.h>
-// #include <AsteriskSCF/Discovery/SmartProxy.h>
-
 #include <AsteriskSCF/Core/Discovery/ServiceLocatorIf.h>
 #include <AsteriskSCF/Core/Discovery/ServiceLocatorEventsIf.h>
 #include <AsteriskSCF/Operations/OperationContext.h>
+#include <AsteriskSCF/Operations/OperationContextCache.h>
+#include <AsteriskSCF/Operations/OperationMonitor.h>
 
 #include "ServiceLocatorManagement.h"
 #include "ServiceManagement.h"
@@ -34,13 +34,15 @@
 using namespace AsteriskSCF::Core::Discovery::V1;
 using namespace AsteriskSCF::Replication::ServiceLocator::V1;
 using namespace AsteriskSCF::ServiceDiscovery;
+using namespace AsteriskSCF::System::V1;
+using namespace AsteriskSCF::Operations;
 
 class ServiceLocatorStateReplicatorItem
 {
 public:
     ServiceLocatorStateReplicatorItem(const ServiceLocatorManagementImplPtr& locatorManagement) :
-	  mLocatorManagement(locatorManagement) 
-    { 
+        mLocatorManagement(locatorManagement)
+    {
     }
 
     ~ServiceLocatorStateReplicatorItem()
@@ -89,95 +91,126 @@ private:
 struct ServiceLocatorStateReplicatorListenerImpl
 {
 public:
-    ServiceLocatorStateReplicatorListenerImpl(ServiceLocatorManagementImplPtr management)
-        : mId(IceUtil::generateUUID()), mLocatorManagement(management) {}
+    ServiceLocatorStateReplicatorListenerImpl(ServiceLocatorManagementImplPtr management) :
+    mOperationContextCache(OperationContextCache::create(DEFAULT_TTL_SECONDS)),
+        mId(IceUtil::generateUUID()), mLocatorManagement(management) {}
+
     void removeStateNoticeImpl(const Ice::StringSeq& itemKeys)
     {
+        boost::mutex::scoped_lock lock(mMutex);
         for (Ice::StringSeq::const_iterator key = itemKeys.begin(); key != itemKeys.end(); ++key)
         {
             // Just erasing this from the map will cause the destructor to actually shut things down
             mStateItems.erase((*key));
         }
     }
-    void setStateNoticeImpl(const ServiceLocatorStateItemSeq& items)
+    void setStateNoticeImpl(const OperationContextPtr& context, const ServiceLocatorStateItemSeq& items)
     {
-        for (ServiceLocatorStateItemSeq::const_iterator item = items.begin(); item != items.end(); ++item)
+        ContextDataPtr data = checkAndThrow(mOperationContextCache, context);
+
+        if (!data)
+        {
+            // retry detected
+            return;
+        }
+
+        boost::mutex::scoped_lock lock(mMutex);
+
+        try
         {
-	    ServiceLocatorServiceStateItemPtr serviceState;
-	    ServiceLocatorParamsStateItemPtr paramsState;
-	    ServiceLocatorComparatorStateItemPtr comparatorState;
-
-	    if ((serviceState = ServiceLocatorServiceStateItemPtr::dynamicCast((*item))))
-	    {
-		std::map<std::string, boost::shared_ptr<ServiceLocatorStateReplicatorItem> >::iterator i = mStateItems.find((*item)->key);
-	        boost::shared_ptr<ServiceLocatorStateReplicatorItem> localitem;
-
-		if ((i == mStateItems.end()))
-		{
-		    boost::shared_ptr<ServiceLocatorStateReplicatorItem> newitem(new ServiceLocatorStateReplicatorItem(mLocatorManagement));
-                    localitem = newitem;
-                    mStateItems.insert(std::make_pair((*item)->key, newitem));
-		    ServiceManagementImplPtr service = mLocatorManagement->addService(
-                        serviceState->service, serviceState->guid, serviceState->managementIdentity);
-		    newitem->setService(service);
-		}
-		else
-		{
-		    localitem = i->second;
-		}
-
-		// The only thing that can be changed by a subsequent state item is the suspend status
-		if (serviceState->suspended == true)
-		{
-		    localitem->getService()->suspend(AsteriskSCF::Operations::createContext());
-		}
-		else
+            for (ServiceLocatorStateItemSeq::const_iterator item = items.begin(); item != items.end(); ++item)
+            {
+                ServiceLocatorServiceStateItemPtr serviceState;
+                ServiceLocatorParamsStateItemPtr paramsState;
+                ServiceLocatorComparatorStateItemPtr comparatorState;
+
+                if ((serviceState = ServiceLocatorServiceStateItemPtr::dynamicCast((*item))))
+                {
+                    std::map<std::string, boost::shared_ptr<ServiceLocatorStateReplicatorItem> >::iterator i = mStateItems.find((*item)->key);
+                    boost::shared_ptr<ServiceLocatorStateReplicatorItem> localitem;
+
+                    if ((i == mStateItems.end()))
+                    {
+                        boost::shared_ptr<ServiceLocatorStateReplicatorItem> newitem(new ServiceLocatorStateReplicatorItem(mLocatorManagement));
+                        localitem = newitem;
+                        mStateItems.insert(std::make_pair((*item)->key, newitem));
+                        ServiceManagementImplPtr service = mLocatorManagement->addService(
+                            serviceState->service, serviceState->guid, serviceState->managementIdentity);
+                        newitem->setService(service);
+                    }
+                    else
+                    {
+                        localitem = i->second;
+                    }
+
+                    // The only thing that can be changed by a subsequent state item is the suspend status
+                    if (serviceState->suspended == true)
+                    {
+                        localitem->getService()->suspend(AsteriskSCF::Operations::createContext(context));
+                    }
+                    else
+                    {
+                        localitem->getService()->unsuspend(AsteriskSCF::Operations::createContext(context));
+                    }
+                }
+                else if ((paramsState = ServiceLocatorParamsStateItemPtr::dynamicCast((*item))))
                 {
-		    localitem->getService()->unsuspend(AsteriskSCF::Operations::createContext());
-		}
-	    }
-	    else if ((paramsState = ServiceLocatorParamsStateItemPtr::dynamicCast((*item))))
-	    {
-		// This is special, we have to find the respective service and then add parameters to it
-		std::map<std::string, boost::shared_ptr<ServiceLocatorStateReplicatorItem> >::iterator i = mStateItems.find(paramsState->serviceKey);
-
-	        if ((i == mStateItems.end()))
-		{
-		    continue;
-		}
-
-		// Parameters are only ever added, they are never modified or removed
-		i->second->getService()->addLocatorParams(AsteriskSCF::Operations::createContext(),
-                    paramsState->params, paramsState->compareGuid);
-	    }
-	    else if ((comparatorState = ServiceLocatorComparatorStateItemPtr::dynamicCast((*item))))
-	    {
-		std::map<std::string, boost::shared_ptr<ServiceLocatorStateReplicatorItem> >::iterator i = mStateItems.find((*item)->key);
-
-                if ((i != mStateItems.end()))
-		{
-		    // If this happens we essentially got a duplicate state item for something that should never change, so ignore it
-		    continue;
-		}
-
-		try
-		{
-		    Ice::Current current;
-		    mLocatorManagement->addCompare(AsteriskSCF::Operations::createContext(), comparatorState->name, comparatorState->comparator, current);
-		    boost::shared_ptr<ServiceLocatorStateReplicatorItem> newitem(new ServiceLocatorStateReplicatorItem(mLocatorManagement));
-		    mStateItems.insert(std::make_pair((*item)->key, newitem));
-		    newitem->setComparator(comparatorState->name);
-		}
-		catch (...)
-		{
-		    // It is possible for this to get reached if a comparator exists locally with the same name as the one we just tried to add
-		}
-	    }
+                    // This is special, we have to find the respective service and then add parameters to it
+                    std::map<std::string, boost::shared_ptr<ServiceLocatorStateReplicatorItem> >::iterator i = mStateItems.find(paramsState->serviceKey);
+
+                    if ((i == mStateItems.end()))
+                    {
+                        continue;
+                    }
+
+                    // Parameters are only ever added, they are never modified or removed
+                    i->second->getService()->addLocatorParams(AsteriskSCF::Operations::createContext(context),
+                        paramsState->params, paramsState->compareGuid);
+                }
+                else if ((comparatorState = ServiceLocatorComparatorStateItemPtr::dynamicCast((*item))))
+                {
+                    std::map<std::string, boost::shared_ptr<ServiceLocatorStateReplicatorItem> >::iterator i = mStateItems.find((*item)->key);
+
+                    if ((i != mStateItems.end()))
+                    {
+                        // If this happens we essentially got a duplicate state item for something that should never change, so ignore it
+                        continue;
+                    }
+
+                    try
+                    {
+                        Ice::Current current;
+                        mLocatorManagement->addCompare(AsteriskSCF::Operations::createContext(context), comparatorState->name, comparatorState->comparator, current);
+                        boost::shared_ptr<ServiceLocatorStateReplicatorItem> newitem(new ServiceLocatorStateReplicatorItem(mLocatorManagement));
+                        mStateItems.insert(std::make_pair((*item)->key, newitem));
+                        newitem->setComparator(comparatorState->name);
+                    }
+                    catch (...)
+                    {
+                        // It is possible for this to get reached if a comparator exists locally with the same name as the one we just tried to add
+                    }
+                }
+            }
+        }
+        catch (const std::exception& e)
+        {
+            data->setException(e);
+        }
+        catch (...)
+        {
+            assert(false);
+            data->setException();
         }
     }
-    std::string mId;
+
+    const std::string& getId() const { return mId; }
+
+private:
+    boost::mutex mMutex;
+    OperationContextCachePtr mOperationContextCache;
+    const std::string mId;
     std::map<std::string, boost::shared_ptr<ServiceLocatorStateReplicatorItem> > mStateItems;
-    ServiceLocatorManagementImplPtr mLocatorManagement;
+    const ServiceLocatorManagementImplPtr mLocatorManagement;
 };
 
 ServiceLocatorStateReplicatorListenerI::ServiceLocatorStateReplicatorListenerI(const ServiceLocatorManagementImplPtr& management)
@@ -187,15 +220,16 @@ ServiceLocatorStateReplicatorListenerI::ServiceLocatorStateReplicatorListenerI(c
 
 void ServiceLocatorStateReplicatorListenerI::stateRemoved(const AsteriskSCF::System::V1::OperationContextPtr&, const Ice::StringSeq& itemKeys, const Ice::Current&)
 {
+    // naturally idempotent
     mImpl->removeStateNoticeImpl(itemKeys);
 }
 
-void ServiceLocatorStateReplicatorListenerI::stateSet(const AsteriskSCF::System::V1::OperationContextPtr&, const ServiceLocatorStateItemSeq& items, const Ice::Current&)
+void ServiceLocatorStateReplicatorListenerI::stateSet(const AsteriskSCF::System::V1::OperationContextPtr& context, const ServiceLocatorStateItemSeq& items, const Ice::Current&)
 {
-    mImpl->setStateNoticeImpl(items);
+    mImpl->setStateNoticeImpl(context, items);
 }
 
 bool ServiceLocatorStateReplicatorListenerI::operator==(const ServiceLocatorStateReplicatorListenerI &rhs)
 {
-    return mImpl->mId == rhs.mImpl->mId;
+    return mImpl->getId() == rhs.mImpl->getId();
 }
diff --git a/src/ServiceManagement.cpp b/src/ServiceManagement.cpp
index d5d186a..493238a 100644
--- a/src/ServiceManagement.cpp
+++ b/src/ServiceManagement.cpp
@@ -1,7 +1,7 @@
 /*
  * Asterisk SCF -- An open-source communications framework.
  *
- * Copyright (C) 2010, Digium, Inc.
+ * Copyright (C) 2010-2012, Digium, Inc.
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk SCF project. Please do not directly contact
@@ -24,24 +24,28 @@
 #include <IceUtil/UUID.h>
 
 #include <boost/shared_ptr.hpp>
-#include <AsteriskSCF/Operations/OperationContext.h>
 
-#include <AsteriskSCF/Core/Discovery/ServiceLocatorIf.h>
+#include <AsteriskSCF/Async/ResponseCollector.h>
 #include <AsteriskSCF/Core/Discovery/ServiceLocatorEventsIf.h>
+#include <AsteriskSCF/Core/Discovery/ServiceLocatorIf.h>
 #include <AsteriskSCF/Logger.h>
-#include <AsteriskSCF/Async/ResponseCollector.h>
+#include <AsteriskSCF/Operations/OperationContext.h>
+#include <AsteriskSCF/Operations/OperationContextCache.h>
+#include <AsteriskSCF/Operations/OperationMonitor.h>
 
 #include "ServiceLocatorStateReplicationIf.h"
 
 #include "ServiceLocatorManagement.h"
 #include "ServiceManagement.h"
 
-using namespace std;
 using namespace AsteriskSCF::Core::Discovery::V1;
+using namespace AsteriskSCF::Operations;
 using namespace AsteriskSCF::Replication::ServiceLocator::V1;
 using namespace AsteriskSCF::ServiceDiscovery;
 using namespace AsteriskSCF::System::Logging;
+using namespace AsteriskSCF::System::V1;
 using namespace AsteriskSCF;
+using namespace std;
 
 namespace
 {
@@ -106,6 +110,7 @@ public:
     ServiceManagementImplPriv(ServiceManagementImpl* impl, ServiceLocatorManagementImplPtr management,
         const Ice::ObjectPrx& service, const Ice::ObjectAdapterPtr& adapter, const AsteriskSCF::System::Discovery::EventsPrx& serviceDiscoveryTopic,
         const string& guid, const Ice::Identity& identity) :
+        mOperationContextCache(OperationContextCache::create(DEFAULT_TTL_SECONDS)),
         mStateItem(new ServiceLocatorServiceStateItem()),
         mManagement(management), mAdapter(adapter), mLocatorTopic(serviceDiscoveryTopic)
     {
@@ -127,6 +132,8 @@ public:
      */
     boost::shared_mutex mLock;
 
+    OperationContextCachePtr mOperationContextCache;
+
     /**
      * Service state replication item.
      */
@@ -280,8 +287,8 @@ void ServiceManagementImpl::isSupported(const ServiceLocatorParamsPtr& params, c
     {
         if ((*spec)->isSupported(params, myCallback))
         {
-            // If we get here, a match was found without needing to call an external comparator. 
-            // We are done. 
+            // If we get here, a match was found without needing to call an external comparator.
+            // We are done.
             break;
         }
     }
@@ -300,13 +307,13 @@ const std::string& ServiceManagementImpl::getGuid() const
  *               that is trying to be found.
  * @param callback Callback to asynchronously rx the results.
  *
- * @return True only if we determined a match without any need for calling a custom comparator. 
- *         Otherwise, the callback may be accumulating the results, or it's not a match. 
+ * @return True only if we determined a match without any need for calling a custom comparator.
+ *         Otherwise, the callback may be accumulating the results, or it's not a match.
  */
 bool ServiceLocatorParamsSpec::isSupported(const ServiceLocatorParamsPtr& params, const IsSupportedCallbackPtr& callback)
 {
-    // Does the component doing the locate 
-    // want to filter based on category. 
+    // Does the component doing the locate
+    // want to filter based on category.
     if (!params->category.empty())
     {
         // Is this the wrong category?
@@ -319,18 +326,18 @@ bool ServiceLocatorParamsSpec::isSupported(const ServiceLocatorParamsPtr& params
         }
     }
 
-    // Does the component doing the locate 
-    // want all services in the category? 
+    // Does the component doing the locate
+    // want all services in the category?
     if (params->service.empty())
     {
-        // If a comparator was provided then yield to it. 
+        // If a comparator was provided then yield to it.
         if (!mStateItem->compareGuid.empty())
         {
             mManagement->isSupported(mStateItem->compareGuid, params, callback);
             return false;
         }
 
-        // Ignore the id and treat this as a wildcard search. 
+        // Ignore the id and treat this as a wildcard search.
 
         lg(Trace) << "  ...isSupported" << debugPrintParams(params) + " = true. Category match explicit, wildcard match service.";
         callback->result(true);
@@ -357,15 +364,15 @@ bool ServiceLocatorParamsSpec::isSupported(const ServiceLocatorParamsPtr& params
         }
     }
 
-    // If a comparator was provided then yield to it. 
+    // If a comparator was provided then yield to it.
     if (!mStateItem->compareGuid.empty())
     {
         mManagement->isSupported(mStateItem->compareGuid, params, callback);
         return false;
     }
 
-    // If we get here we have a match on service and id. 
-    // (and category, if one was passed in.) 
+    // If we get here we have a match on service and id.
+    // (and category, if one was passed in.)
     lg(Trace) << "  ...isSupported" << debugPrintParams(params) + " = true";
     callback->result(true);
     return true;
@@ -375,9 +382,15 @@ bool ServiceLocatorParamsSpec::isSupported(const ServiceLocatorParamsPtr& params
  * Implementation of the addLocatorParams method as defined in service_locator.ice
  */
 void ServiceManagementImpl::addLocatorParams(
-    const AsteriskSCF::System::V1::OperationContextPtr&,
+    const AsteriskSCF::System::V1::OperationContextPtr& context,
     const ServiceLocatorParamsPtr& params, const std::string& compareGuid, const Ice::Current&)
 {
+    if (!mImpl->mOperationContextCache->addOperationContext(context))
+    {
+        // retry detected
+        return;
+    }
+
     boost::unique_lock<boost::shared_mutex> lock(mImpl->mLock);
 
     boost::shared_ptr<ServiceLocatorParamsSpec> spec(new ServiceLocatorParamsSpec(params, compareGuid, mImpl->mManagement, mImpl->mStateItem));
@@ -390,18 +403,39 @@ void ServiceManagementImpl::addLocatorParams(
  */
 void ServiceManagementImpl::suspend(const AsteriskSCF::System::V1::OperationContextPtr& context, const Ice::Current&)
 {
-    boost::unique_lock<boost::shared_mutex> lock(mImpl->mLock);
+    ContextDataPtr data = checkAndThrow(mImpl->mOperationContextCache, context);
 
-    if (!mImpl->mStateItem->suspended)
+    if (!data)
     {
-        lg(Info) << "Suspending " << mImpl->mStateItem->guid << " " << mImpl->mStateItem->service->ice_toString();
-        mImpl->mStateItem->suspended = true;
-        mImpl->mManagement->replicateState(mImpl->mStateItem);
+        // retry detected
+        return;
     }
 
-    if (mImpl->mLocatorTopic)
+    try
+    {
+        boost::unique_lock<boost::shared_mutex> lock(mImpl->mLock);
+
+        if (!mImpl->mStateItem->suspended)
+        {
+            lg(Info) << "Suspending " << mImpl->mStateItem->guid << " " << mImpl->mStateItem->service->ice_toString();
+            mImpl->mStateItem->suspended = true;
+            mImpl->mManagement->replicateState(mImpl->mStateItem);
+        }
+
+        if (mImpl->mLocatorTopic)
+        {
+            mImpl->mLocatorTopic->serviceSuspended(AsteriskSCF::Operations::createContext(context), mImpl->mStateItem->guid);
+        }
+        data->setCompleted();
+    }
+    catch (const std::exception& e)
     {
-        mImpl->mLocatorTopic->serviceSuspended(AsteriskSCF::Operations::createContext(context), mImpl->mStateItem->guid);
+        data->setException(e);
+    }
+    catch (...)
+    {
+        assert(false);
+        data->setException();
     }
 }
 
@@ -410,18 +444,38 @@ void ServiceManagementImpl::suspend(const AsteriskSCF::System::V1::OperationCont
  */
 void ServiceManagementImpl::unsuspend(const AsteriskSCF::System::V1::OperationContextPtr& context, const Ice::Current&)
 {
-    boost::unique_lock<boost::shared_mutex> lock(mImpl->mLock);
+    ContextDataPtr data = checkAndThrow(mImpl->mOperationContextCache, context);
 
-    if (mImpl->mStateItem->suspended)
+    if (!data)
     {
-        lg(Info) << "Un-suspending " << mImpl->mStateItem->guid << " " << mImpl->mStateItem->service->ice_toString();
-        mImpl->mStateItem->suspended = false;
-        mImpl->mManagement->replicateState(mImpl->mStateItem);
+        // retry detected
+        return;
     }
 
-    if (mImpl->mLocatorTopic)
+    try
     {
-        mImpl->mLocatorTopic->serviceUnsuspended(context, mImpl->mStateItem->guid);
+        boost::unique_lock<boost::shared_mutex> lock(mImpl->mLock);
+
+        if (mImpl->mStateItem->suspended)
+        {
+            lg(Info) << "Un-suspending " << mImpl->mStateItem->guid << " " << mImpl->mStateItem->service->ice_toString();
+            mImpl->mStateItem->suspended = false;
+            mImpl->mManagement->replicateState(mImpl->mStateItem);
+        }
+
+        if (mImpl->mLocatorTopic)
+        {
+            mImpl->mLocatorTopic->serviceUnsuspended(context, mImpl->mStateItem->guid);
+        }
+    }
+    catch (const std::exception& e)
+    {
+        data->setException(e);
+    }
+    catch (...)
+    {
+        assert(false);
+        data->setException();
     }
 }
 
@@ -440,7 +494,6 @@ ServiceStatus ServiceManagementImpl::getStatus() const
 
 ServiceLocatorParamsSeq ServiceManagementImpl::getLocatorParams(const Ice::Current&)
 {
-    
     boost::shared_lock<boost::shared_mutex> lock(mImpl->mLock);
     ServiceLocatorParamsSeq result;
     for (std::vector<boost::shared_ptr<ServiceLocatorParamsSpec> >::const_iterator iter = mImpl->mSupportedLocatorParams.begin();
@@ -456,6 +509,14 @@ ServiceLocatorParamsSeq ServiceManagementImpl::getLocatorParams(const Ice::Curre
  */
 void ServiceManagementImpl::unregister(const AsteriskSCF::System::V1::OperationContextPtr& context, const Ice::Current&)
 {
+    ContextDataPtr data = checkAndThrow(mImpl->mOperationContextCache, context);
+
+    if (!data)
+    {
+        // retry detected
+        return;
+    }
+
     /* You'll notice no lock here. That's because we aren't actually modifying any internal state that should
      * be protected, and if we did lock here there is a chance for a deadlock which is super sad.
      */
@@ -473,17 +534,19 @@ void ServiceManagementImpl::unregister(const AsteriskSCF::System::V1::OperationC
         {
             mImpl->mLocatorTopic->serviceUnregistered(AsteriskSCF::Operations::createContext(context), mImpl->mStateItem->guid);
         }
+
+        data->setCompleted();
     }
     catch(const std::exception& e)
     {
         lg(Error) << BOOST_CURRENT_FUNCTION << " : " << e.what();
+        data->setException(e);
 	throw;
     }
     catch(...)
     {
         lg(Error) << BOOST_CURRENT_FUNCTION << " : " << "Unknown exception.";
+        data->setException();
 	throw;
     }
 }
-
-
diff --git a/test/TestServiceLocator.cpp b/test/TestServiceLocator.cpp
index 2572171..aebc687 100644
--- a/test/TestServiceLocator.cpp
+++ b/test/TestServiceLocator.cpp
@@ -1,7 +1,7 @@
 /*
  * Asterisk SCF -- An open-source communications framework.
  *
- * Copyright (C) 2010, Digium, Inc.
+ * Copyright (C) 2010-2012, Digium, Inc.
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk SCF project. Please do not directly contact
@@ -104,7 +104,7 @@ public:
             params->category = category;
             params->service = service;
 
-            // Actually do the locate request 
+            // Actually do the locate request
             foundCompare = ServiceLocatorParamsComparePrx::uncheckedCast(discovery->locate(params));
 
             /* If we get here we didn't get an exception back telling us no service found... which is wrong! */
@@ -113,7 +113,7 @@ public:
         {
             found = false;
         }
-        catch (const Ice::Exception &e)
+        catch (const Ice::Exception& e)
         {
             BOOST_TEST_MESSAGE(e.ice_name());
             BOOST_TEST_MESSAGE(e.what());
@@ -200,7 +200,7 @@ struct GlobalIceFixture
         {
             throw "Invalid service discovery proxy";
         }
-        
+
     } // end Fixture() constructor
 
     ~GlobalIceFixture()
@@ -292,7 +292,7 @@ BOOST_AUTO_TEST_CASE(AddService)
      */
     try
     {
-        testbed.compareManagement = testbed.management->addService(AsteriskSCF::Operations::createContext(), 
+        testbed.compareManagement = testbed.management->addService(AsteriskSCF::Operations::createContext(),
             testbed.compare, "testcompare");
         added = true;
     }
@@ -309,6 +309,19 @@ BOOST_AUTO_TEST_CASE(AddService)
 }
 
 /**
+ * Test retry logic for adding a service
+ */
+BOOST_AUTO_TEST_CASE(AddServiceRetry)
+{
+    AsteriskSCF::System::V1::OperationContextPtr context = AsteriskSCF::Operations::createContext();
+
+    ServiceManagementPrx expected = testbed.management->addService(context, testbed.compare, "testcompare");
+    ServiceManagementPrx actual = testbed.management->addService(context, testbed.compare, "testcompare");
+
+    BOOST_CHECK_EQUAL(expected, actual);
+}
+
+/**
  * Confirm that we can't find a service after we have added one but before we add parameters.
  */
 BOOST_AUTO_TEST_CASE(ServiceNotFoundAfterAdd)
@@ -357,6 +370,26 @@ BOOST_AUTO_TEST_CASE(AddLocatorParamsWithoutCompareService)
     BOOST_CHECK(added);
 }
 
+BOOST_AUTO_TEST_CASE(AddLocatorParamsRetry)
+{
+    AsteriskSCF::System::V1::OperationContextPtr context = AsteriskSCF::Operations::createContext();
+
+    ServiceLocatorParamsPtr params = new ServiceLocatorParams;
+    params->category = "retry-test";
+    params->service = "default";
+
+    size_t originalLocatorCount = testbed.compareManagement->getLocatorParams().size();
+
+    testbed.compareManagement->addLocatorParams(context, params, "");
+    testbed.compareManagement->addLocatorParams(context, params, "");
+
+    size_t expected = originalLocatorCount + 1;
+    size_t actual = testbed.compareManagement->getLocatorParams().size();
+
+    BOOST_CHECK_EQUAL(expected, actual);
+}
+
+
 /**
  * Confirm that we can find a service after we add discovery params that do not use a compare service.
  */
@@ -449,6 +482,7 @@ BOOST_AUTO_TEST_CASE(AddDuplicateCompare)
     }
     catch (const DuplicateCompare&)
     {
+        // expected
     }
     catch (const Ice::Exception &e)
     {
@@ -463,6 +497,46 @@ BOOST_AUTO_TEST_CASE(AddDuplicateCompare)
 }
 
 /**
+ * Tests retry logic in addCompare.
+ */
+BOOST_AUTO_TEST_CASE(AddCompareRetry)
+{
+    AsteriskSCF::System::V1::OperationContextPtr context = AsteriskSCF::Operations::createContext();
+    testbed.management->addCompare(context, "testcompareretry", testbed.compare);
+    BOOST_CHECK_NO_THROW(testbed.management->addCompare(context, "testcompareretry", testbed.compare));
+}
+
+/**
+ * Tests retry logic in addCompare.
+ */
+BOOST_AUTO_TEST_CASE(AddCompareDuplicateRetry)
+{
+    testbed.management->addCompare(AsteriskSCF::Operations::createContext(), "testcompareduplicateretry", testbed.compare);
+
+    AsteriskSCF::System::V1::OperationContextPtr context = AsteriskSCF::Operations::createContext();
+
+    try
+    {
+        testbed.management->addCompare(context, "testcompareretry", testbed.compare);
+        BOOST_FAIL("Expected exception");
+    }
+    catch (const DuplicateCompare&)
+    {
+        // expected
+    }
+
+    try
+    {
+        testbed.management->addCompare(context, "testcompareretry", testbed.compare);
+        BOOST_FAIL("Expected exception");
+    }
+    catch (const DuplicateCompare&)
+    {
+        // expected
+    }
+}
+
+/**
  * Confirm that we can add discovery parameters with a compare service.
  */
 BOOST_AUTO_TEST_CASE(AddLocatorParamsWithCompareService)
@@ -536,7 +610,7 @@ BOOST_AUTO_TEST_CASE(UseServiceFoundWithCompareService)
 BOOST_AUTO_TEST_CASE(FindMultipleServices)
 {
     ServiceLocatorParamsPtr params = new ServiceLocatorParams;
-    ServiceManagementPrx compareManagement = testbed.management->addService(AsteriskSCF::Operations::createContext(), 
+    ServiceManagementPrx compareManagement = testbed.management->addService(AsteriskSCF::Operations::createContext(),
         testbed.compare, "testcompare2");
 
     params->category = "test";
@@ -557,7 +631,7 @@ BOOST_AUTO_TEST_CASE(FindMultipleServices)
 BOOST_AUTO_TEST_CASE(FindMultipleServicesUsingEmptyCategory)
 {
     ServiceLocatorParamsPtr params = new ServiceLocatorParams;
-    ServiceManagementPrx compareManagement = testbed.management->addService(AsteriskSCF::Operations::createContext(), 
+    ServiceManagementPrx compareManagement = testbed.management->addService(AsteriskSCF::Operations::createContext(),
         testbed.compare, "testcompare2");
 
     params->category = "test";
@@ -667,6 +741,33 @@ BOOST_AUTO_TEST_CASE(RemoveNonexistentCompareService)
     BOOST_CHECK(!removed);
 }
 
+BOOST_AUTO_TEST_CASE(RemoveNonexistentCompareRetry)
+{
+    AsteriskSCF::System::V1::OperationContextPtr context = AsteriskSCF::Operations::createContext();
+
+    try
+    {
+        testbed.management->removeCompare(context, "does-not-exist");
+        BOOST_FAIL("Expected exception");
+    }
+    catch(const CompareNotFound&)
+    {
+        // expected
+    }
+
+    testbed.management->addCompare(AsteriskSCF::Operations::createContext(), "does-not-exist", testbed.compare);
+
+    try
+    {
+        testbed.management->removeCompare(context, "does-not-exist");
+        BOOST_FAIL("Expected exception");
+    }
+    catch(const CompareNotFound&)
+    {
+        // expected
+    }
+}
+
 /**
  * Confirm that we can remove a compare service.
  */
@@ -681,8 +782,9 @@ BOOST_AUTO_TEST_CASE(RemoveCompareService)
     }
     catch (const CompareNotFound&)
     {
+        // expected
     }
-    catch (const Ice::Exception &e)
+    catch (const Ice::Exception& e)
     {
         BOOST_TEST_MESSAGE(e.ice_name());
         BOOST_TEST_MESSAGE(e.what());
@@ -694,6 +796,23 @@ BOOST_AUTO_TEST_CASE(RemoveCompareService)
     BOOST_CHECK(removed);
 }
 
+BOOST_AUTO_TEST_CASE(RemoveCompareServiceRetry)
+{
+    testbed.management->addCompare(AsteriskSCF::Operations::createContext(), "remove-retry-compare", testbed.compare);
+
+    AsteriskSCF::System::V1::OperationContextPtr context = AsteriskSCF::Operations::createContext();
+    testbed.management->removeCompare(context, "remove-retry-compare");
+
+    try
+    {
+        testbed.management->removeCompare(context, "remove-retry-compare");
+    }
+    catch (const CompareNotFound& e)
+    {
+        BOOST_FAIL(std::string("Unexpected exception: ") + e.what());
+    }
+}
+
 /**
  * Confirm that we can't remove a compare service a second time.
  */

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


-- 
asterisk-scf/integration/servicediscovery.git



More information about the asterisk-scf-commits mailing list