CppMicroServices

C++ Micro Services: Example 5 - Service Tracker Dictionary Client Module
Example 5 - Service Tracker Dictionary Client Module

In Example 4, we created a more robust client bundle for our dictionary service. Due to the complexity of dealing with dynamic service availability, even that client may not sufficiently address all situations. To deal with this complexity the C++ Micro Services library provides the ServiceTracker utility class. In this example we create a client for the dictionary service that uses the ServiceTracker class to monitor the dynamic availability of the dictionary service, resulting in an even more robust client.

The functionality of the new dictionary client is essentially the same as the one from Example 4. Our module uses its module context to create a ServiceTracker instance to track the dynamic availability of the dictionary service on our behalf. Our client uses the dictionary service returned by the ServiceTracker, which is selected based on a ranking algorithm defined by the C++ Micro Services library. The source code for our modules is as follows in a file called dictionaryclient3/Activator.cpp:

#include "IDictionaryService.h"
#include <usModuleActivator.h>
#include <usModuleContext.h>
#include <usServiceTracker.h>
// Replace that include with your own base class declaration
#include US_BASECLASS_HEADER
US_USE_NAMESPACE
/**
* This class implements a module activator that uses a dictionary
* service to check for the proper spelling of a word by
* checking for its existence in the dictionary. This module
* uses a service tracker to dynamically monitor the availability
* of a dictionary service, instead of providing a custom service
* listener as in Example 4. The module uses the service returned
* by the service tracker, which is selected based on a ranking
* algorithm defined by the C++ Micro Services library.
* Again, the calling thread of the Load() method is used to read
* words from standard input, checking its existence in the dictionary.
* You can stop checking words by entering an empty line, but
* to start checking words again you must unload and then load
* the module again.
*/
class US_ABI_LOCAL Activator : public ModuleActivator
{
public:
Activator()
: m_context(NULL)
, m_tracker(NULL)
{}
/**
* Implements ModuleActivator::Load(). Creates a service
* tracker to monitor dictionary services and starts its "word
* checking loop". It will not be able to check any words until
* the service tracker finds a dictionary service; any discovered
* dictionary service will be automatically used by the client.
* It reads words from standard input and checks for their
* existence in the discovered dictionary.
*
* \note It is very bad practice to use the calling thread to perform a
* lengthy process like this; this is only done for the purpose of
* the tutorial.
*
* @param context the module context for this module.
*/
void Load(ModuleContext *context)
{
m_context = context;
// Create a service tracker to monitor dictionary services.
m_context, LDAPFilter(std::string("(&(") + ServiceConstants::OBJECTCLASS() + "=" +
us_service_interface_iid<IDictionaryService*>() + ")" +
"(Language=*))")
);
m_tracker->Open();
std::cout << "Enter a blank line to exit." << std::endl;
// Loop endlessly until the user enters a blank line
while (std::cin)
{
// Ask the user to enter a word.
std::cout << "Enter word: ";
std::string word;
std::getline(std::cin, word);
// Get the selected dictionary, if available.
IDictionaryService* dictionary = m_tracker->GetService();
// If the user entered a blank line, then
// exit the loop.
if (word.empty())
{
break;
}
// If there is no dictionary, then say so.
else if (dictionary == NULL)
{
std::cout << "No dictionary available." << std::endl;
}
// Otherwise print whether the word is correct or not.
else if (dictionary->CheckWord(word))
{
std::cout << "Correct." << std::endl;
}
else
{
std::cout << "Incorrect." << std::endl;
}
}
// This automatically closes the tracker
delete m_tracker;
}
/**
* Implements ModuleActivator::Unload(). Does nothing since
* the C++ Micro Services library will automatically unget any used services.
* @param context the context for the module.
*/
void Unload(ModuleContext* /*context*/)
{
}
private:
// Module context
ModuleContext* m_context;
// The service tracker
};
US_EXPORT_MODULE_ACTIVATOR(dictionaryclient3, Activator)

Since this client uses the ServiceTracker utility class, it will automatically monitor the dynamic availability of the dictionary service. Again, we must link our module to the dictionaryservice module:

set(_srcs Activator.cpp)
NAME "Dictionary Client 3"
LIBRARY_NAME "dictionaryclient3")
set(dictionaryclient3_DEPENDS dictionaryservice)
CreateExample(dictionaryclient3 ${_srcs})

After running the CppMicroServicesExampleDriver executable, and loading the event listener module, we can use the l dictionaryclient3 command to load our robust dictionary client module:

CppMicroServices-debug> bin/CppMicroServicesExampleDriver
> l eventlistener
Starting to listen for service events.
> l dictionaryclient3
Ex1: Service of type IDictionaryService/1.0 registered.
Enter a blank line to exit.
Enter word:

The above command loads the module and its dependencies (the dictionaryservice module) in a single step. When we load the module, it will use the main thread to prompt us for words. Enter one word at a time to check the words and enter a blank line to stop checking words. To reload the module, we must use the s command to get the module identifier number for the module and first use the u <id> command to unload the module, then the l <id> command to re-load it. To test the dictionary service, enter any of the words in the dictionary (e.g., "welcome", "to", "the", "micro", "services", "tutorial") or any word not in the dictionary.

Since this client monitors the dynamic availability of the dictionary service, it is robust in the face of sudden departures of the the dictionary service. Further, when a dictionary service arrives, it automatically gets the service if it needs it and continues to function. These capabilities are a little difficult to demonstrate since we are using a simple single-threaded approach, but in a multi-threaded or GUI-oriented application this robustness is very useful.

Next: Example 6 - Spell Checker Service Module

Previous: Example 4 - Robust Dictionary Client Module