CppMicroServices

C++ Micro Services: Example 1 - Service Event Listener
Example 1 - Service Event Listener

This example creates a simple module that listens for service events. This example does not do much at first, because it only prints out the details of registering and unregistering services. In the next example we will create a module that implements a service, which will cause this module to actually do something. For now, we will just use this example to help us understand the basics of creating a module and its activator.

A module gains access to the C++ Micro Services API using a unique instance of ModuleContext. This unique module context can be used during static initialization of the module or at any later point during the life-time of the module. To execute code during static initialization (and de-initialization) time, the module must provide an implementation of the ModuleActivator interface; this interface has two methods, Load() and Unload(), that both receive the module's context and are called when the module is loaded (statically initialized) and unloaded, respectively.

Note
You do not need to remember the ModuleContext instance within the ModuleActivator::Load() method and provide custom access methods for later retrieval. Use the GetModuleContext() function to easily retrieve the current module's context.

In the following source code, our module implements the ModuleActivator interface and uses the context to add itself as a listener for service events (in the eventlistener/Activator.cpp file):

#include <usModuleActivator.h>
#include <usModuleContext.h>
US_USE_NAMESPACE
/**
* This class implements a simple module that utilizes the CppMicroServices's
* event mechanism to listen for service events. Upon receiving a service event,
* it prints out the event's details.
*/
class Activator : public ModuleActivator
{
private:
/**
* Implements ModuleActivator::Load(). Prints a message and adds a member
* function to the module context as a service listener.
*
* @param context the framework context for the module.
*/
void Load(ModuleContext* context)
{
std::cout << "Starting to listen for service events." << std::endl;
context->AddServiceListener(this, &Activator::ServiceChanged);
}
/**
* Implements ModuleActivator::Unload(). Prints a message and removes the
* member function from the module context as a service listener.
*
* @param context the framework context for the module.
*/
void Unload(ModuleContext* context)
{
context->RemoveServiceListener(this, &Activator::ServiceChanged);
std::cout << "Stopped listening for service events." << std::endl;
// Note: It is not required that we remove the listener here,
// since the framework will do it automatically anyway.
}
/**
* Prints the details of any service event from the framework.
*
* @param event the fired service event.
*/
void ServiceChanged(const ServiceEvent event)
{
std::string objectClass = ref_any_cast<std::list<std::string> >(event.GetServiceReference().GetProperty(ServiceConstants::OBJECTCLASS())).front();
{
std::cout << "Ex1: Service of type " << objectClass << " registered." << std::endl;
}
else if (event.GetType() == ServiceEvent::UNREGISTERING)
{
std::cout << "Ex1: Service of type " << objectClass << " unregistered." << std::endl;
}
else if (event.GetType() == ServiceEvent::MODIFIED)
{
std::cout << "Ex1: Service of type " << objectClass << " modified." << std::endl;
}
}
};
US_EXPORT_MODULE_ACTIVATOR(eventlistener, Activator)

After implementing the C++ source code for the module activator, we must export the activator such that the C++ Micro Services library can create an instance of it and call the Load() and Unload() methods:

US_EXPORT_MODULE_ACTIVATOR(eventlistener, Activator)

Now we need to compile the source code. This example uses CMake as the build system and the top-level CMakeLists.txt file could look like this:

project(CppMicroServicesExamples)
cmake_minimum_required(VERSION 2.8)
find_package(CppMicroServices NO_MODULE REQUIRED)
include_directories(${CppMicroServices_INCLUDE_DIRS})
#-----------------------------------------------------------------------------
# Set C/CXX flags
#-----------------------------------------------------------------------------
if(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CppMicroServices_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${CppMicroServices_CXX_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${CppMicroServices_CXX_FLAGS_DEBUG}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CppMicroServices_C_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${CppMicroServices_C_FLAGS_RELEASE}")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${CppMicroServices_C_FLAGS_DEBUG}")
endif()
#-----------------------------------------------------------------------------
# Init output directories
#-----------------------------------------------------------------------------
set(CppMicroServicesExamples_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
set(CppMicroServicesExamples_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
set(CppMicroServicesExamples_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
foreach(_type ARCHIVE LIBRARY RUNTIME)
if(NOT CMAKE_${_type}_OUTPUT_DIRECTORY)
set(CMAKE_${_type}_OUTPUT_DIRECTORY ${CppMicroServicesExamples_${_type}_OUTPUT_DIRECTORY})
endif()
endforeach()
function(CreateExample _name)
add_library(Example-${_name} SHARED ${ARGN})
if(${_name}_DEPENDS)
foreach(_dep ${${_name}_DEPENDS})
include_directories(${CppMicroServicesExamples_SOURCE_DIR}/${_dep})
target_link_libraries(Example-${_name} Example-${_dep})
endforeach()
endif()
target_link_libraries(Example-${_name} ${CppMicroServices_LIBRARIES})
set_target_properties(Example-${_name} PROPERTIES
LABELS Examples
OUTPUT_NAME ${_name}
)
endfunction()
add_subdirectory(eventlistener)

and the CMakeLists.txt file in the eventlistener subdirectory is:

set(_srcs Activator.cpp)
NAME "Event Listener"
LIBRARY_NAME "eventlistener")
CreateExample(eventlistener ${_srcs})

The call to usFunctionGenerateModuleInit is necessary to integrate the shared library as a module within the C++ Micro Service library. If you are not using CMake, you have to place a macro call to US_INITIALIZE_MODULE yourself into the module's source code, e.g. in Activator.cpp. Have a look at the Getting Started documentation for more details about using CMake or other build systems (e.g. Makefiles) when writing modules.

To run the examples contained in the C++ Micro Services library, we use a small driver program called CppMicroServicesExampleDriver:

CppMicroServices-build> bin/CppMicroServicesExampleDriver
> h
h               This help text
l <id | name>   Load the module with id <id> or name <name>
u <id>          Unload the module with id <id>
s               Print status information
q               Quit
>

Typing s at the command prompt lists the available, loaded, and unloaded modules. To load the eventlistener module, type l eventlistener at the command prompt:

> s
Id | Name                 | Status
-----------------------------------
 - | dictionaryclient     | -
 - | dictionaryclient2    | -
 - | dictionaryclient3    | -
 - | dictionaryservice    | -
 - | eventlistener        | -
 - | frenchdictionary     | -
 - | spellcheckclient     | -
 - | spellcheckservice    | -
 1 | CppMicroServices     | LOADED
> l eventlistener
Starting to listen for service events.
>

The above command loaded the eventlistener module (by loading its shared library). Keep in mind, that this module will not do much at this point since it only listens for service events and we are not registering any services. In the next example we will register a service that will generate an event for this module to receive. To exit the CppMicroServicesExampleDriver, use the q command.

Next: Example 2 - Dictionary Service Module