[asterisk-scf-commits] asterisk-scf/release/sip.git branch "master" updated.

Commits to the Asterisk SCF project code repositories asterisk-scf-commits at lists.digium.com
Sun May 15 13:31:51 CDT 2011


branch "master" has been updated
       via  81752bfad6b946001b867f04ab47ed1a73203b54 (commit)
       via  3f83eaaeae44fb9716be7e0932fb52b67dbfb759 (commit)
      from  9fee5b0a87b2ca58c6c4c4a9fd20dc4773217d2c (commit)

Summary of changes:
 src/PJSipManager.h           |    2 +
 src/SipConfiguration.cpp     | 1893 +++++++++++++++++++++++++-----------------
 src/SipConfiguration.h       |   30 +-
 src/SipSessionManagerApp.cpp |    2 +-
 4 files changed, 1120 insertions(+), 807 deletions(-)


- Log -----------------------------------------------------------------
commit 81752bfad6b946001b867f04ab47ed1a73203b54
Merge: 9fee5b0 3f83eaa
Author: Brent Eagles <beagles at digium.com>
Date:   Sun May 15 15:57:47 2011 -0230

    Resolving conflicts with merging to recent updates.

diff --cc src/SipConfiguration.cpp
index c437aa8,da49417..bfd715d
--- a/src/SipConfiguration.cpp
+++ b/src/SipConfiguration.cpp
@@@ -21,7 -18,12 +21,9 @@@
  
  #include <boost/thread.hpp>
  #include <boost/shared_ptr.hpp>
+ #include <boost/function.hpp>
+ #include <boost/bind.hpp>
  
 -#include <pjlib.h>
 -#include <pjsip.h>
 -
  #include <AsteriskSCF/System/Component/ConfigurationIf.h>
  
  #include "SipConfigurationIf.h"
