Browse Source

[2974] Added per-library packet context.

Stephen Morris 12 years ago
parent
commit
e686a5339d

+ 82 - 5
src/lib/util/hooks/callout_handle.cc

@@ -13,10 +13,10 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <util/hooks/callout_handle.h>
+#include <util/hooks/library_handle.h>
 
-#include <algorithm>
-#include <functional>
 #include <string>
+#include <utility>
 #include <vector>
 
 using namespace std;
@@ -25,14 +25,91 @@ using namespace isc::util;
 namespace isc {
 namespace util {
 
-// return the name of all context items.
+// Return the name of all argument items.
 
 vector<string>
 CalloutHandle::getArgumentNames() const {
 
     vector<string> names;
-    ArgumentCollection::const_iterator i;
-    for (i = arguments_.begin(); i != arguments_.end(); ++i) {
+    for (ElementCollection::const_iterator i = arguments_.begin();
+         i != arguments_.end(); ++i) {
+        names.push_back(i->first);
+    }
+
+    return (names);
+}
+
+// Return the "current" library handle.
+
+LibraryHandle&
+CalloutHandle::getLibraryHandle() const {
+    boost::shared_ptr<LibraryHandle> handle =
+        library_collection_->getLibraryHandle();
+
+    // Return refernce to this library handle.  This remains valid even
+    // after this method returns, because this object maintains a shared
+    // pointer to the LibraryHandleCollection, which in turn maintains
+    // a shared pointer to the LibraryHandle in question.
+
+    return (*handle);
+}
+
+// Check the current library index.
+
+int
+CalloutHandle::getLibraryIndex() const {
+    int curidx = library_collection_->getLibraryIndex();
+    if (curidx < 0) {
+        isc_throw(InvalidIndex, "current library handle index is not valid");
+    }
+
+    return (curidx);
+}
+
+// Return the context for the currently pointed-to library.  This version is
+// used by the "setContext()" method and creates a context for the current
+// library if it does not exist.
+
+CalloutHandle::ElementCollection&
+CalloutHandle::getContextForLibrary() {
+
+    int libindex = getLibraryIndex();
+
+    // Access a reference to the element collection for the given index,
+    // creating a new element collection if necessary, and return it.
+    return (context_collection_[libindex]);
+}
+
+// The "const" version of the above, used by the "getContext()" method.  If
+// the context for the current library doesn't exist, throw a
+// "NoSuchCalloutContext" exception.
+
+const CalloutHandle::ElementCollection&
+CalloutHandle::getContextForLibrary() const {
+
+    int libindex = getLibraryIndex();
+    ContextCollection::const_iterator libcontext =
+        context_collection_.find(libindex);
+    if (libcontext == context_collection_.end()) {
+        isc_throw(NoSuchCalloutContext, "unable to find callout context "
+                  "associated with the current library handle");
+    }
+
+    // Return a reference to the context's element collection.
+    return (libcontext->second);
+}
+
+// Return the name of all items in the context associated with the current]
+// library.
+
+vector<string>
+CalloutHandle::getContextNames() const {
+
+    vector<string> names;
+
+    const ElementCollection& elements = getContextForLibrary();
+    for (ElementCollection::const_iterator i = elements.begin();
+         i != elements.end(); ++i) {
         names.push_back(i->first);
     }
 

+ 200 - 14
src/lib/util/hooks/callout_handle.h

@@ -36,27 +36,82 @@ public:
         isc::Exception(file, line, what) {}
 };
 
-// Forward declaration of the handle collection class.
+/// @brief No such callout context item
+///
+/// Thrown if an attempt is made to get an item of data from this callout's
+/// context.
+class NoSuchCalloutContext : public Exception {
+public:
+    NoSuchCalloutContext(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+//
+
+// Forward declaration of the library handle and related collection classes.
+class LibraryHandle;
 class LibraryHandleCollection;
 
-/// @brief Per-Packet callout handle
+/// @brief Per-packet callout handle
 ///
 /// An object of this class is associated with every packet (or request)
 /// processed by the server.  It forms the principle means of passing 
 /// data between the server and the user-library callouts.
+///
+/// The class allows access to the following information:
+///
+/// - Arguments.  When the callouts associated with a hook are called, they
+///   are passed information by the server (and can return information to it)
+///   through name/value pairs.  Each of these pairs is an argument and the
+///   information is accessed through the {get,get}Argument() methods.
+///
+/// - Per-packet context.  Each packet has a context associated with it, this
+///   context being a per-library context.  In other words, As a packet passes
+///   through the callouts associated with a given library, the callouts can
+///   associate and retrieve information with the packet.  The per-library
+///   nature of the context means that the callouts within a given library can
+///   pass packet-specific information between one another, they cannot pass
+///   information to callous within another library.  Typically such context
+///   is created in the "context_create" callout and destroyed in the
+///   "context_destroy" callout.  The information is accessed through the
+///   {get,set}Context() methods.
+///
+/// - Per-library context.  Each library has a context associated with it that
+///   is independent of the packet.  All callouts in the library share the
+///   information in the context, regardless of the packet being processed.
+///   The context is typically createed in the library's "load()" method and
+///   destroyed in its "unload()" method.  The information is available by
+///   obtaining the handle to the library through this class's
+///   getLibraryHandle() method, then calling {get,set}Context() on the
+///   object returned.
 
 class CalloutHandle {
-private:
-    /// Typedef to allow abbreviation of iterator specification in methods
-    typedef std::map<std::string, boost::any> ArgumentCollection;
-
 public:
 
+    /// Typedef to allow abbreviation of iterator specification in methods.
+    /// The std::string is the argument name and the "boost::any" is the
+    /// corresponding value associated with it.
+    typedef std::map<std::string, boost::any> ElementCollection;
+
+    /// Typedef to allow abbreviations in specifications when accessing
+    /// context.  The ElementCollection is the name/value collection for
+    /// a particular context.  The "int" corresponds to the index of an
+    /// associated library handle - there is a 1:1 correspondence between
+    /// library handles and a name.value collection.
+    ///
+    /// The collection of contexts is stored in a map, as not every library
+    /// will require creating a context associated with each packet.  In
+    /// addition, the structure is more flexible in that the size does not
+    /// need to be set when the CalloutHandle is constructed.
+    typedef std::map<int, ElementCollection> ContextCollection;
+
+
     /// @brief Constructor
     ///
     /// @param manager Pointer to the collection of library handles.
     CalloutHandle(boost::shared_ptr<LibraryHandleCollection>& collection)
-        : arguments_(), collection_(collection), skip_(false)
+        : arguments_(), context_collection_(), library_collection_(collection),
+          skip_(false)
     {}
 
     /// @brief Set argument
@@ -85,7 +140,7 @@ public:
     ///        but the type of the element is not that expected
     template <typename T>
     void getArgument(const std::string& name, T& value) const {
-        ArgumentCollection::const_iterator element_ptr = arguments_.find(name);
+        ElementCollection::const_iterator element_ptr = arguments_.find(name);
         if (element_ptr == arguments_.end()) {
             isc_throw(NoSuchArgument, "unable to find argument with name " <<
                       name);
@@ -107,8 +162,8 @@ public:
     /// Deletes an argument of the given name.  If an argument of that name
     /// does not exist, the method is a no-op.
     ///
-    /// N.B. If the element is a raw pointer, the pointed-to data is
-    /// NOT deleted by this.
+    /// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
+    /// by this method.
     ///
     /// @param name Name of the element in the argument list to set.
     void deleteArgument(const std::string& name) {
@@ -118,6 +173,9 @@ public:
     /// @brief Delete all arguments
     ///
     /// Deletes all arguments associated with this context.
+    ///
+    /// N.B. If any elements are raw pointers, the pointed-to data is NOT
+    /// deleted by this method.
     void deleteAllArguments() {
         arguments_.clear();
     }
@@ -125,8 +183,8 @@ public:
     /// @brief Set skip flag
     ///
     /// Sets the "skip" variable in the callout handle.  This variable is
-    /// interrogated by the server to see if the remaining callouts should be
-    /// bypassed.
+    /// interrogated by the server to see if the remaining callouts associated
+    /// with the current hook should be bypassed.
     ///
     /// @param skip New value of the "skip" flag.
     void setSkip(bool skip) {
@@ -142,14 +200,142 @@ public:
         return (skip_);
     }
 
+    /// @brief Access current library handle
+    ///
+    /// Returns a reference to the current library handle.  This function is
+    /// only available when called by a callout (which in turn is called
+    /// through the "callCallouts" method), as it is only then that the current
+    /// library index is valid.  A callout would use this method to get to
+    /// the context associated with the library in which it resides.
+    ///
+    /// @return Reference to the current library handle
+    ///
+    /// @throw InvalidIndex thrown if this method is called outside of the
+    ///        "callCallouts() method. (Exception is so-named because the
+    ///        index used to access the library handle in the collection
+    ///        is not valid at that point.)
+    LibraryHandle& getLibraryHandle() const;
+
+    /// @brief Set context
+    ///
+    /// Sets an element in the context associated with the current library.  If
+    /// an element of the name is already present, it is replaced.
+    ///
+    /// @param name Name of the element in the context to set
+    /// @param value Value to set
+    template <typename T>
+    void setContext(const std::string& name, T value) {
+        getContextForLibrary()[name] = value;
+    }
+
+    /// @brief Get context
+    ///
+    /// Gets an element from the context associated with the current library.
+    ///
+    /// @param name Name of the element in the context to get.
+    /// @param value [out] Value to set.  The type of "value" is important:
+    ///        it must match the type of the value set.
+    ///
+    /// @throw NoSuchCalloutContext Thrown if no context element with the name
+    ///        "name" is present.
+    /// @throw boost::bad_any_cast Thrown if the context element is present,
+    ///        but the type of the element is not that expected
+    template <typename T>
+    void getContext(const std::string& name, T& value) const {
+        const ElementCollection& lib_context = getContextForLibrary();
+
+        ElementCollection::const_iterator element_ptr = lib_context.find(name);
+        if (element_ptr == lib_context.end()) {
+            isc_throw(NoSuchCalloutContext, "unable to find callout context "
+                      "item " << name << " in the context associated with "
+                      "current library handle");
+        }
+
+        value = boost::any_cast<T>(element_ptr->second);
+    }
+    
+    /// @brief Get context names
+    ///
+    /// Returns a vector holding the names of items in the context associated
+    /// with the current library.
+    ///
+    /// @return Vector of strings reflecting argument names
+    std::vector<std::string> getContextNames() const;
+
+    /// @brief Delete context element
+    ///
+    /// Deletes an item of the given name from the context associated with the
+    /// current library.  If an item  of that name does not exist, the method is
+    /// a no-op.
+    ///
+    /// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
+    /// by this.
+    ///
+    /// @param name Name of the element in the argument list to set.
+    void deleteContext(const std::string& name) {
+        static_cast<void>(getContextForLibrary().erase(name));
+    }
+
+    /// @brief Delete all context items
+    ///
+    /// Deletes all items from the context associated with the current library.
+    ///
+    /// N.B. If any elements are raw pointers, the pointed-to data is NOT
+    /// deleted by this.
+    void deleteAllContext() {
+        getContextForLibrary().clear();
+    }
+
 
 private:
+    /// @brief Check index
+    ///
+    /// Gets the current library index, throwing an exception if it is not set
+    /// or is invalid for the current library collection.
+    ///
+    /// @return Current library index, valid for this library collection.
+    int getLibraryIndex() const;
+
+    /// @brief Return reference to context for current library
+    ///
+    /// Called by all context-accessing functions, this checks if the current
+    /// library index is valid (throwing an exception if not).  If it is, it
+    /// returns a reference to the appropriate context, creating one if it does
+    /// not exist. (The last task allows the list of library handles to grow
+    /// dynamically - although only if handles are appended to the end of the
+    /// library handle collection.)
+    ///
+    /// @return Reference to the collection of name/value pairs associated
+    ///         with the current library.
+    ElementCollection& getContextForLibrary();
+
+    /// @brief Return reference to context for current library (const version)
+    ///
+    /// Called by all context-accessing functions, this checks if the current
+    /// library index is valid (throwing an exception if not).  If it is, it
+    /// returns a reference to the appropriate context, creating one if it does
+    /// not exist. (The last task allows the list of library handles to grow
+    /// dynamically - although only if handles are appended to the end of the
+    /// library handle collection.)
+    ///
+    /// @return Reference to the collection of name/value pairs associated
+    ///         with the current library.
+    ///
+    /// @throw NoSuchCalloutContext Thrown if there is no ElementCollection
+    ///        associated with the current library.
+    const ElementCollection& getContextForLibrary() const;
+
+    // Member variables
+
     /// Collection of arguments passed to the callouts
-    ArgumentCollection arguments_;
+    ElementCollection arguments_;
+
+    /// Context collection - there is one entry per library context.
+    ContextCollection context_collection_;
 
     /// Library handle collection, used to obtain the correct library handle
     /// during a call to a callout.
-    boost::shared_ptr<LibraryHandleCollection> collection_;
+    boost::shared_ptr<LibraryHandleCollection> library_collection_;
 
     /// "Skip" flag, indicating if the caller should bypass remaining callouts.
     bool skip_;

+ 13 - 0
src/lib/util/hooks/library_handle.cc

@@ -153,6 +153,19 @@ LibraryHandle::getContextNames() const {
 
 // LibraryHandleCollection methods.
 
+// Return pointer to the current library handle.
+
+boost::shared_ptr<LibraryHandle>
+LibraryHandleCollection::getLibraryHandle() const {
+    if ((curidx_ < 0) || (curidx_ >= handles_.size())) {
+        isc_throw(InvalidIndex, "current library handle index of (" <<
+                  curidx_ << ") is not valid for the library handle vector "
+                  "of size " << handles_.size());
+    }
+
+    return (handles_[curidx_]);
+}
+
 // Check if a callout is present on a hook in any of the libraries.
 
 bool

+ 27 - 6
src/lib/util/hooks/library_handle.h

@@ -47,6 +47,18 @@ public:
         isc::Exception(file, line, what) {}
 };
 
+/// @brief Invalid index
+///
+/// Thrown if an attempt is made to obtain a library handle but the current
+/// library handle index is invalid.  This will occur if the method
+/// LibraryHandleCollection::getLibraryHandle() is called outside of a callout.
+
+class InvalidIndex : public Exception {
+public:
+    InvalidIndex(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
 // Forward declaration for CalloutHandle
 class CalloutHandle;
 
@@ -97,6 +109,7 @@ public:
     }
 
     /// @brief Get context
+
     ///
     /// Sets an element in the library context.  If the name does not exist,
     /// a "NoSuchContext" exception is thrown.
@@ -113,7 +126,7 @@ public:
     void getContext(const std::string& name, T& value) const {
         ContextCollection::const_iterator element_ptr = context_.find(name);
         if (element_ptr == context_.end()) {
-            isc_throw(NoSuchContext, "unable to find library context datum " <<
+            isc_throw(NoSuchContext, "unable to find library context item " <<
                       name << " in library handle");
         }
 
@@ -187,7 +200,6 @@ public:
     /// @throw NoSuchHook Thrown if the hook name is unrecognised.
     void deregisterAll(const std::string& name);
 
-
     /// @brief Checks if callouts are present
     ///
     /// @param index Hook index for which callouts are checked.
@@ -285,6 +297,18 @@ public:
         handles_.push_back(handle);
     }
 
+    /// @brief Return current library index
+    ///
+    /// Returns the value of the "current library index".  Although a callout
+    /// callout can retrieve this information, it is of limited use: the
+    /// value is intended for use by the CalloutHandle object in order to
+    /// access the per-library context.
+    ///
+    /// @return Current library index value
+    int getLibraryIndex() const {
+        return (curidx_);
+    }
+
     /// @brief Get current library handle
     ///
     /// Returns a pointer to the current library handle.  This method can
@@ -294,10 +318,7 @@ public:
     ///
     /// @return Pointer to current library handle. This is the handle for
     ///         which a callout is being called.
-    boost::shared_ptr<LibraryHandle> getLibraryHandle() const {
-        return (boost::shared_ptr<LibraryHandle>());
-        /// @todo Return the appropriate handle
-    }
+    boost::shared_ptr<LibraryHandle> getLibraryHandle() const;
 
     /// @brief Checks if callouts are present
     ///

+ 1 - 0
src/lib/util/tests/Makefile.am

@@ -30,6 +30,7 @@ run_unittests_SOURCES += fd_share_tests.cc
 run_unittests_SOURCES += fd_tests.cc
 run_unittests_SOURCES += filename_unittest.cc
 run_unittests_SOURCES += hex_unittest.cc
+run_unittests_SOURCES += handles_unittest.cc
 run_unittests_SOURCES += io_utilities_unittest.cc
 run_unittests_SOURCES += library_handle_unittest.cc
 run_unittests_SOURCES += library_handle_collection_unittest.cc

+ 364 - 0
src/lib/util/tests/handles_unittest.cc

@@ -0,0 +1,364 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/hooks/callout_handle.h>
+#include <util/hooks/library_handle.h>
+#include <util/hooks/server_hooks.h>
+
+#include <boost/lexical_cast.hpp>
+#include <gtest/gtest.h>
+
+#include <string>
+
+/// @file
+/// CalloutHandle/LibraryCalloutHandle interaction tests
+///
+/// This file holds unit tests checking the interaction between the
+/// CalloutHandle and LibraryCalloutHandle[Collection] classes.  In particular,
+/// they check that:
+///
+/// - A CalloutHandle's context is shared between callouts from the same
+///   library, but is separate for each library.
+/// - The LibraryHandle retrieved by the CalloutHandle is the same for each
+///   callout in the library, but different for different libraries.
+///
+/// Some minor interactions between the two classes were checked in the unit
+/// tests for each class (mainly the use of the "skip" flag).
+
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+// This test checks the many-faced nature of the context for both the
+// CalloutContext and the LibraryContext.
+//
+// Three libraries are assumed, and each supplies four callouts.  All callouts
+// manipulate four context elements - two in the CalloutHandle and two in the
+// LibraryHandle, the elements being called "string" and "uint64_t" (which
+// describe the type of data manipulated).
+//
+// As with other tests, each callout shifts data to the left and inserts its
+// own data.  Data is a string of the form "nmwc", where "n" is the number of
+// the library, "m" is the callout number and "w" is an indication of what
+// is being altered (library context ["x"] or callout context ["c"]) and "y" is
+// the indication of what callout was passed as an argument ("x" or "b"). ("x"
+// is used instead of "l" to indicate that library context is being altered,
+// since in the results, these single characters will be mixed with digits and
+// "l" looks too much like "1".)  Hence we have:
+//
+// - "xa" if library context is being altered from a callout made with the
+//        first callout handle passed as argument.
+// - "xb" if library context is being altered from a callout made with the
+//        second callout handle passed as argument.
+// - "ca" if the first callout handle's context is being manipulated.
+// - "cb" if the second callout handle's context is being manipulated.
+//
+//
+// For simplicity, and to cut down the number of functions actually written,
+// the ollout indicator ("a" or "b") ) used in the in the CalloutHandle
+// functions is passed via a CalloutArgument.  The argument is named "string":
+// use of a name the same as that of one of the context elements serves as a
+// check that the argument name space and argument context space are separate.
+//
+// Finally, the fourth callout copies the data into variables accessible
+// to the test program.
+
+// Values to be inspected at the end of the test.
+
+// Values set in the LibraryHandle context.  There are three libraries, so
+// there are three sets of library context.  To avoid a static initialization
+// fiasco, encapsulate these in a function.
+
+std::string& resultLibrary(int i) {
+    static std::string result_library[3];
+    return (result_library[i]);
+}
+
+// Values set in the CalloutHandle context.  There are three libraries, so
+// there are three contexts for the callout, one for each library.
+
+std::string& resultCallout(int i) {
+    static std::string result_callout[3];
+    return (result_callout[i]);
+}
+
+// A simple function to zero the results.
+
+static void zero_results() {
+    for (int i = 0; i < 3; ++i) {
+        resultLibrary(i) = "";
+        resultCallout(i) = "";
+    }
+}
+
+
+// Library callouts.
+
+// Common code for setting the callout and library context values.
+
+int
+execute(CalloutHandle& callout_handle, int library_num, int callout_num) {
+
+    // Obtain the callout handle indicator.
+    string indicator;
+    callout_handle.getArgument("string", indicator);
+
+    // Create the basic data to be appended to the context value.
+    string data = boost::lexical_cast<string>(10 * library_num + callout_num);
+
+    // Get the library context data.  As the context will not exist on the
+    // first call, catch the exception and create it. (In real life, the context
+    // should have been created by the libraries' "load()" function.)
+    string string_value = "";
+    try {
+        callout_handle.getLibraryHandle().getContext("string", string_value);
+    } catch (const NoSuchContext&) {
+        string_value = "";
+    }
+
+    // Update the context value with the library/callout indication (and the
+    // suffix "l" to denote library) and set it.
+    string_value += (data + string("x") + indicator);
+    callout_handle.getLibraryHandle().setContext("string", string_value);
+
+    // Get the context data. As before, this will not exist for the first
+    // callout called. (In real life, the library should create it when the
+    // "context_create" hook gets called before any packet processing takes
+    // place.)
+    try {
+        callout_handle.getContext("string", string_value);
+    } catch (const NoSuchCalloutContext&) {
+        string_value = "";
+    }
+
+    // Update the value and set it.
+    string_value += (data + string("c") + indicator);
+    callout_handle.setContext("string", string_value);
+
+    return (0);
+}
+
+// The following functions are the actual callouts - ther name is of the
+// form "callout_<library number>_<callout number>"
+
+int
+callout_1_1(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 1, 1));
+}
+
+int
+callout_1_2(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 1, 2));
+}
+
+int
+callout_1_3(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 1, 3));
+}
+
+int
+callout_2_1(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 2, 1));
+}
+
+int
+callout_2_2(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 2, 2));
+}
+
+int
+callout_2_3(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 2, 3));
+}
+
+int
+callout_3_1(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 3, 1));
+}
+
+int
+callout_3_2(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 3, 2));
+}
+
+int
+callout_3_3(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 3, 3));
+}
+
+// Common callout code for the fourth hook (which makes the data available for
+// checking).  It copies the library and callout context data to the global
+// variables.
+
+int print_execute(CalloutHandle& callout_handle, int library_num) {
+
+    // Print per-library context values.
+    string result;
+    callout_handle.getLibraryHandle()
+                  .getContext("string", resultLibrary(library_num - 1));
+
+
+    // Print callout context.
+    callout_handle.getContext("string", resultCallout(library_num - 1));
+
+    return (0);
+}
+
+// These are the actual callouts.
+
+int
+print_1(CalloutHandle& callout_handle) {
+    return (print_execute(callout_handle, 1));
+}
+
+int
+print_2(CalloutHandle& callout_handle) {
+    return (print_execute(callout_handle, 2));
+}
+
+int
+print_3(CalloutHandle& callout_handle) {
+    return (print_execute(callout_handle, 3));
+}
+
+// The test begin here
+
+TEST(HandlesTest, ContextCheck) {
+    // Create the LibraryHandleCollection with a set of four callouts
+    // (the test does not use the ContextCreate and ContextDestroy callouts.)
+
+    boost::shared_ptr<ServerHooks> server_hooks(new ServerHooks());
+    const int one_index = server_hooks->registerHook("one");
+    const int two_index = server_hooks->registerHook("two");
+    const int three_index = server_hooks->registerHook("three");
+    const int four_index = server_hooks->registerHook("four");
+
+    // Create the library handle collection and the library handles.
+    boost::shared_ptr<LibraryHandleCollection>
+        collection(new LibraryHandleCollection());
+
+    boost::shared_ptr<LibraryHandle> handle(new LibraryHandle(server_hooks));
+
+    handle->registerCallout("one", callout_1_1);
+    handle->registerCallout("two", callout_1_2);
+    handle->registerCallout("three", callout_1_3);
+    handle->registerCallout("four", print_1);
+
+    collection->addLibraryHandle(handle);
+
+
+    handle.reset(new LibraryHandle(server_hooks));
+    handle->registerCallout("one", callout_2_1);
+    handle->registerCallout("two", callout_2_2);
+    handle->registerCallout("three", callout_2_3);
+    handle->registerCallout("four", print_2);
+    collection->addLibraryHandle(handle);
+
+    handle.reset(new LibraryHandle(server_hooks));
+    handle->registerCallout("one", callout_3_1);
+    handle->registerCallout("two", callout_3_2);
+    handle->registerCallout("three", callout_3_3);
+    handle->registerCallout("four", print_3);
+    collection->addLibraryHandle(handle);
+
+    // Create the callout handles and distinguish them by setting the "long"
+    // argument.
+    CalloutHandle callout_handle_a(collection);
+    callout_handle_a.setArgument("string", string("a"));
+
+    CalloutHandle callout_handle_b(collection);
+    callout_handle_b.setArgument("string", string("b"));
+
+    // Now call the callouts attached to the first three hooks.  Each hook is
+    // called twice (once for each callout handle) before the next hook is
+    // called.
+    collection->callCallouts(one_index, callout_handle_a);
+    collection->callCallouts(one_index, callout_handle_b);
+    collection->callCallouts(two_index, callout_handle_a);
+    collection->callCallouts(two_index, callout_handle_b);
+    collection->callCallouts(three_index, callout_handle_a);
+    collection->callCallouts(three_index, callout_handle_b);
+
+    // Get the results for each callout.  Explicitly zero the variables before
+    // getting the results so we are certain that the values are the results
+    // of the callouts.
+
+    zero_results();
+    collection->callCallouts(four_index, callout_handle_a);
+
+    // To explain the expected library context results:
+    //
+    // The first callCallouts() call above calls the callouts for hook "one"
+    // with callout handle "a".  This calls the callouts attached to hook "one"
+    // from library 1, then those attached to hook "one" from library 2, then
+    // from library 3.  The callout in library 1 appends "11xa" to the first
+    // library's context. The callout in library 2 appends "21xa" to that
+    // library's context.  Finally, the third library's context gets "31xa"
+    // appended to it.
+    //
+    // The next callCallouts() call repeats the calls to the callouts attached
+    // to hook "one", which result in "11xb", "21xb", "31xb" being appended to
+    // the context of libraries 1, 2, and 3 respectively.
+    //
+    // The process is then repeated for hooks "two" and "three", leading to
+    // the expected context values listed below.
+
+    EXPECT_EQ("11xa11xb12xa12xb13xa13xb", resultLibrary(0));
+    EXPECT_EQ("21xa21xb22xa22xb23xa23xb", resultLibrary(1));
+    EXPECT_EQ("31xa31xb32xa32xb33xa33xb", resultLibrary(2));
+
+    // To explain the expected callout context results.
+    //
+    // The callout handle maintains a separate context for each library.  When
+    // the first call to callCallouts() is made, "11ca" gets appended to
+    // the context for library 1 maintained by by the callout handle, "21ca"
+    // gets appended to the context maintained for library 2, and "31ca" to
+    // the context maintained for library 3.
+    //
+    // The next call to callCallouts() calls the same callouts but for a
+    // different callout handle.  It also maintains three contexts (one for
+    // each library) and they will get "11cb", "21cb", "31cb" appended to
+    // them.  These don't affect the contexts maintained  by callout handle a.
+    //
+    // The process is then repeated for hooks "two" and "three", which append
+    // "12ca", "22ca" and "32ca" for hook "two" and "31ca", "32ca" and "33ca"
+    // for hook "three".
+    //
+    // At this point, we have only called the "print" function for callout
+    // handle "a", so the following results are checking the context values
+    // maintained in that callout handle.
+
+    EXPECT_EQ("11ca12ca13ca", resultCallout(0));
+    EXPECT_EQ("21ca22ca23ca", resultCallout(1));
+    EXPECT_EQ("31ca32ca33ca", resultCallout(2));
+
+    // Repeat the checks for callout b.  The library handle context values
+    // should not change, but the context maintained by the callout handle
+    // should.
+
+    zero_results();
+    collection->callCallouts(four_index, callout_handle_b);
+
+    EXPECT_EQ("11xa11xb12xa12xb13xa13xb", resultLibrary(0));
+    EXPECT_EQ("21xa21xb22xa22xb23xa23xb", resultLibrary(1));
+    EXPECT_EQ("31xa31xb32xa32xb33xa33xb", resultLibrary(2));
+
+    EXPECT_EQ("11cb12cb13cb", resultCallout(0));
+    EXPECT_EQ("21cb22cb23cb", resultCallout(1));
+    EXPECT_EQ("31cb32cb33cb", resultCallout(2));
+}
+
+} // Anonymous namespace
+