Added and integrated the ServicePublisher interface in order to improve management...
authorJohannes Schanda <schanda@itestra.de>
Wed, 29 May 2013 14:59:07 +0000 (16:59 +0200)
committerGerrit Code Review <qqmthk1@lpmodthk02.bmwgroup.net>
Mon, 3 Jun 2013 13:39:40 +0000 (15:39 +0200)
Also, an interface for asynchronous service discovery has been added to the Factory.

Makefile.am
README
src/CommonAPI/Factory.h
src/CommonAPI/MainLoopContext.h
src/CommonAPI/Runtime.h
src/CommonAPI/ServicePublisher.h [new file with mode: 0644]
src/test/VariantTest.cpp

index bc817d1..10de2ac 100644 (file)
@@ -49,6 +49,7 @@ CommonAPI_include_HEADERS = \
         src/CommonAPI/SerializableStruct.h \
         src/CommonAPI/SerializableVariant.h \
         src/CommonAPI/SerializableVariant.hpp \
+        src/CommonAPI/ServicePublisher.h \
         src/CommonAPI/Stub.h \
         src/CommonAPI/types.h
 
@@ -67,7 +68,7 @@ check_PROGRAMS = \
         VariantTest
 
 
-TESTS =        ${check_PROGRAMS}
+TESTS = ${check_PROGRAMS}
 VariantTest_includedir=$(includedir)/CommonAPI-${VERSION}/CommonAPI
 VariantTest_SOURCES = src/test/VariantTest.cpp
 VariantTest_CPPFLAGS = ${AM_CPPFLAGS} ${GTEST_CPPFLAGS}
diff --git a/README b/README
index a5b5ce3..ade361f 100644 (file)
--- a/README
+++ b/README
@@ -51,7 +51,14 @@ Use autotools to build this package:
 # make
 # sudo make install (or alternative install process, eg. checkinstall on debian-based distributions, such as Ubuntu)
 ----
-If the environment variable GTEST_CONFIG is set to the path of the gtest-config script in a Gtest tree test will also be built.  
+If the environment variable GTEST_CONFIG is set to the path of the gtest-config script in a Gtest tree test will also be built.
+
+== Elements marked as deprecated
+
+All elements that are marked as deprecated on Common API level are considered to be redundant, and normally
+the other way of acchieving the same (the way pointed to by the marked elements) is the way we recommend to use.
+Elements marked as deprecated will be removed once we have reliable and comprehensive feedback telling us they
+are not used anymore by anybody, but they will remain in Common API for compatibility reasons at least until then.
 
 == Working on the code & contribution
 
index 7f167a1..e06b59e 100644 (file)
@@ -35,6 +35,7 @@ struct DefaultAttributeProxyFactoryHelper;
 template<template<typename ...> class _ProxyClass, template<typename> class _AttributeExtension>
 std::shared_ptr<typename DefaultAttributeProxyFactoryHelper<_ProxyClass, _AttributeExtension>::class_t> createProxyWithDefaultAttributeExtension(Factory* specificFactory, const std::string& participantId, const std::string& domain);
 