@@@ -34,12 -47,291 +47,291 @@@ namespace AsteriskSC
  namespace SipSessionManager
  {
  
- using namespace AsteriskSCF::SIP::V1;
- using namespace AsteriskSCF::System::Configuration::V1;
- using namespace AsteriskSCF::Core::Routing::V1;
+ /**
+  *
+  * The createGroupTemplate() pattern implements boilerplate for creating "empty" copies of a group. All of the basic
+  * group elements should be copied except for the group dictionary. This will be copied separately.
+  *
+  **/
+ 
+ /**
+  * A generic instance of the createGroupTemplate() that creates a new, empty instance of a named group.
+  *
+  * @param group an instance of the group to create a template for.
+  * @return a new instance of type T that may contain members copied from the group parameter.
+  **/
+ template <typename T>
+ T createGroupTemplate(const T& group)
+ {
+     T result = new typename T::element_type;
+     assert(result);
+     result->name = group->name;
+     return result;
+ }
+ 
+ /**
+  * A specialization for SipGeneralGroup, which contains no additional members.
+  **/
+ template<>
+ SipGeneralGroupPtr createGroupTemplate(const SipGeneralGroupPtr&)
+ {
+     return new SipGeneralGroup;
+ }
+ 
+ /**
+  * A specialization for SipDomainGroup which has a data member with a different name.
+  **/
+ template <>
+ SipDomainGroupPtr createGroupTemplate(const SipDomainGroupPtr& source)
+ {
+     SipDomainGroupPtr r(new SipDomainGroup);
+     r->domain = source->domain;
+     return r;
+ }
+ 
+ /**
+  *
+  * performSerialCheck checks to see if an update is out of order and currently ... does nothing. We need to add some
+  * logging or update filtering.
+  *
+  * @param changedItems the items in the update.
+  *
+  * @param localItems the current items in the configuration.
+  * 
+  **/
+ static void performSerialCheck(const ConfigurationItemDict& changedItems, const ConfigurationItemDict& localItems)
+ {
+     for (ConfigurationItemDict::const_iterator item = changedItems.begin();
+          item != changedItems.end();
+          ++item)
+     {
+         // If serial checking is to be skipped for this item just skip over it
+         if (item->second->serialNumber == -1)
+         {
+             continue;
+         }
+ 		
+         ConfigurationItemDict::const_iterator localItem = localItems.find(item->first);
+ 		
+         if (localItem == localItems.end())
+         {
+             // This is a new item so serial checking does not apply
+             continue;
+         }
+ 		
+         if (item->second->serialNumber < item->second->serialNumber)
+         {
+             //
+             // XXX Either throw an exception or log & remove the update!
+             //
+         }
+     }
+ }
+ 
+ typedef boost::function<void()> UpdateCommand;
+ typedef vector<UpdateCommand> UpdateCommandList;
  
- class UDPTransportImplPriv
+ /**
+  * A helper class and visitor for propogating configuration changes to the SIPEndPoint implementation.
+  **/
+ class EndpointConfigHelper : public boost::enable_shared_from_this<EndpointConfigHelper>
  {
+     //
+     // Helper visitor that will be used to interpret the contents of the relevant configuration group.
+     // Reference counting and destruction plays an important part of the processing. The destructor for
+     // the visitor calls back into the helper to do any finalization or cleanup. This pattern will be
+     // revisited in the other helpers in this source file.
+     //
+     class Visitor : public SipConfigurationItemVisitor
+     {
+     public:
+         Visitor(const boost::shared_ptr<EndpointConfigHelper>& config) :
+           mConfig(config)
+         {
+         }
+ 
+         ~Visitor()
+         {
+             try
+             {
+                 //
+                 // Don't forget to tell the helper that all of the updates have been performed
+                 // and it can perform final processing.
+                 //
+                 mConfig->updated(mUpdates);
+             }
+             catch(...)
+             {
+                 //
+                 // NOTE TODO XXX?
+                 // Now here's the tricky part and where we need to look hard. If we gone this far
+                 // in configuration and we fail at this final stage, we really should undo all of
+                 // our configuration changes. This is easier said that done, so perhaps its
+                 // best if, for the time being at least, simply strive to keep *internally* consistent and
+                 // deal with reconciling with other components as a TBD.
+                 //
+             }
+         }
+ 
+         /**
+          * A different sort of approach to collecting the updates. By using boost's bind and function
+          * templates we can store the calls that we need to make and pass it to the configuration
+          * object's instance to call on itself. This allows the lock to only be held in once place,
+          * but the updates to appear to be spread over multiple calls.
+          **/
+         void visitSipAllowableCallDirectionItem(const SipAllowableCallDirectionItemPtr& direction)
+         {
+             mUpdates.push_back(boost::bind(&EndpointConfigHelper::updateDirection, mConfig, direction));
+         }
+         
+         void visitSipSourceTransportAddressItem(const SipSourceTransportAddressItemPtr& source)
+         {
+             mUpdates.push_back(boost::bind(&EndpointConfigHelper::updateSource, mConfig, source));
+         }
+ 
+         void visitSipTargetDestinationAddressItem(const SipTargetDestinationAddressItemPtr& target)
+         {
+             mUpdates.push_back(boost::bind(&EndpointConfigHelper::updateTarget, mConfig, target));
+         };
+ 
+     private:
+ 
+         UpdateCommandList mUpdates;;
+         //
+         // We keep a reference to the parent as the helper itself isn't held by the configuration
+         // object or client.
+         //
+         boost::shared_ptr<EndpointConfigHelper> mConfig;
+     };
+ 
+ public:
+     EndpointConfigHelper(const SipEndpointPtr& endpoint, 
+         const boost::shared_ptr<SipEndpointFactory>& endpointFactory,
 -        const AsteriskSCF::SmartProxy::SmartProxy<LocatorRegistryPrx>& registry,
++        const LocatorRegistrySmartPrx& registry,
+         const string& routingId) :
+       mEndpoint(endpoint),
+       mFactory(endpointFactory),
+       mRegistry(registry),
+       mRoutingId(routingId)
+     {
+     }
+ 
+     virtual ~EndpointConfigHelper()
+     {
+         //
+         // Doesn't really need to do anything at the moment.
+         //
+     }
+ 
+     SipConfigurationItemVisitorPtr getVisitor()
+     {
+         return new Visitor(shared_from_this());
+     }
+ 
+     void updateDirection(const SipAllowableCallDirectionItemPtr& direction)
+     {
+         enum Direction callDirection;
+         switch (direction->callDirection)
+         {
+         case Inbound:
+             callDirection = INBOUND;
+             break;
+         case Outbound:
+             callDirection = OUTBOUND;
+             break;
+         case Both:
+             callDirection = BOTH;
+             break;
+         default:
+             callDirection = NONE;
+         }
+         mEndpoint->setCallDirection(callDirection);
+     }
+ 
+     void updateSource(const SipSourceTransportAddressItemPtr& source)
+     {
+         mEndpoint->setSourceAddress(source->host, source->port);
+     }
+ 
+     void updateTarget(const SipTargetDestinationAddressItemPtr& target)
+     {
+         mEndpoint->setTargetAddress(target->host, target->port);
+     }
+ 
+     void updated(const UpdateCommandList& updates)
+     {
+         //
+         // NOTE: there is room for an inconsistent update, but holding the lock
+         // during RPCs seems like a bad plan. Even calling mFactory from
+         // here is a little suspect.
+         //
+         RegExSeq destinations;
+         bool updateSystem = !updates.empty();
+         {
+             boost::unique_lock<boost::shared_mutex> lock(mLock);
+             for (UpdateCommandList::const_iterator op = updates.begin();
+                  op != updates.end();
+                  ++op)
+             {
+                 UpdateCommand command = *op;
+                 command();
+             }
+             if (updateSystem)
+             {
+                 mFactory->generateRoutingDestinations(destinations);
+             }
+         }
+         if (updateSystem)
+         {
+             mRegistry->setEndpointLocatorDestinationIds(mRoutingId, destinations);
+         }
+     }
+ 
+ private:
+     boost::shared_mutex mLock;
+     SipEndpointPtr mEndpoint;
+     boost::shared_ptr<SipEndpointFactory> mFactory;
 -    AsteriskSCF::SmartProxy::SmartProxy<LocatorRegistryPrx> mRegistry;
++    LocatorRegistrySmartPrx mRegistry;
+     string mRoutingId;
+ };
+ 
+ class ConfigBase
+ {
+ public:
+     virtual ~ConfigBase() {}
+ 
+     virtual SipConfigurationItemVisitorPtr getVisitor() { return 0; }
+ };
+ typedef boost::shared_ptr<ConfigBase> ConfigBasePtr;
+ 
+ /**
+  * The Transport specializations take care of any transport specific configuration and
+  * initialization. The UDP and TCP transports are so similar that they could probably
+  * share some functionality.
+  **/
+ class UDPTransportConfig : public ConfigBase, public boost::enable_shared_from_this<UDPTransportConfig>
+ {
+     class Visitor : public SipConfigurationItemVisitor
+     {
+     public:
+         Visitor(const boost::shared_ptr<UDPTransportConfig>& config) :
+             mConfig(config)
+         {
+         }
+ 
+         void visitSipHostItem(const SipHostItemPtr& hostItem)
+         {
+             //
+             // Where the UDPTransportConfig only has one variable that is configurable,
+             // we can initiate a complete update directly.
+             //
+             mConfig->update(hostItem);
+         }
+     private:
+         boost::shared_ptr<UDPTransportConfig> mConfig;
+         
+     };
+     
  public:
      /**
       * Constructor implementation for this
@@@ -161,18 -691,251 +691,252 @@@ private
       * Transport factory within pjsip.
       */
      pjsip_tpfactory *mTransportFactory;
+ 
+     bool mNeedsUpdating;
  };
+ typedef boost::shared_ptr<TLSTransportConfig> TLSTransportConfigPtr;
+ typedef std::map<std::string, TLSTransportConfigPtr> TLSTransportMap;
+ 
+ //
+ // There are a lot of instances of looking up an item in collection and then doing something if it
+ // is found. This reduces some of the boilerplate duplication.
+ //
+ template <typename C, typename I, typename K>
+ bool getItem(const C& collection, I& iter, const K& key)
+ {
+     iter = collection.find(key);
+     return iter != collection.end();
+ }
  
- class ConfigurationServiceImplPriv
+ /**
+  * ConfigurationData goes into a class by itself to aide in sharing and locking. Operations on the configuration data
+  * have been heavily generalized into templates and type specific helpers that map the protocols described by the
+  * template members to type specific instantiations. The general premise is to remove/reduce repeated code
+  * and keep it restricted the most specialized uses possible. There are definitely a few more things that
+  * could be done.
+  **/
+ class ConfigurationData
  {
  public:
+ 
+     typedef std::map<std::string, SipDomainGroupPtr> DomainMap;
+     typedef std::map<std::string, SipEndpointGroupPtr> EndpointMap;
+     
+     ConfigurationData(PJSipManager* manager, const boost::shared_ptr<SipEndpointFactory>& factory,
+             const std::string& routingId, const LocatorRegistrySmartPrx& registry) : 
+         mGroup(new SipGeneralGroup),
+ 	mPJSipManager(manager), 
+         mEndpointFactory(factory), 
+         mRoutingId(routingId), 
+         mRoutingServiceLocatorRegistry(registry) 
+     {
+     }
+ 
      /**
-      * Constructor for this private class
+      * A simplified version for the general group.
       */
-     ConfigurationServiceImplPriv(PJSipManager *manager, boost::shared_ptr<SipEndpointFactory> factory, std::string& id,
- 	AsteriskSCF::Discovery::SmartProxy<LocatorRegistryPrx> registry) :
- 	mPJSipManager(manager), mEndpointFactory(factory), mRoutingId(id), mRoutingServiceLocatorRegistry(registry) { };
-     
+     void selectInto(const ConfigurationItemDict& requestedItems, ConfigurationItemDict& returnedItems)
+     {
+         boost::shared_lock<boost::shared_mutex> lock(mLock);
+         selectIntoImpl(requestedItems, mGroup->configurationItems, returnedItems);
+     }
+ 
+     /**
+      * The generic version of selecting configuration items out of configuration groups.
+      **/
+     template <typename G>
+     bool selectIntoForGroup(const G& group, const G& newGroup)
+     {
+         boost::shared_lock<boost::shared_mutex> lock(mLock);
+         SipConfigurationGroupPtr g = getGroupFor(group);
+         if (g)
+         {
+             selectIntoImpl(group->configurationItems, g->configurationItems, newGroup->configurationItems);
+             return true;
+         }
+         return false;
+     }
+ 
+     template <typename G>
+     void removeFromGroup(const G& group)
+     {
+         boost::unique_lock<boost::shared_mutex> lock(mLock);
+         SipConfigurationGroupPtr g = getGroupFor(group);
+         if (g)
+         {
+             deleteFrom(group->configurationItems, g->configurationItems);
+         }
+     }
+ 
+     template <typename G>
+     SipConfigurationItemVisitorPtr update(const G& group)
+     {
+         boost::unique_lock<boost::shared_mutex> lock(mLock);
+         G v = getGroupFor(group);
+         if (!v)
+         {
+             v = createGroupTemplate(group);
+         }
+         performSerialCheck(group->configurationItems, v->configurationItems);
+         for (ConfigurationItemDict::const_iterator i = group->configurationItems.begin();
+             i != group->configurationItems.end(); ++i)
+         {
+             //
+             // Using the index operator allows us to effectively overwrite or add the value
+             // as opposed to erasing/inserting.
+             //
+             v->configurationItems[i->first] = i->second;
+         }
+         return updateGroup(v);
+     }
+ 
+     void remove(const SipGeneralGroupPtr&)
+     {
+         boost::unique_lock<boost::shared_mutex> lock(mLock);
+         mGroup = 0;
+     }
+ 
+     void remove(const SipDomainGroupPtr& group)
+     {        
+         boost::unique_lock<boost::shared_mutex> lock(mLock);
+         mDomains.erase(mDomains.find(group->domain));
+     }
+ 
+     void remove(const SipEndpointGroupPtr& group)
+     {
+         boost::unique_lock<boost::shared_mutex> lock(mLock);
+         mEndpoints.erase(mEndpoints.find(group->name));
+     }
+ 
+     void remove(const SipUDPTransportGroupPtr& group)
+     {
+         //
+         // It's a good idea to hold a reference to this until after we release the lock on the config item. That way any
+         // code that might happen as a result of its destruction cannot come back and cause deadlocks on this object.
+         //
+         UDPTransportConfigPtr config;
+         {
+             boost::unique_lock<boost::shared_mutex> lock(mLock);
+             UDPTransportMap::iterator i = mUDP.find(group->name);
+             if (i != mUDP.end())
+             {
+                 config = i->second;
+                 mUDP.erase(i);
+             }
+         }
+     }
+ 
+     void remove(const SipTCPTransportGroupPtr& group)
+     {
+         //
+         // It's a good idea to hold a reference to this until after we release the lock on the config item. That way any
+         // code that might happen as a result of its destruction cannot come back and cause deadlocks on this object.
+         //
+         TCPTransportConfigPtr config;
+         {
+             boost::unique_lock<boost::shared_mutex> lock(mLock);
+             TCPTransportMap::iterator i = mTCP.find(group->name);
+             if (i != mTCP.end())
+             {
+                 config = i->second;
+                 mTCP.erase(i);
+             }
+         }
+     }
+ 
+     void remove(const SipTLSTransportGroupPtr& group)
+     {
+         //
+         // It's a good idea to hold a reference to this until after we release the lock on the config item. That way any
+         // code that might happen as a result of its destruction cannot come back and cause deadlocks on this object.
+         //
+         TLSTransportConfigPtr config;
+         {
+             boost::unique_lock<boost::shared_mutex> lock(mLock);
+             TLSTransportMap::iterator i = mTLS.find(group->name);
+             if (i != mTLS.end())
+             {
+                 config = i->second;
+                 mTLS.erase(i);
+             }
+         }
+     }
+ 
+     SipGeneralGroupPtr getGroupFor(const SipGeneralGroupPtr&)
+     {
+         return mGroup;
+     }
+ 
+     void copyGroupTemplates(ConfigurationGroupSeq& groups)
+     {
+         boost::shared_lock<boost::shared_mutex> lock(mLock);
+         groups.push_back(mGroup);
+         copyTransportTemplates(groups, mUDP);
+         copyTransportTemplates(groups, mTCP);
+         copyTransportTemplates(groups, mTLS);
+         copyTemplates(groups, mDomains);
+         copyTemplates(groups, mEndpoints);
+     }
+ 
+     /**
+      * The following getItemsFor() members basically do a data member selection by type.  Interestingly, this is simply
+      * an extension of the idea behind the visitor extension to Ice, but it takes it further, allowing a large degree of
+      * generalization in a shared data class.
+      **/
+     const SipDomainGroupPtr getGroupFor(const SipDomainGroupPtr& group)
+     {
+         DomainMap::const_iterator i;
+         if (getItem(mDomains, i, group->domain))
+         {
+             return i->second;
+         }
+         return 0;
+     }
+ 
+     const SipEndpointGroupPtr getGroupFor(const SipEndpointGroupPtr& group)
+     {
+         EndpointMap::const_iterator i;
+         if (getItem(mEndpoints, i, group->name))
+         {
+             return i->second;
+         }
+         return 0;
+     }
+ 
+     const SipUDPTransportGroupPtr getGroupFor(const SipUDPTransportGroupPtr& group)
+     {
+         UDPTransportMap::const_iterator i;
+         if (getItem(mUDP, i, group->name))
+         {
+             return i->second->getTypedGroup();
+         }
+         return 0;
+     }
+ 
+     const SipTCPTransportGroupPtr getGroupFor(const SipTCPTransportGroupPtr& group)
+     {
+         TCPTransportMap::const_iterator i;
+         if (getItem(mTCP, i, group->name))
+         {
+             return i->second->getTypedGroup();
+         }
+         return 0;
+     }
+ 
+     const SipTLSTransportGroupPtr getGroupFor(const SipTLSTransportGroupPtr& group)
+     {
+         TLSTransportMap::const_iterator i;
+         if (getItem(mTLS, i, group->name))
+         {
+             return i->second->getTypedGroup();
+         }
+         return 0;
+     }
+ 
+ private:
+ 
+     boost::shared_mutex mLock;
++
      /**
       * Configured SIP domains
       */
@@@ -201,41 -964,223 +965,223 @@@
      /**
       * General SIP configuration
       */
-     SipGeneralGroupPtr mGeneralGroup;
+     SipGeneralGroupPtr mGroup;
+ 
+     /**
+      * PJSipManager Pointer
+      */
+     PJSipManager* mPJSipManager;
+ 
+     /**
+      * Pointer to the endpoint factory used to create endpoints
+      */
+     boost::shared_ptr<SipEndpointFactory> mEndpointFactory;
+ 
+     /**
+      * Identifier for our endpoint locator.
+      */
+     std::string mRoutingId;
+ 
+     /**
+      * Proxy to endpoint locator registry
+      */
 -    AsteriskSCF::SmartProxy::SmartProxy<LocatorRegistryPrx> mRoutingServiceLocatorRegistry;
++    LocatorRegistrySmartPrx mRoutingServiceLocatorRegistry;
+ 
+ 
+     //
+     // Helper function that does the selection out of one dictionary into another.
+     //
+     void selectIntoImpl(const ConfigurationItemDict& requestedItems, const ConfigurationItemDict& localItems,
+ 	    ConfigurationItemDict& returnedItems)
+     {
+         for (ConfigurationItemDict::const_iterator requestedItem = requestedItems.begin();
+              requestedItem != requestedItems.end();
+              ++requestedItem)
+         {
+             if (localItems.find(requestedItem->first) != localItems.end())
+             {
+                 returnedItems.insert(*requestedItem);
+             }
+         }
+     }
+ 
+     void deleteFrom(const ConfigurationItemDict& itemsToDelete, ConfigurationItemDict& data)
+     {
+         for (ConfigurationItemDict::const_iterator i = itemsToDelete.begin();
+              i != itemsToDelete.end(); ++i)
+         {
+             data.erase(data.find(i->first));
+         }
+     }
+     
+     SipConfigurationItemVisitorPtr updateGroup(const SipGeneralGroupPtr&)
+     {
+         return 0;
+     }
+ 
+     SipConfigurationItemVisitorPtr updateGroup(const SipEndpointGroupPtr& group)
+     {
+         EndpointMap::const_iterator i;
+         SipEndpointGroupPtr endpointGroup;
+         if (!getItem(mEndpoints, i, group->name))
+         {
+             endpointGroup = createGroupTemplate(group);
+             mEndpoints.insert(make_pair(group->name, endpointGroup));
+         }
+         else
+         {
+             endpointGroup = i->second;
+         }
+         SipEndpointPtr endpoint = mEndpointFactory->findByName(group->name);
+         if (!endpoint)
+         {
+             //
+             // XXX - something better to do than just ignore?
+             // Should never happen, and we can't do anything else without really messing things up.
+             //
+             return 0;
+         }
+         return boost::shared_ptr<EndpointConfigHelper>(
+             new EndpointConfigHelper(endpoint, mEndpointFactory, mRoutingServiceLocatorRegistry, mRoutingId))->getVisitor();
+     }
+ 
+     SipConfigurationItemVisitorPtr updateGroup(const SipDomainGroupPtr&)
+     {
+         return 0;
+     }
+     
+     SipConfigurationItemVisitorPtr updateGroup(const SipUDPTransportGroupPtr& group)
+     {
+         UDPTransportMap::const_iterator i;
+         UDPTransportConfigPtr transport;
+         if (!getItem(mUDP, i, group->name))
+         {
+             transport.reset(new UDPTransportConfig(group, mPJSipManager));
+             mUDP.insert(make_pair(group->name, transport));
+         }
+         else
+         {
+             transport = i->second;
+         }
+         return transport->getVisitor();
+     }
+ 
+     SipConfigurationItemVisitorPtr updateGroup(const SipTCPTransportGroupPtr& group)
+     {
+         TCPTransportMap::const_iterator i;
+         TCPTransportConfigPtr transport;
+         if (!getItem(mTCP, i, group->name))
+         {
+             transport.reset(new TCPTransportConfig(group, mPJSipManager));
+             mTCP.insert(make_pair(group->name, transport));
+         }
+         else
+         {
+             transport = i->second;
+         }
+         return transport->getVisitor();
+     }
+ 
+     SipConfigurationItemVisitorPtr updateGroup(const SipTLSTransportGroupPtr& group)
+     {
+         TLSTransportMap::const_iterator i;
+         TLSTransportConfigPtr transport;
+         if (!getItem(mTLS, i, group->name))
+         {
+             transport.reset(new TLSTransportConfig(group, mPJSipManager));
+             mTLS.insert(make_pair(group->name, transport));
+         }
+         else
+         {
+             transport = i->second;
+         }
+         return transport->getVisitor();
+     }
  
-     /**
-      * PJSipManager Pointer
-      */
-     PJSipManager *mPJSipManager;
+     template <class T>
+     void copyTemplates(ConfigurationGroupSeq& groups, const T& items)
+     {
+         typedef typename T::const_iterator TIter;
+         for (TIter g = items.begin(); g != items.end(); ++g)
+         {
+             groups.push_back(createGroupTemplate(g->second));
+         }
+     }
  
-     /**
-      * Pointer to the endpoint factory used to create endpoints
-      */
-     boost::shared_ptr<SipEndpointFactory> mEndpointFactory;
+     template <class T>
+     void copyTransportTemplates(ConfigurationGroupSeq& groups, const T& items)
+     {
+         typedef typename T::const_iterator TIter;
+         for (TIter g = items.begin(); g != items.end(); ++g)
+         {
+             groups.push_back(createGroupTemplate(g->second->getTypedGroup()));
+         }
+     }
+ };
+ typedef boost::shared_ptr<ConfigurationData> ConfigurationDataPtr;
  
-     /**
-      * Identifier for our endpoint locator.
-      */
-     std::string mRoutingId;
+ /**
+  * Implementation of the configuration service.
+  */
+ class ConfigurationServiceImpl : public AsteriskSCF::System::Configuration::V1::ConfigurationService
+ {
+ public:
+     ConfigurationServiceImpl(PJSipManager* manager, const boost::shared_ptr<SipEndpointFactory>& endpointFactory, 
+         const std::string& routingId, const LocatorRegistrySmartPrx& locatorProxy);
+     ConfigurationGroupSeq getConfiguration(const ConfigurationGroupSeq&, const Ice::Current&);
+     ConfigurationGroupSeq getConfigurationAll(const ConfigurationGroupSeq&, const Ice::Current&);
+     ConfigurationGroupSeq getConfigurationGroups(const Ice::Current&);
+     void setConfiguration(const ConfigurationGroupSeq&, const Ice::Current&);
+     void removeConfigurationItems(const ConfigurationGroupSeq&, const Ice::Current&);
+     void removeConfigurationGroups(const ConfigurationGroupSeq&, const Ice::Current&);
+         
+     ConfigurationDataPtr getData()
+     {
+         return mData;
+     }
  
-     /**
-      * Proxy to endpoint locator registry
-      */
-     AsteriskSCF::Discovery::SmartProxy<LocatorRegistryPrx> mRoutingServiceLocatorRegistry;
+ private:
+ 
+     //
+     // The configuration data should be kept separate from the servant. There
+     // are several practical reasons, but it comes down to the fact that the 
+     // data is shared and it's better to pass it around then the servant
+     // itself.
+     //
+     ConfigurationDataPtr mData;
  };
  
- ConfigurationServiceImpl::ConfigurationServiceImpl(PJSipManager *manager, boost::shared_ptr<SipEndpointFactory> factory,
-     std::string& id, AsteriskSCF::Discovery::SmartProxy<LocatorRegistryPrx> registry) :
-     mImplPriv(new ConfigurationServiceImplPriv(manager, factory, id, registry))
+ typedef IceUtil::Handle<ConfigurationServiceImpl> ConfigurationServiceImplPtr;
+ 
+ ConfigurationServiceImpl::ConfigurationServiceImpl(PJSipManager* manager,
+         const boost::shared_ptr<SipEndpointFactory>& factory, const std::string& routingId,
+         const LocatorRegistrySmartPrx& registry) :
+     mData(new ConfigurationData(manager, factory, routingId, registry))
+ {
+ }
+ 
+ template <class T>
+ static void getGeneric(const ConfigurationDataPtr& config, const T& group, ConfigurationGroupSeq& groups)
  {
+     T newGroup(createGroupTemplate(group));
+     if (config->selectIntoForGroup(group, newGroup))
+     {
+         groups.push_back(newGroup);
+     }
  }
  
- ConfigurationGroupSeq ConfigurationServiceImpl::getConfiguration(const AsteriskSCF::System::Configuration::V1::ConfigurationGroupSeq& groups, const Ice::Current&)
+ ConfigurationGroupSeq ConfigurationServiceImpl::getConfiguration(
+     const AsteriskSCF::System::Configuration::V1::ConfigurationGroupSeq& groups, const Ice::Current&)
  {
-     class visitor : public SipConfigurationGroupVisitor
+     class Visitor : public SipConfigurationGroupVisitor
      {
      public:
- 	visitor(boost::shared_ptr<ConfigurationServiceImplPriv> implPriv, ConfigurationGroupSeq& visitorGroups) : mImplPriv(implPriv), mGroups(visitorGroups) { };
+ 	Visitor(const ConfigurationServiceImplPtr& impl, ConfigurationGroupSeq& visitorGroups) : 
+           mImpl(impl), 
+           mGroups(visitorGroups) 
+         { 
+             assert(mImpl);
+         };
  
      private:
  	/**
diff --cc src/SipConfiguration.h
index d583bae,d3ee4ad..89c0af1
--- a/src/SipConfiguration.h
+++ b/src/SipConfiguration.h
@@@ -35,31 -35,13 +35,13 @@@ namespace AsteriskSC
  namespace SipSessionManager
  {
  
- /*
-  * Private implementation class for ConfigurationServiceImpl.
-  */
- class ConfigurationServiceImplPriv;
 -typedef AsteriskSCF::SmartProxy::SmartProxy<AsteriskSCF::Core::Routing::V1::LocatorRegistryPrx> LocatorRegistrySmartPrx;
++typedef ::AsteriskSCF::Discovery::SmartProxy<AsteriskSCF::Core::Routing::V1::LocatorRegistryPrx> LocatorRegistrySmartPrx;
  
- /**
-  * Implementation of the configuration service.
-  */
- class ConfigurationServiceImpl : public AsteriskSCF::System::Configuration::V1::ConfigurationService
- {
- public:
-     ConfigurationServiceImpl(PJSipManager*, boost::shared_ptr<SipEndpointFactory>, std::string&,
- 	AsteriskSCF::Discovery::SmartProxy<AsteriskSCF::Core::Routing::V1::LocatorRegistryPrx>);
-     AsteriskSCF::System::Configuration::V1::ConfigurationGroupSeq getConfiguration(const AsteriskSCF::System::Configuration::V1::ConfigurationGroupSeq&, const Ice::Current&);
-     AsteriskSCF::System::Configuration::V1::ConfigurationGroupSeq getConfigurationAll(const AsteriskSCF::System::Configuration::V1::ConfigurationGroupSeq&, const Ice::Current&);
-     AsteriskSCF::System::Configuration::V1::ConfigurationGroupSeq getConfigurationGroups(const Ice::Current&);
-     void setConfiguration(const AsteriskSCF::System::Configuration::V1::ConfigurationGroupSeq&, const Ice::Current&);
-     void removeConfigurationItems(const AsteriskSCF::System::Configuration::V1::ConfigurationGroupSeq&, const Ice::Current&);
-     void removeConfigurationGroups(const AsteriskSCF::System::Configuration::V1::ConfigurationGroupSeq&, const Ice::Current&);
- private:
-     /**
-      * Private implementation details.
-      */
-     boost::shared_ptr<ConfigurationServiceImplPriv> mImplPriv;
- };
+ AsteriskSCF::System::Configuration::V1::ConfigurationServicePtr createConfigurationServant(
+     SipSessionManager::PJSipManager* manager,
+     const boost::shared_ptr<SipEndpointFactory>& endpointFactory,
+     const std::string& routingId,
+     const LocatorRegistrySmartPrx& locatorRegistry);
  
  }; //End namespace SipSessionManager
  

commit 3f83eaaeae44fb9716be7e0932fb52b67dbfb759
Author: Brent Eagles <beagles at digium.com>
Date:   Thu May 12 14:45:25 2011 -0230

    A refactoring of the configuration servant construction and implementation.
    The intent is to take the benefit of the visitor facility a bit further and
    use template code and function overloading to create reusable configuration
    patterns and separate object specific code from the visitor implementations
    themselves. In this refactoring example, the detailed visitor implementations
    are isolated enough that the could be put into source modules alongside
    the objects that are actually being configured.

diff --git a/src/PJSipManager.h b/src/PJSipManager.h
index d188e11..4a2f63a 100644
--- a/src/PJSipManager.h
+++ b/src/PJSipManager.h
@@ -109,6 +109,8 @@ private:
     bool setTransports(pjsip_endpoint *endpoint, const Ice::PropertiesPtr& props);
 };
 
+typedef boost::shared_ptr<PJSipManager> PJSipManagerPtr;
+
 }; //End namespace SipSessionManager
 
 }; //End namespace AsteriskSCF
