Example 5 - Service Tracker Dictionary Client BundleΒΆ
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
cppmicroservices::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 bundle uses its bundle 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 bundles is as follows in a file called
dictionaryclient3/Activator.cpp
:
#include "IDictionaryService.h"
#include "cppmicroservices/BundleActivator.h"
#include "cppmicroservices/BundleContext.h"
#include "cppmicroservices/ServiceTracker.h"
using namespace cppmicroservices;
namespace {
/**
* This class implements a bundle activator that uses a dictionary
* service to check for the proper spelling of a word by
* checking for its existence in the dictionary. This bundle
* 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 bundle 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 Start() 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 bundle again.
*/
class US_ABI_LOCAL Activator : public BundleActivator
{
public:
Activator()
: m_context()
, m_tracker(nullptr)
{}
/**
* Implements BundleActivator::Start(). 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 bundle context for this bundle.
*/
void Start(BundleContext context)
{
m_context = context;
// Create a service tracker to monitor dictionary services.
m_tracker = new ServiceTracker<IDictionaryService>(
m_context,
LDAPFilter(std::string("(&(") + Constants::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.
std::shared_ptr<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) {
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 BundleActivator::Stop(). Does nothing since
* the C++ Micro Services library will automatically unget any used services.
* @param context the context for the bundle.
*/
void Stop(BundleContext /*context*/) {}
private:
// Bundle context
BundleContext m_context;
// The service tracker
ServiceTracker<IDictionaryService>* m_tracker;
};
}
CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(Activator)
Since this client uses the ServiceTracker
utility class, it will
automatically monitor the dynamic availability of the dictionary
service. Like normal, we must create a manifest.json
file that
contains the meta-data for our bundle:
{
"bundle.symbolic_name" : "dictionaryclient3",
"bundle.activator" : true
}
Again, we must link our bundle to the dictionaryservice
bundle:
set(_srcs Activator.cpp)
set(dictionaryclient3_DEPENDS dictionaryservice)
CreateTutorial(dictionaryclient3 ${_srcs})
After running the usTutorialDriver
executable, and starting the
event listener bundle, we can use the start dictionaryclient3
command to start our robust dictionary client bundle:
CppMicroServices-debug> bin/usTutorialDriver
> start eventlistener
Starting to listen for service events.
> start dictionaryclient3
Ex1: Service of type IDictionaryService registered.
Enter a blank line to exit.
Enter word:
The above command starts the bundle and 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 re-start the bundle, we
must first use the stop dictionaryclient3
command to stop the bundle, then
the start dictionaryclient3
command to re-start 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.