+
 /**
  * \brief The main CommonAPI access class. A factory is responsible for creation and destruction of service objects.
  *
@@ -43,6 +44,8 @@ std::shared_ptr<typename DefaultAttributeProxyFactoryHelper<_ProxyClass, _Attrib
  */
 class Factory {
  public:
+    typedef std::function<void(std::vector<std::string>&) > GetAvailableServiceInstancesCallback;
+    typedef std::function<void(bool)> IsServiceInstanceAliveCallback;
 
     /**
      * \brief Creates factory. Don't call manually.
@@ -74,15 +77,15 @@ class Factory {
                const std::string& serviceName,
                const std::string& domain) {
 
-       std::shared_ptr<Proxy> abstractMiddlewareProxy = createProxy(_ProxyClass<_AttributeExtensions...>::getInterfaceId(), participantId, serviceName, domain);
-       return std::make_shared<_ProxyClass<_AttributeExtensions...>>(abstractMiddlewareProxy);
+        std::shared_ptr<Proxy> abstractMiddlewareProxy = createProxy(_ProxyClass<_AttributeExtensions...>::getInterfaceId(), participantId, serviceName, domain);
+        return std::make_shared<_ProxyClass<_AttributeExtensions...>>(abstractMiddlewareProxy);
     }
 
     /**
      * \brief Build a proxy for the specified address
      *
      * Build a proxy for the specified address.
-     * Template this method call for the desired proxy type and attribute extension.
+     * Template this method call for the desired proxy type and attribute extensions.
      *
      * @param serviceAddress The common API address
      * @return a shared pointer to the constructed proxy
@@ -91,21 +94,21 @@ class Factory {
     std::shared_ptr<_ProxyClass<_AttributeExtensions...> >
     buildProxy(const std::string& serviceAddress) {
 
-               std::string domain;
-               std::string serviceName;
-               std::string participantId;
-               if(!splitValidAddress(serviceAddress, domain, serviceName, participantId)) {
-                       return false;
-               }
+        std::string domain;
+        std::string serviceName;
+        std::string participantId;
+        if(!splitValidAddress(serviceAddress, domain, serviceName, participantId)) {
+            return false;
+        }
 
-               return buildProxy<_ProxyClass, _AttributeExtensions...>(participantId, serviceName, domain);
+        return buildProxy<_ProxyClass, _AttributeExtensions...>(participantId, serviceName, domain);
     }
 
     /**
      * \brief Build a proxy for the specified address with one extension for all attributes
      *
      * Build a proxy for the specified address with one extension for all attributes
-     * Template this method call for the desired proxy type attribute extension.
+     * Template this method call for the desired proxy type and attribute extensions.
      *
      * @param participantId The participant ID of the common API address (last part)
      * @param serviceName The service name of the common API address (middle part)
@@ -118,8 +121,8 @@ class Factory {
                                             const std::string& serviceName,
                                             const std::string& domain) {
 
-       std::shared_ptr<Proxy> abstractMiddlewareProxy = createProxy(DefaultAttributeProxyFactoryHelper<_ProxyClass, _AttributeExtension>::class_t::getInterfaceId(), participantId, serviceName, domain);
-       return std::make_shared<typename DefaultAttributeProxyFactoryHelper<_ProxyClass, _AttributeExtension>::class_t>(abstractMiddlewareProxy);
+        std::shared_ptr<Proxy> abstractMiddlewareProxy = createProxy(DefaultAttributeProxyFactoryHelper<_ProxyClass, _AttributeExtension>::class_t::getInterfaceId(), participantId, serviceName, domain);
+        return std::make_shared<typename DefaultAttributeProxyFactoryHelper<_ProxyClass, _AttributeExtension>::class_t>(abstractMiddlewareProxy);
     }
 
     /**
@@ -135,14 +138,14 @@ class Factory {
     std::shared_ptr<typename DefaultAttributeProxyFactoryHelper<_ProxyClass, _AttributeExtension>::class_t>
     buildProxyWithDefaultAttributeExtension(const std::string& serviceAddress) {
 
-               std::string domain;
-               std::string serviceName;
-               std::string participantId;
-               if(!splitValidAddress(serviceAddress, domain, serviceName, participantId)) {
-                       return false;
-               }
+        std::string domain;
+        std::string serviceName;
+        std::string participantId;
+        if(!splitValidAddress(serviceAddress, domain, serviceName, participantId)) {
+            return false;
+        }
 
-               return buildProxyWithDefaultAttributeExtension<_ProxyClass, _AttributeExtension>(participantId, serviceName, domain);
+        return buildProxyWithDefaultAttributeExtension<_ProxyClass, _AttributeExtension>(participantId, serviceName, domain);
     }
 
     /**
@@ -159,54 +162,72 @@ class Factory {
     /**
      * \brief Register a service stub under a specified address
      *
-     * Register a service stub under a specified address
+     * Register a service stub under a specified address. The service will be registered
+     * with the ServicePublisher that is provided by the runtime which you also retrieved
+     * this factory from. It is recommended to use the ServicePublisher for registering
+     * and unregistering purposes.
      *
      * @param stub The stub pointer
      * @param participantId The participant ID of the common API address (last part)
      * @param serviceName The service name of the common API address (middle part)
      * @param domain The domain of the common API address (first part)
      * @return Was the registration successful
+     *
+     * @deprecated Use CommonAPI::Runtime->getServicePublisher()->registerService() instead.
+     * Purpose for this change is to make service management (esp. deregistering) independent
+     * from factory instances.
      */
     template<typename _Stub>
     bool registerService(std::shared_ptr<_Stub> stub,
-                                    const std::string& participantId,
-                                    const std::string& serviceName,
-                                const std::string& domain) {
+                         const std::string& participantId,
+                         const std::string& serviceName,
+                         const std::string& domain) {
 
-       std::shared_ptr<StubBase> stubBase = std::dynamic_pointer_cast<StubBase>(stub);
-               return registerAdapter(stubBase, _Stub::StubAdapterType::getInterfaceId(), participantId, serviceName, domain);
+        std::shared_ptr<StubBase> stubBase = std::dynamic_pointer_cast<StubBase>(stub);
+        return registerAdapter(stubBase, _Stub::StubAdapterType::getInterfaceId(), participantId, serviceName, domain);
     }
 
     /**
      * \brief Register a service stub under a specified address
      *
-     * Register a service stub under a specified address
+     * Register a service stub under a specified address. The service will be registered
+     * with the ServicePublisher that is provided by the runtime which you also retrieved
+     * this factory from. It is recommended to use the ServicePublisher for registering
+     * and unregistering purposes.
      *
      * @param stub The stub pointer
      * @param serviceAddress The common API address
      * @return Was the registration successful
+     *
+     * @deprecated Use CommonAPI::Runtime->getServicePublisher()->registerService() instead.
+     * Purpose for this change is to make service management (esp. deregistering) independent
+     * from factory instances.
      */
     template<typename _Stub>
     bool registerService(std::shared_ptr<_Stub> stub, const std::string& serviceAddress) {
-               std::string domain;
-               std::string serviceName;
-               std::string participantId;
-               if(!splitValidAddress(serviceAddress, domain, serviceName, participantId)) {
-                       return false;
-               }
-
-               return registerService<_Stub>(stub, participantId, serviceName, domain);
+        std::string domain;
+        std::string serviceName;
+        std::string participantId;
+        if(!splitValidAddress(serviceAddress, domain, serviceName, participantId)) {
+            return false;
+        }
+
+        return registerService<_Stub>(stub, participantId, serviceName, domain);
     }
 
     /**
      * \brief Unregister a service stub associated with a specified address
      *
-     * Unregister a service stub associated with a specified address
+     * Unregister a service stub associated with a specified address.
      *
      * @param participantId The participant ID of the common API address (last part)
      * @param serviceName The service name of the common API address (middle part)
      * @param domain The domain of the common API address (first part)
      * @return Was the deregistration successful
+     *
+     * @deprecated Use CommonAPI::Runtime->getServicePublisher()->unregisterService() instead.
+     * Purpose for this change is to make service management (esp. deregistering) independent
+     * from factory instances.
      */
     virtual bool unregisterService(const std::string& participantId, const std::string& serviceName, const std::string& domain) = 0;
 
@@ -217,15 +238,19 @@ class Factory {
      *
      * @param serviceAddress The common API address
      * @return Was the deregistration successful
+     *
+     * @deprecated Use CommonAPI::Runtime->getServicePublisher()->unregisterService() instead.
+     * Purpose for this change is to make service management (esp. deregistering) independent
+     * from factory instances.
      */
     inline bool unregisterService(const std::string& serviceAddress) {
-               std::string domain;
-               std::string serviceName;
-               std::string participantId;
-               if(!splitValidAddress(serviceAddress, domain, serviceName, participantId)) {
-                       return false;
-               }
-               return unregisterService(participantId, serviceName, domain);
+        std::string domain;
+        std::string serviceName;
+        std::string participantId;
+        if(!splitValidAddress(serviceAddress, domain, serviceName, participantId)) {
+            return false;
+        }
+        return unregisterService(participantId, serviceName, domain);
     }
 
     /**
@@ -261,9 +286,39 @@ class Factory {
      */
     virtual bool isServiceInstanceAlive(const std::string& serviceInstanceID, const std::string& serviceName, const std::string& serviceDomainName = "local") = 0;
 
+    /**
+     * \brief Get all instances of a specific service name available. Asynchronous call.
+     *
+     * Get all instances of a specific service name available. Asynchronous call.
+     *
+     * @param serviceName The service name of the common API address (middle part)
+     * @param serviceDomainName The domain of the common API address (first part)
+     */
+    virtual void getAvailableServiceInstancesAsync(GetAvailableServiceInstancesCallback callback, const std::string& serviceName, const std::string& serviceDomainName = "local") = 0;
+
+    /**
+     * \brief Tells whether a particular service instance is available. Asynchronous call.
+     *
+     * Tells whether a particular service instance is available. Asynchronous call.
+     *
+     * @param serviceAddress The common API address of the service
+     */
+    virtual void isServiceInstanceAliveAsync(IsServiceInstanceAliveCallback callback, const std::string& serviceAddress) = 0;
+
+    /**
+     * \brief Tells whether a particular service instance is available. Asynchronous call.
+     *
+     * Tells whether a particular service instance is available. Asynchronous call.
+     *
+     * @param serviceInstanceID The participant ID of the common API address (last part) of the service
+     * @param serviceName The service name of the common API address (middle part) of the service
+     * @param serviceDomainName The domain of the common API address (first part) of the service
+     */
+    virtual void isServiceInstanceAliveAsync(IsServiceInstanceAliveCallback callback, const std::string& serviceInstanceID, const std::string& serviceName, const std::string& serviceDomainName = "local") = 0;
+
  protected:
     virtual std::shared_ptr<Proxy> createProxy(const char* interfaceId, const std::string& participantId, const std::string& serviceName, const std::string& domain) = 0;
-    virtual bool registerAdapter(std::shared_ptr<StubBase> stubBase, const char* interfaceId, const std::string& participantId, const std::string& serivceName, const std::string& domain) = 0;
+    virtual bool registerAdapter(std::shared_ptr<StubBase> stubBase, const char* interfaceId, const std::string& participantId, const std::string& serviceName, const std::string& domain) = 0;
 
  private:
     std::shared_ptr<Runtime> runtime_;
@@ -271,20 +326,20 @@ class Factory {
     const MiddlewareInfo* middlewareInfo_;
 
     inline bool splitValidAddress(const std::string& serviceAddress, std::string& domain, std::string& serviceName, std::string& participantId) {
-       std::istringstream addressStream(serviceAddress);
-               if(!std::getline(addressStream, domain, ':')) {
-                       return false;
-               }
-               if(!std::getline(addressStream, serviceName, ':')) {
-                       return false;
-               }
-               if(!std::getline(addressStream, participantId, ':')) {
-                       return false;
-               }
-               if(std::getline(addressStream, participantId)) {
-                       return false;
-               }
-               return true;
+        std::istringstream addressStream(serviceAddress);
+        if(!std::getline(addressStream, domain, ':')) {
+            return false;
+        }
+        if(!std::getline(addressStream, serviceName, ':')) {
+            return false;
+        }
+        if(!std::getline(addressStream, participantId, ':')) {
+            return false;
+        }
+        if(std::getline(addressStream, participantId)) {
+            return false;
+        }
+        return true;
     }
 };
 
index 13bc4d9..a391db4 100644 (file)
@@ -14,6 +14,7 @@
 #include <limits>
 #include <vector>
 #include <chrono>
+#include <list>
 
 
 namespace CommonAPI {
index d7a366d..937047b 100644 (file)
@@ -9,7 +9,6 @@
 
 
 #include "MiddlewareInfo.h"
-#include "Factory.h"
 #include "MainLoopContext.h"
 
 #include <memory>
@@ -27,6 +26,7 @@ namespace CommonAPI {
 class Factory;
 class Runtime;
 class MainLoopContext;
+class ServicePublisher;
 
 /**
  * \brief Represents the CommonAPI runtime bindings available.
@@ -35,27 +35,30 @@ class MainLoopContext;
  */
 class Runtime {
  public:
-
     /**
-     * \brief Loads the default runtime
+     * \brief Loads the default runtime.
      *
-     * Loads the default runtime. This is the only one available, or the default as defined in configuration
+     * Loads the runtime for the default middleware binding. This either is the only binding available,
+     * or the one defined as default in the configuration.
      *
-     * @return The runtime object for this binding
+     * @return The runtime object for the default binding
      */
     static std::shared_ptr<Runtime> load();
+
     /**
-     * \brief Loads specified runtime
+     * \brief Loads specified runtime.
      *
-     * Loads specified runtime. This is specified by either the well known name defined by the binding, or configured
+     * Loads the runtime for the specified middleware binding. The given middleware ID can be either
+     * the well known name defined by a binding, or a configured alias for a binding.
      *
      * @return The runtime object for specified binding
      */
     static std::shared_ptr<Runtime> load(const std::string& middlewareId);
+
     /**
-     * \brief Called by bindings to register their methods. Do not call from applications.
+     * \brief Called by bindings to register their runtime loaders. Do not call from applications.
      *
-     * Called by bindings to register their methods. Do not call from applications.
+     * Called by bindings to register their runtime loaders. Do not call from applications.
      */
     static void registerRuntimeLoader(std::string middlewareName, MiddlewareRuntimeLoadFunction middlewareRuntimeLoadFunction);
 
@@ -71,17 +74,30 @@ class Runtime {
      * @return A new MainLoopContext object
      */
     std::shared_ptr<MainLoopContext> getNewMainLoopContext() const;
+
     /**
-     * \brief Create a factory for the loaded runtime
+     * \brief Create a factory for the loaded runtime.
      *
-     * Create a factory for the loaded rluntime
+     * Create a factory for the loaded runtime
      *
      * @param In case mainloop integration shall be used, a std::shared_ptr<MainLoopContext> can be passed in.
      *        If no parameter is given, internal threading will handle sending and receiving of messages automatically.
      *
-     * @return Factory object for the loaded runtime
+     * @return Factory object for this runtime
      */
     virtual std::shared_ptr<Factory> createFactory(std::shared_ptr<MainLoopContext> = std::shared_ptr<MainLoopContext>(NULL)) = 0;
+
+    /**
+     * \brief Returns the ServicePublisher object for this runtime.
+     *
+     * Returns the ServicePublisher object for this runtime. Use the interface
+     * provided by the ServicePublisher to publish and de-publish the services that
+     * your application will provide to the outside world over the middleware
+     * represented by this runtime. A ServicePublisher exists once per middleware.
+     *
+     * @return The ServicePublisher object for this runtime
+     */
+    virtual std::shared_ptr<ServicePublisher> getServicePublisher() = 0;
 };
 
 
diff --git a/src/CommonAPI/ServicePublisher.h b/src/CommonAPI/ServicePublisher.h
new file mode 100644 (file)
index 0000000..4d009be
--- /dev/null
@@ -0,0 +1,121 @@
+/* Copyright (C) 2013 BMW Group
+ * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
+ * Author: Juergen Gehring (juergen.gehring@bmw.de)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef COMMONAPI_SERVICE_PUBLISHER_H_
+#define COMMONAPI_SERVICE_PUBLISHER_H_
+
+
+#include "Factory.h"
+
+
+namespace CommonAPI {
+
+/**
+ * \brief Manages all services that shall be published by the application.
+ *
+ * Stubs for all services that shall be published will be registered here.
+ * This class is defined as singleton per loaded runtime (i.e. per loaded middleware).
+ */
+class ServicePublisher {
+ public:
+    virtual ~ServicePublisher() {}
+
+    /**
+     * \brief Registers and publishes a service.
+     *
+     * Registers and publishes a service. Which service is to be published is defined
+     * by the stub-pointer that is given as parameter. The given factory will be used
+     * to construct all necessary middleware specific objects to do the publishing.
+     *
+     * \note Note that a call to this method will always result in a registration
+     * with the middleware the given factory was instantiated for, not the middleware
+     * that matches the runtime this ServicePublisher was retrieved from. Accordingly,
+     * unregistering the service will have to be done by using the ServicePublisher
+     * that is provided by the runtime matching the middleware that also provided
+     * the given factory.
+     *
+     * @param serviceAddress The CommonAPI address the service shall be reachable at
+     * @param stub The stub that provides an implementation for the service
+     * @param factory The factory that will be used to construct all necessary middleware specific objects
+     *
+     * @return 'true' if the service was published successfully, 'false' if not or if another service that uses
+     * the exact same address already is registered.
+     */
+    template<typename _Stub>
+    bool registerService(std::shared_ptr<_Stub> stub,
+                         const std::string& serviceAddress,
+                         std::shared_ptr<Factory> factory) {
+        return factory->registerService<_Stub>(stub, serviceAddress);
+    }
+
+    /**
+     * \brief Registers and publishes a service.
+     *
+     * Registers and publishes a service. Which service is to be published is defined
+     * by the stub-pointer that is given as parameter. The given factory will be used
+     * to construct all necessary middleware specific objects to do the publishing.
+     *
+     * \note Note that a call to this method will always result in a registration
+     * with the middleware the given factory was instantiated for, not the middleware
+     * that matches the runtime this ServicePublisher was retrieved from. Accordingly,
+     * unregistering the service will have to be done by using the ServicePublisher
+     * that is provided by the runtime matching the middleware that also provided
+     * the given factory.
+     *
+     * @param participantId The CommonAPI participant ID the service shall be identified with
+     * @param serviceName The CommonAPI service name the service shall provide
+     * @param domain The CommonAPI domain the service shall be reachable at
+     * @param stub The stub that provides an implementation for the service
+     * @param factory The factory that will be used to construct all necessary middleware specific objects
+     *
+     * @return 'true' if the service was published successfully, 'false' if not or if another service that uses
+     * the exact same address already is registered.
+     */
+    template<typename _Stub>
+    bool registerService(std::shared_ptr<_Stub> stub,
+                         const std::string& participantId,
+                         const std::string& serviceName,
+                         const std::string& domain,
+                         std::shared_ptr<Factory> factory) {
+
+        return factory->registerService<_Stub>(stub, participantId, serviceName, domain);
+    }
+
+    /**
+     * \brief Unregisters and depublishes the service that was published for the given address.
+     *
+     * Unregisters and depublishes the service that was published for the given CommonAPI address.
+     *
+     * @param The CommonAPI address the service was registered for
+     *
+     * @return 'true' if there was a service for the given address and depublishing
+     * was successful, 'false' otherwise
+     */
+    virtual bool unregisterService(const std::string& serviceAddress) = 0;
+
+    /**
+     * \brief Unregisters and depublishes the service that was published for the given address.
+     *
+     * Unregisters and depublishes the service that was published for the given CommonAPI address.
+     *
+     * @param The CommonAPI participant ID the service was identified with
+     * @param The CommonAPI service name the service provided
+     * @param The CommonAPI domain the service was registered for
+     *
+     * @return 'true' if there was a service for the given address and depublishing
+     * was successful, 'false' otherwise
+     */
+    virtual bool unregisterService(const std::string& participantId, const std::string& serviceName, const std::string& domain) {
+        std::string serviceAddress(participantId + ":" + serviceName + ":"+ domain);
+        return unregisterService(serviceAddress);
+    }
+};
+
+} // namespace CommonAPI
+
+
+#endif /* COMMONAPI_SERVICE_PUBLISHER_H_ */
index 14290a7..7fa0920 100755 (executable)
@@ -143,7 +143,6 @@ TEST_F(VariantTest, VariantTestPack) {
 
     Variant<test1, test2> complexTarget = complexSource;
     EXPECT_EQ(1, complexTarget.get<test1>().a);
-
 }
 
 typedef Variant<test1, test2, std::string, uint8_t> ComplexTestVariant;