Example 7 - Spell Checker Client BundleΒΆ

In this example we create a client for the spell checker service we implemented in Example 6. This client monitors the dynamic availability of the spell checker service using the Service Tracker and is very similar in structure to the dictionary client we implemented in Example 5. The functionality of the spell checker client reads passages from standard input and spell checks them using the spell checker service. Our bundle uses its bundle context to create a ServiceTracker object to monitor spell checker services. The source code for our bundle is as follows in a file called spellcheckclient/Activator.cpp:

#include "ISpellCheckService.h"

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

#include <cstring>
#include <iostream>

using namespace cppmicroservices;

namespace {

/**
 * This class implements a bundle that uses a spell checker
 * service to check the spelling of a passage. This bundle
 * is essentially identical to Example 5, in that it uses the
 * Service Tracker to monitor the dynamic availability of the
 * spell checker service. When starting this bundle, the thread
 * calling the Start() method is used to read passages from
 * standard input. You can stop spell checking passages by
 * entering an empty line, but to start spell checking again
 * you must un-load 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 object to monitor spell checker services. Enters
   * a spell check loop where it reads passages from standard
   * input and checks their spelling using the spell checker service.
   *
   * \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 spell check services.
    m_tracker = new ServiceTracker<ISpellCheckService>(m_context);
    m_tracker->Open();

    //std::cout << "Tracker count is :" << m_tracker->GetTrackingCount() << std::endl;
    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 passage.
      std::cout << "Enter passage: ";

      std::string passage;
      std::getline(std::cin, passage);

      // Get the selected spell check service, if available.
      std::shared_ptr<ISpellCheckService> checker = m_tracker->GetService();

      // If the user entered a blank line, then
      // exit the loop.
      if (passage.empty())
      {
        break;
      }
      // If there is no spell checker, then say so.
      else if (checker == nullptr)
      {
        std::cout << "No spell checker available." << std::endl;
      }
      // Otherwise check passage and print misspelled words.
      else
      {
        std::vector<std::string> errors = checker->Check(passage);

        if (errors.empty())
        {
          std::cout << "Passage is correct." << std::endl;
        }
        else
        {
          std::cout << "Incorrect word(s):" << std::endl;
          for (std::size_t i = 0; i < errors.size(); ++i)
          {
            std::cout << "    " << errors[i] << 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<ISpellCheckService>* m_tracker;
};

}

CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(Activator)

After running the usTutorialDriver program use the status command to make sure that only the bundles from Example 2, Example 2b, and Example 6 are started; use the start (start <id | name>) and stop (stop <id | name>) commands as appropriate to start and stop the various tutorial bundles, respectively. Now we can start our spell checker client bundle by entering start spellcheckclient:

CppMicroServices-build> bin/usTutorialDriver
> start eventlistener
Starting to listen for service events.
> start spellcheckservice
> status
Id | Symbolic Name        | State
-----------------------------------
 0 | system_bundle        | ACTIVE
 1 | eventlistener        | ACTIVE
 2 | dictionaryservice    | INSTALLED
 3 | frenchdictionary     | INSTALLED
 4 | dictionaryclient     | INSTALLED
 5 | dictionaryclient2    | INSTALLED
 6 | dictionaryclient3    | INSTALLED
 7 | spellcheckservice    | ACTIVE
 8 | spellcheckclient     | INSTALLED
>

To trigger the registration of the spell checker service from our bundle, we start the frenchdictionary using the start frenchdictionary command. If the bundle from Example 1 is still active, then we should see it print out the details of the service event it receives when our new bundle registers its spell checker service:

CppMicroServices-build> bin/usTutorialDriver
> start spellcheckservice
> start frenchdictionary
> start spellcheckclient
Enter a blank line to exit.
Enter passage:

When we start the bundle, it will use the main thread to prompt us for passages; a passage is a collection of words separated by spaces, commas, periods, exclamation points, question marks, colons, or semi-colons. Enter a passage and press the enter key to spell check the passage or enter a blank line to stop spell checking passages. To restart the bundle, we must first use the stop command to stop the bundle, then the start command to re-start it.

Since this client uses the Service Tracker to monitor the dynamic availability of the spell checker service, it is robust in the scenario where the spell checker service suddenly departs. Further, when a spell checker 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.