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.