Example 3 - Dictionary Client BundleΒΆ

This example creates a bundle that is a client of the dictionary service implemented in Example 2. In the following source code, our bundle uses its bundle context to query for a dictionary service. Our client bundle uses the first dictionary service it finds, and if none are found, it prints a message and stops. Services operate with no additional overhead. The source code for our bundle is as follows in a file called dictionaryclient/Activator.cpp:

#include "IDictionaryService.h"

#include "cppmicroservices/BundleActivator.h"
#include "cppmicroservices/BundleContext.h"

#include <iostream>

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 check for its existence in the dictionary.
 * This bundles uses the first service that it finds and does not monitor the
 * dynamic availability of the service (i.e., it does not listen for the arrival
 * or departure of dictionary services). When starting this bundle, the thread
 * calling the Start() method is used to read words from standard input. 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:
  /**
   * Implements BundleActivator::Start(). Queries for all available dictionary
   * services. If none are found it simply prints a message and returns,
   * otherwise it reads words from standard input and checks for their
   * existence from the first dictionary that it finds.
   *
   * \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)
  {
    // Query for all service references matching any language.
    std::vector<ServiceReference<IDictionaryService>> refs =
      context.GetServiceReferences<IDictionaryService>("(Language=*)");

    if (!refs.empty()) {
      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);

        // If the user entered a blank line, then
        // exit the loop.
        if (word.empty()) {
          break;
        }

        // First, get a dictionary service and then check
        // if the word is correct.
        std::shared_ptr<IDictionaryService> dictionary =
          context.GetService<IDictionaryService>(refs.front());
        if (dictionary->CheckWord(word)) {
          std::cout << "Correct." << std::endl;
        } else {
          std::cout << "Incorrect." << std::endl;
        }
      }
    } else {
      std::cout << "Couldn't find any dictionary service..." << std::endl;
    }
  }

  /**
   * 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*/)
  {
    // NOTE: The service is automatically released.
  }
};
}

CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(Activator)

Note that we do not need to unget or release the service in the Stop() method, because the C++ Micro Services library will automatically do so for us. We must create a manifest.json file with the meta-data for our bundle, which contains the following:

{
  "bundle.symbolic_name" : "dictionaryclient",
  "bundle.activator" : true
}

Since we are using the IDictionaryService interface defined in Example 1, we must link our bundle to the dictionaryservice bundle:

set(_srcs Activator.cpp)

set(dictionaryclient_DEPENDS dictionaryservice)
CreateTutorial(dictionaryclient ${_srcs})

After running the usTutorialDriver executable, and starting the event listener bundle, we can use the start dictionaryclient command to start our dictionary client bundle:

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

The above command starts the pre-installed bundle. When we start the bundle, 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 bundle, we must first use the stop dictionaryclient command to stop the bundle, then the start dictionaryclient 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.

This example client is simple enough and, in fact, is too simple. What would happen if the dictionary service were to unregister suddenly? Our client would abort with a segmentation fault due to a null pointer access when trying to use the service object. This dynamic service availability issue is a central tenent of the service model. As a result, we must make our client more robust in dealing with such situations. In Example 4, we explore a slightly more complicated dictionary client that dynamically monitors service availability.