diff --git a/src/SipConfiguration.cpp b/src/SipConfiguration.cpp
index 76fa596..da49417 100644
--- a/src/SipConfiguration.cpp
+++ b/src/SipConfiguration.cpp
@@ -18,6 +18,8 @@
 
 #include <boost/thread.hpp>
 #include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
 
 #include <pjlib.h>
 #include <pjsip.h>
@@ -28,35 +30,361 @@
 #include "PJSipManager.h"
 #include "SipEndpointFactory.h"
 #include "SipConfiguration.h"
+#include <vector>
+
+using namespace AsteriskSCF::SIP::V1;
+using namespace AsteriskSCF::System::Configuration::V1;
+using namespace AsteriskSCF::Core::Routing::V1;
+using namespace std;
+
+//
+// Note: the general group receives a default value in this refactoring no matter what, I suspect that this is actually
+// wrong and will address.
+//
 
 namespace AsteriskSCF
 {
 namespace SipSessionManager
 {
 
-using namespace AsteriskSCF::SIP::V1;
-using namespace AsteriskSCF::System::Configuration::V1;
-using namespace AsteriskSCF::Core::Routing::V1;
+/**
+ *
+ * The createGroupTemplate() pattern implements boilerplate for creating "empty" copies of a group. All of the basic
+ * group elements should be copied except for the group dictionary. This will be copied separately.
+ *
+ **/
+
+/**
+ * A generic instance of the createGroupTemplate() that creates a new, empty instance of a named group.
+ *
+ * @param group an instance of the group to create a template for.
+ * @return a new instance of type T that may contain members copied from the group parameter.
+ **/
+template <typename T>
+T createGroupTemplate(const T& group)
+{
+    T result = new typename T::element_type;
+    assert(result);
+    result->name = group->name;
+    return result;
+}
+
+/**
+ * A specialization for SipGeneralGroup, which contains no additional members.
+ **/
+template<>
+SipGeneralGroupPtr createGroupTemplate(const SipGeneralGroupPtr&)
+{
+    return new SipGeneralGroup;
+}
+
+/**
+ * A specialization for SipDomainGroup which has a data member with a different name.
+ **/
+template <>
+SipDomainGroupPtr createGroupTemplate(const SipDomainGroupPtr& source)
+{
+    SipDomainGroupPtr r(new SipDomainGroup);
+    r->domain = source->domain;
+    return r;
+}
+
+/**
+ *
+ * performSerialCheck checks to see if an update is out of order and currently ... does nothing. We need to add some
+ * logging or update filtering.
+ *
+ * @param changedItems the items in the update.
+ *
+ * @param localItems the current items in the configuration.
+ * 
+ **/
+static void performSerialCheck(const ConfigurationItemDict& changedItems, const ConfigurationItemDict& localItems)
+{
+    for (ConfigurationItemDict::const_iterator item = changedItems.begin();
+         item != changedItems.end();
+         ++item)
+    {
+        // If serial checking is to be skipped for this item just skip over it
+        if (item->second->serialNumber == -1)
+        {
+            continue;
+        }
+		
+        ConfigurationItemDict::const_iterator localItem = localItems.find(item->first);
+		
+        if (localItem == localItems.end())
+        {
+            // This is a new item so serial checking does not apply
+            continue;
+        }
+		
+        if (item->second->serialNumber < item->second->serialNumber)
+        {
+            //
+            // XXX Either throw an exception or log & remove the update!
+            //
+        }
+    }
+}
+
+typedef boost::function<void()> UpdateCommand;
+typedef vector<UpdateCommand> UpdateCommandList;
+
+/**
+ * A helper class and visitor for propogating configuration changes to the SIPEndPoint implementation.
+ **/
+class EndpointConfigHelper : public boost::enable_shared_from_this<EndpointConfigHelper>
+{
+    //
+    // Helper visitor that will be used to interpret the contents of the relevant configuration group.
+    // Reference counting and destruction plays an important part of the processing. The destructor for
+    // the visitor calls back into the helper to do any finalization or cleanup. This pattern will be
+    // revisited in the other helpers in this source file.
+    //
+    class Visitor : public SipConfigurationItemVisitor
+    {
+    public:
+        Visitor(const boost::shared_ptr<EndpointConfigHelper>& config) :
+          mConfig(config)
+        {
+        }
+
+        ~Visitor()
+        {
+            try
+            {
+                //
+                // Don't forget to tell the helper that all of the updates have been performed
+                // and it can perform final processing.
+                //
+                mConfig->updated(mUpdates);
+            }
+            catch(...)
+            {
+                //
+                // NOTE TODO XXX?
+                // Now here's the tricky part and where we need to look hard. If we gone this far
+                // in configuration and we fail at this final stage, we really should undo all of
+                // our configuration changes. This is easier said that done, so perhaps its
+                // best if, for the time being at least, simply strive to keep *internally* consistent and
+                // deal with reconciling with other components as a TBD.
+                //
+            }
+        }
+
+        /**
+         * A different sort of approach to collecting the updates. By using boost's bind and function
+         * templates we can store the calls that we need to make and pass it to the configuration
+         * object's instance to call on itself. This allows the lock to only be held in once place,
+         * but the updates to appear to be spread over multiple calls.
+         **/
+        void visitSipAllowableCallDirectionItem(const SipAllowableCallDirectionItemPtr& direction)
+        {
+            mUpdates.push_back(boost::bind(&EndpointConfigHelper::updateDirection, mConfig, direction));
+        }
+        
+        void visitSipSourceTransportAddressItem(const SipSourceTransportAddressItemPtr& source)
+        {
+            mUpdates.push_back(boost::bind(&EndpointConfigHelper::updateSource, mConfig, source));
+        }
+
+        void visitSipTargetDestinationAddressItem(const SipTargetDestinationAddressItemPtr& target)
+        {
+            mUpdates.push_back(boost::bind(&EndpointConfigHelper::updateTarget, mConfig, target));
+        };
+
+    private:
+
+        UpdateCommandList mUpdates;;
+        //
+        // We keep a reference to the parent as the helper itself isn't held by the configuration
+        // object or client.
+        //
+        boost::shared_ptr<EndpointConfigHelper> mConfig;
+    };
+
+public:
+    EndpointConfigHelper(const SipEndpointPtr& endpoint, 
+        const boost::shared_ptr<SipEndpointFactory>& endpointFactory,
+        const AsteriskSCF::SmartProxy::SmartProxy<LocatorRegistryPrx>& registry,
+        const string& routingId) :
+      mEndpoint(endpoint),
+      mFactory(endpointFactory),
+      mRegistry(registry),
+      mRoutingId(routingId)
+    {
+    }
+
+    virtual ~EndpointConfigHelper()
+    {
+        //
+        // Doesn't really need to do anything at the moment.
+        //
+    }
+
+    SipConfigurationItemVisitorPtr getVisitor()
+    {
+        return new Visitor(shared_from_this());
+    }
+
+    void updateDirection(const SipAllowableCallDirectionItemPtr& direction)
+    {
+        enum Direction callDirection;
+        switch (direction->callDirection)
+        {
+        case Inbound:
+            callDirection = INBOUND;
+            break;
+        case Outbound:
+            callDirection = OUTBOUND;
+            break;
+        case Both:
+            callDirection = BOTH;
+            break;
+        default:
+            callDirection = NONE;
+        }
+        mEndpoint->setCallDirection(callDirection);
+    }
+
+    void updateSource(const SipSourceTransportAddressItemPtr& source)
+    {
+        mEndpoint->setSourceAddress(source->host, source->port);
+    }
+
+    void updateTarget(const SipTargetDestinationAddressItemPtr& target)
+    {
+        mEndpoint->setTargetAddress(target->host, target->port);
+    }
+
+    void updated(const UpdateCommandList& updates)
+    {
+        //
+        // NOTE: there is room for an inconsistent update, but holding the lock
+        // during RPCs seems like a bad plan. Even calling mFactory from
+        // here is a little suspect.
+        //
+        RegExSeq destinations;
+        bool updateSystem = !updates.empty();
+        {
+            boost::unique_lock<boost::shared_mutex> lock(mLock);
+            for (UpdateCommandList::const_iterator op = updates.begin();
+                 op != updates.end();
+                 ++op)
+            {
+                UpdateCommand command = *op;
+                command();
+            }
+            if (updateSystem)
+            {
+                mFactory->generateRoutingDestinations(destinations);
+            }
+        }
+        if (updateSystem)
+        {
+            mRegistry->setEndpointLocatorDestinationIds(mRoutingId, destinations);
+        }
+    }
+
+private:
+    boost::shared_mutex mLock;
+    SipEndpointPtr mEndpoint;
+    boost::shared_ptr<SipEndpointFactory> mFactory;
+    AsteriskSCF::SmartProxy::SmartProxy<LocatorRegistryPrx> mRegistry;
+    string mRoutingId;
+};
+
+class ConfigBase
+{
+public:
+    virtual ~ConfigBase() {}
 
-class UDPTransportImplPriv
+    virtual SipConfigurationItemVisitorPtr getVisitor() { return 0; }
+};
+typedef boost::shared_ptr<ConfigBase> ConfigBasePtr;
+
+/**
+ * The Transport specializations take care of any transport specific configuration and
+ * initialization. The UDP and TCP transports are so similar that they could probably
+ * share some functionality.
+ **/
+class UDPTransportConfig : public ConfigBase, public boost::enable_shared_from_this<UDPTransportConfig>
 {
+    class Visitor : public SipConfigurationItemVisitor
+    {
+    public:
+        Visitor(const boost::shared_ptr<UDPTransportConfig>& config) :
+            mConfig(config)
+        {
+        }
+
+        void visitSipHostItem(const SipHostItemPtr& hostItem)
+        {
+            //
+            // Where the UDPTransportConfig only has one variable that is configurable,
+            // we can initiate a complete update directly.
+            //
+            mConfig->update(hostItem);
+        }
+    private:
+        boost::shared_ptr<UDPTransportConfig> mConfig;
+        
+    };
+    
 public:
     /**
      * Constructor implementation for this
      */
-    UDPTransportImplPriv() : mTransport(0) { };
-    
+    UDPTransportConfig(const SipUDPTransportGroupPtr& group, PJSipManager* manager) :
+        mGroup(group),
+        mManager(manager),
+        mTransport(0)
+    {
+    };
+
     /**
      * Destructor implementation that shuts down the transport gracefully if we go away
      */
-    ~UDPTransportImplPriv()
+    ~UDPTransportConfig()
     {
-	if (mTransport)
-	{
-	    pjsip_transport_shutdown(mTransport);
-	}
+        if (mTransport)
+        {
+            pjsip_transport_shutdown(mTransport);
+        }
     };
-    
+
+    SipConfigurationItemVisitorPtr getVisitor()
+    {
+        return new Visitor(shared_from_this());
+    }
+
+    void update(const SipHostItemPtr& hostItem)
+    {
+        pjsip_transport* oldTransport = 0;
+        boost::unique_lock<boost::shared_mutex> lock(mLock);
+        if (mAddress != hostItem->host || mPort != hostItem->port)
+        {
+            mAddress = hostItem->host;
+            mPort = hostItem->port;
+            oldTransport = mTransport;
+            mTransport = mManager->createUDPTransport(mAddress, mPort);
+        }
+        if (oldTransport)
+        {
+            pjsip_transport_shutdown(oldTransport);
+        }
+        
+    }
+        
+    SipUDPTransportGroupPtr getTypedGroup() const
+    {
+        return mGroup;
+    }
+private:
+    boost::shared_mutex mLock;
+    SipConfigurationItemVisitorPtr mCurrentVisitor;
+
     /**
      * Address itself.
      */
@@ -70,32 +398,85 @@ public:
     /**
      * Configuration group itself.
      */
-    SipUDPTransportGroupPtr mGroup;
+    mutable SipUDPTransportGroupPtr mGroup;
+
+    PJSipManager* mManager;
     
     /**
      * Transport within pjsip.
      */
     pjsip_transport *mTransport;
 };
+typedef boost::shared_ptr<UDPTransportConfig> UDPTransportConfigPtr;
+typedef std::map<std::string, UDPTransportConfigPtr> UDPTransportMap;
 
-class TCPTransportImplPriv
+class TCPTransportConfig : public ConfigBase, public boost::enable_shared_from_this<TCPTransportConfig>
 {
+    class Visitor : public SipConfigurationItemVisitor
+    {
+    public:
+        Visitor(const boost::shared_ptr<TCPTransportConfig>& config) :
+            mConfig(config)
+        {
+        }
+
+        void visitSipHostItem(const SipHostItemPtr& hostItem)
+        {
+            //
+            // Where the TCPTransportConfig only has one variable that is configurable,
+            // we can initiate a complete update directly.
+            //
+            mConfig->update(hostItem);
+        }
+    private:
+        boost::shared_ptr<TCPTransportConfig> mConfig;
+    };
+    
 public:
     /**
      * Constructor implementation for this
      */
-    TCPTransportImplPriv() : mTransportFactory(0) { };
+    TCPTransportConfig(const SipTCPTransportGroupPtr& group, PJSipManager* manager) :
+        mGroup(group),
+        mManager(manager),
+        mTransportFactory(0)
+    {
+    };
 
     /**
      * Destructor implementation that shuts down the transport gracefully if we go away
      */
-    ~TCPTransportImplPriv()
+    ~TCPTransportConfig()
     {
         if (mTransportFactory)
         {
             // I hope in the future it will be possible to shut down TCP based transports but for now... it is not
         }
     };
+    
+    SipConfigurationItemVisitorPtr getVisitor()
+    {
+        return new Visitor(shared_from_this());
+    }
+
+    void update(const SipHostItemPtr& hostItem)
+    {
+        boost::unique_lock<boost::shared_mutex> lock(mLock);
+        if (mAddress != hostItem->host || mPort != hostItem->port)
+        {
+            mAddress = hostItem->host;
+            mPort = hostItem->port;
+            mTransportFactory = mManager->createTCPTransport(mAddress, mPort);
+        }
+    }
+
+    SipTCPTransportGroupPtr getTypedGroup() const
+    {
+        return mGroup;
+    }
+
+private:
+    boost::shared_mutex mLock;
 
     /**
      * Address itself.
@@ -110,26 +491,83 @@ public:
     /**
      * Configuration group itself.
      */
-    SipTCPTransportGroupPtr mGroup;
+    mutable SipTCPTransportGroupPtr mGroup;
+
+    PJSipManager* mManager;
 
     /**
      * Transport factory within pjsip.
      */
     pjsip_tpfactory *mTransportFactory;
 };
+typedef boost::shared_ptr<TCPTransportConfig> TCPTransportConfigPtr;
+typedef std::map<std::string, TCPTransportConfigPtr> TCPTransportMap;
 
-class TLSTransportImplPriv
+class TLSTransportConfig : public ConfigBase, public boost::enable_shared_from_this<TLSTransportConfig>
 {
+    class Visitor : public SipConfigurationItemVisitor
+    {
+    public:
+        Visitor(const boost::shared_ptr<TLSTransportConfig>& config) :
+            mConfig(config)
+        {
+        }
+
+        ~Visitor()
+        {
+            try
+            {
+                mConfig->updated(mUpdates);
+            }
+            catch(...)
+            {
+                //
+                // See comment in the EndpointConfigHelper's Visitor::~Visitor.
+                //
+            }
+        }
+
+        void visitSipHostItem(const SipHostItemPtr& hostItem)
+        {
+            mUpdates.push_back(boost::bind(&TLSTransportConfig::updateHost, mConfig, hostItem));
+        }
+
+        void visitSipCryptoCertificateItem(const SipCryptoCertificateItemPtr& certificateItem)
+        {
+            mUpdates.push_back(boost::bind(&TLSTransportConfig::updateCertificate, mConfig, certificateItem));
+        }
+
+        void visitSipCryptoRequirementsItem(const SipCryptoRequirementsItemPtr& requirementsItem)
+        {
+            mUpdates.push_back(boost::bind(&TLSTransportConfig::updateRequirements, mConfig, requirementsItem));
+        }
+        
+        void visitSipCryptoItem(const ::AsteriskSCF::SIP::V1::SipCryptoItemPtr& cryptoItem)
+        {
+            mUpdates.push_back(boost::bind(&TLSTransportConfig::updateCrypto, mConfig, cryptoItem));
+        }
+        
+    private:
+        UpdateCommandList mUpdates;
+        boost::shared_ptr<TLSTransportConfig> mConfig;
+    };
 public:
     /**
      * Constructor implementation for this
      */
-    TLSTransportImplPriv() : mTransportFactory(0) { pjsip_tls_setting_default(&mTLSSettings); };
+    TLSTransportConfig(const SipTLSTransportGroupPtr& group, PJSipManager* manager) :
+        mGroup(group),
+        mManager(manager),
+        mTransportFactory(0),
+        mNeedsUpdating(false)
+    {
+        pjsip_tls_setting_default(&mTLSSettings);
+    };
 
     /**
      * Destructor implementation that shuts down the transport gracefully if we go away
      */
-    ~TLSTransportImplPriv()
+    ~TLSTransportConfig()
     {
         if (mTransportFactory)
         {
@@ -137,6 +575,96 @@ public:
         }
     };
 
+    SipConfigurationItemVisitorPtr getVisitor()
+    {
+        return new Visitor(shared_from_this());
+    }
+
+    void updateHost(const SipHostItemPtr& hostItem)
+    {
+        if (mAddress != hostItem->host || mPort != hostItem->port)
+        {
+            mAddress = hostItem->host;
+            mPort = hostItem->port;
+            mNeedsUpdating = true;
+        }
+    }
+
+    void updateCertificate(const SipCryptoCertificateItemPtr& certificate)
+    {
+        mNeedsUpdating = true;
+        //
+        // TODO: This is a little sketchy. We need to make sure that the certificateItem CAN NOT GO AWAY whilst
+        // mTLSSettings might get used. I'm referring to the fact that pj_str doesn't copy the string.
+        //
+        mTLSSettings.ca_list_file = pj_str((char*)certificate->certificateAuthority.c_str());
+        mTLSSettings.cert_file = pj_str((char*)certificate->certificate.c_str());
+        mTLSSettings.privkey_file = pj_str((char*)certificate->privateKey.c_str());
+        mTLSSettings.password = pj_str((char*)certificate->privateKeyPassword.c_str());
+    }
+
+    void updateRequirements(const SipCryptoRequirementsItemPtr& requirements)
+    {
+        mNeedsUpdating = true;
+        mTLSSettings.verify_server = (requirements->requireVerifiedServer) ? PJ_TRUE : PJ_FALSE;
+        mTLSSettings.verify_client = (requirements->requireVerifiedClient) ? PJ_TRUE : PJ_FALSE;
+        mTLSSettings.require_client_cert = (requirements->requireClientCertificate) ? PJ_TRUE : PJ_FALSE;
+    }
+
+    void updateCrypto(const SipCryptoItemPtr& crypto)
+    {
+        mNeedsUpdating = true;
+        if (crypto->protocolMethod == PROTOCOLMETHODUNSPECIFIED)
+        {
+            mTLSSettings.method = PJSIP_SSL_UNSPECIFIED_METHOD;
+        }
+        else if (crypto->protocolMethod == PROTOCOLMETHODTLSV1)
+        {
+            mTLSSettings.method = PJSIP_TLSV1_METHOD;
+        }
+        else if (crypto->protocolMethod == PROTOCOLMETHODSSLV2)
+        {
+            mTLSSettings.method = PJSIP_SSLV2_METHOD;
+        }
+        else if (crypto->protocolMethod == PROTOCOLMETHODSSLV3)
+        {
+            mTLSSettings.method = PJSIP_SSLV3_METHOD;
+        }
+        else if (crypto->protocolMethod == PROTOCOLMETHODSSLV23)
+        {
+           mTLSSettings.method = PJSIP_SSLV23_METHOD;
+        }
+        mTLSSettings.ciphers = pj_str((char*)crypto->supportedCiphers.c_str());
+        mTLSSettings.server_name = pj_str((char*)crypto->serverName.c_str());
+        mTLSSettings.timeout.sec = crypto->timeout;
+    }
+
+    void updated(const UpdateCommandList& updates)
+    {
+        boost::unique_lock<boost::shared_mutex> lock(mLock);
+        for (UpdateCommandList::const_iterator op = updates.begin();
+             op != updates.end();
+             ++op)
+        {
+            UpdateCommand command = *op;
+            command();
+        }
+        if (mNeedsUpdating)
+        {
+            mNeedsUpdating = false;
+            mTransportFactory = mManager->createTLSTransport(mAddress, mPort, &mTLSSettings);
+        }
+    }
+
+    SipTLSTransportGroupPtr getTypedGroup() const
+    {
+        return mGroup;
+    }
+
+private:
+
+    boost::shared_mutex mLock;
+    
     /**
      * Address itself.
      */
@@ -155,217 +683,559 @@ public:
     /**
      * Configuration group itself.
      */
-    SipTLSTransportGroupPtr mGroup;
+    mutable SipTLSTransportGroupPtr mGroup;
+
+    PJSipManager* mManager;
 
     /**
      * Transport factory within pjsip.
      */
     pjsip_tpfactory *mTransportFactory;
+
+    bool mNeedsUpdating;
 };
+typedef boost::shared_ptr<TLSTransportConfig> TLSTransportConfigPtr;
+typedef std::map<std::string, TLSTransportConfigPtr> TLSTransportMap;
+
+//
+// There are a lot of instances of looking up an item in collection and then doing something if it
+// is found. This reduces some of the boilerplate duplication.
+//
+template <typename C, typename I, typename K>
+bool getItem(const C& collection, I& iter, const K& key)
+{
+    iter = collection.find(key);
+    return iter != collection.end();
+}
 
-class ConfigurationServiceImplPriv
+/**
+ * ConfigurationData goes into a class by itself to aide in sharing and locking. Operations on the configuration data
+ * have been heavily generalized into templates and type specific helpers that map the protocols described by the
+ * template members to type specific instantiations. The general premise is to remove/reduce repeated code
+ * and keep it restricted the most specialized uses possible. There are definitely a few more things that
+ * could be done.
+ **/
+class ConfigurationData
 {
 public:
+
+    typedef std::map<std::string, SipDomainGroupPtr> DomainMap;
+    typedef std::map<std::string, SipEndpointGroupPtr> EndpointMap;
+    
+    ConfigurationData(PJSipManager* manager, const boost::shared_ptr<SipEndpointFactory>& factory,
+            const std::string& routingId, const LocatorRegistrySmartPrx& registry) : 
+        mGroup(new SipGeneralGroup),
+	mPJSipManager(manager), 
+        mEndpointFactory(factory), 
+        mRoutingId(routingId), 
+        mRoutingServiceLocatorRegistry(registry) 
+    {
+    }
+
     /**
-     * Constructor for this private class
+     * A simplified version for the general group.
      */
-    ConfigurationServiceImplPriv(PJSipManager *manager, boost::shared_ptr<SipEndpointFactory> factory, std::string& id,
-	AsteriskSCF::SmartProxy::SmartProxy<LocatorRegistryPrx> registry) :
-	mPJSipManager(manager), mEndpointFactory(factory), mRoutingId(id), mRoutingServiceLocatorRegistry(registry) { };
-    
+    void selectInto(const ConfigurationItemDict& requestedItems, ConfigurationItemDict& returnedItems)
+    {
+        boost::shared_lock<boost::shared_mutex> lock(mLock);
+        selectIntoImpl(requestedItems, mGroup->configurationItems, returnedItems);
+    }
+
+    /**
+     * The generic version of selecting configuration items out of configuration groups.
+     **/
+    template <typename G>
+    bool selectIntoForGroup(const G& group, const G& newGroup)
+    {
+        boost::shared_lock<boost::shared_mutex> lock(mLock);
+        SipConfigurationGroupPtr g = getGroupFor(group);
+        if (g)
+        {
+            selectIntoImpl(group->configurationItems, g->configurationItems, newGroup->configurationItems);
+            return true;
+        }
+        return false;
+    }
+
+    template <typename G>
+    void removeFromGroup(const G& group)
+    {
+        boost::unique_lock<boost::shared_mutex> lock(mLock);
+        SipConfigurationGroupPtr g = getGroupFor(group);
+        if (g)
+        {
+            deleteFrom(group->configurationItems, g->configurationItems);
+        }
+    }
+
+    template <typename G>
+    SipConfigurationItemVisitorPtr update(const G& group)
+    {
+        boost::unique_lock<boost::shared_mutex> lock(mLock);
+        G v = getGroupFor(group);
+        if (!v)
+        {
+            v = createGroupTemplate(group);
+        }
+        performSerialCheck(group->configurationItems, v->configurationItems);
+        for (ConfigurationItemDict::const_iterator i = group->configurationItems.begin();
+            i != group->configurationItems.end(); ++i)
+        {
+            //
+            // Using the index operator allows us to effectively overwrite or add the value
+            // as opposed to erasing/inserting.
+            //
+            v->configurationItems[i->first] = i->second;
+        }
+        return updateGroup(v);
+    }
+
+    void remove(const SipGeneralGroupPtr&)
+    {
+        boost::unique_lock<boost::shared_mutex> lock(mLock);
+        mGroup = 0;
+    }
+
+    void remove(const SipDomainGroupPtr& group)
+    {        
+        boost::unique_lock<boost::shared_mutex> lock(mLock);
+        mDomains.erase(mDomains.find(group->domain));
+    }
+
+    void remove(const SipEndpointGroupPtr& group)
+    {
+        boost::unique_lock<boost::shared_mutex> lock(mLock);
+        mEndpoints.erase(mEndpoints.find(group->name));
+    }
+
+    void remove(const SipUDPTransportGroupPtr& group)
+    {
+        //
+        // It's a good idea to hold a reference to this until after we release the lock on the config item. That way any
+        // code that might happen as a result of its destruction cannot come back and cause deadlocks on this object.
+        //
+        UDPTransportConfigPtr config;
+        {
+            boost::unique_lock<boost::shared_mutex> lock(mLock);
+            UDPTransportMap::iterator i = mUDP.find(group->name);
+            if (i != mUDP.end())
+            {
+                config = i->second;
+                mUDP.erase(i);
+            }
+        }
+    }
+
+    void remove(const SipTCPTransportGroupPtr& group)
+    {
+        //
+        // It's a good idea to hold a reference to this until after we release the lock on the config item. That way any
+        // code that might happen as a result of its destruction cannot come back and cause deadlocks on this object.
+        //
+        TCPTransportConfigPtr config;
+        {
+            boost::unique_lock<boost::shared_mutex> lock(mLock);
+            TCPTransportMap::iterator i = mTCP.find(group->name);
+            if (i != mTCP.end())
+            {
+                config = i->second;
+                mTCP.erase(i);
+            }
+        }
+    }
+
+    void remove(const SipTLSTransportGroupPtr& group)
+    {
+        //
+        // It's a good idea to hold a reference to this until after we release the lock on the config item. That way any
+        // code that might happen as a result of its destruction cannot come back and cause deadlocks on this object.
+        //
+        TLSTransportConfigPtr config;
+        {
+            boost::unique_lock<boost::shared_mutex> lock(mLock);
+            TLSTransportMap::iterator i = mTLS.find(group->name);
+            if (i != mTLS.end())
+            {
+                config = i->second;
+                mTLS.erase(i);
+            }
+        }
+    }
+
+    SipGeneralGroupPtr getGroupFor(const SipGeneralGroupPtr&)
+    {
+        return mGroup;
+    }
+
+    void copyGroupTemplates(ConfigurationGroupSeq& groups)
+    {
+        boost::shared_lock<boost::shared_mutex> lock(mLock);
+        groups.push_back(mGroup);
+        copyTransportTemplates(groups, mUDP);
+        copyTransportTemplates(groups, mTCP);
+        copyTransportTemplates(groups, mTLS);
+        copyTemplates(groups, mDomains);
+        copyTemplates(groups, mEndpoints);
+    }
+
+    /**
+     * The following getItemsFor() members basically do a data member selection by type.  Interestingly, this is simply
+     * an extension of the idea behind the visitor extension to Ice, but it takes it further, allowing a large degree of
+     * generalization in a shared data class.
+     **/
+    const SipDomainGroupPtr getGroupFor(const SipDomainGroupPtr& group)
+    {
+        DomainMap::const_iterator i;
+        if (getItem(mDomains, i, group->domain))
+        {
+            return i->second;
+        }
+        return 0;
+    }
+
+    const SipEndpointGroupPtr getGroupFor(const SipEndpointGroupPtr& group)
+    {
+        EndpointMap::const_iterator i;
+        if (getItem(mEndpoints, i, group->name))
+        {
+            return i->second;
+        }
+        return 0;
+    }
+
+    const SipUDPTransportGroupPtr getGroupFor(const SipUDPTransportGroupPtr& group)
+    {
+        UDPTransportMap::const_iterator i;
+        if (getItem(mUDP, i, group->name))
+        {
+            return i->second->getTypedGroup();
+        }
+        return 0;
+    }
+
+    const SipTCPTransportGroupPtr getGroupFor(const SipTCPTransportGroupPtr& group)
+    {
+        TCPTransportMap::const_iterator i;
+        if (getItem(mTCP, i, group->name))
+        {
+            return i->second->getTypedGroup();
+        }
+        return 0;
+    }
+
+    const SipTLSTransportGroupPtr getGroupFor(const SipTLSTransportGroupPtr& group)
+    {
+        TLSTransportMap::const_iterator i;
+        if (getItem(mTLS, i, group->name))
+        {
+            return i->second->getTypedGroup();
+        }
+        return 0;
+    }
+
+private:
+
+    boost::shared_mutex mLock;
     /**
      * Configured SIP domains
      */
-    std::map<std::string, SipDomainGroupPtr> mConfiguredDomains;
+    DomainMap mDomains;
     
     /**
      * Configured UDP SIP transports
      */
-    std::map<std::string, boost::shared_ptr<UDPTransportImplPriv> > mConfiguredUDPTransports;
+    UDPTransportMap mUDP;
     
     /**
      * Configured TCP SIP transports
      */
-    std::map<std::string, boost::shared_ptr<TCPTransportImplPriv> > mConfiguredTCPTransports;
+    TCPTransportMap mTCP;
     
     /**
      * Configured TLS SIP transports
      */
-    std::map<std::string, boost::shared_ptr<TLSTransportImplPriv> > mConfiguredTLSTransports;
+    TLSTransportMap mTLS;
     
     /**
      * Configured SIP endpoints
      */
-    std::map<std::string, SipEndpointGroupPtr> mConfiguredEndpoints;
+    std::map<std::string, SipEndpointGroupPtr> mEndpoints;
     
     /**
      * General SIP configuration
      */
-    SipGeneralGroupPtr mGeneralGroup;
+    SipGeneralGroupPtr mGroup;
+
+    /**
+     * PJSipManager Pointer
+     */
+    PJSipManager* mPJSipManager;
+
+    /**
+     * Pointer to the endpoint factory used to create endpoints
+     */
+    boost::shared_ptr<SipEndpointFactory> mEndpointFactory;
+
+    /**
+     * Identifier for our endpoint locator.
+     */
+    std::string mRoutingId;
+
+    /**
+     * Proxy to endpoint locator registry
+     */
+    AsteriskSCF::SmartProxy::SmartProxy<LocatorRegistryPrx> mRoutingServiceLocatorRegistry;
+
+
+    //
+    // Helper function that does the selection out of one dictionary into another.
+    //
+    void selectIntoImpl(const ConfigurationItemDict& requestedItems, const ConfigurationItemDict& localItems,
+	    ConfigurationItemDict& returnedItems)
+    {
+        for (ConfigurationItemDict::const_iterator requestedItem = requestedItems.begin();
+             requestedItem != requestedItems.end();
+             ++requestedItem)
+        {
+            if (localItems.find(requestedItem->first) != localItems.end())
+            {
+                returnedItems.insert(*requestedItem);
+            }
+        }
+    }
+
+    void deleteFrom(const ConfigurationItemDict& itemsToDelete, ConfigurationItemDict& data)
+    {
+        for (ConfigurationItemDict::const_iterator i = itemsToDelete.begin();
+             i != itemsToDelete.end(); ++i)
+        {
+            data.erase(data.find(i->first));
+        }
+    }
+    
+    SipConfigurationItemVisitorPtr updateGroup(const SipGeneralGroupPtr&)
+    {
+        return 0;
+    }
+
+    SipConfigurationItemVisitorPtr updateGroup(const SipEndpointGroupPtr& group)
+    {
+        EndpointMap::const_iterator i;
+        SipEndpointGroupPtr endpointGroup;
+        if (!getItem(mEndpoints, i, group->name))
+        {
+            endpointGroup = createGroupTemplate(group);
+            mEndpoints.insert(make_pair(group->name, endpointGroup));
+        }
+        else
+        {
+            endpointGroup = i->second;
+        }
+        SipEndpointPtr endpoint = mEndpointFactory->findByName(group->name);
+        if (!endpoint)
+        {
+            //
+            // XXX - something better to do than just ignore?
+            // Should never happen, and we can't do anything else without really messing things up.
+            //
+            return 0;
+        }
+        return boost::shared_ptr<EndpointConfigHelper>(
+            new EndpointConfigHelper(endpoint, mEndpointFactory, mRoutingServiceLocatorRegistry, mRoutingId))->getVisitor();
+    }
+
+    SipConfigurationItemVisitorPtr updateGroup(const SipDomainGroupPtr&)
+    {
+        return 0;
+    }
+    
+    SipConfigurationItemVisitorPtr updateGroup(const SipUDPTransportGroupPtr& group)
+    {
+        UDPTransportMap::const_iterator i;
+        UDPTransportConfigPtr transport;
+        if (!getItem(mUDP, i, group->name))
+        {
+            transport.reset(new UDPTransportConfig(group, mPJSipManager));
+            mUDP.insert(make_pair(group->name, transport));
+        }
+        else
+        {
+            transport = i->second;
+        }
+        return transport->getVisitor();
+    }
+
+    SipConfigurationItemVisitorPtr updateGroup(const SipTCPTransportGroupPtr& group)
+    {
+        TCPTransportMap::const_iterator i;
+        TCPTransportConfigPtr transport;
+        if (!getItem(mTCP, i, group->name))
+        {
+            transport.reset(new TCPTransportConfig(group, mPJSipManager));
+            mTCP.insert(make_pair(group->name, transport));
+        }
+        else
+        {
+            transport = i->second;
+        }
+        return transport->getVisitor();
+    }
+
+    SipConfigurationItemVisitorPtr updateGroup(const SipTLSTransportGroupPtr& group)
+    {
+        TLSTransportMap::const_iterator i;
+        TLSTransportConfigPtr transport;
+        if (!getItem(mTLS, i, group->name))
+        {
+            transport.reset(new TLSTransportConfig(group, mPJSipManager));
+            mTLS.insert(make_pair(group->name, transport));
+        }
+        else
+        {
+            transport = i->second;
... 1209 lines suppressed ...


-- 
asterisk-scf/release/sip.git



More information about the asterisk-scf-commits mailing list